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_writer.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
     10 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
     11 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "net/base/io_buffer.h"
     14 #include "net/base/net_errors.h"
     15 
     16 using content::BrowserThread;
     17 
     18 namespace chromeos {
     19 namespace file_system_provider {
     20 namespace {
     21 
     22 // Dicards the callback from CloseFile().
     23 void EmptyStatusCallback(base::File::Error /* result */) {
     24 }
     25 
     26 }  // namespace
     27 
     28 class FileStreamWriter::OperationRunner
     29     : public base::RefCountedThreadSafe<FileStreamWriter::OperationRunner> {
     30  public:
     31   OperationRunner() : file_handle_(-1) {}
     32 
     33   // Opens a file for writing and calls the completion callback. Must be called
     34   // on UI thread.
     35   void OpenFileOnUIThread(
     36       const storage::FileSystemURL& url,
     37       const storage::AsyncFileUtil::StatusCallback& callback) {
     38     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     39 
     40     util::FileSystemURLParser parser(url);
     41     if (!parser.Parse()) {
     42       BrowserThread::PostTask(
     43           BrowserThread::IO,
     44           FROM_HERE,
     45           base::Bind(callback, base::File::FILE_ERROR_SECURITY));
     46       return;
     47     }
     48 
     49     file_system_ = parser.file_system()->GetWeakPtr();
     50     abort_callback_ = parser.file_system()->OpenFile(
     51         parser.file_path(),
     52         ProvidedFileSystemInterface::OPEN_FILE_MODE_WRITE,
     53         base::Bind(
     54             &OperationRunner::OnOpenFileCompletedOnUIThread, this, callback));
     55   }
     56 
     57   // Closes a file. Ignores result, since outlives the caller. Must be called on
     58   // UI thread.
     59   void CloseFileOnUIThread() {
     60     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     61     if (file_system_.get() && file_handle_ != -1) {
     62       // Closing a file must not be aborted, since we could end up on files
     63       // which are never closed.
     64       file_system_->CloseFile(file_handle_, base::Bind(&EmptyStatusCallback));
     65     }
     66   }
     67 
     68   // Requests writing bytes to the file. In case of either success or a failure
     69   // |callback| is executed. Must be called on UI thread.
     70   void WriteFileOnUIThread(
     71       scoped_refptr<net::IOBuffer> buffer,
     72       int64 offset,
     73       int length,
     74       const storage::AsyncFileUtil::StatusCallback& callback) {
     75     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     76 
     77     // If the file system got unmounted, then abort the writing operation.
     78     if (!file_system_.get()) {
     79       BrowserThread::PostTask(
     80           BrowserThread::IO,
     81           FROM_HERE,
     82           base::Bind(callback, base::File::FILE_ERROR_ABORT));
     83       return;
     84     }
     85 
     86     abort_callback_ = file_system_->WriteFile(
     87         file_handle_,
     88         buffer.get(),
     89         offset,
     90         length,
     91         base::Bind(
     92             &OperationRunner::OnWriteFileCompletedOnUIThread, this, callback));
     93   }
     94 
     95   // Aborts the most recent operation (if exists), and calls the callback.
     96   void AbortOnUIThread(const storage::AsyncFileUtil::StatusCallback& callback) {
     97     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     98 
     99     if (abort_callback_.is_null()) {
    100       // No operation to be cancelled. At most a callback call, which will be
    101       // discarded.
    102       BrowserThread::PostTask(BrowserThread::IO,
    103                               FROM_HERE,
    104                               base::Bind(callback, base::File::FILE_OK));
    105       return;
    106     }
    107 
    108     const ProvidedFileSystemInterface::AbortCallback abort_callback =
    109         abort_callback_;
    110     abort_callback_ = ProvidedFileSystemInterface::AbortCallback();
    111     abort_callback.Run(base::Bind(
    112         &OperationRunner::OnAbortCompletedOnUIThread, this, callback));
    113   }
    114 
    115  private:
    116   friend class base::RefCountedThreadSafe<OperationRunner>;
    117 
    118   virtual ~OperationRunner() {}
    119 
    120   // Remembers a file handle for further operations and forwards the result to
    121   // the IO thread.
    122   void OnOpenFileCompletedOnUIThread(
    123       const storage::AsyncFileUtil::StatusCallback& callback,
    124       int file_handle,
    125       base::File::Error result) {
    126     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    127 
    128     file_handle_ = file_handle;
    129     BrowserThread::PostTask(
    130         BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
    131   }
    132 
    133   // Forwards a response of writing to a file to the IO thread.
    134   void OnWriteFileCompletedOnUIThread(
    135       const storage::AsyncFileUtil::StatusCallback& callback,
    136       base::File::Error result) {
    137     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    138     BrowserThread::PostTask(
    139         BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
    140   }
    141 
    142   // Forwards a response of aborting an operation to the IO thread.
    143   void OnAbortCompletedOnUIThread(
    144       const storage::AsyncFileUtil::StatusCallback& callback,
    145       base::File::Error result) {
    146     DCHECK_CURRENTLY_ON(BrowserThread::UI);
    147     BrowserThread::PostTask(
    148         BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
    149   }
    150 
    151   ProvidedFileSystemInterface::AbortCallback abort_callback_;
    152   base::WeakPtr<ProvidedFileSystemInterface> file_system_;
    153   int file_handle_;
    154 
    155   DISALLOW_COPY_AND_ASSIGN(OperationRunner);
    156 };
    157 
    158 FileStreamWriter::FileStreamWriter(const storage::FileSystemURL& url,
    159                                    int64 initial_offset)
    160     : url_(url),
    161       current_offset_(initial_offset),
    162       runner_(new OperationRunner),
    163       state_(NOT_INITIALIZED),
    164       weak_ptr_factory_(this) {
    165 }
    166 
    167 FileStreamWriter::~FileStreamWriter() {
    168   BrowserThread::PostTask(
    169       BrowserThread::UI,
    170       FROM_HERE,
    171       base::Bind(&OperationRunner::CloseFileOnUIThread, runner_));
    172 }
    173 
    174 void FileStreamWriter::Initialize(
    175     const base::Closure& pending_closure,
    176     const net::CompletionCallback& error_callback) {
    177   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    178   DCHECK_EQ(NOT_INITIALIZED, state_);
    179   state_ = INITIALIZING;
    180 
    181   BrowserThread::PostTask(
    182       BrowserThread::UI,
    183       FROM_HERE,
    184       base::Bind(&OperationRunner::OpenFileOnUIThread,
    185                  runner_,
    186                  url_,
    187                  base::Bind(&FileStreamWriter::OnOpenFileCompleted,
    188                             weak_ptr_factory_.GetWeakPtr(),
    189                             pending_closure,
    190                             error_callback)));
    191 }
    192 
    193 void FileStreamWriter::OnOpenFileCompleted(
    194     const base::Closure& pending_closure,
    195     const net::CompletionCallback& error_callback,
    196     base::File::Error result) {
    197   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    198   DCHECK_EQ(INITIALIZING, state_);
    199 
    200   // In case of an error, return immediately using the |error_callback| of the
    201   // Write() pending request.
    202   if (result != base::File::FILE_OK) {
    203     state_ = FAILED;
    204     error_callback.Run(net::FileErrorToNetError(result));
    205     return;
    206   }
    207 
    208   DCHECK_EQ(base::File::FILE_OK, result);
    209   state_ = INITIALIZED;
    210 
    211   // Run the task waiting for the initialization to be completed.
    212   pending_closure.Run();
    213 }
    214 
    215 int FileStreamWriter::Write(net::IOBuffer* buffer,
    216                             int buffer_length,
    217                             const net::CompletionCallback& callback) {
    218   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    219   TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
    220                            "FileStreamWriter::Write",
    221                            this,
    222                            "buffer_length",
    223                            buffer_length);
    224 
    225   switch (state_) {
    226     case NOT_INITIALIZED:
    227       // Lazily initialize with the first call to Write().
    228       Initialize(base::Bind(&FileStreamWriter::WriteAfterInitialized,
    229                             weak_ptr_factory_.GetWeakPtr(),
    230                             make_scoped_refptr(buffer),
    231                             buffer_length,
    232                             base::Bind(&FileStreamWriter::OnWriteCompleted,
    233                                        weak_ptr_factory_.GetWeakPtr(),
    234                                        callback)),
    235                  base::Bind(&FileStreamWriter::OnWriteCompleted,
    236                             weak_ptr_factory_.GetWeakPtr(),
    237                             callback));
    238       break;
    239 
    240     case INITIALIZING:
    241       NOTREACHED();
    242       break;
    243 
    244     case INITIALIZED:
    245       WriteAfterInitialized(buffer,
    246                             buffer_length,
    247                             base::Bind(&FileStreamWriter::OnWriteCompleted,
    248                                        weak_ptr_factory_.GetWeakPtr(),
    249                                        callback));
    250       break;
    251 
    252     case FAILED:
    253       NOTREACHED();
    254       break;
    255   }
    256 
    257   return net::ERR_IO_PENDING;
    258 }
    259 
    260 int FileStreamWriter::Cancel(const net::CompletionCallback& callback) {
    261   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    262 
    263   BrowserThread::PostTask(
    264       BrowserThread::UI,
    265       FROM_HERE,
    266       base::Bind(&OperationRunner::AbortOnUIThread,
    267                  runner_,
    268                  base::Bind(&FileStreamWriter::OnAbortCompleted,
    269                             weak_ptr_factory_.GetWeakPtr(),
    270                             callback)));
    271   return net::ERR_IO_PENDING;
    272 }
    273 
    274 int FileStreamWriter::Flush(const net::CompletionCallback& callback) {
    275   if (state_ != INITIALIZED)
    276     return net::ERR_FAILED;
    277 
    278   return net::OK;
    279 }
    280 
    281 void FileStreamWriter::OnWriteFileCompleted(
    282     int buffer_length,
    283     const net::CompletionCallback& callback,
    284     base::File::Error result) {
    285   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    286   DCHECK_EQ(INITIALIZED, state_);
    287 
    288   if (result != base::File::FILE_OK) {
    289     state_ = FAILED;
    290     callback.Run(net::FileErrorToNetError(result));
    291     return;
    292   }
    293 
    294   current_offset_ += buffer_length;
    295   callback.Run(buffer_length);
    296 }
    297 
    298 void FileStreamWriter::OnWriteCompleted(net::CompletionCallback callback,
    299                                         int result) {
    300   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    301   callback.Run(result);
    302   TRACE_EVENT_ASYNC_END0(
    303       "file_system_provider", "FileStreamWriter::Write", this);
    304 }
    305 
    306 void FileStreamWriter::OnAbortCompleted(const net::CompletionCallback& callback,
    307                                         base::File::Error result) {
    308   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    309 
    310   if (result != base::File::FILE_OK)
    311     state_ = FAILED;
    312 
    313   callback.Run(net::FileErrorToNetError(result));
    314 }
    315 
    316 void FileStreamWriter::WriteAfterInitialized(
    317     scoped_refptr<net::IOBuffer> buffer,
    318     int buffer_length,
    319     const net::CompletionCallback& callback) {
    320   DCHECK_CURRENTLY_ON(BrowserThread::IO);
    321   DCHECK_EQ(INITIALIZED, state_);
    322 
    323   BrowserThread::PostTask(
    324       BrowserThread::UI,
    325       FROM_HERE,
    326       base::Bind(&OperationRunner::WriteFileOnUIThread,
    327                  runner_,
    328                  buffer,
    329                  current_offset_,
    330                  buffer_length,
    331                  base::Bind(&FileStreamWriter::OnWriteFileCompleted,
    332                             weak_ptr_factory_.GetWeakPtr(),
    333                             buffer_length,
    334                             callback)));
    335 }
    336 
    337 }  // namespace file_system_provider
    338 }  // namespace chromeos
    339