/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MemMapSnapshot.h" #include "base/eintr_wrapper.h" #include "base/file_util.h" #include "mozilla/ResultExtensions.h" #include "mozilla/ScopeExit.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "nsIFile.h" #ifdef XP_UNIX # include #endif namespace mozilla { using loader::AutoMemMap; namespace ipc { Result MemMapSnapshot::Init(size_t aSize) { MOZ_ASSERT(!mInitialized); MOZ_TRY(Create(aSize)); mInitialized = true; return Ok(); } Result MemMapSnapshot::Finalize(AutoMemMap& aMem) { MOZ_ASSERT(mInitialized); MOZ_TRY(Freeze(aMem)); mInitialized = false; return Ok(); } #if defined(XP_WIN) Result MemMapSnapshot::Create(size_t aSize) { HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, DWORD(aSize), nullptr); if (!handle) { return Err(NS_ERROR_FAILURE); } mFile.emplace(handle); return mMem.initWithHandle(mFile.ref(), aSize, PR_PROT_READWRITE); } Result MemMapSnapshot::Freeze(AutoMemMap& aMem) { auto orig = mFile.ref().ClonePlatformHandle(); mFile.reset(); HANDLE handle; if (!::DuplicateHandle( GetCurrentProcess(), orig.release(), GetCurrentProcess(), &handle, GENERIC_READ | FILE_MAP_READ, false, DUPLICATE_CLOSE_SOURCE)) { return Err(NS_ERROR_FAILURE); } return aMem.initWithHandle(FileDescriptor(handle), mMem.size()); } #elif defined(XP_UNIX) Result MemMapSnapshot::Create(size_t aSize) { FilePath path; ScopedCloseFile fd(file_util::CreateAndOpenTemporaryShmemFile(&path)); if (!fd) { return Err(NS_ERROR_FAILURE); } if (HANDLE_EINTR(ftruncate(fileno(fd), aSize)) != 0) { return Err(NS_ERROR_FAILURE); } MOZ_TRY(mMem.init(FILEToFileDescriptor(fd), PR_PROT_READWRITE)); mPath.Assign(path.value().data(), path.value().length()); return Ok(); } Result MemMapSnapshot::Freeze(AutoMemMap& aMem) { // Delete the shm file after we're done here, whether we succeed or not. The // open file descriptor will keep it alive until all remaining references // are closed, at which point it will be automatically freed. auto cleanup = MakeScopeExit([&]() { PR_Delete(mPath.get()); }); // Make the shm file readonly. This doesn't make a difference in practice, // since we open and share a read-only file descriptor, and then delete the // file. But it doesn't hurt, either. chmod(mPath.get(), 0400); nsCOMPtr file; MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false, getter_AddRefs(file))); return aMem.init(file); } #else # error "Unsupported build configuration" #endif } // namespace ipc } // namespace mozilla