| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/file_system/file_system_manager_impl.h" |
| |
| #include <utility> |
| |
| #include "base/check_op.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/notreached.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/uuid.h" |
| #include "build/build_config.h" |
| #include "components/services/filesystem/public/mojom/types.mojom.h" |
| #include "content/browser/blob_storage/chrome_blob_storage_context.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/file_system/browser_file_system_helper.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver_set.h" |
| #include "storage/browser/blob/blob_data_builder.h" |
| #include "storage/browser/blob/blob_impl.h" |
| #include "storage/browser/blob/blob_storage_context.h" |
| #include "storage/browser/blob/shareable_file_reference.h" |
| #include "storage/browser/file_system/copy_or_move_hook_delegate.h" |
| #include "storage/browser/file_system/file_observers.h" |
| #include "storage/browser/file_system/file_permission_policy.h" |
| #include "storage/browser/file_system/file_system_backend.h" |
| #include "storage/browser/file_system/file_system_context.h" |
| #include "storage/browser/file_system/file_system_file_util.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| #include "storage/browser/file_system/isolated_context.h" |
| #include "storage/common/file_system/file_system_info.h" |
| #include "storage/common/file_system/file_system_types.h" |
| #include "storage/common/file_system/file_system_util.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| #include "third_party/blink/public/mojom/blob/blob.mojom.h" |
| #include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| using storage::BlobDataBuilder; |
| using storage::BlobStorageContext; |
| using storage::FileSystemBackend; |
| using storage::FileSystemFileUtil; |
| using storage::FileSystemOperation; |
| using storage::FileSystemURL; |
| |
| namespace content { |
| |
| namespace { |
| |
| void RevokeFilePermission(int child_id, const base::FilePath& path) { |
| ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( |
| child_id, path); |
| } |
| |
| storage::FileSystemType ToStorageFileSystemType( |
| blink::mojom::FileSystemType type) { |
| switch (type) { |
| case blink::mojom::FileSystemType::kTemporary: |
| return storage::FileSystemType::kFileSystemTypeTemporary; |
| case blink::mojom::FileSystemType::kPersistent: |
| return storage::FileSystemType::kFileSystemTypePersistent; |
| case blink::mojom::FileSystemType::kIsolated: |
| return storage::FileSystemType::kFileSystemTypeIsolated; |
| case blink::mojom::FileSystemType::kExternal: |
| return storage::FileSystemType::kFileSystemTypeExternal; |
| } |
| NOTREACHED(); |
| } |
| |
| blink::mojom::FileSystemType ToMojoFileSystemType( |
| storage::FileSystemType type) { |
| switch (type) { |
| case storage::FileSystemType::kFileSystemTypeTemporary: |
| return blink::mojom::FileSystemType::kTemporary; |
| case storage::FileSystemType::kFileSystemTypePersistent: |
| return blink::mojom::FileSystemType::kPersistent; |
| case storage::FileSystemType::kFileSystemTypeIsolated: |
| return blink::mojom::FileSystemType::kIsolated; |
| case storage::FileSystemType::kFileSystemTypeExternal: |
| return blink::mojom::FileSystemType::kExternal; |
| // Internal enum types |
| case storage::FileSystemType::kFileSystemTypeUnknown: |
| case storage::FileSystemType::kFileSystemInternalTypeEnumStart: |
| case storage::FileSystemType::kFileSystemTypeTest: |
| case storage::FileSystemType::kFileSystemTypeLocal: |
| case storage::FileSystemType::kFileSystemTypeDragged: |
| case storage::FileSystemType::kFileSystemTypeLocalMedia: |
| case storage::FileSystemType::kFileSystemTypeDeviceMedia: |
| case storage::FileSystemType::kFileSystemTypeSyncable: |
| case storage::FileSystemType::kFileSystemTypeSyncableForInternalSync: |
| case storage::FileSystemType::kFileSystemTypeLocalForPlatformApp: |
| case storage::FileSystemType::kFileSystemTypeForTransientFile: |
| case storage::FileSystemType::kFileSystemTypeProvided: |
| case storage::FileSystemType::kFileSystemTypeDeviceMediaAsFileStorage: |
| case storage::FileSystemType::kFileSystemTypeArcContent: |
| case storage::FileSystemType::kFileSystemTypeArcDocumentsProvider: |
| case storage::FileSystemType::kFileSystemTypeDriveFs: |
| case storage::FileSystemType::kFileSystemTypeSmbFs: |
| case storage::FileSystemType::kFileSystemTypeFuseBox: |
| case storage::FileSystemType::kFileSystemInternalTypeEnumEnd: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| } |
| |
| blink::mojom::FileSystemInfoPtr ToMojoFileSystemInfo( |
| const storage::FileSystemInfo& info) { |
| return blink::mojom::FileSystemInfo::New( |
| info.name, info.root_url, ToMojoFileSystemType(info.mount_type)); |
| } |
| |
| } // namespace |
| |
| class FileSystemManagerImpl::FileSystemCancellableOperationImpl |
| : public blink::mojom::FileSystemCancellableOperation { |
| using OperationID = storage::FileSystemOperationRunner::OperationID; |
| |
| public: |
| FileSystemCancellableOperationImpl( |
| OperationID id, |
| FileSystemManagerImpl* file_system_manager_impl) |
| : id_(id), file_system_manager_impl_(file_system_manager_impl) {} |
| ~FileSystemCancellableOperationImpl() override = default; |
| |
| private: |
| void Cancel(CancelCallback callback) override { |
| file_system_manager_impl_->Cancel(id_, std::move(callback)); |
| } |
| |
| const OperationID id_; |
| // |file_system_manager_impl| owns |this| through a UniqueReceiverSet. |
| const raw_ptr<FileSystemManagerImpl> file_system_manager_impl_; |
| }; |
| |
| class FileSystemManagerImpl::ReceivedSnapshotListenerImpl |
| : public blink::mojom::ReceivedSnapshotListener { |
| public: |
| ReceivedSnapshotListenerImpl(int snapshot_id, |
| FileSystemManagerImpl* file_system_manager_impl) |
| : snapshot_id_(snapshot_id), |
| file_system_manager_impl_(file_system_manager_impl) {} |
| ~ReceivedSnapshotListenerImpl() override = default; |
| |
| private: |
| void DidReceiveSnapshotFile() override { |
| file_system_manager_impl_->DidReceiveSnapshotFile(snapshot_id_); |
| } |
| |
| const int snapshot_id_; |
| // |file_system_manager_impl| owns |this| through a UniqueReceiverSet. |
| const raw_ptr<FileSystemManagerImpl> file_system_manager_impl_; |
| }; |
| |
| struct FileSystemManagerImpl::WriteSyncCallbackEntry { |
| WriteSyncCallback callback; |
| int64_t bytes; |
| |
| explicit WriteSyncCallbackEntry(WriteSyncCallback cb) |
| : callback(std::move(cb)), bytes(0) {} |
| }; |
| |
| struct FileSystemManagerImpl::ReadDirectorySyncCallbackEntry { |
| ReadDirectorySyncCallback callback; |
| std::vector<filesystem::mojom::DirectoryEntryPtr> entries; |
| |
| explicit ReadDirectorySyncCallbackEntry(ReadDirectorySyncCallback cb) |
| : callback(std::move(cb)) {} |
| }; |
| |
| FileSystemManagerImpl::FileSystemManagerImpl( |
| int process_id, |
| scoped_refptr<storage::FileSystemContext> file_system_context, |
| scoped_refptr<ChromeBlobStorageContext> blob_storage_context) |
| : process_id_(process_id), |
| context_(std::move(file_system_context)), |
| blob_storage_context_(std::move(blob_storage_context)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(context_); |
| DCHECK(blob_storage_context_); |
| receivers_.set_disconnect_handler(base::BindRepeating( |
| &FileSystemManagerImpl::OnConnectionError, base::Unretained(this))); |
| } |
| |
| FileSystemManagerImpl::~FileSystemManagerImpl() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| base::WeakPtr<FileSystemManagerImpl> FileSystemManagerImpl::GetWeakPtr() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| void FileSystemManagerImpl::BindReceiver( |
| const blink::StorageKey& storage_key, |
| mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!operation_runner_) |
| operation_runner_ = context_->CreateFileSystemOperationRunner(); |
| receivers_.Add(this, std::move(receiver), storage_key); |
| } |
| |
| void FileSystemManagerImpl::Open(const url::Origin& origin, |
| blink::mojom::FileSystemType file_system_type, |
| OpenCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanAccessDataForOrigin, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, origin), |
| base::BindOnce(&FileSystemManagerImpl::ContinueOpen, |
| weak_factory_.GetWeakPtr(), origin, file_system_type, |
| receivers_.GetBadMessageCallback(), std::move(callback), |
| receivers_.current_context())); |
| } |
| |
| void FileSystemManagerImpl::ContinueOpen( |
| const url::Origin& origin, |
| blink::mojom::FileSystemType file_system_type, |
| mojo::ReportBadMessageCallback bad_message_callback, |
| OpenCallback callback, |
| const blink::StorageKey& storage_key, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!security_check_success) { |
| std::move(bad_message_callback).Run("FSMI_OPEN_INVALID_ORIGIN"); |
| return; |
| } |
| |
| context_->OpenFileSystem( |
| storage_key, /*bucket=*/std::nullopt, |
| ToStorageFileSystemType(file_system_type), |
| storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, |
| base::BindOnce(&FileSystemManagerImpl::DidOpenFileSystem, GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ResolveURL(const GURL& filesystem_url, |
| ResolveURLCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url( |
| context_->CrackURL(filesystem_url, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(blink::mojom::FileSystemInfo::New(), |
| base::FilePath(), false, opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueResolveURL, |
| weak_factory_.GetWeakPtr(), url, std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueResolveURL( |
| const storage::FileSystemURL& url, |
| ResolveURLCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(blink::mojom::FileSystemInfo::New(), |
| base::FilePath(), false, |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| context_->ResolveURL( |
| url, base::BindOnce(&FileSystemManagerImpl::DidResolveURL, GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::Move(const GURL& src_path, |
| const GURL& dest_path, |
| MoveCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL src_url( |
| context_->CrackURL(src_path, receivers_.current_context())); |
| FileSystemURL dest_url( |
| context_->CrackURL(dest_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(src_url); |
| if (!opt_error) |
| opt_error = ValidateFileSystemURL(dest_url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanMoveFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, src_url, dest_url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueMove, |
| weak_factory_.GetWeakPtr(), src_url, dest_url, |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueMove(const storage::FileSystemURL& src_url, |
| const storage::FileSystemURL& dest_url, |
| MoveCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->Move(src_url, dest_url, |
| storage::FileSystemOperation::CopyOrMoveOptionSet(), |
| FileSystemOperation::ERROR_BEHAVIOR_ABORT, |
| std::make_unique<storage::CopyOrMoveHookDelegate>(), |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::Copy(const GURL& src_path, |
| const GURL& dest_path, |
| CopyCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL src_url( |
| context_->CrackURL(src_path, receivers_.current_context())); |
| FileSystemURL dest_url( |
| context_->CrackURL(dest_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(src_url); |
| if (!opt_error) |
| opt_error = ValidateFileSystemURL(dest_url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanCopyFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, src_url, dest_url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueCopy, |
| weak_factory_.GetWeakPtr(), src_url, dest_url, |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueCopy(const storage::FileSystemURL& src_url, |
| const storage::FileSystemURL& dest_url, |
| CopyCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->Copy(src_url, dest_url, |
| storage::FileSystemOperation::CopyOrMoveOptionSet(), |
| FileSystemOperation::ERROR_BEHAVIOR_ABORT, |
| std::make_unique<storage::CopyOrMoveHookDelegate>(), |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::Remove(const GURL& path, |
| bool recursive, |
| RemoveCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanDeleteFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueRemove, |
| weak_factory_.GetWeakPtr(), url, recursive, |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueRemove(const storage::FileSystemURL& url, |
| bool recursive, |
| RemoveCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->Remove(url, recursive, |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ReadMetadata(const GURL& path, |
| ReadMetadataCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(base::File::Info(), opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueReadMetadata, |
| weak_factory_.GetWeakPtr(), url, std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueReadMetadata( |
| const storage::FileSystemURL& url, |
| ReadMetadataCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::Info(), |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->GetMetadata( |
| url, |
| {storage::FileSystemOperation::GetMetadataField::kIsDirectory, |
| storage::FileSystemOperation::GetMetadataField::kSize, |
| storage::FileSystemOperation::GetMetadataField::kLastModified}, |
| base::BindOnce(&FileSystemManagerImpl::DidGetMetadata, GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::Create(const GURL& path, |
| bool exclusive, |
| bool is_directory, |
| bool recursive, |
| CreateCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanCreateFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueCreate, |
| weak_factory_.GetWeakPtr(), url, exclusive, is_directory, |
| recursive, std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueCreate(const storage::FileSystemURL& url, |
| bool exclusive, |
| bool is_directory, |
| bool recursive, |
| CreateCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| if (is_directory) { |
| fs_op_runner->CreateDirectory( |
| url, exclusive, recursive, |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, GetWeakPtr(), |
| std::move(callback))); |
| } else { |
| fs_op_runner->CreateFile(url, exclusive, |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| } |
| } |
| |
| void FileSystemManagerImpl::Exists(const GURL& path, |
| bool is_directory, |
| ExistsCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueExists, |
| weak_factory_.GetWeakPtr(), url, is_directory, |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueExists(const storage::FileSystemURL& url, |
| bool is_directory, |
| ExistsCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| if (is_directory) { |
| fs_op_runner->DirectoryExists( |
| url, base::BindOnce(&FileSystemManagerImpl::DidFinish, GetWeakPtr(), |
| std::move(callback))); |
| } else { |
| fs_op_runner->FileExists( |
| url, base::BindOnce(&FileSystemManagerImpl::DidFinish, GetWeakPtr(), |
| std::move(callback))); |
| } |
| } |
| |
| void FileSystemManagerImpl::ReadDirectory( |
| const GURL& path, |
| mojo::PendingRemote<blink::mojom::FileSystemOperationListener> |
| pending_listener) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| mojo::Remote<blink::mojom::FileSystemOperationListener> listener( |
| std::move(pending_listener)); |
| if (opt_error) { |
| listener->ErrorOccurred(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueReadDirectory, |
| weak_factory_.GetWeakPtr(), url, std::move(listener))); |
| } |
| |
| void FileSystemManagerImpl::ContinueReadDirectory( |
| const storage::FileSystemURL& url, |
| mojo::Remote<blink::mojom::FileSystemOperationListener> listener, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!security_check_success) { |
| listener->ErrorOccurred(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| OperationListenerID listener_id = AddOpListener(std::move(listener)); |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->ReadDirectory( |
| url, base::BindRepeating(&FileSystemManagerImpl::DidReadDirectory, |
| GetWeakPtr(), listener_id)); |
| } |
| |
| void FileSystemManagerImpl::ReadDirectorySync( |
| const GURL& path, |
| ReadDirectorySyncCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url(context_->CrackURL(path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(std::vector<filesystem::mojom::DirectoryEntryPtr>(), |
| opt_error.value()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueReadDirectorySync, |
| weak_factory_.GetWeakPtr(), url, std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueReadDirectorySync( |
| const storage::FileSystemURL& url, |
| ReadDirectorySyncCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(std::vector<filesystem::mojom::DirectoryEntryPtr>(), |
| base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->ReadDirectory( |
| url, base::BindRepeating( |
| &FileSystemManagerImpl::DidReadDirectorySync, GetWeakPtr(), |
| base::Owned( |
| new ReadDirectorySyncCallbackEntry(std::move(callback))))); |
| } |
| |
| void FileSystemManagerImpl::Write( |
| const GURL& file_path, |
| mojo::PendingRemote<blink::mojom::Blob> blob, |
| int64_t position, |
| mojo::PendingReceiver<blink::mojom::FileSystemCancellableOperation> |
| op_receiver, |
| mojo::PendingRemote<blink::mojom::FileSystemOperationListener> |
| pending_listener) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url( |
| context_->CrackURL(file_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| mojo::Remote<blink::mojom::FileSystemOperationListener> listener( |
| std::move(pending_listener)); |
| if (opt_error) { |
| listener->ErrorOccurred(opt_error.value()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce( |
| &FileSystemManagerImpl::ResolveBlobForWrite, |
| weak_factory_.GetWeakPtr(), std::move(blob), |
| base::BindOnce(&FileSystemManagerImpl::ContinueWrite, |
| weak_factory_.GetWeakPtr(), url, position, |
| std::move(op_receiver), std::move(listener)))); |
| } |
| |
| void FileSystemManagerImpl::ResolveBlobForWrite( |
| mojo::PendingRemote<blink::mojom::Blob> blob, |
| base::OnceCallback<void(std::unique_ptr<storage::BlobDataHandle>)> callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(nullptr); |
| return; |
| } |
| |
| blob_storage_context_->context()->GetBlobDataFromBlobRemote( |
| std::move(blob), std::move(callback)); |
| } |
| |
| void FileSystemManagerImpl::ContinueWrite( |
| const storage::FileSystemURL& url, |
| int64_t position, |
| mojo::PendingReceiver<blink::mojom::FileSystemCancellableOperation> |
| op_receiver, |
| mojo::Remote<blink::mojom::FileSystemOperationListener> listener, |
| std::unique_ptr<storage::BlobDataHandle> blob) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!blob) { |
| listener->ErrorOccurred(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| OperationListenerID listener_id = AddOpListener(std::move(listener)); |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| OperationID op_id = |
| fs_op_runner->Write(url, std::move(blob), position, |
| base::BindRepeating(&FileSystemManagerImpl::DidWrite, |
| GetWeakPtr(), listener_id)); |
| cancellable_operations_.Add( |
| std::make_unique<FileSystemCancellableOperationImpl>(op_id, this), |
| std::move(op_receiver)); |
| } |
| |
| void FileSystemManagerImpl::WriteSync( |
| const GURL& file_path, |
| mojo::PendingRemote<blink::mojom::Blob> blob, |
| int64_t position, |
| WriteSyncCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url( |
| context_->CrackURL(file_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(0, opt_error.value()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ResolveBlobForWrite, |
| weak_factory_.GetWeakPtr(), std::move(blob), |
| base::BindOnce(&FileSystemManagerImpl::ContinueWriteSync, |
| weak_factory_.GetWeakPtr(), url, position, |
| std::move(callback)))); |
| } |
| |
| void FileSystemManagerImpl::ContinueWriteSync( |
| const storage::FileSystemURL& url, |
| int64_t position, |
| WriteSyncCallback callback, |
| std::unique_ptr<storage::BlobDataHandle> blob) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!blob) { |
| std::move(callback).Run(0, base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->Write( |
| url, std::move(blob), position, |
| base::BindRepeating( |
| &FileSystemManagerImpl::DidWriteSync, GetWeakPtr(), |
| base::Owned(new WriteSyncCallbackEntry(std::move(callback))))); |
| } |
| |
| void FileSystemManagerImpl::Truncate( |
| const GURL& file_path, |
| int64_t length, |
| mojo::PendingReceiver<blink::mojom::FileSystemCancellableOperation> |
| op_receiver, |
| TruncateCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url( |
| context_->CrackURL(file_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueTruncate, |
| weak_factory_.GetWeakPtr(), url, length, |
| std::move(op_receiver), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueTruncate( |
| const storage::FileSystemURL& url, |
| int64_t length, |
| mojo::PendingReceiver<blink::mojom::FileSystemCancellableOperation> |
| op_receiver, |
| TruncateCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| OperationID op_id = |
| fs_op_runner->Truncate(url, length, |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| cancellable_operations_.Add( |
| std::make_unique<FileSystemCancellableOperationImpl>(op_id, this), |
| std::move(op_receiver)); |
| } |
| |
| void FileSystemManagerImpl::TruncateSync(const GURL& file_path, |
| int64_t length, |
| TruncateSyncCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| FileSystemURL url( |
| context_->CrackURL(file_path, receivers_.current_context())); |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(opt_error.value()); |
| return; |
| } |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanWriteFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueTruncateSync, |
| weak_factory_.GetWeakPtr(), url, length, |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueTruncateSync( |
| const storage::FileSystemURL& url, |
| int64_t length, |
| TruncateSyncCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::FILE_ERROR_SECURITY); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| fs_op_runner->Truncate(url, length, |
| base::BindOnce(&FileSystemManagerImpl::DidFinish, |
| GetWeakPtr(), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::CreateSnapshotFile( |
| const GURL& file_path, |
| CreateSnapshotFileCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| FileSystemURL url( |
| context_->CrackURL(file_path, receivers_.current_context())); |
| |
| // Make sure if this file can be read by the renderer as this is |
| // called when the renderer is about to create a new File object |
| // (for reading the file). |
| std::optional<base::File::Error> opt_error = ValidateFileSystemURL(url); |
| if (opt_error) { |
| std::move(callback).Run(base::File::Info(), base::FilePath(), |
| opt_error.value(), mojo::NullRemote()); |
| return; |
| } |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueCreateSnapshotFile, |
| weak_factory_.GetWeakPtr(), url, std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::ContinueCreateSnapshotFile( |
| const storage::FileSystemURL& url, |
| CreateSnapshotFileCallback callback, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| if (!security_check_success) { |
| std::move(callback).Run(base::File::Info(), base::FilePath(), |
| base::File::FILE_ERROR_SECURITY, |
| mojo::NullRemote()); |
| return; |
| } |
| |
| storage::FileSystemOperationRunner* fs_op_runner = operation_runner(); |
| if (!fs_op_runner) { |
| /* A null FileSystemOperationRunner at this point means the corresponding |
| * renderer was terminated, so return early to ignore the requested |
| * FileSystemOperation. */ |
| return; |
| } |
| |
| FileSystemBackend* backend = context_->GetFileSystemBackend(url.type()); |
| if (backend->SupportsStreaming(url)) { |
| fs_op_runner->GetMetadata( |
| url, |
| {storage::FileSystemOperation::GetMetadataField::kIsDirectory, |
| storage::FileSystemOperation::GetMetadataField::kSize, |
| storage::FileSystemOperation::GetMetadataField::kLastModified}, |
| base::BindOnce(&FileSystemManagerImpl::DidGetMetadataForStreaming, |
| GetWeakPtr(), std::move(callback))); |
| } else { |
| fs_op_runner->CreateSnapshotFile( |
| url, base::BindOnce(&FileSystemManagerImpl::DidCreateSnapshot, |
| GetWeakPtr(), std::move(callback), url)); |
| } |
| } |
| |
| void FileSystemManagerImpl::GetPlatformPath(const GURL& path, |
| GetPlatformPathCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| base::FilePath platform_path; |
| context_->default_file_task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FileSystemManagerImpl::GetPlatformPathOnFileThread, path, |
| process_id_, context_, GetWeakPtr(), |
| receivers_.current_context(), std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::RegisterBlob( |
| const std::string& content_type, |
| const GURL& url, |
| uint64_t length, |
| std::optional<base::Time> expected_modification_time, |
| RegisterBlobCallback callback) { |
| storage::FileSystemURL crack_url = |
| context_->CrackURL(url, receivers_.current_context()); |
| |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| &ChildProcessSecurityPolicyImpl::CanReadFileSystemFile, |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, crack_url), |
| base::BindOnce(&FileSystemManagerImpl::ContinueRegisterBlob, |
| weak_factory_.GetWeakPtr(), content_type, url, length, |
| expected_modification_time, std::move(callback), |
| crack_url)); |
| } |
| |
| void FileSystemManagerImpl::ContinueRegisterBlob( |
| const std::string& content_type, |
| const GURL& url, |
| uint64_t length, |
| std::optional<base::Time> expected_modification_time, |
| RegisterBlobCallback callback, |
| storage::FileSystemURL crack_url, |
| bool security_check_success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::string uuid = base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| mojo::PendingRemote<blink::mojom::Blob> blob_remote; |
| mojo::PendingReceiver<blink::mojom::Blob> blob_receiver = |
| blob_remote.InitWithNewPipeAndPassReceiver(); |
| |
| if (crack_url.is_valid() && |
| context_->GetFileSystemBackend(crack_url.type()) && |
| security_check_success) { |
| blob_storage_context_->CreateFileSystemBlob( |
| context_, std::move(blob_receiver), crack_url, uuid, content_type, |
| length, expected_modification_time.value_or(base::Time())); |
| } else { |
| std::unique_ptr<storage::BlobDataHandle> handle = |
| blob_storage_context_->context()->AddBrokenBlob( |
| uuid, content_type, "", |
| storage::BlobStatus::ERR_REFERENCED_FILE_UNAVAILABLE); |
| storage::BlobImpl::Create(std::move(handle), std::move(blob_receiver)); |
| } |
| |
| std::move(callback).Run(blink::mojom::SerializedBlob::New( |
| uuid, content_type, length, std::move(blob_remote))); |
| } |
| |
| void FileSystemManagerImpl::Cancel( |
| OperationID op_id, |
| FileSystemCancellableOperationImpl::CancelCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| operation_runner()->Cancel( |
| op_id, base::BindOnce(&FileSystemManagerImpl::DidFinish, GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void FileSystemManagerImpl::DidReceiveSnapshotFile(int snapshot_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| in_transit_snapshot_files_.Remove(snapshot_id); |
| } |
| |
| void FileSystemManagerImpl::OnConnectionError() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (receivers_.empty()) { |
| in_transit_snapshot_files_.Clear(); |
| operation_runner_.reset(); |
| cancellable_operations_.Clear(); |
| } |
| } |
| |
| void FileSystemManagerImpl::DidFinish( |
| base::OnceCallback<void(base::File::Error)> callback, |
| base::File::Error error_code) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::move(callback).Run(error_code); |
| } |
| |
| void FileSystemManagerImpl::DidGetMetadata(ReadMetadataCallback callback, |
| base::File::Error result, |
| const base::File::Info& info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::move(callback).Run(info, result); |
| } |
| |
| void FileSystemManagerImpl::DidGetMetadataForStreaming( |
| CreateSnapshotFileCallback callback, |
| base::File::Error result, |
| const base::File::Info& info) { |
| // For now, streaming Blobs are implemented as a successful snapshot file |
| // creation with an empty path. |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::move(callback).Run(info, base::FilePath(), result, mojo::NullRemote()); |
| } |
| |
| void FileSystemManagerImpl::DidReadDirectory( |
| OperationListenerID listener_id, |
| base::File::Error result, |
| std::vector<filesystem::mojom::DirectoryEntry> entries, |
| bool has_more) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| blink::mojom::FileSystemOperationListener* listener = |
| GetOpListener(listener_id); |
| if (!listener) |
| return; |
| if (result != base::File::FILE_OK) { |
| DCHECK(!has_more); |
| listener->ErrorOccurred(result); |
| RemoveOpListener(listener_id); |
| return; |
| } |
| std::vector<filesystem::mojom::DirectoryEntryPtr> entry_struct_ptrs; |
| for (const auto& entry : entries) { |
| entry_struct_ptrs.emplace_back( |
| filesystem::mojom::DirectoryEntry::New(entry)); |
| } |
| listener->ResultsRetrieved(std::move(entry_struct_ptrs), has_more); |
| if (!has_more) |
| RemoveOpListener(listener_id); |
| } |
| |
| void FileSystemManagerImpl::DidReadDirectorySync( |
| ReadDirectorySyncCallbackEntry* callback_entry, |
| base::File::Error result, |
| std::vector<filesystem::mojom::DirectoryEntry> entries, |
| bool has_more) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| for (const auto& entry : entries) { |
| callback_entry->entries.emplace_back( |
| filesystem::mojom::DirectoryEntry::New(std::move(entry))); |
| } |
| if (result != base::File::FILE_OK || !has_more) { |
| std::move(callback_entry->callback) |
| .Run(std::move(callback_entry->entries), result); |
| } |
| } |
| |
| void FileSystemManagerImpl::DidWrite(OperationListenerID listener_id, |
| base::File::Error result, |
| int64_t bytes, |
| bool complete) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| blink::mojom::FileSystemOperationListener* listener = |
| GetOpListener(listener_id); |
| if (!listener) |
| return; |
| if (result == base::File::FILE_OK) { |
| listener->DidWrite(bytes, complete); |
| if (complete) |
| RemoveOpListener(listener_id); |
| } else { |
| listener->ErrorOccurred(result); |
| RemoveOpListener(listener_id); |
| } |
| } |
| |
| void FileSystemManagerImpl::DidWriteSync(WriteSyncCallbackEntry* entry, |
| base::File::Error result, |
| int64_t bytes, |
| bool complete) { |
| entry->bytes += bytes; |
| if (complete || result != base::File::FILE_OK) |
| std::move(entry->callback).Run(entry->bytes, result); |
| } |
| |
| void FileSystemManagerImpl::DidOpenFileSystem( |
| OpenCallback callback, |
| const FileSystemURL& root, |
| const std::string& filesystem_name, |
| base::File::Error result) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(root.is_valid() || result != base::File::FILE_OK); |
| std::move(callback).Run(filesystem_name, root.ToGURL(), result); |
| // For OpenFileSystem we do not create a new operation, so no unregister here. |
| } |
| |
| void FileSystemManagerImpl::DidResolveURL( |
| ResolveURLCallback callback, |
| base::File::Error result, |
| const storage::FileSystemInfo& info, |
| const base::FilePath& file_path, |
| storage::FileSystemContext::ResolvedEntryType type) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (result == base::File::FILE_OK && |
| type == storage::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND) |
| result = base::File::FILE_ERROR_NOT_FOUND; |
| |
| base::FilePath normalized_path( |
| storage::VirtualPath::GetNormalizedFilePath(file_path)); |
| std::move(callback).Run( |
| ToMojoFileSystemInfo(info), std::move(normalized_path), |
| type == storage::FileSystemContext::RESOLVED_ENTRY_DIRECTORY, result); |
| // For ResolveURL we do not create a new operation, so no unregister here. |
| } |
| |
| void FileSystemManagerImpl::DidCreateSnapshot( |
| CreateSnapshotFileCallback callback, |
| const storage::FileSystemURL& url, |
| base::File::Error result, |
| const base::File::Info& info, |
| const base::FilePath& platform_path, |
| scoped_refptr<storage::ShareableFileReference> /* unused */) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (result != base::File::FILE_OK) { |
| std::move(callback).Run(base::File::Info(), base::FilePath(), result, |
| mojo::NullRemote()); |
| return; |
| } |
| |
| // Post a task to use ChildProcessSecurityPolicy to check and grant file read |
| // permission on the UI thread, since access to these functions on the IO |
| // thread should be avoided. |
| GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( |
| FROM_HERE, |
| base::BindOnce( |
| [](ChildProcessSecurityPolicyImpl* security_policy, int process_id, |
| const base::FilePath& platform_path) { |
| bool can_read_file = |
| security_policy->CanReadFile(process_id, platform_path); |
| if (!can_read_file) { |
| // Give per-file read permission to the snapshot file if it hasn't |
| // it yet. In order for the renderer to be able to read the file |
| // via File object, it must be granted per-file read permission |
| // for the file's platform path. By now, it has already been |
| // verified that the renderer has sufficient permissions to read |
| // the file, so giving per-file permission here must be safe. |
| security_policy->GrantReadFile(process_id, platform_path); |
| } |
| return can_read_file; |
| }, |
| // ChildProcessSecurityPolicyImpl::GetInstance() is a singleton so |
| // refcounting is unnecessary. |
| base::Unretained(ChildProcessSecurityPolicyImpl::GetInstance()), |
| process_id_, platform_path), |
| base::BindOnce(&FileSystemManagerImpl::ContinueDidCreateSnapshot, |
| weak_factory_.GetWeakPtr(), std::move(callback), url, |
| result, info, platform_path)); |
| } |
| |
| void FileSystemManagerImpl::ContinueDidCreateSnapshot( |
| CreateSnapshotFileCallback callback, |
| const storage::FileSystemURL& url, |
| base::File::Error result, |
| const base::File::Info& info, |
| const base::FilePath& platform_path, |
| bool security_check_success) { |
| scoped_refptr<storage::ShareableFileReference> file_ref = |
| storage::ShareableFileReference::Get(platform_path); |
| |
| if (!security_check_success) { |
| // Revoke all permissions for the file when the last ref of the file |
| // is dropped. |
| if (!file_ref.get()) { |
| // Create a reference for temporary permission handling. |
| file_ref = storage::ShareableFileReference::GetOrCreate( |
| platform_path, |
| storage::ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE, |
| context_->default_file_task_runner()); |
| } |
| file_ref->AddFinalReleaseCallback( |
| base::BindOnce(&RevokeFilePermission, process_id_)); |
| } |
| |
| if (file_ref.get()) { |
| // This ref is held until DidReceiveSnapshotFile is called. |
| int request_id = in_transit_snapshot_files_.Add(file_ref); |
| mojo::PendingRemote<blink::mojom::ReceivedSnapshotListener> listener; |
| snapshot_listeners_.Add( |
| std::make_unique<ReceivedSnapshotListenerImpl>(request_id, this), |
| listener.InitWithNewPipeAndPassReceiver()); |
| // Return the file info and platform_path. |
| std::move(callback).Run(info, platform_path, result, std::move(listener)); |
| return; |
| } |
| |
| // Return the file info and platform_path. |
| std::move(callback).Run(info, platform_path, result, mojo::NullRemote()); |
| } |
| |
| void FileSystemManagerImpl::DidGetPlatformPath( |
| scoped_refptr<storage::FileSystemContext> /*context*/, |
| GetPlatformPathCallback callback, |
| base::FilePath platform_path) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| std::move(callback).Run(platform_path); |
| } |
| |
| // static |
| void FileSystemManagerImpl::GetPlatformPathOnFileThread( |
| const GURL& path, |
| int process_id, |
| scoped_refptr<storage::FileSystemContext> context, |
| base::WeakPtr<FileSystemManagerImpl> file_system_manager, |
| const blink::StorageKey& storage_key, |
| GetPlatformPathCallback callback) { |
| DCHECK(context->default_file_task_runner()->RunsTasksInCurrentSequence()); |
| |
| // Bind `context` to the callback to ensure it stays alive. |
| DoGetPlatformPath( |
| context, process_id, path, storage_key, |
| base::BindOnce( |
| [](base::WeakPtr<FileSystemManagerImpl> file_system_manager, |
| scoped_refptr<storage::FileSystemContext> context, |
| GetPlatformPathCallback callback, |
| const base::FilePath& platform_path) { |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FileSystemManagerImpl::DidGetPlatformPath, |
| std::move(file_system_manager), |
| std::move(context), std::move(callback), |
| platform_path)); |
| }, |
| std::move(file_system_manager), context, std::move(callback))); |
| } |
| |
| std::optional<base::File::Error> FileSystemManagerImpl::ValidateFileSystemURL( |
| const storage::FileSystemURL& url) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!FileSystemURLIsValid(context_.get(), url)) |
| return base::File::FILE_ERROR_INVALID_URL; |
| |
| return std::nullopt; |
| } |
| |
| FileSystemManagerImpl::OperationListenerID FileSystemManagerImpl::AddOpListener( |
| mojo::Remote<blink::mojom::FileSystemOperationListener> listener) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| int op_id = next_operation_listener_id_++; |
| listener.set_disconnect_handler( |
| base::BindOnce(&FileSystemManagerImpl::OnConnectionErrorForOpListeners, |
| base::Unretained(this), op_id)); |
| op_listeners_[op_id] = std::move(listener); |
| return op_id; |
| } |
| |
| void FileSystemManagerImpl::RemoveOpListener(OperationListenerID listener_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(op_listeners_.find(listener_id) != op_listeners_.end()); |
| op_listeners_.erase(listener_id); |
| } |
| |
| blink::mojom::FileSystemOperationListener* FileSystemManagerImpl::GetOpListener( |
| OperationListenerID listener_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (op_listeners_.find(listener_id) == op_listeners_.end()) |
| return nullptr; |
| return &*op_listeners_[listener_id]; |
| } |
| |
| void FileSystemManagerImpl::OnConnectionErrorForOpListeners( |
| OperationListenerID listener_id) { |
| RemoveOpListener(listener_id); |
| } |
| |
| } // namespace content |