chromium/content/browser/file_system_access/file_system_access_file_handle_impl.cc

// Copyright 2019 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_access/file_system_access_file_handle_impl.h"

#include "base/files/file_error_or.h"
#include "base/files/file_util.h"
#include "base/files/safe_base_name.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/uuid.h"
#include "build/build_config.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "content/browser/file_system_access/features.h"
#include "content/browser/file_system_access/file_system_access_access_handle_host_impl.h"
#include "content/browser/file_system_access/file_system_access_error.h"
#include "content/browser/file_system_access/file_system_access_handle_base.h"
#include "content/browser/file_system_access/file_system_access_lock_manager.h"
#include "content/browser/file_system_access/file_system_access_manager_impl.h"
#include "content/browser/file_system_access/file_system_access_transfer_token_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_frame_host.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/mime_util.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/file_system/copy_or_move_hook_delegate.h"
#include "storage/browser/file_system/file_system_operation_runner.h"
#include "storage/common/file_system/file_system_types.h"
#include "third_party/blink/public/mojom/blob/blob.mojom.h"
#include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_cloud_identifier.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_file_modification_host.mojom.h"
#include "third_party/blink/public/mojom/file_system_access/file_system_access_transfer_token.mojom.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/path_service.h"
#include "base/strings/escape.h"
#include "content/public/common/content_paths.h"
#endif

#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif

#if BUILDFLAG(IS_MAC)
#include <sys/clonefile.h>

#include "base/files/file.h"
#endif

FileSystemAccessStatus;
BlobDataHandle;
BlobImpl;
FileSystemOperation;
FileSystemOperationRunner;

namespace content {

namespace {

std::pair<base::File, base::FileErrorOr<int64_t>> GetFileLengthOnBlockingThread(
    base::File file) {}

#if BUILDFLAG(IS_ANDROID)
void EnsureSwapDirExists(base::FilePath swap_dir) {
  if (!base::PathExists(swap_dir)) {
    if (!base::CreateDirectory(swap_dir)) {
      DLOG(ERROR) << "Error creating swap dir " << swap_dir;
    }
  }
}
#endif

bool HasWritePermission(const base::FilePath& path) {}

#if BUILDFLAG(IS_MAC)
// Creates a copy-on-write file at `swap_url`. This will fail if the file
// already exists. Must be called on a sequence which allows blocking.
base::File::Error CreateCowSwapFile(const storage::FileSystemURL& source_url,
                                    const storage::FileSystemURL& swap_url) {
  return clonefile(source_url.path().value().c_str(),
                   swap_url.path().value().c_str(),
                   /*flags=*/0) == 0
             ? base::File::Error::FILE_OK
             : base::File::GetLastFileError();
}
#endif  // BUILDFLAG(IS_MAC)

file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
CreateFileAccessCallback(const GURL& destination) {}

}  // namespace

FileSystemAccessFileHandleImpl::FileSystemAccessFileHandleImpl(
    FileSystemAccessManagerImpl* manager,
    const BindingContext& context,
    const storage::FileSystemURL& url,
    const SharedHandleState& handle_state)
    :{}

FileSystemAccessFileHandleImpl::~FileSystemAccessFileHandleImpl() = default;

void FileSystemAccessFileHandleImpl::GetPermissionStatus(
    bool writable,
    GetPermissionStatusCallback callback) {}

void FileSystemAccessFileHandleImpl::RequestPermission(
    bool writable,
    RequestPermissionCallback callback) {}

void FileSystemAccessFileHandleImpl::AsBlob(AsBlobCallback callback) {}

void FileSystemAccessFileHandleImpl::CreateFileWriter(
    bool keep_existing_data,
    bool auto_close,
    blink::mojom::FileSystemAccessWritableFileStreamLockMode mode,
    CreateFileWriterCallback callback) {}

void FileSystemAccessFileHandleImpl::Move(
    mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken>
        destination_directory,
    const std::string& new_entry_name,
    MoveCallback callback) {}

void FileSystemAccessFileHandleImpl::Rename(const std::string& new_entry_name,
                                            RenameCallback callback) {}

void FileSystemAccessFileHandleImpl::Remove(RemoveCallback callback) {}

void FileSystemAccessFileHandleImpl::OpenAccessHandle(
    blink::mojom::FileSystemAccessAccessHandleLockMode mode,
    OpenAccessHandleCallback callback) {}

void FileSystemAccessFileHandleImpl::DidTakeAccessHandleLock(
    OpenAccessHandleCallback callback,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock) {}

void FileSystemAccessFileHandleImpl::DoOpenIncognitoFile(
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    OpenAccessHandleCallback callback) {}

void FileSystemAccessFileHandleImpl::DoOpenFile(
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    OpenAccessHandleCallback callback) {}

void FileSystemAccessFileHandleImpl::DoGetLengthAfterOpenFile(
    OpenAccessHandleCallback callback,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    base::File file,
    base::ScopedClosureRunner on_close_callback) {}

void FileSystemAccessFileHandleImpl::DidOpenFileAndGetLength(
    OpenAccessHandleCallback callback,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    base::ScopedClosureRunner on_close_callback,
    std::pair<base::File, base::FileErrorOr<int64_t>> file_and_length) {}

void FileSystemAccessFileHandleImpl::IsSameEntry(
    mojo::PendingRemote<blink::mojom::FileSystemAccessTransferToken> token,
    IsSameEntryCallback callback) {}

void FileSystemAccessFileHandleImpl::IsSameEntryImpl(
    IsSameEntryCallback callback,
    FileSystemAccessTransferTokenImpl* other) {}

void FileSystemAccessFileHandleImpl::Transfer(
    mojo::PendingReceiver<blink::mojom::FileSystemAccessTransferToken> token) {}

void FileSystemAccessFileHandleImpl::DidGetMetaDataForBlob(
    AsBlobCallback callback,
    base::File::Error result,
    const base::File::Info& info) {}

void FileSystemAccessFileHandleImpl::CreateFileWriterImpl(
    bool keep_existing_data,
    bool auto_close,
    blink::mojom::FileSystemAccessWritableFileStreamLockMode mode,
    CreateFileWriterCallback callback) {}

void FileSystemAccessFileHandleImpl::DidVerifyHasWritePermissions(
    bool keep_existing_data,
    bool auto_close,
    blink::mojom::FileSystemAccessWritableFileStreamLockMode mode,
    CreateFileWriterCallback callback,
    bool can_write) {}

void FileSystemAccessFileHandleImpl::StartCreateSwapFile(
    int start_count,
    bool keep_existing_data,
    bool auto_close,
    CreateFileWriterCallback callback,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock) {}

void FileSystemAccessFileHandleImpl::DidTakeSwapLock(
    int count,
    const storage::FileSystemURL& swap_url,
    bool keep_existing_data,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    CreateFileWriterCallback callback,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock) {}

void FileSystemAccessFileHandleImpl::DidCheckSwapFileExists(
    int count,
    const storage::FileSystemURL& swap_url,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock,
    CreateFileWriterCallback callback,
    base::File::Error result) {}

void FileSystemAccessFileHandleImpl::CreateSwapFileFromCopy(
    int count,
    const storage::FileSystemURL& swap_url,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock,
    CreateFileWriterCallback callback) {}

#if BUILDFLAG(IS_MAC)
void FileSystemAccessFileHandleImpl::CreateClonedSwapFile(
    int count,
    const storage::FileSystemURL& swap_url,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock,
    CreateFileWriterCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(count >= 0);
  DCHECK(max_swap_files_ >= 0);
  DCHECK(CanUseCowSwapFile());

  auto after_clone_callback = base::BindOnce(
      &FileSystemAccessFileHandleImpl::DidCloneSwapFile,
      weak_factory_.GetWeakPtr(), count, swap_url, auto_close, std::move(lock),
      std::move(swap_lock), std::move(callback));

  if (swap_file_cloning_will_fail_for_testing_) {
    std::move(after_clone_callback).Run(base::File::Error::FILE_ERROR_ABORT);
    return;
  }

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock()},
      base::BindOnce(&CreateCowSwapFile, url(), swap_url),
      std::move(after_clone_callback));
}

void FileSystemAccessFileHandleImpl::DidCloneSwapFile(
    int count,
    const storage::FileSystemURL& swap_url,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock,
    CreateFileWriterCallback callback,
    base::File::Error result) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(CanUseCowSwapFile());

  swap_file_clone_result_for_testing_ = result;

  if (result == base::File::FILE_ERROR_EXISTS) {
    // Cloning fails if the destination file exists. The file must have been
    // created between the FileExists check and the clone attempt. Attempt to
    // find another unused filename.
    StartCreateSwapFile(count + 1, /*keep_existing_data=*/true, auto_close,
                        std::move(callback), std::move(lock));
    return;
  }

  if (result != base::File::FILE_OK) {
    // Cloning could fail if the file's underlying file system does not support
    // copy-on-write, such as when accessing FAT formatted external USB drives
    // (which do not support copy-on-write) from a Mac (which otherwise does).
    // In that case, fall back on the create + copy technique.
    CreateSwapFileFromCopy(count, swap_url, auto_close, std::move(lock),
                           std::move(swap_lock), std::move(callback));
    return;
  }

  std::move(callback).Run(
      file_system_access_error::Ok(),
      manager()->CreateFileWriter(
          context(), url(), swap_url, std::move(lock), std::move(swap_lock),
          FileSystemAccessManagerImpl::SharedHandleState(
              handle_state().read_grant, handle_state().write_grant),
          auto_close));
}
#endif  // BUILDFLAG(IS_MAC)

void FileSystemAccessFileHandleImpl::DidCreateSwapFile(
    int count,
    const storage::FileSystemURL& swap_url,
    bool keep_existing_data,
    bool auto_close,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> lock,
    scoped_refptr<FileSystemAccessLockManager::LockHandle> swap_lock,
    CreateFileWriterCallback callback,
    base::File::Error result) {}

void FileSystemAccessFileHandleImpl::GetUniqueId(GetUniqueIdCallback callback) {}

#if BUILDFLAG(IS_MAC)
bool FileSystemAccessFileHandleImpl::CanUseCowSwapFile() const {
  return url().type() == storage::kFileSystemTypeLocal;
}
#endif  // BUILDFLAG(IS_MAC)

void FileSystemAccessFileHandleImpl::GetCloudIdentifiers(
    GetCloudIdentifiersCallback callback) {}

base::WeakPtr<FileSystemAccessHandleBase>
FileSystemAccessFileHandleImpl::AsWeakPtr() {}

}  // namespace content