Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / fileapi / file_stream_reader.cc
blob2705185fe923af8c1e6c1f238a035749eb1e697d
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.
5 #include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h"
7 #include "base/files/file.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/trace_event/trace_event.h"
10 #include "chrome/browser/chromeos/file_system_provider/abort_callback.h"
11 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
12 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
13 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
14 #include "chrome/browser/chromeos/file_system_provider/scoped_file_opener.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
19 using content::BrowserThread;
21 namespace chromeos {
22 namespace file_system_provider {
24 // Converts net::CompletionCallback to net::Int64CompletionCallback.
25 void Int64ToIntCompletionCallback(net::CompletionCallback callback,
26 int64 result) {
27 callback.Run(static_cast<int>(result));
30 class FileStreamReader::OperationRunner
31 : public base::RefCountedThreadSafe<
32 FileStreamReader::OperationRunner,
33 content::BrowserThread::DeleteOnUIThread> {
34 public:
35 OperationRunner() : file_handle_(-1) {}
37 // Opens a file for reading and calls the completion callback. Must be called
38 // on UI thread.
39 void OpenFileOnUIThread(
40 const storage::FileSystemURL& url,
41 const storage::AsyncFileUtil::StatusCallback& callback) {
42 DCHECK_CURRENTLY_ON(BrowserThread::UI);
43 DCHECK(abort_callback_.is_null());
45 util::FileSystemURLParser parser(url);
46 if (!parser.Parse()) {
47 BrowserThread::PostTask(
48 BrowserThread::IO,
49 FROM_HERE,
50 base::Bind(callback, base::File::FILE_ERROR_SECURITY));
51 return;
54 file_system_ = parser.file_system()->GetWeakPtr();
55 file_path_ = parser.file_path();
56 file_opener_.reset(new ScopedFileOpener(
57 parser.file_system(), parser.file_path(), OPEN_FILE_MODE_READ,
58 base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread, this,
59 callback)));
62 // Requests reading contents of a file. In case of either success or a failure
63 // |callback| is executed. It can be called many times, until |has_more| is
64 // set to false. This function guarantees that it will succeed only if the
65 // file has not been changed while reading. Must be called on UI thread.
66 void ReadFileOnUIThread(
67 scoped_refptr<net::IOBuffer> buffer,
68 int64 offset,
69 int length,
70 const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
71 DCHECK_CURRENTLY_ON(BrowserThread::UI);
72 DCHECK(abort_callback_.is_null());
74 // If the file system got unmounted, then abort the reading operation.
75 if (!file_system_.get()) {
76 BrowserThread::PostTask(
77 BrowserThread::IO,
78 FROM_HERE,
79 base::Bind(
80 callback, 0, false /* has_more */, base::File::FILE_ERROR_ABORT));
81 return;
84 abort_callback_ = file_system_->ReadFile(
85 file_handle_,
86 buffer.get(),
87 offset,
88 length,
89 base::Bind(
90 &OperationRunner::OnReadFileCompletedOnUIThread, this, callback));
93 // Requests metadata of a file. In case of either succes or a failure,
94 // |callback| is executed. Must be called on UI thread.
95 void GetMetadataOnUIThread(
96 const ProvidedFileSystemInterface::GetMetadataCallback& callback) {
97 DCHECK_CURRENTLY_ON(BrowserThread::UI);
98 DCHECK(abort_callback_.is_null());
100 // If the file system got unmounted, then abort the get length operation.
101 if (!file_system_.get()) {
102 BrowserThread::PostTask(
103 BrowserThread::IO,
104 FROM_HERE,
105 base::Bind(callback,
106 base::Passed(make_scoped_ptr<EntryMetadata>(NULL)),
107 base::File::FILE_ERROR_ABORT));
108 return;
111 abort_callback_ = file_system_->GetMetadata(
112 file_path_,
113 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT,
114 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread,
115 this,
116 callback));
119 // Aborts the most recent operation (if exists) and closes a file if opened.
120 // The runner must not be used anymore after calling this method.
121 void CloseRunnerOnUIThread() {
122 DCHECK_CURRENTLY_ON(BrowserThread::UI);
124 if (!abort_callback_.is_null()) {
125 const AbortCallback last_abort_callback = abort_callback_;
126 abort_callback_ = AbortCallback();
127 last_abort_callback.Run();
130 // Close the file (if opened).
131 file_opener_.reset();
134 private:
135 friend struct content::BrowserThread::DeleteOnThread<
136 content::BrowserThread::UI>;
137 friend class base::DeleteHelper<OperationRunner>;
139 virtual ~OperationRunner() {}
141 // Remembers a file handle for further operations and forwards the result to
142 // the IO thread.
143 void OnOpenFileCompletedOnUIThread(
144 const storage::AsyncFileUtil::StatusCallback& callback,
145 int file_handle,
146 base::File::Error result) {
147 DCHECK_CURRENTLY_ON(BrowserThread::UI);
148 abort_callback_ = AbortCallback();
150 if (result == base::File::FILE_OK)
151 file_handle_ = file_handle;
153 BrowserThread::PostTask(
154 BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
157 // Forwards a metadata to the IO thread.
158 void OnGetMetadataCompletedOnUIThread(
159 const ProvidedFileSystemInterface::GetMetadataCallback& callback,
160 scoped_ptr<EntryMetadata> metadata,
161 base::File::Error result) {
162 DCHECK_CURRENTLY_ON(BrowserThread::UI);
163 abort_callback_ = AbortCallback();
165 BrowserThread::PostTask(
166 BrowserThread::IO,
167 FROM_HERE,
168 base::Bind(callback, base::Passed(&metadata), result));
171 // Forwards a response of reading from a file to the IO thread.
172 void OnReadFileCompletedOnUIThread(
173 const ProvidedFileSystemInterface::ReadChunkReceivedCallback&
174 chunk_received_callback,
175 int chunk_length,
176 bool has_more,
177 base::File::Error result) {
178 DCHECK_CURRENTLY_ON(BrowserThread::UI);
179 if (!has_more)
180 abort_callback_ = AbortCallback();
182 BrowserThread::PostTask(
183 BrowserThread::IO,
184 FROM_HERE,
185 base::Bind(chunk_received_callback, chunk_length, has_more, result));
188 AbortCallback abort_callback_;
189 base::WeakPtr<ProvidedFileSystemInterface> file_system_;
190 base::FilePath file_path_;
191 scoped_ptr<ScopedFileOpener> file_opener_;
192 int file_handle_;
194 DISALLOW_COPY_AND_ASSIGN(OperationRunner);
197 FileStreamReader::FileStreamReader(storage::FileSystemContext* context,
198 const storage::FileSystemURL& url,
199 int64 initial_offset,
200 const base::Time& expected_modification_time)
201 : url_(url),
202 current_offset_(initial_offset),
203 current_length_(0),
204 expected_modification_time_(expected_modification_time),
205 runner_(new OperationRunner),
206 state_(NOT_INITIALIZED),
207 weak_ptr_factory_(this) {
210 FileStreamReader::~FileStreamReader() {
211 // FileStreamReader doesn't have a Cancel() method like in FileStreamWriter.
212 // Therefore, aborting and/or closing an opened file is done from the
213 // destructor.
214 BrowserThread::PostTask(
215 BrowserThread::UI, FROM_HERE,
216 base::Bind(&OperationRunner::CloseRunnerOnUIThread, runner_));
218 // If a read is in progress, mark it as completed.
219 TRACE_EVENT_ASYNC_END0("file_system_provider", "FileStreamReader::Read",
220 this);
223 void FileStreamReader::Initialize(
224 const base::Closure& pending_closure,
225 const net::Int64CompletionCallback& error_callback) {
226 DCHECK_EQ(NOT_INITIALIZED, state_);
227 state_ = INITIALIZING;
229 BrowserThread::PostTask(
230 BrowserThread::UI,
231 FROM_HERE,
232 base::Bind(&OperationRunner::OpenFileOnUIThread,
233 runner_,
234 url_,
235 base::Bind(&FileStreamReader::OnOpenFileCompleted,
236 weak_ptr_factory_.GetWeakPtr(),
237 pending_closure,
238 error_callback)));
241 void FileStreamReader::OnOpenFileCompleted(
242 const base::Closure& pending_closure,
243 const net::Int64CompletionCallback& error_callback,
244 base::File::Error result) {
245 DCHECK_CURRENTLY_ON(BrowserThread::IO);
246 DCHECK_EQ(INITIALIZING, state_);
248 // In case of an error, return immediately using the |error_callback| of the
249 // Read() or GetLength() pending request.
250 if (result != base::File::FILE_OK) {
251 state_ = FAILED;
252 error_callback.Run(net::FileErrorToNetError(result));
253 return;
256 DCHECK_EQ(base::File::FILE_OK, result);
258 // Verify the last modification time.
259 BrowserThread::PostTask(
260 BrowserThread::UI,
261 FROM_HERE,
262 base::Bind(&OperationRunner::GetMetadataOnUIThread,
263 runner_,
264 base::Bind(&FileStreamReader::OnInitializeCompleted,
265 weak_ptr_factory_.GetWeakPtr(),
266 pending_closure,
267 error_callback)));
270 void FileStreamReader::OnInitializeCompleted(
271 const base::Closure& pending_closure,
272 const net::Int64CompletionCallback& error_callback,
273 scoped_ptr<EntryMetadata> metadata,
274 base::File::Error result) {
275 DCHECK_CURRENTLY_ON(BrowserThread::IO);
276 DCHECK_EQ(INITIALIZING, state_);
278 // In case of an error, abort.
279 if (result != base::File::FILE_OK) {
280 state_ = FAILED;
281 error_callback.Run(net::FileErrorToNetError(result));
282 return;
285 // If the file modification time has changed, then abort. Note, that the file
286 // may be changed without affecting the modification time.
287 DCHECK(metadata.get());
288 if (!expected_modification_time_.is_null() &&
289 metadata->modification_time != expected_modification_time_) {
290 state_ = FAILED;
291 error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
292 return;
295 DCHECK_EQ(base::File::FILE_OK, result);
296 state_ = INITIALIZED;
298 // Run the task waiting for the initialization to be completed.
299 pending_closure.Run();
302 int FileStreamReader::Read(net::IOBuffer* buffer,
303 int buffer_length,
304 const net::CompletionCallback& callback) {
305 DCHECK_CURRENTLY_ON(BrowserThread::IO);
306 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
307 "FileStreamReader::Read",
308 this,
309 "buffer_length",
310 buffer_length);
312 switch (state_) {
313 case NOT_INITIALIZED:
314 // Lazily initialize with the first call to Read().
315 Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized,
316 weak_ptr_factory_.GetWeakPtr(),
317 make_scoped_refptr(buffer),
318 buffer_length,
319 base::Bind(&FileStreamReader::OnReadCompleted,
320 weak_ptr_factory_.GetWeakPtr(),
321 callback)),
322 base::Bind(&Int64ToIntCompletionCallback,
323 base::Bind(&FileStreamReader::OnReadCompleted,
324 weak_ptr_factory_.GetWeakPtr(),
325 callback)));
326 break;
328 case INITIALIZING:
329 NOTREACHED();
330 break;
332 case INITIALIZED:
333 ReadAfterInitialized(buffer,
334 buffer_length,
335 base::Bind(&FileStreamReader::OnReadCompleted,
336 weak_ptr_factory_.GetWeakPtr(),
337 callback));
338 break;
340 case FAILED:
341 NOTREACHED();
342 break;
345 return net::ERR_IO_PENDING;
348 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback,
349 int result) {
350 DCHECK_CURRENTLY_ON(BrowserThread::IO);
351 callback.Run(static_cast<int>(result));
352 TRACE_EVENT_ASYNC_END0(
353 "file_system_provider", "FileStreamReader::Read", this);
356 int64 FileStreamReader::GetLength(
357 const net::Int64CompletionCallback& callback) {
358 DCHECK_CURRENTLY_ON(BrowserThread::IO);
360 switch (state_) {
361 case NOT_INITIALIZED:
362 // Lazily initialize with the first call to GetLength().
363 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized,
364 weak_ptr_factory_.GetWeakPtr(),
365 callback),
366 callback);
367 break;
369 case INITIALIZING:
370 NOTREACHED();
371 break;
373 case INITIALIZED:
374 GetLengthAfterInitialized(callback);
375 break;
377 case FAILED:
378 NOTREACHED();
379 break;
382 return net::ERR_IO_PENDING;
385 void FileStreamReader::ReadAfterInitialized(
386 scoped_refptr<net::IOBuffer> buffer,
387 int buffer_length,
388 const net::CompletionCallback& callback) {
389 DCHECK_CURRENTLY_ON(BrowserThread::IO);
390 DCHECK_EQ(INITIALIZED, state_);
392 current_length_ = 0;
393 BrowserThread::PostTask(
394 BrowserThread::UI,
395 FROM_HERE,
396 base::Bind(&OperationRunner::ReadFileOnUIThread,
397 runner_,
398 buffer,
399 current_offset_,
400 buffer_length,
401 base::Bind(&FileStreamReader::OnReadChunkReceived,
402 weak_ptr_factory_.GetWeakPtr(),
403 callback)));
406 void FileStreamReader::GetLengthAfterInitialized(
407 const net::Int64CompletionCallback& callback) {
408 DCHECK_CURRENTLY_ON(BrowserThread::IO);
409 DCHECK_EQ(INITIALIZED, state_);
411 BrowserThread::PostTask(
412 BrowserThread::UI,
413 FROM_HERE,
414 base::Bind(
415 &OperationRunner::GetMetadataOnUIThread,
416 runner_,
417 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived,
418 weak_ptr_factory_.GetWeakPtr(),
419 callback)));
422 void FileStreamReader::OnReadChunkReceived(
423 const net::CompletionCallback& callback,
424 int chunk_length,
425 bool has_more,
426 base::File::Error result) {
427 DCHECK_CURRENTLY_ON(BrowserThread::IO);
428 DCHECK_EQ(INITIALIZED, state_);
430 current_length_ += chunk_length;
432 // If this is the last chunk with a success, then finalize.
433 if (!has_more && result == base::File::FILE_OK) {
434 current_offset_ += current_length_;
435 callback.Run(current_length_);
436 return;
439 // In case of an error, abort.
440 if (result != base::File::FILE_OK) {
441 DCHECK(!has_more);
442 state_ = FAILED;
443 callback.Run(net::FileErrorToNetError(result));
444 return;
447 // More data is about to come, so do not call the callback yet.
448 DCHECK(has_more);
451 void FileStreamReader::OnGetMetadataForGetLengthReceived(
452 const net::Int64CompletionCallback& callback,
453 scoped_ptr<EntryMetadata> metadata,
454 base::File::Error result) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO);
456 DCHECK_EQ(INITIALIZED, state_);
458 // In case of an error, abort.
459 if (result != base::File::FILE_OK) {
460 state_ = FAILED;
461 callback.Run(net::FileErrorToNetError(result));
462 return;
465 // If the file modification time has changed, then abort. Note, that the file
466 // may be changed without affecting the modification time.
467 DCHECK(metadata.get());
468 if (!expected_modification_time_.is_null() &&
469 metadata->modification_time != expected_modification_time_) {
470 callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
471 return;
474 DCHECK_EQ(base::File::FILE_OK, result);
475 callback.Run(metadata->size);
478 } // namespace file_system_provider
479 } // namespace chromeos