910e62b5创建于 1月15日历史提交
// 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