Home | History | Annotate | Download | only in files
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/files/file_proxy.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/files/file.h"
     10 #include "base/files/file_util.h"
     11 #include "base/location.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/task_runner.h"
     14 #include "base/task_runner_util.h"
     15 
     16 namespace {
     17 
     18 void FileDeleter(base::File file) {
     19 }
     20 
     21 }  // namespace
     22 
     23 namespace base {
     24 
     25 class FileHelper {
     26  public:
     27    FileHelper(FileProxy* proxy, File file)
     28       : file_(file.Pass()),
     29         error_(File::FILE_ERROR_FAILED),
     30         task_runner_(proxy->task_runner()),
     31         proxy_(AsWeakPtr(proxy)) {
     32    }
     33 
     34    void PassFile() {
     35      if (proxy_)
     36        proxy_->SetFile(file_.Pass());
     37      else if (file_.IsValid())
     38        task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
     39    }
     40 
     41  protected:
     42   File file_;
     43   File::Error error_;
     44 
     45  private:
     46   scoped_refptr<TaskRunner> task_runner_;
     47   WeakPtr<FileProxy> proxy_;
     48   DISALLOW_COPY_AND_ASSIGN(FileHelper);
     49 };
     50 
     51 namespace {
     52 
     53 class GenericFileHelper : public FileHelper {
     54  public:
     55   GenericFileHelper(FileProxy* proxy, File file)
     56       : FileHelper(proxy, file.Pass()) {
     57   }
     58 
     59   void Close() {
     60     file_.Close();
     61     error_ = File::FILE_OK;
     62   }
     63 
     64   void SetTimes(Time last_access_time, Time last_modified_time) {
     65     bool rv = file_.SetTimes(last_access_time, last_modified_time);
     66     error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
     67   }
     68 
     69   void SetLength(int64 length) {
     70     if (file_.SetLength(length))
     71       error_ = File::FILE_OK;
     72   }
     73 
     74   void Flush() {
     75     if (file_.Flush())
     76       error_ = File::FILE_OK;
     77   }
     78 
     79   void Reply(const FileProxy::StatusCallback& callback) {
     80     PassFile();
     81     if (!callback.is_null())
     82       callback.Run(error_);
     83   }
     84 
     85  private:
     86   DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
     87 };
     88 
     89 class CreateOrOpenHelper : public FileHelper {
     90  public:
     91   CreateOrOpenHelper(FileProxy* proxy, File file)
     92       : FileHelper(proxy, file.Pass()) {
     93   }
     94 
     95   void RunWork(const FilePath& file_path, int file_flags) {
     96     file_.Initialize(file_path, file_flags);
     97     error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
     98   }
     99 
    100   void Reply(const FileProxy::StatusCallback& callback) {
    101     DCHECK(!callback.is_null());
    102     PassFile();
    103     callback.Run(error_);
    104   }
    105 
    106  private:
    107   DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
    108 };
    109 
    110 class CreateTemporaryHelper : public FileHelper {
    111  public:
    112   CreateTemporaryHelper(FileProxy* proxy, File file)
    113       : FileHelper(proxy, file.Pass()) {
    114   }
    115 
    116   void RunWork(uint32 additional_file_flags) {
    117     // TODO(darin): file_util should have a variant of CreateTemporaryFile
    118     // that returns a FilePath and a File.
    119     if (!CreateTemporaryFile(&file_path_)) {
    120       // TODO(davidben): base::CreateTemporaryFile should preserve the error
    121       // code.
    122       error_ = File::FILE_ERROR_FAILED;
    123       return;
    124     }
    125 
    126     uint32 file_flags = File::FLAG_WRITE |
    127                         File::FLAG_TEMPORARY |
    128                         File::FLAG_CREATE_ALWAYS |
    129                         additional_file_flags;
    130 
    131     file_.Initialize(file_path_, file_flags);
    132     if (file_.IsValid()) {
    133       error_ = File::FILE_OK;
    134     } else {
    135       error_ = file_.error_details();
    136       DeleteFile(file_path_, false);
    137       file_path_.clear();
    138     }
    139   }
    140 
    141   void Reply(const FileProxy::CreateTemporaryCallback& callback) {
    142     DCHECK(!callback.is_null());
    143     PassFile();
    144     callback.Run(error_, file_path_);
    145   }
    146 
    147  private:
    148   FilePath file_path_;
    149   DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
    150 };
    151 
    152 class GetInfoHelper : public FileHelper {
    153  public:
    154   GetInfoHelper(FileProxy* proxy, File file)
    155       : FileHelper(proxy, file.Pass()) {
    156   }
    157 
    158   void RunWork() {
    159     if (file_.GetInfo(&file_info_))
    160       error_  = File::FILE_OK;
    161   }
    162 
    163   void Reply(const FileProxy::GetFileInfoCallback& callback) {
    164     PassFile();
    165     DCHECK(!callback.is_null());
    166     callback.Run(error_, file_info_);
    167   }
    168 
    169  private:
    170   File::Info file_info_;
    171   DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
    172 };
    173 
    174 class ReadHelper : public FileHelper {
    175  public:
    176   ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
    177       : FileHelper(proxy, file.Pass()),
    178         buffer_(new char[bytes_to_read]),
    179         bytes_to_read_(bytes_to_read),
    180         bytes_read_(0) {
    181   }
    182 
    183   void RunWork(int64 offset) {
    184     bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
    185     error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
    186   }
    187 
    188   void Reply(const FileProxy::ReadCallback& callback) {
    189     PassFile();
    190     DCHECK(!callback.is_null());
    191     callback.Run(error_, buffer_.get(), bytes_read_);
    192   }
    193 
    194  private:
    195   scoped_ptr<char[]> buffer_;
    196   int bytes_to_read_;
    197   int bytes_read_;
    198   DISALLOW_COPY_AND_ASSIGN(ReadHelper);
    199 };
    200 
    201 class WriteHelper : public FileHelper {
    202  public:
    203   WriteHelper(FileProxy* proxy,
    204               File file,
    205               const char* buffer, int bytes_to_write)
    206       : FileHelper(proxy, file.Pass()),
    207         buffer_(new char[bytes_to_write]),
    208         bytes_to_write_(bytes_to_write),
    209         bytes_written_(0) {
    210     memcpy(buffer_.get(), buffer, bytes_to_write);
    211   }
    212 
    213   void RunWork(int64 offset) {
    214     bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
    215     error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
    216   }
    217 
    218   void Reply(const FileProxy::WriteCallback& callback) {
    219     PassFile();
    220     if (!callback.is_null())
    221       callback.Run(error_, bytes_written_);
    222   }
    223 
    224  private:
    225   scoped_ptr<char[]> buffer_;
    226   int bytes_to_write_;
    227   int bytes_written_;
    228   DISALLOW_COPY_AND_ASSIGN(WriteHelper);
    229 };
    230 
    231 }  // namespace
    232 
    233 FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
    234 }
    235 
    236 FileProxy::~FileProxy() {
    237   if (file_.IsValid())
    238     task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
    239 }
    240 
    241 bool FileProxy::CreateOrOpen(const FilePath& file_path,
    242                              uint32 file_flags,
    243                              const StatusCallback& callback) {
    244   DCHECK(!file_.IsValid());
    245   CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
    246   return task_runner_->PostTaskAndReply(
    247       FROM_HERE,
    248       Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
    249            file_flags),
    250       Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback));
    251 }
    252 
    253 bool FileProxy::CreateTemporary(uint32 additional_file_flags,
    254                                 const CreateTemporaryCallback& callback) {
    255   DCHECK(!file_.IsValid());
    256   CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
    257   return task_runner_->PostTaskAndReply(
    258       FROM_HERE,
    259       Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
    260            additional_file_flags),
    261       Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
    262 }
    263 
    264 bool FileProxy::IsValid() const {
    265   return file_.IsValid();
    266 }
    267 
    268 void FileProxy::SetFile(File file) {
    269   DCHECK(!file_.IsValid());
    270   file_ = file.Pass();
    271 }
    272 
    273 File FileProxy::TakeFile() {
    274   return file_.Pass();
    275 }
    276 
    277 PlatformFile FileProxy::GetPlatformFile() const {
    278   return file_.GetPlatformFile();
    279 }
    280 
    281 bool FileProxy::Close(const StatusCallback& callback) {
    282   DCHECK(file_.IsValid());
    283   GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
    284   return task_runner_->PostTaskAndReply(
    285       FROM_HERE,
    286       Bind(&GenericFileHelper::Close, Unretained(helper)),
    287       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
    288 }
    289 
    290 bool FileProxy::GetInfo(const GetFileInfoCallback& callback) {
    291   DCHECK(file_.IsValid());
    292   GetInfoHelper* helper = new GetInfoHelper(this, file_.Pass());
    293   return task_runner_->PostTaskAndReply(
    294       FROM_HERE,
    295       Bind(&GetInfoHelper::RunWork, Unretained(helper)),
    296       Bind(&GetInfoHelper::Reply, Owned(helper), callback));
    297 }
    298 
    299 bool FileProxy::Read(int64 offset,
    300                      int bytes_to_read,
    301                      const ReadCallback& callback) {
    302   DCHECK(file_.IsValid());
    303   if (bytes_to_read < 0)
    304     return false;
    305 
    306   ReadHelper* helper = new ReadHelper(this, file_.Pass(), bytes_to_read);
    307   return task_runner_->PostTaskAndReply(
    308       FROM_HERE,
    309       Bind(&ReadHelper::RunWork, Unretained(helper), offset),
    310       Bind(&ReadHelper::Reply, Owned(helper), callback));
    311 }
    312 
    313 bool FileProxy::Write(int64 offset,
    314                       const char* buffer,
    315                       int bytes_to_write,
    316                       const WriteCallback& callback) {
    317   DCHECK(file_.IsValid());
    318   if (bytes_to_write <= 0 || buffer == NULL)
    319     return false;
    320 
    321   WriteHelper* helper =
    322       new WriteHelper(this, file_.Pass(), buffer, bytes_to_write);
    323   return task_runner_->PostTaskAndReply(
    324       FROM_HERE,
    325       Bind(&WriteHelper::RunWork, Unretained(helper), offset),
    326       Bind(&WriteHelper::Reply, Owned(helper), callback));
    327 }
    328 
    329 bool FileProxy::SetTimes(Time last_access_time,
    330                          Time last_modified_time,
    331                          const StatusCallback& callback) {
    332   DCHECK(file_.IsValid());
    333   GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
    334   return task_runner_->PostTaskAndReply(
    335       FROM_HERE,
    336       Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time,
    337            last_modified_time),
    338       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
    339 }
    340 
    341 bool FileProxy::SetLength(int64 length, const StatusCallback& callback) {
    342   DCHECK(file_.IsValid());
    343   GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
    344   return task_runner_->PostTaskAndReply(
    345       FROM_HERE,
    346       Bind(&GenericFileHelper::SetLength, Unretained(helper), length),
    347       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
    348 }
    349 
    350 bool FileProxy::Flush(const StatusCallback& callback) {
    351   DCHECK(file_.IsValid());
    352   GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
    353   return task_runner_->PostTaskAndReply(
    354       FROM_HERE,
    355       Bind(&GenericFileHelper::Flush, Unretained(helper)),
    356       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
    357 }
    358 
    359 }  // namespace base
    360