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/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"
17 using content::BrowserThread;
20 namespace file_system_provider {
23 // Dicards the callback from CloseFile().
24 void EmptyStatusCallback(base::File::Error /* result */) {
27 // Converts net::CompletionCallback to net::Int64CompletionCallback.
28 void Int64ToIntCompletionCallback(net::CompletionCallback callback,
30 callback.Run(static_cast<int>(result));
35 class FileStreamReader::OperationRunner
36 : public base::RefCountedThreadSafe<FileStreamReader::OperationRunner> {
38 OperationRunner() : file_handle_(-1) {}
40 // Opens a file for reading and calls the completion callback. Must be called
42 void OpenFileOnUIThread(
43 const storage::FileSystemURL& url,
44 const storage::AsyncFileUtil::StatusCallback& callback) {
45 DCHECK_CURRENTLY_ON(BrowserThread::UI);
47 util::FileSystemURLParser parser(url);
48 if (!parser.Parse()) {
49 BrowserThread::PostTask(
52 base::Bind(callback, base::File::FILE_ERROR_SECURITY));
56 file_system_ = parser.file_system()->GetWeakPtr();
57 file_path_ = parser.file_path();
58 abort_callback_ = parser.file_system()->OpenFile(
60 ProvidedFileSystemInterface::OPEN_FILE_MODE_READ,
62 &OperationRunner::OnOpenFileCompletedOnUIThread, this, callback));
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));
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,
84 const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) {
85 DCHECK_CURRENTLY_ON(BrowserThread::UI);
87 // If the file system got unmounted, then abort the reading operation.
88 if (!file_system_.get()) {
89 BrowserThread::PostTask(
93 callback, 0, false /* has_more */, base::File::FILE_ERROR_ABORT));
97 abort_callback_ = file_system_->ReadFile(
103 &OperationRunner::OnReadFileCompletedOnUIThread, this, callback));
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);
112 // If the file system got unmounted, then abort the get length operation.
113 if (!file_system_.get()) {
114 BrowserThread::PostTask(
118 base::Passed(make_scoped_ptr<EntryMetadata>(NULL)),
119 base::File::FILE_ERROR_ABORT));
123 abort_callback_ = file_system_->GetMetadata(
125 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT,
126 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread,
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);
135 if (abort_callback_.is_null()) {
136 // No operation to be cancelled. At most a callback call, which will be
138 BrowserThread::PostTask(BrowserThread::IO,
140 base::Bind(callback, base::File::FILE_OK));
144 const ProvidedFileSystemInterface::AbortCallback abort_callback =
146 abort_callback_ = ProvidedFileSystemInterface::AbortCallback();
147 abort_callback.Run(base::Bind(
148 &OperationRunner::OnAbortCompletedOnUIThread, this, callback));
152 friend class base::RefCountedThreadSafe<OperationRunner>;
154 virtual ~OperationRunner() {}
156 // Remembers a file handle for further operations and forwards the result to
158 void OnOpenFileCompletedOnUIThread(
159 const storage::AsyncFileUtil::StatusCallback& callback,
161 base::File::Error result) {
162 DCHECK_CURRENTLY_ON(BrowserThread::UI);
164 file_handle_ = file_handle;
165 BrowserThread::PostTask(
166 BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
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(
178 base::Bind(callback, base::Passed(&metadata), result));
181 // Forwards a response of reading from a file to the IO thread.
182 void OnReadFileCompletedOnUIThread(
183 const ProvidedFileSystemInterface::ReadChunkReceivedCallback&
184 chunk_received_callback,
187 base::File::Error result) {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 BrowserThread::PostTask(
192 base::Bind(chunk_received_callback, chunk_length, has_more, result));
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));
204 ProvidedFileSystemInterface::AbortCallback abort_callback_;
205 base::WeakPtr<ProvidedFileSystemInterface> file_system_;
206 base::FilePath file_path_;
209 DISALLOW_COPY_AND_ASSIGN(OperationRunner);
212 FileStreamReader::FileStreamReader(storage::FileSystemContext* context,
213 const storage::FileSystemURL& url,
214 int64 initial_offset,
215 const base::Time& expected_modification_time)
217 current_offset_(initial_offset),
219 expected_modification_time_(expected_modification_time),
220 runner_(new OperationRunner),
221 state_(NOT_INITIALIZED),
222 weak_ptr_factory_(this) {
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,
230 base::Bind(&OperationRunner::AbortOnUIThread,
232 base::Bind(&EmptyStatusCallback)));
234 BrowserThread::PostTask(
237 base::Bind(&OperationRunner::CloseFileOnUIThread, runner_));
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;
246 BrowserThread::PostTask(
249 base::Bind(&OperationRunner::OpenFileOnUIThread,
252 base::Bind(&FileStreamReader::OnOpenFileCompleted,
253 weak_ptr_factory_.GetWeakPtr(),
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_);
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) {
269 error_callback.Run(net::FileErrorToNetError(result));
273 DCHECK_EQ(base::File::FILE_OK, result);
275 // Verify the last modification time.
276 BrowserThread::PostTask(
279 base::Bind(&OperationRunner::GetMetadataOnUIThread,
281 base::Bind(&FileStreamReader::OnInitializeCompleted,
282 weak_ptr_factory_.GetWeakPtr(),
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_);
295 // In case of an error, abort.
296 if (result != base::File::FILE_OK) {
298 error_callback.Run(net::FileErrorToNetError(result));
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_) {
308 error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED);
312 DCHECK_EQ(base::File::FILE_OK, result);
313 state_ = INITIALIZED;
315 // Run the task waiting for the initialization to be completed.
316 pending_closure.Run();
319 int FileStreamReader::Read(net::IOBuffer* buffer,
321 const net::CompletionCallback& callback) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO);
323 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
324 "FileStreamReader::Read",
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),
336 base::Bind(&FileStreamReader::OnReadCompleted,
337 weak_ptr_factory_.GetWeakPtr(),
339 base::Bind(&Int64ToIntCompletionCallback,
340 base::Bind(&FileStreamReader::OnReadCompleted,
341 weak_ptr_factory_.GetWeakPtr(),
350 ReadAfterInitialized(buffer,
352 base::Bind(&FileStreamReader::OnReadCompleted,
353 weak_ptr_factory_.GetWeakPtr(),
362 return net::ERR_IO_PENDING;
365 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback,
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);
373 int64 FileStreamReader::GetLength(
374 const net::Int64CompletionCallback& callback) {
375 DCHECK_CURRENTLY_ON(BrowserThread::IO);
378 case NOT_INITIALIZED:
379 // Lazily initialize with the first call to GetLength().
380 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized,
381 weak_ptr_factory_.GetWeakPtr(),
391 GetLengthAfterInitialized(callback);
399 return net::ERR_IO_PENDING;
402 void FileStreamReader::ReadAfterInitialized(
403 scoped_refptr<net::IOBuffer> buffer,
405 const net::CompletionCallback& callback) {
406 DCHECK_CURRENTLY_ON(BrowserThread::IO);
407 DCHECK_EQ(INITIALIZED, state_);
410 BrowserThread::PostTask(
413 base::Bind(&OperationRunner::ReadFileOnUIThread,
418 base::Bind(&FileStreamReader::OnReadChunkReceived,
419 weak_ptr_factory_.GetWeakPtr(),
423 void FileStreamReader::GetLengthAfterInitialized(
424 const net::Int64CompletionCallback& callback) {
425 DCHECK_CURRENTLY_ON(BrowserThread::IO);
426 DCHECK_EQ(INITIALIZED, state_);
428 BrowserThread::PostTask(
432 &OperationRunner::GetMetadataOnUIThread,
434 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived,
435 weak_ptr_factory_.GetWeakPtr(),
439 void FileStreamReader::OnReadChunkReceived(
440 const net::CompletionCallback& callback,
443 base::File::Error result) {
444 DCHECK_CURRENTLY_ON(BrowserThread::IO);
445 DCHECK_EQ(INITIALIZED, state_);
447 current_length_ += chunk_length;
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_);
456 // In case of an error, abort.
457 if (result != base::File::FILE_OK) {
460 callback.Run(net::FileErrorToNetError(result));
464 // More data is about to come, so do not call the callback yet.
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_);
475 // In case of an error, abort.
476 if (result != base::File::FILE_OK) {
478 callback.Run(net::FileErrorToNetError(result));
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);
491 DCHECK_EQ(base::File::FILE_OK, result);
492 callback.Run(metadata->size);
495 } // namespace file_system_provider
496 } // namespace chromeos