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_writer.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/thread_task_runner_handle.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 class FileStreamWriter::OperationRunner
25 : public base::RefCountedThreadSafe
<
26 FileStreamWriter::OperationRunner
,
27 content::BrowserThread::DeleteOnUIThread
> {
29 OperationRunner() : file_handle_(-1) {}
31 // Opens a file for writing and calls the completion callback. Must be called
33 void OpenFileOnUIThread(
34 const storage::FileSystemURL
& url
,
35 const storage::AsyncFileUtil::StatusCallback
& callback
) {
36 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
37 DCHECK(abort_callback_
.is_null());
39 util::FileSystemURLParser
parser(url
);
40 if (!parser
.Parse()) {
41 BrowserThread::PostTask(
44 base::Bind(callback
, base::File::FILE_ERROR_SECURITY
));
48 file_system_
= parser
.file_system()->GetWeakPtr();
49 file_opener_
.reset(new ScopedFileOpener(
50 parser
.file_system(), parser
.file_path(), OPEN_FILE_MODE_WRITE
,
51 base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread
, this,
55 // Requests writing bytes to the file. In case of either success or a failure
56 // |callback| is executed. Must be called on UI thread.
57 void WriteFileOnUIThread(
58 scoped_refptr
<net::IOBuffer
> buffer
,
61 const storage::AsyncFileUtil::StatusCallback
& callback
) {
62 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
63 DCHECK(abort_callback_
.is_null());
65 // If the file system got unmounted, then abort the writing operation.
66 if (!file_system_
.get()) {
67 BrowserThread::PostTask(
70 base::Bind(callback
, base::File::FILE_ERROR_ABORT
));
74 abort_callback_
= file_system_
->WriteFile(
80 &OperationRunner::OnWriteFileCompletedOnUIThread
, this, callback
));
83 // Aborts the most recent operation (if exists) and closes a file if opened.
84 // The runner must not be used anymore after calling this method.
85 void CloseRunnerOnUIThread() {
86 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
88 if (!abort_callback_
.is_null()) {
89 const AbortCallback last_abort_callback
= abort_callback_
;
90 abort_callback_
= AbortCallback();
91 last_abort_callback
.Run();
94 // Close the file (if opened).
99 friend struct content::BrowserThread::DeleteOnThread
<
100 content::BrowserThread::UI
>;
101 friend class base::DeleteHelper
<OperationRunner
>;
103 virtual ~OperationRunner() {}
105 // Remembers a file handle for further operations and forwards the result to
107 void OnOpenFileCompletedOnUIThread(
108 const storage::AsyncFileUtil::StatusCallback
& callback
,
110 base::File::Error result
) {
111 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
113 abort_callback_
= AbortCallback();
114 if (result
== base::File::FILE_OK
)
115 file_handle_
= file_handle
;
117 BrowserThread::PostTask(
118 BrowserThread::IO
, FROM_HERE
, base::Bind(callback
, result
));
121 // Forwards a response of writing to a file to the IO thread.
122 void OnWriteFileCompletedOnUIThread(
123 const storage::AsyncFileUtil::StatusCallback
& callback
,
124 base::File::Error result
) {
125 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
127 abort_callback_
= AbortCallback();
128 BrowserThread::PostTask(
129 BrowserThread::IO
, FROM_HERE
, base::Bind(callback
, result
));
132 AbortCallback abort_callback_
;
133 base::WeakPtr
<ProvidedFileSystemInterface
> file_system_
;
134 scoped_ptr
<ScopedFileOpener
> file_opener_
;
137 DISALLOW_COPY_AND_ASSIGN(OperationRunner
);
140 FileStreamWriter::FileStreamWriter(const storage::FileSystemURL
& url
,
141 int64 initial_offset
)
143 current_offset_(initial_offset
),
144 runner_(new OperationRunner
),
145 state_(NOT_INITIALIZED
),
146 weak_ptr_factory_(this) {
149 FileStreamWriter::~FileStreamWriter() {
150 // Close the runner explicitly if the file streamer is
151 if (state_
!= CANCELLING
) {
152 BrowserThread::PostTask(
153 BrowserThread::UI
, FROM_HERE
,
154 base::Bind(&OperationRunner::CloseRunnerOnUIThread
, runner_
));
157 // If a write is in progress, mark it as completed.
158 TRACE_EVENT_ASYNC_END0("file_system_provider", "FileStreamWriter::Write",
162 void FileStreamWriter::Initialize(
163 const base::Closure
& pending_closure
,
164 const net::CompletionCallback
& error_callback
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
166 DCHECK_EQ(NOT_INITIALIZED
, state_
);
167 state_
= INITIALIZING
;
169 BrowserThread::PostTask(
172 base::Bind(&OperationRunner::OpenFileOnUIThread
,
175 base::Bind(&FileStreamWriter::OnOpenFileCompleted
,
176 weak_ptr_factory_
.GetWeakPtr(),
181 void FileStreamWriter::OnOpenFileCompleted(
182 const base::Closure
& pending_closure
,
183 const net::CompletionCallback
& error_callback
,
184 base::File::Error result
) {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
186 DCHECK(state_
== INITIALIZING
|| state_
== CANCELLING
);
187 if (state_
== CANCELLING
)
190 // In case of an error, return immediately using the |error_callback| of the
191 // Write() pending request.
192 if (result
!= base::File::FILE_OK
) {
194 error_callback
.Run(net::FileErrorToNetError(result
));
198 DCHECK_EQ(base::File::FILE_OK
, result
);
199 state_
= INITIALIZED
;
201 // Run the task waiting for the initialization to be completed.
202 pending_closure
.Run();
205 int FileStreamWriter::Write(net::IOBuffer
* buffer
,
207 const net::CompletionCallback
& callback
) {
208 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
209 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
210 "FileStreamWriter::Write",
216 case NOT_INITIALIZED
:
217 // Lazily initialize with the first call to Write().
218 Initialize(base::Bind(&FileStreamWriter::WriteAfterInitialized
,
219 weak_ptr_factory_
.GetWeakPtr(),
220 make_scoped_refptr(buffer
),
222 base::Bind(&FileStreamWriter::OnWriteCompleted
,
223 weak_ptr_factory_
.GetWeakPtr(),
225 base::Bind(&FileStreamWriter::OnWriteCompleted
,
226 weak_ptr_factory_
.GetWeakPtr(),
235 WriteAfterInitialized(buffer
,
237 base::Bind(&FileStreamWriter::OnWriteCompleted
,
238 weak_ptr_factory_
.GetWeakPtr(),
249 return net::ERR_IO_PENDING
;
252 int FileStreamWriter::Cancel(const net::CompletionCallback
& callback
) {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
255 if (state_
!= INITIALIZING
&& state_
!= EXECUTING
)
256 return net::ERR_UNEXPECTED
;
260 // Abort and optimistically return an OK result code, as the aborting
261 // operation is always forced and can't be cancelled. Similarly, for closing
263 BrowserThread::PostTask(
264 BrowserThread::UI
, FROM_HERE
,
265 base::Bind(&OperationRunner::CloseRunnerOnUIThread
, runner_
));
266 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
267 base::Bind(callback
, net::OK
));
269 // If a write is in progress, mark it as completed.
270 TRACE_EVENT_ASYNC_END0("file_system_provider", "FileStreamWriter::Write",
273 return net::ERR_IO_PENDING
;
276 int FileStreamWriter::Flush(const net::CompletionCallback
& callback
) {
277 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
278 DCHECK_NE(CANCELLING
, state_
);
280 base::ThreadTaskRunnerHandle::Get()->PostTask(
282 base::Bind(callback
, state_
== INITIALIZED
? net::OK
: net::ERR_FAILED
));
284 return net::ERR_IO_PENDING
;
287 void FileStreamWriter::OnWriteFileCompleted(
289 const net::CompletionCallback
& callback
,
290 base::File::Error result
) {
291 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
292 DCHECK(state_
== EXECUTING
|| state_
== CANCELLING
);
293 if (state_
== CANCELLING
)
296 state_
= INITIALIZED
;
298 if (result
!= base::File::FILE_OK
) {
300 callback
.Run(net::FileErrorToNetError(result
));
304 current_offset_
+= buffer_length
;
305 callback
.Run(buffer_length
);
308 void FileStreamWriter::OnWriteCompleted(net::CompletionCallback callback
,
310 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
311 if (state_
!= CANCELLING
)
312 callback
.Run(result
);
314 TRACE_EVENT_ASYNC_END0(
315 "file_system_provider", "FileStreamWriter::Write", this);
318 void FileStreamWriter::WriteAfterInitialized(
319 scoped_refptr
<net::IOBuffer
> buffer
,
321 const net::CompletionCallback
& callback
) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
323 DCHECK(state_
== INITIALIZED
|| state_
== CANCELLING
);
324 if (state_
== CANCELLING
)
329 BrowserThread::PostTask(
332 base::Bind(&OperationRunner::WriteFileOnUIThread
,
337 base::Bind(&FileStreamWriter::OnWriteFileCompleted
,
338 weak_ptr_factory_
.GetWeakPtr(),
343 } // namespace file_system_provider
344 } // namespace chromeos