| // 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 |