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
;
22 namespace file_system_provider
{
24 // Converts net::CompletionCallback to net::Int64CompletionCallback.
25 void Int64ToIntCompletionCallback(net::CompletionCallback callback
,
27 callback
.Run(static_cast<int>(result
));
30 class FileStreamReader::OperationRunner
31 : public base::RefCountedThreadSafe
<
32 FileStreamReader::OperationRunner
,
33 content::BrowserThread::DeleteOnUIThread
> {
35 OperationRunner() : file_handle_(-1) {}
37 // Opens a file for reading and calls the completion callback. Must be called
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(
50 base::Bind(callback
, base::File::FILE_ERROR_SECURITY
));
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,
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
,
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(
80 callback
, 0, false /* has_more */, base::File::FILE_ERROR_ABORT
));
84 abort_callback_
= file_system_
->ReadFile(
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(
106 base::Passed(make_scoped_ptr
<EntryMetadata
>(NULL
)),
107 base::File::FILE_ERROR_ABORT
));
111 abort_callback_
= file_system_
->GetMetadata(
113 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT
,
114 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread
,
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();
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
143 void OnOpenFileCompletedOnUIThread(
144 const storage::AsyncFileUtil::StatusCallback
& callback
,
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(
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
,
177 base::File::Error result
) {
178 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
180 abort_callback_
= AbortCallback();
182 BrowserThread::PostTask(
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_
;
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
)
202 current_offset_(initial_offset
),
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
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",
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(
232 base::Bind(&OperationRunner::OpenFileOnUIThread
,
235 base::Bind(&FileStreamReader::OnOpenFileCompleted
,
236 weak_ptr_factory_
.GetWeakPtr(),
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
) {
252 error_callback
.Run(net::FileErrorToNetError(result
));
256 DCHECK_EQ(base::File::FILE_OK
, result
);
258 // Verify the last modification time.
259 BrowserThread::PostTask(
262 base::Bind(&OperationRunner::GetMetadataOnUIThread
,
264 base::Bind(&FileStreamReader::OnInitializeCompleted
,
265 weak_ptr_factory_
.GetWeakPtr(),
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
) {
281 error_callback
.Run(net::FileErrorToNetError(result
));
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_
) {
291 error_callback
.Run(net::ERR_UPLOAD_FILE_CHANGED
);
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
,
304 const net::CompletionCallback
& callback
) {
305 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
306 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
307 "FileStreamReader::Read",
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
),
319 base::Bind(&FileStreamReader::OnReadCompleted
,
320 weak_ptr_factory_
.GetWeakPtr(),
322 base::Bind(&Int64ToIntCompletionCallback
,
323 base::Bind(&FileStreamReader::OnReadCompleted
,
324 weak_ptr_factory_
.GetWeakPtr(),
333 ReadAfterInitialized(buffer
,
335 base::Bind(&FileStreamReader::OnReadCompleted
,
336 weak_ptr_factory_
.GetWeakPtr(),
345 return net::ERR_IO_PENDING
;
348 void FileStreamReader::OnReadCompleted(net::CompletionCallback callback
,
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
);
361 case NOT_INITIALIZED
:
362 // Lazily initialize with the first call to GetLength().
363 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized
,
364 weak_ptr_factory_
.GetWeakPtr(),
374 GetLengthAfterInitialized(callback
);
382 return net::ERR_IO_PENDING
;
385 void FileStreamReader::ReadAfterInitialized(
386 scoped_refptr
<net::IOBuffer
> buffer
,
388 const net::CompletionCallback
& callback
) {
389 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
390 DCHECK_EQ(INITIALIZED
, state_
);
393 BrowserThread::PostTask(
396 base::Bind(&OperationRunner::ReadFileOnUIThread
,
401 base::Bind(&FileStreamReader::OnReadChunkReceived
,
402 weak_ptr_factory_
.GetWeakPtr(),
406 void FileStreamReader::GetLengthAfterInitialized(
407 const net::Int64CompletionCallback
& callback
) {
408 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
409 DCHECK_EQ(INITIALIZED
, state_
);
411 BrowserThread::PostTask(
415 &OperationRunner::GetMetadataOnUIThread
,
417 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived
,
418 weak_ptr_factory_
.GetWeakPtr(),
422 void FileStreamReader::OnReadChunkReceived(
423 const net::CompletionCallback
& callback
,
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_
);
439 // In case of an error, abort.
440 if (result
!= base::File::FILE_OK
) {
443 callback
.Run(net::FileErrorToNetError(result
));
447 // More data is about to come, so do not call the callback yet.
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
) {
461 callback
.Run(net::FileErrorToNetError(result
));
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
);
474 DCHECK_EQ(base::File::FILE_OK
, result
);
475 callback
.Run(metadata
->size
);
478 } // namespace file_system_provider
479 } // namespace chromeos