зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1344415 - Introduce a security check about which files can be opened by a content process, r=ehsan
Entries API gives access to the filesystem. With this patch we allow that only to patches, previously shared by a FilePicker or a DataTransfer event.
This commit is contained in:
Родитель
2a318e8c71
Коммит
a628be7b36
|
@ -42,6 +42,7 @@
|
|||
#include "mozilla/dom/DocumentFragment.h"
|
||||
#include "mozilla/dom/DOMTypes.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/HTMLMediaElement.h"
|
||||
#include "mozilla/dom/HTMLTemplateElement.h"
|
||||
|
@ -7885,6 +7886,19 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (aParent) {
|
||||
bool isDir = false;
|
||||
if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
|
||||
nsAutoString path;
|
||||
if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
|
||||
fss->GrantAccessToContentProcess(aParent->ChildID(), path);
|
||||
}
|
||||
}
|
||||
|
||||
blobImpl = new FileBlobImpl(file);
|
||||
|
||||
IgnoredErrorResult rv;
|
||||
|
|
|
@ -10,8 +10,12 @@
|
|||
#include "GetDirectoryListingTask.h"
|
||||
#include "GetFileOrDirectoryTask.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/FileSystemBase.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
|
@ -69,14 +73,104 @@ FileSystemRequestParent::Initialize(const FileSystemParams& aParams)
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class CheckPermissionRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
CheckPermissionRunnable(already_AddRefed<ContentParent> aParent,
|
||||
FileSystemRequestParent* aActor,
|
||||
FileSystemTaskParentBase* aTask,
|
||||
const nsAString& aPath)
|
||||
: mContentParent(aParent)
|
||||
, mActor(aActor)
|
||||
, mTask(aTask)
|
||||
, mPath(aPath)
|
||||
, mBackgroundEventTarget(NS_GetCurrentThread())
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
MOZ_ASSERT(mContentParent);
|
||||
MOZ_ASSERT(mActor);
|
||||
MOZ_ASSERT(mTask);
|
||||
MOZ_ASSERT(mBackgroundEventTarget);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
auto raii = mozilla::MakeScopeExit([&] { mContentParent = nullptr; });
|
||||
|
||||
|
||||
if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) {
|
||||
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
|
||||
if (NS_WARN_IF(!fss ||
|
||||
!fss->ContentProcessHasAccessTo(mContentParent->ChildID(),
|
||||
mPath))) {
|
||||
mContentParent->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(mBackgroundEventTarget, mActor.forget());
|
||||
}
|
||||
|
||||
RefPtr<ContentParent> mContentParent;
|
||||
RefPtr<FileSystemRequestParent> mActor;
|
||||
RefPtr<FileSystemTaskParentBase> mTask;
|
||||
const nsString mPath;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
|
||||
};
|
||||
|
||||
} // anonymous
|
||||
|
||||
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)))) {
|
||||
Unused << Send__delete__(this, FileSystemErrorResponse(NS_ERROR_DOM_SECURITY_ERR));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager());
|
||||
|
||||
// If the ContentParent is null we are dealing with a same-process actor.
|
||||
if (!parent) {
|
||||
mTask->Start();
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new CheckPermissionRunnable(parent.forget(), this, mTask, path);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- 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 "FileSystemSecurity.h"
|
||||
#include "FileSystemUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
StaticRefPtr<FileSystemSecurity> gFileSystemSecurity;
|
||||
|
||||
} // anonymous
|
||||
|
||||
/* static */ already_AddRefed<FileSystemSecurity>
|
||||
FileSystemSecurity::Get()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<FileSystemSecurity>
|
||||
FileSystemSecurity::GetOrCreate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
if (!gFileSystemSecurity) {
|
||||
gFileSystemSecurity = new FileSystemSecurity();
|
||||
ClearOnShutdown(&gFileSystemSecurity);
|
||||
}
|
||||
|
||||
RefPtr<FileSystemSecurity> service = gFileSystemSecurity.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
FileSystemSecurity::FileSystemSecurity()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
}
|
||||
|
||||
FileSystemSecurity::~FileSystemSecurity()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemSecurity::GrantAccessToContentProcess(ContentParentId aId,
|
||||
const nsAString& aDirectoryPath)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
nsTArray<nsString>* paths;
|
||||
if (!mPaths.Get(aId, &paths)) {
|
||||
paths = new nsTArray<nsString>();
|
||||
mPaths.Put(aId, paths);
|
||||
} else if (paths->Contains(aDirectoryPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
paths->AppendElement(aDirectoryPath);
|
||||
}
|
||||
|
||||
void
|
||||
FileSystemSecurity::Forget(ContentParentId aId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
mPaths.Remove(aId);
|
||||
}
|
||||
|
||||
bool
|
||||
FileSystemSecurity::ContentProcessHasAccessTo(ContentParentId aId,
|
||||
const nsAString& aPath)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
AssertIsInMainProcess();
|
||||
|
||||
nsTArray<nsString>* paths;
|
||||
if (!mPaths.Get(aId, &paths)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0, len = paths->Length(); i < len; ++i) {
|
||||
if (FileSystemUtils::IsDescendantPath(paths->ElementAt(i), aPath)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_FileSystemSecurity_h
|
||||
#define mozilla_dom_FileSystemSecurity_h
|
||||
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FileSystemSecurity final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(FileSystemSecurity)
|
||||
|
||||
static already_AddRefed<FileSystemSecurity>
|
||||
Get();
|
||||
|
||||
static already_AddRefed<FileSystemSecurity>
|
||||
GetOrCreate();
|
||||
|
||||
void
|
||||
GrantAccessToContentProcess(ContentParentId aId,
|
||||
const nsAString& aDirectoryPath);
|
||||
|
||||
void
|
||||
Forget(ContentParentId aId);
|
||||
|
||||
bool
|
||||
ContentProcessHasAccessTo(ContentParentId aId, const nsAString& aPath);
|
||||
|
||||
private:
|
||||
FileSystemSecurity();
|
||||
~FileSystemSecurity();
|
||||
|
||||
nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mPaths;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_FileSystemSecurity_h
|
|
@ -231,6 +231,9 @@ public:
|
|||
NS_IMETHOD
|
||||
Run() override;
|
||||
|
||||
virtual nsresult
|
||||
GetTargetPath(nsAString& aPath) const = 0;
|
||||
|
||||
private:
|
||||
/*
|
||||
* Wrap the task result to FileSystemResponseValue for sending it through IPC.
|
||||
|
|
|
@ -20,24 +20,12 @@ TokenizerIgnoreNothing(char16_t /* aChar */)
|
|||
} // anonymous namespace
|
||||
|
||||
/* static */ bool
|
||||
FileSystemUtils::IsDescendantPath(nsIFile* aFile,
|
||||
nsIFile* aDescendantFile)
|
||||
FileSystemUtils::IsDescendantPath(const nsAString& aPath,
|
||||
const nsAString& aDescendantPath)
|
||||
{
|
||||
nsAutoString path;
|
||||
nsresult rv = aFile->GetPath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString descendantPath;
|
||||
rv = aDescendantFile->GetPath(descendantPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the sub-directory path to see if it has the parent path as prefix.
|
||||
if (descendantPath.Length() <= path.Length() ||
|
||||
!StringBeginsWith(descendantPath, path)) {
|
||||
if (!aDescendantPath.Equals(aPath) &&
|
||||
!StringBeginsWith(aDescendantPath, aPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ public:
|
|||
* Return true if aDescendantPath is a descendant of aPath.
|
||||
*/
|
||||
static bool
|
||||
IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
|
||||
IsDescendantPath(const nsAString& aPath,
|
||||
const nsAString& aDescendantPath);
|
||||
|
||||
/**
|
||||
* Return true if this is valid DOMPath. It also splits the path in
|
||||
|
|
|
@ -380,5 +380,11 @@ GetDirectoryListingTaskParent::IOWork()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetDirectoryListingTaskParent::GetTargetPath(nsAString& aPath) const
|
||||
{
|
||||
return mTargetPath->GetPath(aPath);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -69,6 +69,9 @@ public:
|
|||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
GetTargetPath(nsAString& aPath) const override;
|
||||
|
||||
private:
|
||||
GetDirectoryListingTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemGetDirectoryListingParams& aParam,
|
||||
|
|
|
@ -270,5 +270,11 @@ GetFileOrDirectoryTaskParent::IOWork()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetFileOrDirectoryTaskParent::GetTargetPath(nsAString& aPath) const
|
||||
{
|
||||
return mTargetPath->GetPath(aPath);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -62,6 +62,9 @@ public:
|
|||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
GetTargetPath(nsAString& aPath) const override;
|
||||
|
||||
protected:
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
|
|
@ -253,5 +253,11 @@ GetFilesTaskParent::IOWork()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GetFilesTaskParent::GetTargetPath(nsAString& aPath) const
|
||||
{
|
||||
return mTargetPath->GetPath(aPath);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -71,6 +71,9 @@ public:
|
|||
FileSystemRequestParent* aParent,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
GetTargetPath(nsAString& aPath) const override;
|
||||
|
||||
private:
|
||||
GetFilesTaskParent(FileSystemBase* aFileSystem,
|
||||
const FileSystemGetFilesParams& aParam,
|
||||
|
|
|
@ -16,6 +16,7 @@ var script;
|
|||
|
||||
function setup_tests() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
|
||||
["dom.filesystem.pathcheck.disabled", true],
|
||||
["dom.webkitBlink.filesystem.enabled", true]]}, next);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ function setup_tests() {
|
|||
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
|
||||
["dom.webkitBlink.dirPicker.enabled", true],
|
||||
["dom.filesystem.pathcheck.disabled", true],
|
||||
["dom.webkitBlink.filesystem.enabled", true]]}, next);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ var entries;
|
|||
|
||||
function setup_tests() {
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true],
|
||||
["dom.filesystem.pathcheck.disabled", true],
|
||||
["dom.webkitBlink.filesystem.enabled", true]]}, next);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ EXPORTS.mozilla.dom += [
|
|||
'Directory.h',
|
||||
'FileSystemBase.h',
|
||||
'FileSystemRequestParent.h',
|
||||
'FileSystemSecurity.h',
|
||||
'FileSystemTaskBase.h',
|
||||
'FileSystemUtils.h',
|
||||
'GetFilesHelper.h',
|
||||
|
@ -25,6 +26,7 @@ UNIFIED_SOURCES += [
|
|||
'Directory.cpp',
|
||||
'FileSystemBase.cpp',
|
||||
'FileSystemRequestParent.cpp',
|
||||
'FileSystemSecurity.cpp',
|
||||
'FileSystemTaskBase.cpp',
|
||||
'FileSystemUtils.cpp',
|
||||
'GetDirectoryListingTask.cpp',
|
||||
|
|
|
@ -11,6 +11,7 @@ function createRelativePath(parentDir, dirOrFile) {
|
|||
function setup_tests(aNext) {
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
|
||||
["dom.filesystem.pathcheck.disabled", true],
|
||||
["dom.webkitBlink.dirPicker.enabled", true]]}, aNext);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileCreatorHelper.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/dom/ExternalHelperAppParent.h"
|
||||
#include "mozilla/dom/GetFilesHelper.h"
|
||||
#include "mozilla/dom/GeolocationBinding.h"
|
||||
|
@ -1680,6 +1681,11 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
|||
mHangMonitorActor = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
|
||||
if (fss) {
|
||||
fss->Forget(ChildID());
|
||||
}
|
||||
|
||||
if (why == NormalShutdown && !mCalledClose) {
|
||||
// If we shut down normally but haven't called Close, assume somebody
|
||||
// else called Close on us. In that case, we still need to call
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/FileBlobImpl.h"
|
||||
#include "mozilla/dom/FileSystemSecurity.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
|
@ -145,6 +146,8 @@ FilePickerParent::IORunnable::Destroy()
|
|||
void
|
||||
FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
|
||||
{
|
||||
nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
|
||||
|
||||
if (mMode == nsIFilePicker::modeGetFolder) {
|
||||
MOZ_ASSERT(aData.Length() <= 1);
|
||||
if (aData.IsEmpty()) {
|
||||
|
@ -154,13 +157,18 @@ FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData
|
|||
|
||||
MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
|
||||
|
||||
// Let's inform the security singleton about the given access of this tab on
|
||||
// this directory path.
|
||||
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
|
||||
fss->GrantAccessToContentProcess(parent->ChildID(),
|
||||
aData[0].mDirectoryPath);
|
||||
|
||||
InputDirectory input;
|
||||
input.directoryPath() = aData[0].mDirectoryPath;
|
||||
Unused << Send__delete__(this, input, mResult);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
|
||||
InfallibleTArray<PBlobParent*> blobs;
|
||||
|
||||
for (unsigned i = 0; i < aData.Length(); i++) {
|
||||
|
|
|
@ -787,11 +787,18 @@ BackgroundParentImpl::AllocPFileSystemRequestParent(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
result->Start();
|
||||
|
||||
return result.forget().take();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
BackgroundParentImpl::RecvPFileSystemRequestConstructor(
|
||||
PFileSystemRequestParent* aActor,
|
||||
const FileSystemParams& params)
|
||||
{
|
||||
static_cast<FileSystemRequestParent*>(aActor)->Start();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
bool
|
||||
BackgroundParentImpl::DeallocPFileSystemRequestParent(
|
||||
PFileSystemRequestParent* aDoomed)
|
||||
|
|
|
@ -193,6 +193,10 @@ protected:
|
|||
virtual PFileSystemRequestParent*
|
||||
AllocPFileSystemRequestParent(const FileSystemParams&) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvPFileSystemRequestConstructor(PFileSystemRequestParent* actor,
|
||||
const FileSystemParams& params) override;
|
||||
|
||||
virtual bool
|
||||
DeallocPFileSystemRequestParent(PFileSystemRequestParent*) override;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче