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.
4 
5 #include "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/macros.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "base/trace_event/trace_event.h"
14 #include "chrome/browser/chromeos/file_system_provider/abort_callback.h"
15 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
16 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
17 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
18 #include "chrome/browser/chromeos/file_system_provider/scoped_file_opener.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/net_errors.h"
23 
24 using content::BrowserThread;
25 
26 namespace chromeos {
27 namespace file_system_provider {
28 
29 class FileStreamWriter::OperationRunner
30     : public base::RefCountedThreadSafe<
31           FileStreamWriter::OperationRunner,
32           content::BrowserThread::DeleteOnUIThread> {
33  public:
OperationRunner()34   OperationRunner() : file_handle_(0) {}
35 
36   // Opens a file for writing and calls the completion callback. Must be called
37   // on UI thread.
OpenFileOnUIThread(const storage::FileSystemURL & url,storage::AsyncFileUtil::StatusCallback callback)38   void OpenFileOnUIThread(const storage::FileSystemURL& url,
39                           storage::AsyncFileUtil::StatusCallback callback) {
40     DCHECK_CURRENTLY_ON(BrowserThread::UI);
41     DCHECK(abort_callback_.is_null());
42 
43     util::FileSystemURLParser parser(url);
44     if (!parser.Parse()) {
45       content::GetIOThreadTaskRunner({})->PostTask(
46           FROM_HERE,
47           base::BindOnce(std::move(callback), base::File::FILE_ERROR_SECURITY));
48       return;
49     }
50 
51     file_system_ = parser.file_system()->GetWeakPtr();
52     file_opener_.reset(new ScopedFileOpener(
53         parser.file_system(), parser.file_path(), OPEN_FILE_MODE_WRITE,
54         base::Bind(&OperationRunner::OnOpenFileCompletedOnUIThread, this,
55                    base::Passed(&callback))));
56   }
57 
58   // Requests writing bytes to the file. In case of either success or a failure
59   // |callback| is executed. Must be called on UI thread.
WriteFileOnUIThread(scoped_refptr<net::IOBuffer> buffer,int64_t offset,int length,storage::AsyncFileUtil::StatusCallback callback)60   void WriteFileOnUIThread(scoped_refptr<net::IOBuffer> buffer,
61                            int64_t offset,
62                            int length,
63                            storage::AsyncFileUtil::StatusCallback callback) {
64     DCHECK_CURRENTLY_ON(BrowserThread::UI);
65     DCHECK(abort_callback_.is_null());
66 
67     // If the file system got unmounted, then abort the writing operation.
68     if (!file_system_.get()) {
69       content::GetIOThreadTaskRunner({})->PostTask(
70           FROM_HERE,
71           base::BindOnce(std::move(callback), base::File::FILE_ERROR_ABORT));
72       return;
73     }
74 
75     abort_callback_ = file_system_->WriteFile(
76         file_handle_, buffer.get(), offset, length,
77         base::BindOnce(&OperationRunner::OnWriteFileCompletedOnUIThread, this,
78                        std::move(callback)));
79   }
80 
81   // Aborts the most recent operation (if exists) and closes a file if opened.
82   // The runner must not be used anymore after calling this method.
CloseRunnerOnUIThread()83   void CloseRunnerOnUIThread() {
84     DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 
86     if (!abort_callback_.is_null())
87       std::move(abort_callback_).Run();
88 
89     // Close the file (if opened).
90     file_opener_.reset();
91   }
92 
93  private:
94   friend struct content::BrowserThread::DeleteOnThread<
95       content::BrowserThread::UI>;
96   friend class base::DeleteHelper<OperationRunner>;
97 
~OperationRunner()98   virtual ~OperationRunner() {}
99 
100   // Remembers a file handle for further operations and forwards the result to
101   // the IO thread.
OnOpenFileCompletedOnUIThread(storage::AsyncFileUtil::StatusCallback callback,int file_handle,base::File::Error result)102   void OnOpenFileCompletedOnUIThread(
103       storage::AsyncFileUtil::StatusCallback callback,
104       int file_handle,
105       base::File::Error result) {
106     DCHECK_CURRENTLY_ON(BrowserThread::UI);
107 
108     abort_callback_.Reset();
109     if (result == base::File::FILE_OK)
110       file_handle_ = file_handle;
111 
112     content::GetIOThreadTaskRunner({})->PostTask(
113         FROM_HERE, base::BindOnce(std::move(callback), result));
114   }
115 
116   // Forwards a response of writing to a file to the IO thread.
OnWriteFileCompletedOnUIThread(storage::AsyncFileUtil::StatusCallback callback,base::File::Error result)117   void OnWriteFileCompletedOnUIThread(
118       storage::AsyncFileUtil::StatusCallback callback,
119       base::File::Error result) {
120     DCHECK_CURRENTLY_ON(BrowserThread::UI);
121 
122     abort_callback_.Reset();
123     content::GetIOThreadTaskRunner({})->PostTask(
124         FROM_HERE, base::BindOnce(std::move(callback), result));
125   }
126 
127   AbortCallback abort_callback_;
128   base::WeakPtr<ProvidedFileSystemInterface> file_system_;
129   std::unique_ptr<ScopedFileOpener> file_opener_;
130   int file_handle_;
131 
132   DISALLOW_COPY_AND_ASSIGN(OperationRunner);
133 };
134 
FileStreamWriter(const storage::FileSystemURL & url,int64_t initial_offset)135 FileStreamWriter::FileStreamWriter(const storage::FileSystemURL& url,
136                                    int64_t initial_offset)
137     : url_(url),
138       current_offset_(initial_offset),
139       runner_(new OperationRunner),
140       state_(NOT_INITIALIZED) {}
141 
~FileStreamWriter()142 FileStreamWriter::~FileStreamWriter() {
143   // Close the runner explicitly if the file streamer is
144   if (state_ != CANCELLING) {
145     content::GetUIThreadTaskRunner({})->PostTask(
146         FROM_HERE,
147         base::BindOnce(&OperationRunner::CloseRunnerOnUIThread, runner_));
148   }
149 
150   // If a write is in progress, mark it as completed.
151   TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
152                                   "FileStreamWriter::Write", this);
153 }
154 
Initialize(base::OnceClosure pending_closure,net::CompletionOnceCallback error_callback)155 void FileStreamWriter::Initialize(base::OnceClosure pending_closure,
156                                   net::CompletionOnceCallback error_callback) {
157   DCHECK_CURRENTLY_ON(BrowserThread::IO);
158   DCHECK_EQ(NOT_INITIALIZED, state_);
159   state_ = INITIALIZING;
160 
161   content::GetUIThreadTaskRunner({})->PostTask(
162       FROM_HERE,
163       base::BindOnce(&OperationRunner::OpenFileOnUIThread, runner_, url_,
164                      base::BindOnce(&FileStreamWriter::OnOpenFileCompleted,
165                                     weak_ptr_factory_.GetWeakPtr(),
166                                     std::move(pending_closure),
167                                     std::move(error_callback))));
168 }
169 
OnOpenFileCompleted(base::OnceClosure pending_closure,net::CompletionOnceCallback error_callback,base::File::Error result)170 void FileStreamWriter::OnOpenFileCompleted(
171     base::OnceClosure pending_closure,
172     net::CompletionOnceCallback error_callback,
173     base::File::Error result) {
174   DCHECK_CURRENTLY_ON(BrowserThread::IO);
175   DCHECK(state_ == INITIALIZING || state_ == CANCELLING);
176   if (state_ == CANCELLING)
177     return;
178 
179   // In case of an error, return immediately using the |error_callback| of the
180   // Write() pending request.
181   if (result != base::File::FILE_OK) {
182     state_ = FAILED;
183     std::move(error_callback).Run(net::FileErrorToNetError(result));
184     return;
185   }
186 
187   DCHECK_EQ(base::File::FILE_OK, result);
188   state_ = INITIALIZED;
189 
190   // Run the task waiting for the initialization to be completed.
191   std::move(pending_closure).Run();
192 }
193 
Write(net::IOBuffer * buffer,int buffer_length,net::CompletionOnceCallback callback)194 int FileStreamWriter::Write(net::IOBuffer* buffer,
195                             int buffer_length,
196                             net::CompletionOnceCallback callback) {
197   DCHECK_CURRENTLY_ON(BrowserThread::IO);
198   TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("file_system_provider",
199                                     "FileStreamWriter::Write", this,
200                                     "buffer_length", buffer_length);
201 
202   write_callback_ = std::move(callback);
203   switch (state_) {
204     case NOT_INITIALIZED:
205       // Lazily initialize with the first call to Write().
206       Initialize(
207           base::BindOnce(&FileStreamWriter::WriteAfterInitialized,
208                          weak_ptr_factory_.GetWeakPtr(),
209                          base::WrapRefCounted(buffer), buffer_length,
210                          base::BindOnce(&FileStreamWriter::OnWriteCompleted,
211                                         weak_ptr_factory_.GetWeakPtr())),
212           base::BindOnce(&FileStreamWriter::OnWriteCompleted,
213                          weak_ptr_factory_.GetWeakPtr()));
214       break;
215 
216     case INITIALIZING:
217       NOTREACHED();
218       break;
219 
220     case INITIALIZED:
221       WriteAfterInitialized(buffer, buffer_length,
222                             base::BindOnce(&FileStreamWriter::OnWriteCompleted,
223                                            weak_ptr_factory_.GetWeakPtr()));
224       break;
225 
226     case EXECUTING:
227     case FAILED:
228     case CANCELLING:
229       NOTREACHED();
230       break;
231   }
232 
233   return net::ERR_IO_PENDING;
234 }
235 
Cancel(net::CompletionOnceCallback callback)236 int FileStreamWriter::Cancel(net::CompletionOnceCallback callback) {
237   DCHECK_CURRENTLY_ON(BrowserThread::IO);
238 
239   if (state_ != INITIALIZING && state_ != EXECUTING)
240     return net::ERR_UNEXPECTED;
241 
242   state_ = CANCELLING;
243 
244   // Abort and optimistically return an OK result code, as the aborting
245   // operation is always forced and can't be cancelled. Similarly, for closing
246   // files.
247   content::GetUIThreadTaskRunner({})->PostTask(
248       FROM_HERE,
249       base::BindOnce(&OperationRunner::CloseRunnerOnUIThread, runner_));
250   base::ThreadTaskRunnerHandle::Get()->PostTask(
251       FROM_HERE, base::BindOnce(std::move(callback), net::OK));
252 
253   // If a write is in progress, mark it as completed.
254   TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
255                                   "FileStreamWriter::Write", this);
256 
257   return net::ERR_IO_PENDING;
258 }
259 
Flush(net::CompletionOnceCallback callback)260 int FileStreamWriter::Flush(net::CompletionOnceCallback callback) {
261   DCHECK_CURRENTLY_ON(BrowserThread::IO);
262   DCHECK_NE(CANCELLING, state_);
263 
264   base::ThreadTaskRunnerHandle::Get()->PostTask(
265       FROM_HERE,
266       base::BindOnce(std::move(callback),
267                      state_ == INITIALIZED ? net::OK : net::ERR_FAILED));
268 
269   return net::ERR_IO_PENDING;
270 }
271 
OnWriteFileCompleted(int buffer_length,net::CompletionOnceCallback callback,base::File::Error result)272 void FileStreamWriter::OnWriteFileCompleted(
273     int buffer_length,
274     net::CompletionOnceCallback callback,
275     base::File::Error result) {
276   DCHECK_CURRENTLY_ON(BrowserThread::IO);
277   DCHECK(state_ == EXECUTING || state_ == CANCELLING);
278   if (state_ == CANCELLING)
279     return;
280 
281   state_ = INITIALIZED;
282 
283   if (result != base::File::FILE_OK) {
284     state_ = FAILED;
285     std::move(callback).Run(net::FileErrorToNetError(result));
286     return;
287   }
288 
289   current_offset_ += buffer_length;
290   std::move(callback).Run(buffer_length);
291 }
292 
OnWriteCompleted(int result)293 void FileStreamWriter::OnWriteCompleted(int result) {
294   DCHECK_CURRENTLY_ON(BrowserThread::IO);
295   if (state_ != CANCELLING)
296     std::move(write_callback_).Run(result);
297 
298   TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
299                                   "FileStreamWriter::Write", this);
300 }
301 
WriteAfterInitialized(scoped_refptr<net::IOBuffer> buffer,int buffer_length,net::CompletionOnceCallback callback)302 void FileStreamWriter::WriteAfterInitialized(
303     scoped_refptr<net::IOBuffer> buffer,
304     int buffer_length,
305     net::CompletionOnceCallback callback) {
306   DCHECK_CURRENTLY_ON(BrowserThread::IO);
307   DCHECK(state_ == INITIALIZED || state_ == CANCELLING);
308   if (state_ == CANCELLING)
309     return;
310 
311   state_ = EXECUTING;
312 
313   content::GetUIThreadTaskRunner({})->PostTask(
314       FROM_HERE,
315       base::BindOnce(&OperationRunner::WriteFileOnUIThread, runner_, buffer,
316                      current_offset_, buffer_length,
317                      base::BindOnce(&FileStreamWriter::OnWriteFileCompleted,
318                                     weak_ptr_factory_.GetWeakPtr(),
319                                     buffer_length, std::move(callback))));
320 }
321 
322 }  // namespace file_system_provider
323 }  // namespace chromeos
324