/* -*- 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 "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/PFileSystemParams.h" #include "GetDirectoryListingTask.h" #include "GetFileOrDirectoryTask.h" #include "GetFilesTask.h" #include "mozilla/dom/BlobImpl.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/FileSystemBase.h" #include "mozilla/dom/FileSystemSecurity.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/dom/OSFileSystem.h" #include "mozilla/Preferences.h" #include "mozilla/ScopeExit.h" #include "mozilla/Unused.h" #include "nsProxyRelease.h" using namespace mozilla::ipc; namespace mozilla::dom { FileSystemRequestParent::FileSystemRequestParent() : mDestroyed(false) { AssertIsOnBackgroundThread(); } FileSystemRequestParent::~FileSystemRequestParent() { AssertIsOnBackgroundThread(); } #define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \ case FileSystemParams::TFileSystem##name##Params: { \ const FileSystem##name##Params& p = aParams; \ mFileSystem = new OSFileSystemParent(p.filesystem()); \ MOZ_ASSERT(mFileSystem); \ mTask = name##TaskParent::Create(mFileSystem, p, this, rv); \ if (NS_WARN_IF(rv.Failed())) { \ rv.SuppressException(); \ return false; \ } \ break; \ } bool FileSystemRequestParent::Initialize(const FileSystemParams& aParams) { AssertIsOnBackgroundThread(); ErrorResult rv; switch (aParams.type()) { FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory) FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFiles) default: { MOZ_CRASH("not reached"); break; } } if (NS_WARN_IF(!mTask || !mFileSystem)) { // Should never reach here. return false; } return true; } namespace { class CheckPermissionRunnable final : public Runnable { public: CheckPermissionRunnable( already_AddRefed aParent, FileSystemRequestParent* aActor, FileSystemTaskParentBase* aTask, const nsAString& aPath) : Runnable("dom::CheckPermissionRunnable"), mContentHandle(aParent), mActor(aActor), mTask(aTask), mPath(aPath), mBackgroundEventTarget(GetCurrentSerialEventTarget()) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(mContentHandle); MOZ_ASSERT(mActor); MOZ_ASSERT(mTask); MOZ_ASSERT(mBackgroundEventTarget); } NS_IMETHOD Run() override { if (NS_IsMainThread()) { if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) { RefPtr fss = FileSystemSecurity::Get(); if (NS_WARN_IF(!fss || !fss->ContentProcessHasAccessTo( mContentHandle->ChildID(), mPath))) { AssertIsOnMainThread(); if (RefPtr contentParent = mContentHandle->GetContentParent()) { contentParent->KillHard("This path is not allowed."); } return NS_OK; } } return mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); } AssertIsOnBackgroundThread(); // It can happen that this actor has been destroyed in the meantime we were // on the main-thread. if (!mActor->Destroyed()) { mTask->Start(); } return NS_OK; } private: ~CheckPermissionRunnable() { NS_ProxyRelease("CheckPermissionRunnable::mActor", mBackgroundEventTarget, mActor.forget()); } RefPtr mContentHandle; RefPtr mActor; RefPtr mTask; const nsString mPath; nsCOMPtr mBackgroundEventTarget; }; } // namespace void FileSystemRequestParent::Start() { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(mFileSystem); MOZ_ASSERT(mTask); nsAutoString path; if (NS_WARN_IF(NS_FAILED(mTask->GetTargetPath(path)))) { (void)Send__delete__(this, FileSystemErrorResponse(NS_ERROR_DOM_SECURITY_ERR)); return; } RefPtr parent = BackgroundParent::GetContentParentHandle(Manager()); // If the ThreadsafeContentParentHandle is null we are dealing with a // same-process actor. if (!parent) { mTask->Start(); return; } RefPtr runnable = new CheckPermissionRunnable(parent.forget(), this, mTask, path); NS_DispatchToMainThread(runnable); } void FileSystemRequestParent::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnBackgroundThread(); MOZ_ASSERT(!mDestroyed); if (!mFileSystem) { return; } mFileSystem->Shutdown(); mFileSystem = nullptr; mTask = nullptr; mDestroyed = true; } } // namespace mozilla::dom