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 "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 */) {
30 class FileStreamWriter::OperationRunner
31 : public base::RefCountedThreadSafe
<FileStreamWriter::OperationRunner
> {
33 OperationRunner() : file_handle_(-1) {}
35 // Opens a file for writing and calls the completion callback. Must be called
37 void OpenFileOnUIThread(
38 const storage::FileSystemURL
& url
,
39 const storage::AsyncFileUtil::StatusCallback
& callback
) {
40 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
41 DCHECK(abort_callback_
.is_null());
43 util::FileSystemURLParser
parser(url
);
44 if (!parser
.Parse()) {
45 BrowserThread::PostTask(
48 base::Bind(callback
, base::File::FILE_ERROR_SECURITY
));
52 file_system_
= parser
.file_system()->GetWeakPtr();
53 abort_callback_
= parser
.file_system()->OpenFile(
54 parser
.file_path(), OPEN_FILE_MODE_WRITE
,
55 base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread
, this,
59 // Closes a file. Ignores result, since outlives the caller. Must be called on
61 void CloseFileOnUIThread() {
62 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
63 DCHECK(abort_callback_
.is_null());
65 if (file_system_
.get() && file_handle_
!= -1) {
66 // Closing a file must not be aborted, since we could end up on files
67 // which are never closed.
68 file_system_
->CloseFile(file_handle_
, base::Bind(&EmptyStatusCallback
));
72 // Requests writing bytes to the file. In case of either success or a failure
73 // |callback| is executed. Must be called on UI thread.
74 void WriteFileOnUIThread(
75 scoped_refptr
<net::IOBuffer
> buffer
,
78 const storage::AsyncFileUtil::StatusCallback
& callback
) {
79 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
80 DCHECK(abort_callback_
.is_null());
82 // If the file system got unmounted, then abort the writing operation.
83 if (!file_system_
.get()) {
84 BrowserThread::PostTask(
87 base::Bind(callback
, base::File::FILE_ERROR_ABORT
));
91 abort_callback_
= file_system_
->WriteFile(
97 &OperationRunner::OnWriteFileCompletedOnUIThread
, this, callback
));
100 // Aborts the most recent operation (if exists).
101 void AbortOnUIThread() {
102 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
103 if (abort_callback_
.is_null())
106 const AbortCallback last_abort_callback
= abort_callback_
;
107 abort_callback_
= AbortCallback();
108 last_abort_callback
.Run();
112 friend class base::RefCountedThreadSafe
<OperationRunner
>;
114 virtual ~OperationRunner() {}
116 // Remembers a file handle for further operations and forwards the result to
118 void OnOpenFileCompletedOnUIThread(
119 const storage::AsyncFileUtil::StatusCallback
& callback
,
121 base::File::Error result
) {
122 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
124 abort_callback_
= AbortCallback();
125 if (result
== base::File::FILE_OK
)
126 file_handle_
= file_handle
;
128 BrowserThread::PostTask(
129 BrowserThread::IO
, FROM_HERE
, base::Bind(callback
, result
));
132 // Forwards a response of writing to a file to the IO thread.
133 void OnWriteFileCompletedOnUIThread(
134 const storage::AsyncFileUtil::StatusCallback
& callback
,
135 base::File::Error result
) {
136 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
138 abort_callback_
= AbortCallback();
139 BrowserThread::PostTask(
140 BrowserThread::IO
, FROM_HERE
, base::Bind(callback
, result
));
143 AbortCallback abort_callback_
;
144 base::WeakPtr
<ProvidedFileSystemInterface
> file_system_
;
147 DISALLOW_COPY_AND_ASSIGN(OperationRunner
);
150 FileStreamWriter::FileStreamWriter(const storage::FileSystemURL
& url
,
151 int64 initial_offset
)
153 current_offset_(initial_offset
),
154 runner_(new OperationRunner
),
155 state_(NOT_INITIALIZED
),
156 weak_ptr_factory_(this) {
159 FileStreamWriter::~FileStreamWriter() {
160 BrowserThread::PostTask(
163 base::Bind(&OperationRunner::CloseFileOnUIThread
, runner_
));
166 void FileStreamWriter::Initialize(
167 const base::Closure
& pending_closure
,
168 const net::CompletionCallback
& error_callback
) {
169 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
170 DCHECK_EQ(NOT_INITIALIZED
, state_
);
171 state_
= INITIALIZING
;
173 BrowserThread::PostTask(
176 base::Bind(&OperationRunner::OpenFileOnUIThread
,
179 base::Bind(&FileStreamWriter::OnOpenFileCompleted
,
180 weak_ptr_factory_
.GetWeakPtr(),
185 void FileStreamWriter::OnOpenFileCompleted(
186 const base::Closure
& pending_closure
,
187 const net::CompletionCallback
& error_callback
,
188 base::File::Error result
) {
189 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
190 DCHECK_EQ(INITIALIZING
, state_
);
192 // In case of an error, return immediately using the |error_callback| of the
193 // Write() pending request.
194 if (result
!= base::File::FILE_OK
) {
196 error_callback
.Run(net::FileErrorToNetError(result
));
200 DCHECK_EQ(base::File::FILE_OK
, result
);
201 state_
= INITIALIZED
;
203 // Run the task waiting for the initialization to be completed.
204 pending_closure
.Run();
207 int FileStreamWriter::Write(net::IOBuffer
* buffer
,
209 const net::CompletionCallback
& callback
) {
210 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
211 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
212 "FileStreamWriter::Write",
218 case NOT_INITIALIZED
:
219 // Lazily initialize with the first call to Write().
220 Initialize(base::Bind(&FileStreamWriter::WriteAfterInitialized
,
221 weak_ptr_factory_
.GetWeakPtr(),
222 make_scoped_refptr(buffer
),
224 base::Bind(&FileStreamWriter::OnWriteCompleted
,
225 weak_ptr_factory_
.GetWeakPtr(),
227 base::Bind(&FileStreamWriter::OnWriteCompleted
,
228 weak_ptr_factory_
.GetWeakPtr(),
237 WriteAfterInitialized(buffer
,
239 base::Bind(&FileStreamWriter::OnWriteCompleted
,
240 weak_ptr_factory_
.GetWeakPtr(),
250 return net::ERR_IO_PENDING
;
253 int FileStreamWriter::Cancel(const net::CompletionCallback
& callback
) {
254 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
256 if (state_
!= INITIALIZING
&& state_
!= EXECUTING
)
257 return net::ERR_UNEXPECTED
;
259 // Abort and Optimistically return an OK result code, as the aborting
260 // operation is always forced and can't be cancelled.
261 BrowserThread::PostTask(
262 BrowserThread::UI
, FROM_HERE
,
263 base::Bind(&OperationRunner::AbortOnUIThread
, runner_
));
264 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
265 base::Bind(callback
, net::OK
));
267 return net::ERR_IO_PENDING
;
270 int FileStreamWriter::Flush(const net::CompletionCallback
& callback
) {
271 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
272 base::ThreadTaskRunnerHandle::Get()->PostTask(
274 base::Bind(callback
, state_
== INITIALIZED
? net::OK
: net::ERR_FAILED
));
276 return net::ERR_IO_PENDING
;
279 void FileStreamWriter::OnWriteFileCompleted(
281 const net::CompletionCallback
& callback
,
282 base::File::Error result
) {
283 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
284 DCHECK_EQ(EXECUTING
, state_
);
285 state_
= INITIALIZED
;
287 if (result
!= base::File::FILE_OK
) {
289 callback
.Run(net::FileErrorToNetError(result
));
293 current_offset_
+= buffer_length
;
294 callback
.Run(buffer_length
);
297 void FileStreamWriter::OnWriteCompleted(net::CompletionCallback callback
,
299 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
300 callback
.Run(result
);
301 TRACE_EVENT_ASYNC_END0(
302 "file_system_provider", "FileStreamWriter::Write", this);
305 void FileStreamWriter::WriteAfterInitialized(
306 scoped_refptr
<net::IOBuffer
> buffer
,
308 const net::CompletionCallback
& callback
) {
309 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
310 DCHECK_EQ(INITIALIZED
, state_
);
313 BrowserThread::PostTask(
316 base::Bind(&OperationRunner::WriteFileOnUIThread
,
321 base::Bind(&FileStreamWriter::OnWriteFileCompleted
,
322 weak_ptr_factory_
.GetWeakPtr(),
327 } // namespace file_system_provider
328 } // namespace chromeos