| // Copyright 2014 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 "base/files/file_proxy.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/task_runner.h" |
| #include "base/task_runner_util.h" |
| |
| namespace { |
| |
| void FileDeleter(base::File file) { |
| } |
| |
| } // namespace |
| |
| namespace base { |
| |
| class FileHelper { |
| public: |
| FileHelper(FileProxy* proxy, File file) |
| : file_(std::move(file)), |
| error_(File::FILE_ERROR_FAILED), |
| task_runner_(proxy->task_runner()), |
| proxy_(AsWeakPtr(proxy)) { |
| } |
| |
| 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_; |
| |
| private: |
| scoped_refptr<TaskRunner> task_runner_; |
| WeakPtr<FileProxy> proxy_; |
| DISALLOW_COPY_AND_ASSIGN(FileHelper); |
| }; |
| |
| namespace { |
| |
| class GenericFileHelper : public FileHelper { |
| public: |
| GenericFileHelper(FileProxy* proxy, File file) |
| : FileHelper(proxy, std::move(file)) { |
| } |
| |
| 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_); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GenericFileHelper); |
| }; |
| |
| class CreateOrOpenHelper : public FileHelper { |
| public: |
| CreateOrOpenHelper(FileProxy* proxy, File file) |
| : FileHelper(proxy, std::move(file)) { |
| } |
| |
| void RunWork(const FilePath& file_path, int 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_); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper); |
| }; |
| |
| class CreateTemporaryHelper : public FileHelper { |
| public: |
| CreateTemporaryHelper(FileProxy* proxy, File file) |
| : FileHelper(proxy, std::move(file)) { |
| } |
| |
| 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_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_, false); |
| file_path_.clear(); |
| } |
| } |
| |
| void Reply(FileProxy::CreateTemporaryCallback callback) { |
| DCHECK(!callback.is_null()); |
| PassFile(); |
| std::move(callback).Run(error_, file_path_); |
| } |
| |
| private: |
| FilePath file_path_; |
| DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper); |
| }; |
| |
| class GetInfoHelper : public FileHelper { |
| public: |
| GetInfoHelper(FileProxy* proxy, File file) |
| : FileHelper(proxy, std::move(file)) { |
| } |
| |
| 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_; |
| DISALLOW_COPY_AND_ASSIGN(GetInfoHelper); |
| }; |
| |
| class ReadHelper : public FileHelper { |
| public: |
| ReadHelper(FileProxy* proxy, File file, int bytes_to_read) |
| : FileHelper(proxy, std::move(file)), |
| buffer_(new char[bytes_to_read]), |
| bytes_to_read_(bytes_to_read), |
| bytes_read_(0) { |
| } |
| |
| void RunWork(int64_t offset) { |
| bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_); |
| error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; |
| } |
| |
| void Reply(FileProxy::ReadCallback callback) { |
| PassFile(); |
| DCHECK(!callback.is_null()); |
| std::move(callback).Run(error_, buffer_.get(), bytes_read_); |
| } |
| |
| private: |
| std::unique_ptr<char[]> buffer_; |
| int bytes_to_read_; |
| int bytes_read_; |
| DISALLOW_COPY_AND_ASSIGN(ReadHelper); |
| }; |
| |
| class WriteHelper : public FileHelper { |
| public: |
| WriteHelper(FileProxy* proxy, |
| File file, |
| const char* buffer, int bytes_to_write) |
| : FileHelper(proxy, std::move(file)), |
| buffer_(new char[bytes_to_write]), |
| bytes_to_write_(bytes_to_write), |
| bytes_written_(0) { |
| memcpy(buffer_.get(), buffer, bytes_to_write); |
| } |
| |
| void RunWork(int64_t offset) { |
| bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_); |
| error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; |
| } |
| |
| void Reply(FileProxy::WriteCallback callback) { |
| PassFile(); |
| if (!callback.is_null()) |
| std::move(callback).Run(error_, bytes_written_); |
| } |
| |
| private: |
| std::unique_ptr<char[]> buffer_; |
| int bytes_to_write_; |
| int bytes_written_; |
| DISALLOW_COPY_AND_ASSIGN(WriteHelper); |
| }; |
| |
| } // 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(this, 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(this, 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(this, 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(this, 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(this, 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, |
| const char* buffer, |
| int bytes_to_write, |
| WriteCallback callback) { |
| DCHECK(file_.IsValid()); |
| if (bytes_to_write <= 0 || buffer == nullptr) |
| return false; |
| |
| WriteHelper* helper = |
| new WriteHelper(this, std::move(file_), buffer, bytes_to_write); |
| 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(this, 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(this, 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(this, std::move(file_)); |
| return task_runner_->PostTaskAndReply( |
| FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)), |
| BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); |
| } |
| |
| } // namespace base |