blob: 8dfc2cf5c39a194ccc2a6ab8f1fdf3a5ed795b78 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// 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/async_file_util_adapter.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/macros.h"
#include "base/sequenced_task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/services/filesystem/public/mojom/types.mojom.h"
#include "storage/browser/blob/shareable_file_reference.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_operation_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/common/file_system/file_system_util.h"
using base::Owned;
using base::Unretained;
namespace storage {
namespace {
class EnsureFileExistsHelper {
public:
EnsureFileExistsHelper() : error_(base::File::FILE_OK), created_(false) {}
void RunWork(FileSystemFileUtil* file_util,
FileSystemOperationContext* context,
const FileSystemURL& url) {
error_ = file_util->EnsureFileExists(context, url, &created_);
}
void Reply(AsyncFileUtil::EnsureFileExistsCallback callback) {
std::move(callback).Run(error_, created_);
}
private:
base::File::Error error_;
bool created_;
DISALLOW_COPY_AND_ASSIGN(EnsureFileExistsHelper);
};
class GetFileInfoHelper {
public:
GetFileInfoHelper() : error_(base::File::FILE_OK) {}
void GetFileInfo(FileSystemFileUtil* file_util,
FileSystemOperationContext* context,
const FileSystemURL& url,
bool calculate_total_size) {
error_ = file_util->GetFileInfo(context, url, &file_info_, &platform_path_);
if (error_ == base::File::FILE_OK && calculate_total_size &&
file_info_.is_directory) {
file_info_.size = 0;
auto enumerator = file_util->CreateFileEnumerator(context, url, true);
base::FilePath path = enumerator->Next();
while (!path.empty()) {
if (!enumerator->IsDirectory()) {
file_info_.size += enumerator->Size();
}
path = enumerator->Next();
}
}
}
void CreateSnapshotFile(FileSystemFileUtil* file_util,
FileSystemOperationContext* context,
const FileSystemURL& url) {
scoped_file_ = file_util->CreateSnapshotFile(context, url, &error_,
&file_info_, &platform_path_);
}
void ReplyFileInfo(AsyncFileUtil::GetFileInfoCallback callback) {
std::move(callback).Run(error_, file_info_);
}
void ReplySnapshotFile(AsyncFileUtil::CreateSnapshotFileCallback callback) {
std::move(callback).Run(
error_, file_info_, platform_path_,
ShareableFileReference::GetOrCreate(std::move(scoped_file_)));
}
private:
base::File::Error error_;
base::File::Info file_info_;
base::FilePath platform_path_;
ScopedFile scoped_file_;
DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper);
};
void ReadDirectoryHelper(FileSystemFileUtil* file_util,
FileSystemOperationContext* context,
const FileSystemURL& url,
base::SingleThreadTaskRunner* origin_runner,
AsyncFileUtil::ReadDirectoryCallback callback) {
base::File::Info file_info;
base::FilePath platform_path;
base::File::Error error =
file_util->GetFileInfo(context, url, &file_info, &platform_path);
if (error == base::File::FILE_OK && !file_info.is_directory)
error = base::File::FILE_ERROR_NOT_A_DIRECTORY;
std::vector<filesystem::mojom::DirectoryEntry> entries;
if (error != base::File::FILE_OK) {
origin_runner->PostTask(FROM_HERE, base::BindOnce(callback, error, entries,
false /* has_more */));
return;
}
// Note: Increasing this value may make some tests in LayoutTests meaningless.
// (Namely, read-directory-many.html and read-directory-sync-many.html are
// assuming that they are reading much more entries than this constant.)
const size_t kResultChunkSize = 100;
std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum(
file_util->CreateFileEnumerator(context, url, false));
base::FilePath current;
while (!(current = file_enum->Next()).empty()) {
entries.emplace_back(VirtualPath::BaseName(current),
file_enum->IsDirectory()
? filesystem::mojom::FsFileType::DIRECTORY
: filesystem::mojom::FsFileType::REGULAR_FILE);
if (entries.size() == kResultChunkSize) {
origin_runner->PostTask(
FROM_HERE, base::BindOnce(callback, base::File::FILE_OK, entries,
true /* has_more */));
entries.clear();
}
}
origin_runner->PostTask(FROM_HERE,
base::BindOnce(callback, base::File::FILE_OK, entries,
false /* has_more */));
}
void RunCreateOrOpenCallback(FileSystemOperationContext* context,
AsyncFileUtil::CreateOrOpenCallback callback,
base::File file) {
if (callback.IsCancelled()) {
// If |callback| been cancelled, free |file| on the correct task runner.
context->task_runner()->PostTask(
FROM_HERE,
BindOnce([](base::File file) { file.Close(); }, std::move(file)));
return;
}
std::move(callback).Run(std::move(file), base::OnceClosure());
}
} // namespace
AsyncFileUtilAdapter::AsyncFileUtilAdapter(FileSystemFileUtil* sync_file_util)
: sync_file_util_(sync_file_util) {
DCHECK(sync_file_util_.get());
}
AsyncFileUtilAdapter::~AsyncFileUtilAdapter() = default;
void AsyncFileUtilAdapter::CreateOrOpen(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
int file_flags,
CreateOrOpenCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
BindOnce(&FileSystemFileUtil::CreateOrOpen,
Unretained(sync_file_util_.get()), context_ptr, url, file_flags),
BindOnce(&RunCreateOrOpenCallback, base::Owned(context_ptr),
std::move(callback)));
}
void AsyncFileUtilAdapter::EnsureFileExists(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
EnsureFileExistsCallback callback) {
EnsureFileExistsHelper* helper = new EnsureFileExistsHelper;
FileSystemOperationContext* context_ptr = context.release();
const bool success = context_ptr->task_runner()->PostTaskAndReply(
FROM_HERE,
BindOnce(&EnsureFileExistsHelper::RunWork, Unretained(helper),
sync_file_util_.get(), base::Owned(context_ptr), url),
BindOnce(&EnsureFileExistsHelper::Reply, Owned(helper),
std::move(callback)));
DCHECK(success);
}
void AsyncFileUtilAdapter::CreateDirectory(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
bool exclusive,
bool recursive,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::CreateDirectory,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), url, exclusive, recursive),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::GetFileInfo(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
int fields,
GetFileInfoCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
GetFileInfoHelper* helper = new GetFileInfoHelper;
bool calculate_total_size =
(fields & FileSystemOperation::GET_METADATA_FIELD_TOTAL_SIZE);
const bool success = context_ptr->task_runner()->PostTaskAndReply(
FROM_HERE,
BindOnce(&GetFileInfoHelper::GetFileInfo, Unretained(helper),
sync_file_util_.get(), base::Owned(context_ptr), url,
calculate_total_size),
BindOnce(&GetFileInfoHelper::ReplyFileInfo, Owned(helper),
std::move(callback)));
DCHECK(success);
}
void AsyncFileUtilAdapter::ReadDirectory(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
ReadDirectoryCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = context_ptr->task_runner()->PostTask(
FROM_HERE,
BindOnce(&ReadDirectoryHelper, sync_file_util_.get(),
base::Owned(context_ptr), url,
base::RetainedRef(base::ThreadTaskRunnerHandle::Get()),
callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::Touch(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
const base::Time& last_access_time,
const base::Time& last_modified_time,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(
&FileSystemFileUtil::Touch, Unretained(sync_file_util_.get()),
base::Owned(context_ptr), url, last_access_time, last_modified_time),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::Truncate(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
int64_t length,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::Truncate,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), url, length),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::CopyFileLocal(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
CopyFileProgressCallback progress_callback,
StatusCallback callback) {
// TODO(hidehiko): Support progress_callback.
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::CopyOrMoveFile,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), src_url, dest_url, option,
true /* copy */),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::MoveFileLocal(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::CopyOrMoveFile,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), src_url, dest_url, option,
false /* copy */),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::CopyInForeignFile(
std::unique_ptr<FileSystemOperationContext> context,
const base::FilePath& src_file_path,
const FileSystemURL& dest_url,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::CopyInForeignFile,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), src_file_path, dest_url),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::DeleteFile(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::DeleteFile,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), url),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::DeleteDirectory(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
StatusCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
const bool success = base::PostTaskAndReplyWithResult(
context_ptr->task_runner(), FROM_HERE,
base::BindOnce(&FileSystemFileUtil::DeleteDirectory,
Unretained(sync_file_util_.get()),
base::Owned(context_ptr), url),
std::move(callback));
DCHECK(success);
}
void AsyncFileUtilAdapter::DeleteRecursively(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
StatusCallback callback) {
std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
}
void AsyncFileUtilAdapter::CreateSnapshotFile(
std::unique_ptr<FileSystemOperationContext> context,
const FileSystemURL& url,
CreateSnapshotFileCallback callback) {
FileSystemOperationContext* context_ptr = context.release();
GetFileInfoHelper* helper = new GetFileInfoHelper;
const bool success = context_ptr->task_runner()->PostTaskAndReply(
FROM_HERE,
BindOnce(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper),
sync_file_util_.get(), base::Owned(context_ptr), url),
BindOnce(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper),
std::move(callback)));
DCHECK(success);
}
} // namespace storage