910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "storage/browser/file_system/file_system_operation_impl.h"

#include <stdint.h>

#include <limits>
#include <memory>
#include <tuple>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "storage/browser/file_system/async_file_util.h"
#include "storage/browser/file_system/copy_or_move_hook_delegate.h"
#include "storage/browser/file_system/copy_or_move_operation_delegate.h"
#include "storage/browser/file_system/file_observers.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/remove_operation_delegate.h"
#include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/common/file_system/file_system_types.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"

namespace storage {

namespace {

// Takes ownership and destruct on the target thread.
void Destruct(base::File file) {}

void DidOpenFile(scoped_refptr<FileSystemContext> context,
                 base::WeakPtr<FileSystemOperationImpl> operation,
                 FileSystemOperationImpl::OpenFileCallback callback,
                 base::File file,
                 base::OnceClosure on_close_callback) {
  if (!operation) {
    context->default_file_task_runner()->PostTask(
        FROM_HERE, base::BindOnce(&Destruct, std::move(file)));
    return;
  }
  std::move(callback).Run(std::move(file), std::move(on_close_callback));
}

}  // namespace

std::unique_ptr<FileSystemOperation> FileSystemOperation::Create(
    OperationType type,
    const FileSystemURL& url,
    FileSystemContext* file_system_context,
    std::unique_ptr<FileSystemOperationContext> operation_context) {
  return std::make_unique<FileSystemOperationImpl>(
      type, url, file_system_context, std::move(operation_context),
      base::PassKey<FileSystemOperation>());
}

FileSystemOperationImpl::~FileSystemOperationImpl() = default;

void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
                                         bool exclusive,
                                         StatusCallback callback) {
  CheckOperationType(OperationType::kCreateFile);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      url,
      base::BindOnce(&FileSystemOperationImpl::DoCreateFile,
                     weak_factory_.GetWeakPtr(), url,
                     std::move(split_callback.first), exclusive),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
                                              bool exclusive,
                                              bool recursive,
                                              StatusCallback callback) {
  CheckOperationType(OperationType::kCreateDirectory);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      url,
      base::BindOnce(&FileSystemOperationImpl::DoCreateDirectory,
                     weak_factory_.GetWeakPtr(), url,
                     std::move(split_callback.first), exclusive, recursive),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

void FileSystemOperationImpl::Copy(
    const FileSystemURL& src_url,
    const FileSystemURL& dest_url,
    CopyOrMoveOptionSet options,
    ErrorBehavior error_behavior,
    std::unique_ptr<CopyOrMoveHookDelegate> copy_or_move_hook_delegate,
    StatusCallback callback) {
  DCHECK(copy_or_move_hook_delegate);
  CheckOperationType(OperationType::kCopy);
  DCHECK(!recursive_operation_delegate_);

  recursive_operation_delegate_ = std::make_unique<CopyOrMoveOperationDelegate>(
      file_system_context(), src_url, dest_url,
      CopyOrMoveOperationDelegate::OPERATION_COPY, options, error_behavior,
      std::move(copy_or_move_hook_delegate),
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
  recursive_operation_delegate_->RunRecursively();
}

void FileSystemOperationImpl::Move(
    const FileSystemURL& src_url,
    const FileSystemURL& dest_url,
    CopyOrMoveOptionSet options,
    ErrorBehavior error_behavior,
    std::unique_ptr<CopyOrMoveHookDelegate> copy_or_move_hook_delegate,
    StatusCallback callback) {
  DCHECK(copy_or_move_hook_delegate);
  CheckOperationType(OperationType::kMove);
  DCHECK(!recursive_operation_delegate_);
  recursive_operation_delegate_ = std::make_unique<CopyOrMoveOperationDelegate>(
      file_system_context(), src_url, dest_url,
      CopyOrMoveOperationDelegate::OPERATION_MOVE, options, error_behavior,
      std::move(copy_or_move_hook_delegate),
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
  recursive_operation_delegate_->RunRecursively();
}

void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
                                              StatusCallback callback) {
  CheckOperationType(OperationType::kDirectoryExists);
  async_file_util_->GetFileInfo(
      std::move(operation_context_), url, {GetMetadataField::kIsDirectory},
      base::BindOnce(&FileSystemOperationImpl::DidDirectoryExists,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
                                         StatusCallback callback) {
  CheckOperationType(OperationType::kFileExists);
  async_file_util_->GetFileInfo(
      std::move(operation_context_), url, {GetMetadataField::kIsDirectory},
      base::BindOnce(&FileSystemOperationImpl::DidFileExists,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void FileSystemOperationImpl::GetMetadata(const FileSystemURL& url,
                                          GetMetadataFieldSet fields,
                                          GetMetadataCallback callback) {
  CheckOperationType(OperationType::kGetMetadata);
  async_file_util_->GetFileInfo(std::move(operation_context_), url, fields,
                                std::move(callback));
}

void FileSystemOperationImpl::ReadDirectory(
    const FileSystemURL& url,
    const ReadDirectoryCallback& callback) {
  CheckOperationType(OperationType::kReadDirectory);
  async_file_util_->ReadDirectory(std::move(operation_context_), url, callback);
}

void FileSystemOperationImpl::Remove(const FileSystemURL& url,
                                     bool recursive,
                                     StatusCallback callback) {
  CheckOperationType(OperationType::kRemove);
  DCHECK(!recursive_operation_delegate_);

  if (recursive) {
    // For recursive removal, try to delegate the operation to AsyncFileUtil
    // first. If not supported, it is delegated to RemoveOperationDelegate
    // in DidDeleteRecursively.
    async_file_util_->DeleteRecursively(
        std::move(operation_context_), url,
        base::BindOnce(&FileSystemOperationImpl::DidDeleteRecursively,
                       weak_factory_.GetWeakPtr(), url, std::move(callback)));
    return;
  }

  recursive_operation_delegate_ = std::make_unique<RemoveOperationDelegate>(
      file_system_context(), url,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
  recursive_operation_delegate_->Run();
}

void FileSystemOperationImpl::WriteBlob(
    const FileSystemURL& url,
    std::unique_ptr<FileWriterDelegate> writer_delegate,
    std::unique_ptr<BlobReader> blob_reader,
    const WriteCallback& callback) {
  CheckOperationType(OperationType::kWrite);
  file_writer_delegate_ = std::move(writer_delegate);
  file_writer_delegate_->Start(
      std::move(blob_reader),
      base::BindRepeating(&FileSystemOperationImpl::DidWrite,
                          weak_factory_.GetWeakPtr(), url, callback));
}

void FileSystemOperationImpl::Write(
    const FileSystemURL& url,
    std::unique_ptr<FileWriterDelegate> writer_delegate,
    mojo::ScopedDataPipeConsumerHandle data_pipe,
    const WriteCallback& callback) {
  CheckOperationType(OperationType::kWrite);
  file_writer_delegate_ = std::move(writer_delegate);
  file_writer_delegate_->Start(
      std::move(data_pipe),
      base::BindRepeating(&FileSystemOperationImpl::DidWrite,
                          weak_factory_.GetWeakPtr(), url, callback));
}

void FileSystemOperationImpl::Truncate(const FileSystemURL& url,
                                       int64_t length,
                                       StatusCallback callback) {
  CheckOperationType(OperationType::kTruncate);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      url,
      base::BindOnce(&FileSystemOperationImpl::DoTruncate,
                     weak_factory_.GetWeakPtr(), url,
                     std::move(split_callback.first), length),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
                                        const base::Time& last_access_time,
                                        const base::Time& last_modified_time,
                                        StatusCallback callback) {
  CheckOperationType(OperationType::kTouchFile);

  async_file_util_->Touch(
      std::move(operation_context_), url, last_access_time, last_modified_time,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
                                       uint32_t file_flags,
                                       OpenFileCallback callback) {
  CheckOperationType(OperationType::kOpenFile);

  if (file_flags &
      (base::File::FLAG_WIN_TEMPORARY | base::File::FLAG_WIN_HIDDEN)) {
    std::move(callback).Run(base::File(base::File::FILE_ERROR_FAILED),
                            base::OnceClosure());
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      url,
      base::BindOnce(&FileSystemOperationImpl::DoOpenFile,
                     weak_factory_.GetWeakPtr(), url,
                     std::move(split_callback.first), file_flags),
      base::BindOnce(std::move(split_callback.second),
                     base::File(base::File::FILE_ERROR_FAILED),
                     base::OnceClosure()));
}

// We can only get here on a write or truncate that's not yet completed.
// We don't support cancelling any other operation at this time.
void FileSystemOperationImpl::Cancel(StatusCallback cancel_callback) {
  DCHECK(cancel_callback_.is_null());
  cancel_callback_ = std::move(cancel_callback);

  if (file_writer_delegate_.get()) {
    CHECK_EQ(OperationType::kWrite, type_);
    // This will call DidWrite() with ABORT status code.
    file_writer_delegate_->Cancel();
  } else if (recursive_operation_delegate_) {
    // This will call DidFinishOperation() with ABORT status code.
    recursive_operation_delegate_->Cancel();
  } else {
    // For truncate we have no way to cancel the inflight operation (for now).
    // Let it just run and dispatch cancel callback later.
    CHECK_EQ(OperationType::kTruncate, type_);
  }
}

void FileSystemOperationImpl::CreateSnapshotFile(
    const FileSystemURL& url,
    SnapshotFileCallback callback) {
  CheckOperationType(OperationType::kCreateSnapshotFile);
  async_file_util_->CreateSnapshotFile(std::move(operation_context_), url,
                                       std::move(callback));
}

void FileSystemOperationImpl::CopyInForeignFile(
    const base::FilePath& src_local_disk_file_path,
    const FileSystemURL& dest_url,
    StatusCallback callback) {
  CheckOperationType(OperationType::kCopyInForeignFile);

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      dest_url,
      base::BindOnce(&FileSystemOperationImpl::DoCopyInForeignFile,
                     weak_factory_.GetWeakPtr(), src_local_disk_file_path,
                     dest_url, std::move(split_callback.first)),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

void FileSystemOperationImpl::RemoveFile(const FileSystemURL& url,
                                         StatusCallback callback) {
  CheckOperationType(OperationType::kRemove);
  async_file_util_->DeleteFile(
      std::move(operation_context_), url,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void FileSystemOperationImpl::RemoveDirectory(const FileSystemURL& url,
                                              StatusCallback callback) {
  CheckOperationType(OperationType::kRemove);
  async_file_util_->DeleteDirectory(
      std::move(operation_context_), url,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void FileSystemOperationImpl::CopyFileLocal(
    const FileSystemURL& src_url,
    const FileSystemURL& dest_url,
    CopyOrMoveOptionSet options,
    const CopyFileProgressCallback& progress_callback,
    StatusCallback callback) {
  CheckOperationType(OperationType::kCopy);
  // Don't just DCHECK src_url.IsInSameFileSystem(dest_url). We don't care if
  // the two URLs are mounted in two different isolated file systems. As long
  // as their origin and type are the same, they are part of the same file
  // system, and local operations are allowed. See https://crbug.com/1396116.
  DCHECK(src_url.origin() == dest_url.origin() ||
         (src_url.origin().opaque() && dest_url.origin().opaque()));
  DCHECK_EQ(src_url.type(), dest_url.type());

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      dest_url,
      base::BindOnce(&FileSystemOperationImpl::DoCopyFileLocal,
                     weak_factory_.GetWeakPtr(), src_url, dest_url, options,
                     progress_callback, std::move(split_callback.first)),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

void FileSystemOperationImpl::MoveFileLocal(const FileSystemURL& src_url,
                                            const FileSystemURL& dest_url,
                                            CopyOrMoveOptionSet options,
                                            StatusCallback callback) {
  CheckOperationType(OperationType::kMove);
  // Don't just DCHECK src_url.IsInSameFileSystem(dest_url). We don't care if
  // the two URLs are mounted in two different isolated file systems. As long
  // as their origin and type are the same, they are part of the same file
  // system, and local operations are allowed. See https://crbug.com/1396116.
  DCHECK(src_url.origin() == dest_url.origin() ||
         (src_url.origin().opaque() && dest_url.origin().opaque()));
  DCHECK_EQ(src_url.type(), dest_url.type());

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  GetBucketSpaceRemainingAndRunTask(
      dest_url,
      base::BindOnce(&FileSystemOperationImpl::DoMoveFileLocal,
                     weak_factory_.GetWeakPtr(), src_url, dest_url, options,
                     std::move(split_callback.first)),
      base::BindOnce(std::move(split_callback.second),
                     base::File::FILE_ERROR_FAILED));
}

base::File::Error FileSystemOperationImpl::SyncGetPlatformPath(
    const FileSystemURL& url,
    base::FilePath* platform_path) {
  CheckOperationType(OperationType::kGetLocalPath);
  if (!file_system_context()->IsSandboxFileSystem(url.type()))
    return base::File::FILE_ERROR_INVALID_OPERATION;
  FileSystemFileUtil* file_util =
      file_system_context()->sandbox_delegate()->sync_file_util();
  file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
  return base::File::FILE_OK;
}

FileSystemOperationImpl::FileSystemOperationImpl(
    OperationType type,
    const FileSystemURL& url,
    FileSystemContext* file_system_context,
    std::unique_ptr<FileSystemOperationContext> operation_context,
    base::PassKey<FileSystemOperation>)
    : type_(type),
      file_system_context_(file_system_context),
      operation_context_(std::move(operation_context)),
      async_file_util_(nullptr) {
  weak_ptr_ = weak_factory_.GetWeakPtr();

  DCHECK(operation_context_.get());
  async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
  DCHECK(async_file_util_);
}

void FileSystemOperationImpl::GetBucketSpaceRemainingAndRunTask(
    const FileSystemURL& url,
    base::OnceClosure task,
    base::OnceClosure error_callback) {
  const scoped_refptr<QuotaManagerProxy>& quota_manager_proxy =
      file_system_context()->quota_manager_proxy();
  if (!quota_manager_proxy ||
      !file_system_context()->GetQuotaUtil(url.type())) {
    // If we don't have the quota manager or the requested filesystem type
    // does not support quota, we should be able to let it go.
    operation_context_->set_allowed_bytes_growth(
        std::numeric_limits<int64_t>::max());
    std::move(task).Run();
    return;
  }

  BucketLocator bucket =
      url.bucket().value_or(BucketLocator::ForDefaultBucket(url.storage_key()));

  DCHECK(quota_manager_proxy);
  quota_manager_proxy->GetBucketSpaceRemaining(
      bucket, base::SequencedTaskRunner::GetCurrentDefault(),
      base::BindOnce(&FileSystemOperationImpl::DidGetBucketSpaceRemaining,
                     weak_ptr_, std::move(task), std::move(error_callback)));
}

void FileSystemOperationImpl::DidGetBucketSpaceRemaining(
    base::OnceClosure task,
    base::OnceClosure error_callback,
    QuotaErrorOr<int64_t> space_left) {
  if (!space_left.has_value()) {
    LOG(WARNING) << "Got unexpected quota error";
    std::move(error_callback).Run();
    return;
  }

  operation_context_->set_allowed_bytes_growth(space_left.value());
  std::move(task).Run();
}

void FileSystemOperationImpl::DoCreateFile(const FileSystemURL& url,
                                           StatusCallback callback,
                                           bool exclusive) {
  async_file_util_->EnsureFileExists(
      std::move(operation_context_), url,
      base::BindOnce(
          exclusive ? &FileSystemOperationImpl::DidEnsureFileExistsExclusive
                    : &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
          weak_ptr_, std::move(callback)));
}

void FileSystemOperationImpl::DoCreateDirectory(const FileSystemURL& url,
                                                StatusCallback callback,
                                                bool exclusive,
                                                bool recursive) {
  async_file_util_->CreateDirectory(
      std::move(operation_context_), url, exclusive, recursive,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DoCopyFileLocal(
    const FileSystemURL& src_url,
    const FileSystemURL& dest_url,
    CopyOrMoveOptionSet options,
    const CopyFileProgressCallback& progress_callback,
    StatusCallback callback) {
  async_file_util_->CopyFileLocal(
      std::move(operation_context_), src_url, dest_url, options,
      progress_callback,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DoMoveFileLocal(const FileSystemURL& src_url,
                                              const FileSystemURL& dest_url,
                                              CopyOrMoveOptionSet options,
                                              StatusCallback callback) {
  async_file_util_->MoveFileLocal(
      std::move(operation_context_), src_url, dest_url, options,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DoCopyInForeignFile(
    const base::FilePath& src_local_disk_file_path,
    const FileSystemURL& dest_url,
    StatusCallback callback) {
  async_file_util_->CopyInForeignFile(
      std::move(operation_context_), src_local_disk_file_path, dest_url,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
                                         StatusCallback callback,
                                         int64_t length) {
  async_file_util_->Truncate(
      std::move(operation_context_), url, length,
      base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
                                         OpenFileCallback callback,
                                         uint32_t file_flags) {
  async_file_util_->CreateOrOpen(
      std::move(operation_context_), url, file_flags,
      base::BindOnce(&DidOpenFile, file_system_context_, weak_ptr_,
                     std::move(callback)));
}

void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
    StatusCallback callback,
    base::File::Error rv,
    bool created) {
  if (rv == base::File::FILE_OK && !created) {
    std::move(callback).Run(base::File::FILE_ERROR_EXISTS);
  } else {
    DidFinishOperation(std::move(callback), rv);
  }
}

void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
    StatusCallback callback,
    base::File::Error rv,
    bool /* created */) {
  DidFinishOperation(std::move(callback), rv);
}

void FileSystemOperationImpl::DidFinishOperation(StatusCallback callback,
                                                 base::File::Error rv) {
  if (!cancel_callback_.is_null()) {
    StatusCallback cancel_callback = std::move(cancel_callback_);
    std::move(callback).Run(rv);

    // Return OK only if we succeeded to stop the operation.
    std::move(cancel_callback)
        .Run(rv == base::File::FILE_ERROR_ABORT
                 ? base::File::FILE_OK
                 : base::File::FILE_ERROR_INVALID_OPERATION);
  } else {
    std::move(callback).Run(rv);
  }
}

void FileSystemOperationImpl::DidDirectoryExists(
    StatusCallback callback,
    base::File::Error rv,
    const base::File::Info& file_info) {
  if (rv == base::File::FILE_OK && !file_info.is_directory)
    rv = base::File::FILE_ERROR_NOT_A_DIRECTORY;
  std::move(callback).Run(rv);
}

void FileSystemOperationImpl::DidFileExists(StatusCallback callback,
                                            base::File::Error rv,
                                            const base::File::Info& file_info) {
  if (rv == base::File::FILE_OK && file_info.is_directory)
    rv = base::File::FILE_ERROR_NOT_A_FILE;
  std::move(callback).Run(rv);
}

void FileSystemOperationImpl::DidDeleteRecursively(const FileSystemURL& url,
                                                   StatusCallback callback,
                                                   base::File::Error rv) {
  if (rv == base::File::FILE_ERROR_INVALID_OPERATION) {
    // Recursive removal is not supported on this platform.
    DCHECK(!recursive_operation_delegate_);
    recursive_operation_delegate_ = std::make_unique<RemoveOperationDelegate>(
        file_system_context(), url,
        base::BindOnce(&FileSystemOperationImpl::DidFinishOperation, weak_ptr_,
                       std::move(callback)));
    recursive_operation_delegate_->RunRecursively();
    return;
  }

  std::move(callback).Run(rv);
}

void FileSystemOperationImpl::DidWrite(
    const FileSystemURL& url,
    const WriteCallback& write_callback,
    base::File::Error rv,
    int64_t bytes,
    FileWriterDelegate::WriteProgressStatus write_status) {
  const bool complete =
      (write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
  if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
    DCHECK(operation_context_);
    operation_context_->change_observers()->Notify(
        &FileChangeObserver::OnModifyFile, url);
  }

  StatusCallback cancel_callback = std::move(cancel_callback_);
  write_callback.Run(rv, bytes, complete);
  if (!cancel_callback.is_null())
    std::move(cancel_callback).Run(base::File::FILE_OK);
}

void FileSystemOperationImpl::CheckOperationType(OperationType type) {
  CHECK_EQ(type, type_);
  CHECK(!operation_called_);
  operation_called_ = true;
}

}  // namespace storage