Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_system_provider / fileapi / file_stream_reader.cc
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