// Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/files/file_proxy.h" #include #include #include #include "base/containers/heap_array.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/location.h" #include "base/numerics/safe_conversions.h" #include "base/task/task_runner.h" namespace { void FileDeleter(base::File file) { } } // namespace namespace base { class FileHelper { public: FileHelper(base::WeakPtr proxy, File file) : file_(std::move(file)), task_runner_(proxy->task_runner()), proxy_(proxy) {} FileHelper(const FileHelper&) = delete; FileHelper& operator=(const FileHelper&) = delete; void PassFile() { if (proxy_) proxy_->SetFile(std::move(file_)); else if (file_.IsValid()) task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_))); } protected: File file_; File::Error error_ = File::FILE_ERROR_FAILED; private: scoped_refptr task_runner_; WeakPtr proxy_; }; namespace { class GenericFileHelper : public FileHelper { public: GenericFileHelper(base::WeakPtr proxy, File file) : FileHelper(std::move(proxy), std::move(file)) {} GenericFileHelper(const GenericFileHelper&) = delete; GenericFileHelper& operator=(const GenericFileHelper&) = delete; void Close() { file_.Close(); error_ = File::FILE_OK; } void SetTimes(Time last_access_time, Time last_modified_time) { bool rv = file_.SetTimes(last_access_time, last_modified_time); error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED; } void SetLength(int64_t length) { if (file_.SetLength(length)) error_ = File::FILE_OK; } void Flush() { if (file_.Flush()) error_ = File::FILE_OK; } void Reply(FileProxy::StatusCallback callback) { PassFile(); if (!callback.is_null()) std::move(callback).Run(error_); } }; class CreateOrOpenHelper : public FileHelper { public: CreateOrOpenHelper(base::WeakPtr proxy, File file) : FileHelper(std::move(proxy), std::move(file)) {} CreateOrOpenHelper(const CreateOrOpenHelper&) = delete; CreateOrOpenHelper& operator=(const CreateOrOpenHelper&) = delete; void RunWork(const FilePath& file_path, uint32_t file_flags) { file_.Initialize(file_path, file_flags); error_ = file_.IsValid() ? File::FILE_OK : file_.error_details(); } void Reply(FileProxy::StatusCallback callback) { DCHECK(!callback.is_null()); PassFile(); std::move(callback).Run(error_); } }; class CreateTemporaryHelper : public FileHelper { public: CreateTemporaryHelper(base::WeakPtr proxy, File file) : FileHelper(std::move(proxy), std::move(file)) {} CreateTemporaryHelper(const CreateTemporaryHelper&) = delete; CreateTemporaryHelper& operator=(const CreateTemporaryHelper&) = delete; void RunWork(uint32_t additional_file_flags) { // TODO(darin): file_util should have a variant of CreateTemporaryFile // that returns a FilePath and a File. if (!CreateTemporaryFile(&file_path_)) { // TODO(davidben): base::CreateTemporaryFile should preserve the error // code. error_ = File::FILE_ERROR_FAILED; return; } uint32_t file_flags = File::FLAG_WRITE | File::FLAG_WIN_TEMPORARY | File::FLAG_CREATE_ALWAYS | additional_file_flags; file_.Initialize(file_path_, file_flags); if (file_.IsValid()) { error_ = File::FILE_OK; } else { error_ = file_.error_details(); DeleteFile(file_path_); file_path_.clear(); } } void Reply(FileProxy::CreateTemporaryCallback callback) { DCHECK(!callback.is_null()); PassFile(); std::move(callback).Run(error_, file_path_); } private: FilePath file_path_; }; class GetInfoHelper : public FileHelper { public: GetInfoHelper(base::WeakPtr proxy, File file) : FileHelper(std::move(proxy), std::move(file)) {} GetInfoHelper(const GetInfoHelper&) = delete; GetInfoHelper& operator=(const GetInfoHelper&) = delete; void RunWork() { if (file_.GetInfo(&file_info_)) error_ = File::FILE_OK; } void Reply(FileProxy::GetFileInfoCallback callback) { PassFile(); DCHECK(!callback.is_null()); std::move(callback).Run(error_, file_info_); } private: File::Info file_info_; }; class ReadHelper : public FileHelper { public: ReadHelper(base::WeakPtr proxy, File file, int bytes_to_read) : FileHelper(std::move(proxy), std::move(file)), // SAFETY - References to `buffer_` are provided as a span only after // successfully reading some bytes. buffer_(base::HeapArray::Uninit( static_cast(bytes_to_read))) {} ReadHelper(const ReadHelper&) = delete; ReadHelper& operator=(const ReadHelper&) = delete; void RunWork(int64_t offset) { std::optional result = file_.Read(offset, buffer_); if (!result.has_value()) { bytes_read_ = -1; error_ = File::FILE_ERROR_FAILED; return; } bytes_read_ = checked_cast(result.value()); error_ = File::FILE_OK; } void Reply(FileProxy::ReadCallback callback) { PassFile(); DCHECK(!callback.is_null()); base::span read_span; if (error_ == File::FILE_OK) { read_span = buffer_.first(checked_cast(bytes_read_)); } std::move(callback).Run(error_, base::as_chars(read_span)); } private: base::HeapArray buffer_; int bytes_read_ = 0; }; class WriteHelper : public FileHelper { public: WriteHelper(base::WeakPtr proxy, File file, base::span data) : FileHelper(std::move(proxy), std::move(file)), buffer_(base::HeapArray::CopiedFrom(data)) {} WriteHelper(const WriteHelper&) = delete; WriteHelper& operator=(const WriteHelper&) = delete; void RunWork(int64_t offset) { std::optional result = file_.Write(offset, buffer_); if (!result.has_value()) { bytes_written_ = -1; error_ = File::FILE_ERROR_FAILED; return; } bytes_written_ = checked_cast(result.value()); error_ = File::FILE_OK; } void Reply(FileProxy::WriteCallback callback) { PassFile(); if (!callback.is_null()) std::move(callback).Run(error_, bytes_written_); } private: base::HeapArray buffer_; int bytes_written_ = 0; }; } // namespace FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {} FileProxy::~FileProxy() { if (file_.IsValid()) task_runner_->PostTask(FROM_HERE, BindOnce(&FileDeleter, std::move(file_))); } bool FileProxy::CreateOrOpen(const FilePath& file_path, uint32_t file_flags, StatusCallback callback) { DCHECK(!file_.IsValid()); CreateOrOpenHelper* helper = new CreateOrOpenHelper(weak_ptr_factory_.GetWeakPtr(), File()); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path, file_flags), BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::CreateTemporary(uint32_t additional_file_flags, CreateTemporaryCallback callback) { DCHECK(!file_.IsValid()); CreateTemporaryHelper* helper = new CreateTemporaryHelper(weak_ptr_factory_.GetWeakPtr(), File()); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper), additional_file_flags), BindOnce(&CreateTemporaryHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::IsValid() const { return file_.IsValid(); } void FileProxy::SetFile(File file) { DCHECK(!file_.IsValid()); file_ = std::move(file); } File FileProxy::TakeFile() { return std::move(file_); } File FileProxy::DuplicateFile() { return file_.Duplicate(); } PlatformFile FileProxy::GetPlatformFile() const { return file_.GetPlatformFile(); } bool FileProxy::Close(StatusCallback callback) { DCHECK(file_.IsValid()); GenericFileHelper* helper = new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_)); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)), BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::GetInfo(GetFileInfoCallback callback) { DCHECK(file_.IsValid()); GetInfoHelper* helper = new GetInfoHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_)); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)), BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) { DCHECK(file_.IsValid()); if (bytes_to_read < 0) return false; ReadHelper* helper = new ReadHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_), bytes_to_read); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset), BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::Write(int64_t offset, base::span data, WriteCallback callback) { DCHECK(file_.IsValid()); if (data.empty()) { return false; } WriteHelper* helper = new WriteHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_), data); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset), BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::SetTimes(Time last_access_time, Time last_modified_time, StatusCallback callback) { DCHECK(file_.IsValid()); GenericFileHelper* helper = new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_)); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time, last_modified_time), BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::SetLength(int64_t length, StatusCallback callback) { DCHECK(file_.IsValid()); GenericFileHelper* helper = new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_)); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length), BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); } bool FileProxy::Flush(StatusCallback callback) { DCHECK(file_.IsValid()); GenericFileHelper* helper = new GenericFileHelper(weak_ptr_factory_.GetWeakPtr(), std::move(file_)); return task_runner_->PostTaskAndReply( FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)), BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); } } // namespace base