Home | History | Annotate | Download | only in fileapi
      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 "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "base/files/file.h"
      9 #include "base/memory/ref_counted.h"
     10 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
     11 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
     12 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "net/base/io_buffer.h"
     15 #include "net/base/net_errors.h"
     16 
     17 using content::BrowserThread;
     18 
     19 namespace chromeos {
     20 namespace file_system_provider {
     21 namespace {
     22 
     23 // Dicards the callback from CloseFile().
     24 void EmptyStatusCallback(base::File::Error /* result */) {
     25 }
     26 
     27 // Converts net::CompletionCallback to net::Int64CompletionCallback.
     28 void Int64ToIntCompletionCallback(net::CompletionCallback callback,
     29                                   int64 result) {
     30   callback.Run(static_cast<int>(result));
     31 }
     32 
     33 }  // namespace
     34 
     35 class FileStreamReader::OperationRunner
     36     : public base::RefCountedThreadSafe<FileStreamReader::OperationRunner> {
     37  public:
     38   OperationRunner() : file_handle_(-1) {}
     39 
     40   // Opens a file for reading and calls the completion callback. Must be called
     41   // on UI thread.
     42   void OpenFileOnUIThread(
     43       const storage::FileSystemURL& url,
     44       const storage::AsyncFileUtil::StatusCallback& callback) {
     45     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     46 
     47     util::FileSystemURLParser parser(url);
     48     if (!parser.Parse()) {
     49       BrowserThread::PostTask(
     50           BrowserThread::IO,
     51           FROM_HERE,
     52           base::Bind(callback, base::File::FILE_ERROR_SECURITY));
     53       return;
     54     }
     55 
     56     file_system_ = parser.file_system()->GetWeakPtr();
     57     file_path_ = parser.file_path();
     58     abort_callback_ = parser.file_system()->OpenFile(
     59         file_path_,
     60         ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
     61         base::Bind(
     62             &OperationRunner::OnOpenFileCompletedOnUIThread, this, callback));
     63   }
     64 
     65   // Closes a file. Ignores result, since it is called from a constructor.
     66   // Must be called on UI thread.
     67   void CloseFileOnUIThread() {
     68     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     69     if (file_system_.get() && file_handle_ != -1) {
     70       // Closing a file must not be aborted, since we could end up on files
     71       // which are never closed.
     72       file_system_->CloseFile(file_handle_, base::Bind(&EmptyStatusCallback));
     73     }
     74   }
     75 
     76   // Requests reading contents of a file. In case of either success or a failure
     77   // |callback| is executed. It can be called many times, until |has_more| is
     78   // set to false. This function guarantees that it will succeed only if the
     79   // file has not been changed while reading. Must be called on UI thread.
     80   void ReadFileOnUIThread(
     81       scoped_refptr<net::IOBuffer> buffer,
     82       int64 offset,
     83       int length,
     84       const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
     85     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     86 
     87     // If the file system got unmounted, then abort the reading operation.
     88     if (!file_system_.get()) {
     89       BrowserThread::PostTask(
     90           BrowserThread::IO,
     91           FROM_HERE,
     92           base::Bind(
     93               callback, 0, false /* has_more */, base::File::FILE_ERROR_ABORT));
     94       return;
     95     }
     96 
     97     abort_callback_ = file_system_->ReadFile(
     98         file_handle_,
     99         buffer.get(),
    100         offset,
    101         length,
    102         base::Bind(
    103             &OperationRunner::OnReadFileCompletedOnUIThread, this, callback));
    104   }
    105 
    106   // Requests metadata of a file. In case of either succes or a failure,
    107   // |callback| is executed. Must be called on UI thread.
    108   void GetMetadataOnUIThread(
    109       const ProvidedFileSystemInterface::GetMetadataCallback& callback) {
    110     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    111 
    112     // If the file system got unmounted, then abort the get length operation.
    113     if (!file_system_.get()) {
    114       BrowserThread::PostTask(
    115           BrowserThread::IO,
    116           FROM_HERE,
    117           base::Bind(callback,
    118                      base::Passed(make_scoped_ptr<EntryMetadata>(NULL)),
    119                      base::File::FILE_ERROR_ABORT));
    120       return;
    121     }
    122 
    123     abort_callback_ = file_system_->GetMetadata(
    124         file_path_,
    125         ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT,
    126         base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread,
    127                    this,
    128                    callback));
    129   }
    130 
    131   // Aborts the most recent operation (if exists), and calls the callback.
    132   void AbortOnUIThread(const storage::AsyncFileUtil::StatusCallback& callback) {
    133     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    134 
    135     if (abort_callback_.is_null()) {
    136       // No operation to be cancelled. At most a callback call, which will be
    137       // discarded.
    138       BrowserThread::PostTask(BrowserThread::IO,
    139                               FROM_HERE,
    140                               base::Bind(callback, base::File::FILE_OK));
    141       return;
    142     }
    143 
    144     const ProvidedFileSystemInterface::AbortCallback abort_callback =
    145         abort_callback_;
    146     abort_callback_ = ProvidedFileSystemInterface::AbortCallback();
    147     abort_callback.Run(base::Bind(
    148         &OperationRunner::OnAbortCompletedOnUIThread, this, callback));
    149   }
    150 
    151  private:
    152   friend class base::RefCountedThreadSafe<OperationRunner>;
    153 
    154   virtual ~OperationRunner() {}
    155 
    156   // Remembers a file handle for further operations and forwards the result to
    157   // the IO thread.
    158   void OnOpenFileCompletedOnUIThread(
    159       const storage::AsyncFileUtil::StatusCallback& callback,
    160       int file_handle,
    161       base::File::Error result) {
    162     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    163 
    164     file_handle_ = file_handle;
    165     BrowserThread::PostTask(
    166         BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
    167   }
    168 
    169   // Forwards a metadata to the IO thread.
    170   void OnGetMetadataCompletedOnUIThread(
    171       const ProvidedFileSystemInterface::GetMetadataCallback& callback,
    172       scoped_ptr<EntryMetadata> metadata,
    173       base::File::Error result) {
    174     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    175     BrowserThread::PostTask(
    176         BrowserThread::IO,
    177         FROM_HERE,
    178         base::Bind(callback, base::Passed(&metadata), result));
    179   }
    180 
    181   // Forwards a response of reading from a file to the IO thread.
    182   void OnReadFileCompletedOnUIThread(
    183       const ProvidedFileSystemInterface::ReadChunkReceivedCallback&
    184           chunk_received_callback,
    185       int chunk_length,
    186       bool has_more,
    187       base::File::Error result) {
    188     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    189     BrowserThread::PostTask(
    190         BrowserThread::IO,
    191         FROM_HERE,
    192         base::Bind(chunk_received_callback, chunk_length, has_more, result));
    193   }
    194 
    195   // Forwards a response of aborting an operation to the IO thread.
    196   void OnAbortCompletedOnUIThread(
    197       const storage::AsyncFileUtil::StatusCallback& callback,
    198       base::File::Error result) {
    199     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    200     BrowserThread::PostTask(
    201         BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
    202   }
    203 
    204   ProvidedFileSystemInterface::AbortCallback abort_callback_;
    205   base::WeakPtr<ProvidedFileSystemInterface> file_system_;
    206   base::FilePath file_path_;
    207   int file_handle_;
    208 
    209   DISALLOW_COPY_AND_ASSIGN(OperationRunner);
    210 };
    211 
    212 FileStreamReader::FileStreamReader(storage::FileSystemContext* context,
    213                                    const storage::FileSystemURL& url,
    214                                    int64 initial_offset,
    215                                    const base::Time& expected_modification_time)
    216     : url_(url),
    217       current_offset_(initial_offset),
    218       current_length_(0),
    219       expected_modification_time_(expected_modification_time),
    220       runner_(new OperationRunner),
    221       state_(NOT_INITIALIZED),
    222       weak_ptr_factory_(this) {
    223 }
    224 
    225 FileStreamReader::~FileStreamReader() {
    226   // FileStreamReader doesn't have a Cancel() method like in FileStreamWriter.
    227   // Therefore, aborting is done from the destructor.
    228   BrowserThread::PostTask(BrowserThread::UI,
    229                           FROM_HERE,
    230                           base::Bind(&OperationRunner::AbortOnUIThread,
    231                                      runner_,
    232                                      base::Bind(&EmptyStatusCallback)));
    233 
    234   BrowserThread::PostTask(
    235       BrowserThread::UI,
    236       FROM_HERE,
    237       base::Bind(&OperationRunner::CloseFileOnUIThread, runner_));
    238 }
    239 
    240 void FileStreamReader::Initialize(
    241     const base::Closure& pending_closure,
    242     const net::Int64CompletionCallback& error_callback) {
    243   DCHECK_EQ(NOT_INITIALIZED, state_);
    244   state_ = INITIALIZING;
    245 
    246   BrowserThread::PostTask(
    247       BrowserThread::UI,
    248       FROM_HERE,
    249       base::Bind(&OperationRunner::OpenFileOnUIThread,
    250                  runner_,
    251                  url_,
    252                  base::Bind(&FileStreamReader::OnOpenFileCompleted,
    253                             weak_ptr_factory_.GetWeakPtr(),
    254                             pending_closure,
    255                             error_callback)));
    256 }
    257 
    258 void FileStreamReader::OnOpenFileCompleted(
    259     const base::Closure& pending_closure,
    260     const net::Int64CompletionCallback& error_callback,
    261     base::File::Error result) {
    262   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    263   DCHECK_EQ(INITIALIZING, state_);
    264 
    265   // In case of an error, return immediately using the |error_callback| of the
    266   // Read() or GetLength() pending request.
    267   if (result != base::File::FILE_OK) {
    268     state_ = FAILED;
    269     error_callback.Run(net::FileErrorToNetError(result));
    270     return;
    271   }
    272 
    273   DCHECK_EQ(base::File::FILE_OK, result);
    274 
    275   // Verify the last modification time.
    276   BrowserThread::PostTask(
    277       BrowserThread::UI,
    278       FROM_HERE,
    279       base::Bind(&OperationRunner::GetMetadataOnUIThread,
    280                  runner_,
    281                  base::Bind(&FileStreamReader::OnInitializeCompleted,
    282                             weak_ptr_factory_.GetWeakPtr(),
    283                             pending_closure,
    284                             error_callback)));
    285 }
    286 
    287 void FileStreamReader::OnInitializeCompleted(
    288     const base::Closure& pending_closure,
    289     const net::Int64CompletionCallback& error_callback,
    290     scoped_ptr<EntryMetadata> metadata,
    291     base::File::Error result) {
    292   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    293   DCHECK_EQ(INITIALIZING, state_);
    294 
    295   // In case of an error, abort.
    296   if (result != base::File::FILE_OK) {
    297     state_ = FAILED;
    298     error_callback.Run(net::FileErrorToNetError(result));
    299     return;
    300   }
    301 
    302   // If the file modification time has changed, then abort. Note, that the file
    303   // may be changed without affecting the modification time.
    304   DCHECK(metadata.get());
    305   if (!expected_modification_time_.is_null() &&
    306       metadata->modification_time != expected_modification_time_) {
    307     state_ = FAILED;
    308     error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
    309     return;
    310   }
    311 
    312   DCHECK_EQ(base::File::FILE_OK, result);
    313   state_ = INITIALIZED;
    314 
    315   // Run the task waiting for the initialization to be completed.
    316   pending_closure.Run();
    317 }
    318 
    319 int FileStreamReader::Read(net::IOBuffer* buffer,
    320                            int buffer_length,
    321                            const net::CompletionCallback& callback) {
    322   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    323   TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
    324                            "FileStreamReader::Read",
    325                            this,
    326                            "buffer_length",
    327                            buffer_length);
    328 
    329   switch (state_) {
    330     case NOT_INITIALIZED:
    331       // Lazily initialize with the first call to Read().
    332     Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized,
    333                           weak_ptr_factory_.GetWeakPtr(),
    334                           make_scoped_refptr(buffer),
    335                           buffer_length,
    336                           base::Bind(&FileStreamReader::OnReadCompleted,
    337                                      weak_ptr_factory_.GetWeakPtr(),
    338                                      callback)),
    339                base::Bind(&Int64ToIntCompletionCallback,
    340                           base::Bind(&FileStreamReader::OnReadCompleted,
    341                                      weak_ptr_factory_.GetWeakPtr(),
    342                                      callback)));
    343     break;
    344 
    345     case INITIALIZING:
    346       NOTREACHED();
    347       break;
    348 
    349     case INITIALIZED:
    350       ReadAfterInitialized(buffer,
    351                            buffer_length,
    352                            base::Bind(&FileStreamReader::OnReadCompleted,
    353                                       weak_ptr_factory_.GetWeakPtr(),
    354                                       callback));
    355       break;
    356 
    357     case FAILED:
    358       NOTREACHED();
    359       break;
    360   }
    361 
    362   return net::ERR_IO_PENDING;
    363 }
    364 
    365 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback,
    366                                        int result) {
    367   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    368   callback.Run(static_cast<int>(result));
    369   TRACE_EVENT_ASYNC_END0(
    370       "file_system_provider", "FileStreamReader::Read", this);
    371 }
    372 
    373 int64 FileStreamReader::GetLength(
    374     const net::Int64CompletionCallback& callback) {
    375   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    376 
    377   switch (state_) {
    378     case NOT_INITIALIZED:
    379       // Lazily initialize with the first call to GetLength().
    380     Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized,
    381                           weak_ptr_factory_.GetWeakPtr(),
    382                           callback),
    383                callback);
    384     break;
    385 
    386     case INITIALIZING:
    387       NOTREACHED();
    388       break;
    389 
    390     case INITIALIZED:
    391       GetLengthAfterInitialized(callback);
    392       break;
    393 
    394     case FAILED:
    395       NOTREACHED();
    396       break;
    397   }
    398 
    399   return net::ERR_IO_PENDING;
    400 }
    401 
    402 void FileStreamReader::ReadAfterInitialized(
    403     scoped_refptr<net::IOBuffer> buffer,
    404     int buffer_length,
    405     const net::CompletionCallback& callback) {
    406   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    407   DCHECK_EQ(INITIALIZED, state_);
    408 
    409   current_length_ = 0;
    410   BrowserThread::PostTask(
    411       BrowserThread::UI,
    412       FROM_HERE,
    413       base::Bind(&OperationRunner::ReadFileOnUIThread,
    414                  runner_,
    415                  buffer,
    416                  current_offset_,
    417                  buffer_length,
    418                  base::Bind(&FileStreamReader::OnReadChunkReceived,
    419                             weak_ptr_factory_.GetWeakPtr(),
    420                             callback)));
    421 }
    422 
    423 void FileStreamReader::GetLengthAfterInitialized(
    424     const net::Int64CompletionCallback& callback) {
    425   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    426   DCHECK_EQ(INITIALIZED, state_);
    427 
    428   BrowserThread::PostTask(
    429       BrowserThread::UI,
    430       FROM_HERE,
    431       base::Bind(
    432           &OperationRunner::GetMetadataOnUIThread,
    433           runner_,
    434           base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived,
    435                      weak_ptr_factory_.GetWeakPtr(),
    436                      callback)));
    437 }
    438 
    439 void FileStreamReader::OnReadChunkReceived(
    440     const net::CompletionCallback& callback,
    441     int chunk_length,
    442     bool has_more,
    443     base::File::Error result) {
    444   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    445   DCHECK_EQ(INITIALIZED, state_);
    446 
    447   current_length_ += chunk_length;
    448 
    449   // If this is the last chunk with a success, then finalize.
    450   if (!has_more && result == base::File::FILE_OK) {
    451     current_offset_ += current_length_;
    452     callback.Run(current_length_);
    453     return;
    454   }
    455 
    456   // In case of an error, abort.
    457   if (result != base::File::FILE_OK) {
    458     DCHECK(!has_more);
    459     state_ = FAILED;
    460     callback.Run(net::FileErrorToNetError(result));
    461     return;
    462   }
    463 
    464   // More data is about to come, so do not call the callback yet.
    465   DCHECK(has_more);
    466 }
    467 
    468 void FileStreamReader::OnGetMetadataForGetLengthReceived(
    469     const net::Int64CompletionCallback& callback,
    470     scoped_ptr<EntryMetadata> metadata,
    471     base::File::Error result) {
    472   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    473   DCHECK_EQ(INITIALIZED, state_);
    474 
    475   // In case of an error, abort.
    476   if (result != base::File::FILE_OK) {
    477     state_ = FAILED;
    478     callback.Run(net::FileErrorToNetError(result));
    479     return;
    480   }
    481 
    482   // If the file modification time has changed, then abort. Note, that the file
    483   // may be changed without affecting the modification time.
    484   DCHECK(metadata.get());
    485   if (!expected_modification_time_.is_null() &&
    486       metadata->modification_time != expected_modification_time_) {
    487     callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
    488     return;
    489   }
    490 
    491   DCHECK_EQ(base::File::FILE_OK, result);
    492   callback.Run(metadata->size);
    493 }
    494 
    495 }  // namespace file_system_provider
    496 }  // namespace chromeos
    497