Home | History | Annotate | Download | only in drive
      1 // Copyright 2013 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 "chrome/browser/chromeos/drive/local_file_reader.h"
      6 
      7 #include <errno.h>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/files/file_path.h"
     12 #include "base/location.h"
     13 #include "base/platform_file.h"
     14 #include "base/sequenced_task_runner.h"
     15 #include "base/task_runner_util.h"
     16 #include "net/base/completion_callback.h"
     17 #include "net/base/io_buffer.h"
     18 #include "net/base/net_errors.h"
     19 
     20 namespace drive {
     21 namespace util {
     22 
     23 namespace {
     24 
     25 // Opens the file at |file_path| and seeks to the |offset| from begin.
     26 // Returns the net::Error code. If succeeded, |platform_file| is set to point
     27 // the opened file.
     28 // This function should run on the blocking pool.
     29 int OpenAndSeekOnBlockingPool(const base::FilePath& file_path,
     30                               int64 offset,
     31                               base::PlatformFile* platform_file) {
     32   DCHECK(platform_file);
     33   DCHECK_EQ(base::kInvalidPlatformFileValue, *platform_file);
     34 
     35   // First of all, open the file.
     36   const int open_flags = base::PLATFORM_FILE_OPEN |
     37                          base::PLATFORM_FILE_READ |
     38                          base::PLATFORM_FILE_ASYNC;
     39   base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
     40   base::PlatformFile file =
     41       base::CreatePlatformFile(file_path, open_flags, NULL, &error);
     42   if (file == base::kInvalidPlatformFileValue) {
     43     DCHECK_NE(base::PLATFORM_FILE_OK, error);
     44     return net::PlatformFileErrorToNetError(error);
     45   }
     46 
     47   // If succeeded, seek to the |offset| from begin.
     48   int64 position = base::SeekPlatformFile(
     49       file, base::PLATFORM_FILE_FROM_BEGIN, offset);
     50   if (position < 0) {
     51     // If failed, close the file and return an error.
     52     base::ClosePlatformFile(file);
     53     return net::ERR_FAILED;
     54   }
     55 
     56   *platform_file = file;
     57   return net::OK;
     58 }
     59 
     60 // Reads the data from the |platform_file| and copies it to the |buffer| at
     61 // most |buffer_length| size. Returns the number of copied bytes if succeeded,
     62 // or the net::Error code.
     63 // This function should run on the blocking pool.
     64 int ReadOnBlockingPool(base::PlatformFile platform_file,
     65                        scoped_refptr<net::IOBuffer> buffer,
     66                        int buffer_length) {
     67   DCHECK_NE(base::kInvalidPlatformFileValue, platform_file);
     68   int result = base::ReadPlatformFileCurPosNoBestEffort(
     69       platform_file, buffer->data(), buffer_length);
     70   return result < 0 ? net::MapSystemError(errno) : result;
     71 }
     72 
     73 // Posts a task to close the |platform_file| into |task_runner|.
     74 // Or, if |platform_file| is kInvalidPlatformFileValue, does nothing.
     75 void PostCloseIfNeeded(base::TaskRunner* task_runner,
     76                        base::PlatformFile platform_file) {
     77   DCHECK(task_runner);
     78   if (platform_file != base::kInvalidPlatformFileValue) {
     79     task_runner->PostTask(
     80         FROM_HERE,
     81         base::Bind(
     82             base::IgnoreResult(&base::ClosePlatformFile), platform_file));
     83   }
     84 }
     85 
     86 }  // namespace
     87 
     88 class LocalFileReader::ScopedPlatformFile {
     89  public:
     90   explicit ScopedPlatformFile(base::TaskRunner* task_runner)
     91       : task_runner_(task_runner),
     92         platform_file_(base::kInvalidPlatformFileValue) {
     93     DCHECK(task_runner);
     94   }
     95 
     96   ~ScopedPlatformFile() {
     97     PostCloseIfNeeded(task_runner_.get(), platform_file_);
     98   }
     99 
    100   base::PlatformFile* ptr() { return &platform_file_; }
    101 
    102   base::PlatformFile release() {
    103     base::PlatformFile result = platform_file_;
    104     platform_file_ = base::kInvalidPlatformFileValue;
    105     return result;
    106   }
    107 
    108  private:
    109   scoped_refptr<base::TaskRunner> task_runner_;
    110   base::PlatformFile platform_file_;
    111 
    112   DISALLOW_COPY_AND_ASSIGN(ScopedPlatformFile);
    113 };
    114 
    115 LocalFileReader::LocalFileReader(
    116     base::SequencedTaskRunner* sequenced_task_runner)
    117     : sequenced_task_runner_(sequenced_task_runner),
    118       platform_file_(base::kInvalidPlatformFileValue),
    119       weak_ptr_factory_(this) {
    120   DCHECK(sequenced_task_runner_.get());
    121 }
    122 
    123 LocalFileReader::~LocalFileReader() {
    124   PostCloseIfNeeded(sequenced_task_runner_.get(), platform_file_);
    125 }
    126 
    127 void LocalFileReader::Open(const base::FilePath& file_path,
    128                            int64 offset,
    129                            const net::CompletionCallback& callback) {
    130   DCHECK(!callback.is_null());
    131   DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_);
    132 
    133   ScopedPlatformFile* platform_file =
    134       new ScopedPlatformFile(sequenced_task_runner_.get());
    135   base::PostTaskAndReplyWithResult(
    136       sequenced_task_runner_.get(),
    137       FROM_HERE,
    138       base::Bind(
    139           &OpenAndSeekOnBlockingPool, file_path, offset, platform_file->ptr()),
    140       base::Bind(&LocalFileReader::OpenAfterBlockingPoolTask,
    141                  weak_ptr_factory_.GetWeakPtr(),
    142                  callback,
    143                  base::Owned(platform_file)));
    144 }
    145 
    146 void LocalFileReader::Read(net::IOBuffer* in_buffer,
    147                            int buffer_length,
    148                            const net::CompletionCallback& callback) {
    149   DCHECK(!callback.is_null());
    150   DCHECK_NE(base::kInvalidPlatformFileValue, platform_file_);
    151 
    152   scoped_refptr<net::IOBuffer> buffer(in_buffer);
    153   base::PostTaskAndReplyWithResult(
    154       sequenced_task_runner_.get(),
    155       FROM_HERE,
    156       base::Bind(&ReadOnBlockingPool, platform_file_, buffer, buffer_length),
    157       callback);
    158 }
    159 
    160 void LocalFileReader::OpenAfterBlockingPoolTask(
    161     const net::CompletionCallback& callback,
    162     ScopedPlatformFile* platform_file,
    163     int open_result) {
    164   DCHECK(!callback.is_null());
    165   DCHECK(platform_file);
    166   DCHECK_EQ(base::kInvalidPlatformFileValue, platform_file_);
    167 
    168   if (open_result == net::OK) {
    169     DCHECK_NE(base::kInvalidPlatformFileValue, *platform_file->ptr());
    170     platform_file_ = platform_file->release();
    171   }
    172 
    173   callback.Run(open_result);
    174 }
    175 
    176 }  // namespace util
    177 }  // namespace drive
    178