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 "content/public/browser/browser_thread.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
18 using content::BrowserThread
;
21 namespace file_system_provider
{
24 // Dicards the callback from CloseFile().
25 void EmptyStatusCallback(base::File::Error
/* result */) {
28 // Converts net::CompletionCallback to net::Int64CompletionCallback.
29 void Int64ToIntCompletionCallback(net::CompletionCallback callback
,
31 callback
.Run(static_cast<int>(result
));
36 class FileStreamReader::OperationRunner
37 : public base::RefCountedThreadSafe
<FileStreamReader::OperationRunner
> {
39 OperationRunner() : file_handle_(-1) {}
41 // Opens a file for reading and calls the completion callback. Must be called
43 void OpenFileOnUIThread(
44 const storage::FileSystemURL
& url
,
45 const storage::AsyncFileUtil::StatusCallback
& callback
) {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
47 DCHECK(abort_callback_
.is_null());
49 util::FileSystemURLParser
parser(url
);
50 if (!parser
.Parse()) {
51 BrowserThread::PostTask(
54 base::Bind(callback
, base::File::FILE_ERROR_SECURITY
));
58 file_system_
= parser
.file_system()->GetWeakPtr();
59 file_path_
= parser
.file_path();
60 abort_callback_
= parser
.file_system()->OpenFile(
61 file_path_
, OPEN_FILE_MODE_READ
,
62 base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread
, this,
66 // Closes a file. Ignores result, since it is called from a constructor.
67 // Must be called on UI thread.
68 void CloseFileOnUIThread() {
69 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
70 DCHECK(abort_callback_
.is_null());
72 if (file_system_
.get() && file_handle_
!= -1) {
73 // Closing a file must not be aborted, since we could end up on files
74 // which are never closed.
75 file_system_
->CloseFile(file_handle_
, base::Bind(&EmptyStatusCallback
));
79 // Requests reading contents of a file. In case of either success or a failure
80 // |callback| is executed. It can be called many times, until |has_more| is
81 // set to false. This function guarantees that it will succeed only if the
82 // file has not been changed while reading. Must be called on UI thread.
83 void ReadFileOnUIThread(
84 scoped_refptr
<net::IOBuffer
> buffer
,
87 const ProvidedFileSystemInterface::ReadChunkReceivedCallback
& callback
) {
88 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
89 DCHECK(abort_callback_
.is_null());
91 // If the file system got unmounted, then abort the reading operation.
92 if (!file_system_
.get()) {
93 BrowserThread::PostTask(
97 callback
, 0, false /* has_more */, base::File::FILE_ERROR_ABORT
));
101 abort_callback_
= file_system_
->ReadFile(
107 &OperationRunner::OnReadFileCompletedOnUIThread
, this, callback
));
110 // Requests metadata of a file. In case of either succes or a failure,
111 // |callback| is executed. Must be called on UI thread.
112 void GetMetadataOnUIThread(
113 const ProvidedFileSystemInterface::GetMetadataCallback
& callback
) {
114 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
115 DCHECK(abort_callback_
.is_null());
117 // If the file system got unmounted, then abort the get length operation.
118 if (!file_system_
.get()) {
119 BrowserThread::PostTask(
123 base::Passed(make_scoped_ptr
<EntryMetadata
>(NULL
)),
124 base::File::FILE_ERROR_ABORT
));
128 abort_callback_
= file_system_
->GetMetadata(
130 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT
,
131 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread
,
136 // Aborts the most recent operation (if exists), and calls the callback.
137 void AbortOnUIThread() {
138 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
139 if (abort_callback_
.is_null())
142 const AbortCallback last_abort_callback
= abort_callback_
;
143 abort_callback_
= AbortCallback();
144 last_abort_callback
.Run();
148 friend class base::RefCountedThreadSafe
<OperationRunner
>;
150 virtual ~OperationRunner() {}
152 // Remembers a file handle for further operations and forwards the result to
154 void OnOpenFileCompletedOnUIThread(
155 const storage::AsyncFileUtil::StatusCallback
& callback
,
157 base::File::Error result
) {
158 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
159 abort_callback_
= AbortCallback();
161 if (result
== base::File::FILE_OK
)
162 file_handle_
= file_handle
;
164 BrowserThread::PostTask(
165 BrowserThread::IO
, FROM_HERE
, base::Bind(callback
, result
));
168 // Forwards a metadata to the IO thread.
169 void OnGetMetadataCompletedOnUIThread(
170 const ProvidedFileSystemInterface::GetMetadataCallback
& callback
,
171 scoped_ptr
<EntryMetadata
> metadata
,
172 base::File::Error result
) {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
174 abort_callback_
= AbortCallback();
176 BrowserThread::PostTask(
179 base::Bind(callback
, base::Passed(&metadata
), result
));
182 // Forwards a response of reading from a file to the IO thread.
183 void OnReadFileCompletedOnUIThread(
184 const ProvidedFileSystemInterface::ReadChunkReceivedCallback
&
185 chunk_received_callback
,
188 base::File::Error result
) {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
191 abort_callback_
= AbortCallback();
193 BrowserThread::PostTask(
196 base::Bind(chunk_received_callback
, chunk_length
, has_more
, result
));
199 AbortCallback abort_callback_
;
200 base::WeakPtr
<ProvidedFileSystemInterface
> file_system_
;
201 base::FilePath file_path_
;
204 DISALLOW_COPY_AND_ASSIGN(OperationRunner
);
207 FileStreamReader::FileStreamReader(storage::FileSystemContext
* context
,
208 const storage::FileSystemURL
& url
,
209 int64 initial_offset
,
210 const base::Time
& expected_modification_time
)
212 current_offset_(initial_offset
),
214 expected_modification_time_(expected_modification_time
),
215 runner_(new OperationRunner
),
216 state_(NOT_INITIALIZED
),
217 weak_ptr_factory_(this) {
220 FileStreamReader::~FileStreamReader() {
221 // FileStreamReader doesn't have a Cancel() method like in FileStreamWriter.
222 // Therefore, aborting is done from the destructor.
223 BrowserThread::PostTask(
224 BrowserThread::UI
, FROM_HERE
,
225 base::Bind(&OperationRunner::AbortOnUIThread
, runner_
));
227 BrowserThread::PostTask(
230 base::Bind(&OperationRunner::CloseFileOnUIThread
, runner_
));
233 void FileStreamReader::Initialize(
234 const base::Closure
& pending_closure
,
235 const net::Int64CompletionCallback
& error_callback
) {
236 DCHECK_EQ(NOT_INITIALIZED
, state_
);
237 state_
= INITIALIZING
;
239 BrowserThread::PostTask(
242 base::Bind(&OperationRunner::OpenFileOnUIThread
,
245 base::Bind(&FileStreamReader::OnOpenFileCompleted
,
246 weak_ptr_factory_
.GetWeakPtr(),
251 void FileStreamReader::OnOpenFileCompleted(
252 const base::Closure
& pending_closure
,
253 const net::Int64CompletionCallback
& error_callback
,
254 base::File::Error result
) {
255 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
256 DCHECK_EQ(INITIALIZING
, state_
);
258 // In case of an error, return immediately using the |error_callback| of the
259 // Read() or GetLength() pending request.
260 if (result
!= base::File::FILE_OK
) {
262 error_callback
.Run(net::FileErrorToNetError(result
));
266 DCHECK_EQ(base::File::FILE_OK
, result
);
268 // Verify the last modification time.
269 BrowserThread::PostTask(
272 base::Bind(&OperationRunner::GetMetadataOnUIThread
,
274 base::Bind(&FileStreamReader::OnInitializeCompleted
,
275 weak_ptr_factory_
.GetWeakPtr(),
280 void FileStreamReader::OnInitializeCompleted(
281 const base::Closure
& pending_closure
,
282 const net::Int64CompletionCallback
& error_callback
,
283 scoped_ptr
<EntryMetadata
> metadata
,
284 base::File::Error result
) {
285 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
286 DCHECK_EQ(INITIALIZING
, state_
);
288 // In case of an error, abort.
289 if (result
!= base::File::FILE_OK
) {
291 error_callback
.Run(net::FileErrorToNetError(result
));
295 // If the file modification time has changed, then abort. Note, that the file
296 // may be changed without affecting the modification time.
297 DCHECK(metadata
.get());
298 if (!expected_modification_time_
.is_null() &&
299 metadata
->modification_time
!= expected_modification_time_
) {
301 error_callback
.Run(net::ERR_UPLOAD_FILE_CHANGED
);
305 DCHECK_EQ(base::File::FILE_OK
, result
);
306 state_
= INITIALIZED
;
308 // Run the task waiting for the initialization to be completed.
309 pending_closure
.Run();
312 int FileStreamReader::Read(net::IOBuffer
* buffer
,
314 const net::CompletionCallback
& callback
) {
315 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
316 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
317 "FileStreamReader::Read",
323 case NOT_INITIALIZED
:
324 // Lazily initialize with the first call to Read().
325 Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized
,
326 weak_ptr_factory_
.GetWeakPtr(),
327 make_scoped_refptr(buffer
),
329 base::Bind(&FileStreamReader::OnReadCompleted
,
330 weak_ptr_factory_
.GetWeakPtr(),
332 base::Bind(&Int64ToIntCompletionCallback
,
333 base::Bind(&FileStreamReader::OnReadCompleted
,
334 weak_ptr_factory_
.GetWeakPtr(),
343 ReadAfterInitialized(buffer
,
345 base::Bind(&FileStreamReader::OnReadCompleted
,
346 weak_ptr_factory_
.GetWeakPtr(),
355 return net::ERR_IO_PENDING
;
358 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback
,
360 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
361 callback
.Run(static_cast<int>(result
));
362 TRACE_EVENT_ASYNC_END0(
363 "file_system_provider", "FileStreamReader::Read", this);
366 int64
FileStreamReader::GetLength(
367 const net::Int64CompletionCallback
& callback
) {
368 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
371 case NOT_INITIALIZED
:
372 // Lazily initialize with the first call to GetLength().
373 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized
,
374 weak_ptr_factory_
.GetWeakPtr(),
384 GetLengthAfterInitialized(callback
);
392 return net::ERR_IO_PENDING
;
395 void FileStreamReader::ReadAfterInitialized(
396 scoped_refptr
<net::IOBuffer
> buffer
,
398 const net::CompletionCallback
& callback
) {
399 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
400 DCHECK_EQ(INITIALIZED
, state_
);
403 BrowserThread::PostTask(
406 base::Bind(&OperationRunner::ReadFileOnUIThread
,
411 base::Bind(&FileStreamReader::OnReadChunkReceived
,
412 weak_ptr_factory_
.GetWeakPtr(),
416 void FileStreamReader::GetLengthAfterInitialized(
417 const net::Int64CompletionCallback
& callback
) {
418 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
419 DCHECK_EQ(INITIALIZED
, state_
);
421 BrowserThread::PostTask(
425 &OperationRunner::GetMetadataOnUIThread
,
427 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived
,
428 weak_ptr_factory_
.GetWeakPtr(),
432 void FileStreamReader::OnReadChunkReceived(
433 const net::CompletionCallback
& callback
,
436 base::File::Error result
) {
437 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
438 DCHECK_EQ(INITIALIZED
, state_
);
440 current_length_
+= chunk_length
;
442 // If this is the last chunk with a success, then finalize.
443 if (!has_more
&& result
== base::File::FILE_OK
) {
444 current_offset_
+= current_length_
;
445 callback
.Run(current_length_
);
449 // In case of an error, abort.
450 if (result
!= base::File::FILE_OK
) {
453 callback
.Run(net::FileErrorToNetError(result
));
457 // More data is about to come, so do not call the callback yet.
461 void FileStreamReader::OnGetMetadataForGetLengthReceived(
462 const net::Int64CompletionCallback
& callback
,
463 scoped_ptr
<EntryMetadata
> metadata
,
464 base::File::Error result
) {
465 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
466 DCHECK_EQ(INITIALIZED
, state_
);
468 // In case of an error, abort.
469 if (result
!= base::File::FILE_OK
) {
471 callback
.Run(net::FileErrorToNetError(result
));
475 // If the file modification time has changed, then abort. Note, that the file
476 // may be changed without affecting the modification time.
477 DCHECK(metadata
.get());
478 if (!expected_modification_time_
.is_null() &&
479 metadata
->modification_time
!= expected_modification_time_
) {
480 callback
.Run(net::ERR_UPLOAD_FILE_CHANGED
);
484 DCHECK_EQ(base::File::FILE_OK
, result
);
485 callback
.Run(metadata
->size
);
488 } // namespace file_system_provider
489 } // namespace chromeos