file_stream_writer.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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 "base/debug/trace_event.h"
8#include "base/memory/ref_counted.h"
9#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h"
10#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
11#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
12#include "content/public/browser/browser_thread.h"
13#include "net/base/io_buffer.h"
14#include "net/base/net_errors.h"
15
16using content::BrowserThread;
17
18namespace chromeos {
19namespace file_system_provider {
20namespace {
21
22// Dicards the callback from CloseFile().
23void EmptyStatusCallback(base::File::Error /* result */) {
24}
25
26// Opens a file for writing and calls the completion callback. Must be called
27// on UI thread.
28void OpenFileOnUIThread(
29    const fileapi::FileSystemURL& url,
30    const FileStreamWriter::OpenFileCompletedCallback& callback) {
31  DCHECK_CURRENTLY_ON(BrowserThread::UI);
32
33  util::FileSystemURLParser parser(url);
34  if (!parser.Parse()) {
35    callback.Run(base::WeakPtr<ProvidedFileSystemInterface>(),
36                 base::FilePath(),
37                 0 /* file_handle */,
38                 base::File::FILE_ERROR_SECURITY);
39    return;
40  }
41
42  parser.file_system()->OpenFile(
43      parser.file_path(),
44      ProvidedFileSystemInterface::OPEN_FILE_MODE_WRITE,
45      base::Bind(
46          callback, parser.file_system()->GetWeakPtr(), parser.file_path()));
47}
48
49// Forwards results of calling OpenFileOnUIThread back to the IO thread.
50void OnOpenFileCompletedOnUIThread(
51    const FileStreamWriter::OpenFileCompletedCallback& callback,
52    base::WeakPtr<ProvidedFileSystemInterface> file_system,
53    const base::FilePath& file_path,
54    int file_handle,
55    base::File::Error result) {
56  DCHECK_CURRENTLY_ON(BrowserThread::UI);
57  BrowserThread::PostTask(
58      BrowserThread::IO,
59      FROM_HERE,
60      base::Bind(callback, file_system, file_path, file_handle, result));
61}
62
63// Closes a file. Ignores result, since it is called from a constructor.
64// Must be called on UI thread.
65void CloseFileOnUIThread(base::WeakPtr<ProvidedFileSystemInterface> file_system,
66                         int file_handle) {
67  DCHECK_CURRENTLY_ON(BrowserThread::UI);
68  if (file_system.get())
69    file_system->CloseFile(file_handle, base::Bind(&EmptyStatusCallback));
70}
71
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.
74void WriteFileOnUIThread(
75    base::WeakPtr<ProvidedFileSystemInterface> file_system,
76    int file_handle,
77    scoped_refptr<net::IOBuffer> buffer,
78    int64 offset,
79    int length,
80    const fileapi::AsyncFileUtil::StatusCallback& callback) {
81  DCHECK_CURRENTLY_ON(BrowserThread::UI);
82
83  // If the file system got unmounted, then abort the writing operation.
84  if (!file_system.get()) {
85    callback.Run(base::File::FILE_ERROR_ABORT);
86    return;
87  }
88
89  file_system->WriteFile(file_handle, buffer, offset, length, callback);
90}
91
92// Forward the completion callback to IO thread.
93void OnWriteFileCompletedOnUIThread(
94    const fileapi::AsyncFileUtil::StatusCallback& callback,
95    base::File::Error result) {
96  DCHECK_CURRENTLY_ON(BrowserThread::UI);
97  BrowserThread::PostTask(
98      BrowserThread::IO, FROM_HERE, base::Bind(callback, result));
99}
100
101}  // namespace
102
103FileStreamWriter::FileStreamWriter(const fileapi::FileSystemURL& url,
104                                   int64 initial_offset)
105    : url_(url),
106      current_offset_(initial_offset),
107      state_(NOT_INITIALIZED),
108      file_handle_(0),
109      weak_ptr_factory_(this) {
110}
111
112FileStreamWriter::~FileStreamWriter() {
113  BrowserThread::PostTask(
114      BrowserThread::UI,
115      FROM_HERE,
116      base::Bind(&CloseFileOnUIThread, file_system_, file_handle_));
117}
118
119void FileStreamWriter::Initialize(
120    const base::Closure& pending_closure,
121    const net::CompletionCallback& error_callback) {
122  DCHECK_EQ(NOT_INITIALIZED, state_);
123  state_ = INITIALIZING;
124
125  BrowserThread::PostTask(
126      BrowserThread::UI,
127      FROM_HERE,
128      base::Bind(&OpenFileOnUIThread,
129                 url_,
130                 base::Bind(&OnOpenFileCompletedOnUIThread,
131                            base::Bind(&FileStreamWriter::OnOpenFileCompleted,
132                                       weak_ptr_factory_.GetWeakPtr(),
133                                       pending_closure,
134                                       error_callback))));
135}
136
137void FileStreamWriter::OnOpenFileCompleted(
138    const base::Closure& pending_closure,
139    const net::CompletionCallback& error_callback,
140    base::WeakPtr<ProvidedFileSystemInterface> file_system,
141    const base::FilePath& file_path,
142    int file_handle,
143    base::File::Error result) {
144  DCHECK_CURRENTLY_ON(BrowserThread::IO);
145  DCHECK_EQ(INITIALIZING, state_);
146
147  // In case of an error, return immediately using the |error_callback| of the
148  // Write() pending request.
149  if (result != base::File::FILE_OK) {
150    state_ = FAILED;
151    error_callback.Run(net::FileErrorToNetError(result));
152    return;
153  }
154
155  file_system_ = file_system;
156  file_path_ = file_path;
157  file_handle_ = file_handle;
158  DCHECK_LT(0, file_handle);
159  DCHECK_EQ(base::File::FILE_OK, result);
160  state_ = INITIALIZED;
161
162  // Run the task waiting for the initialization to be completed.
163  pending_closure.Run();
164}
165
166int FileStreamWriter::Write(net::IOBuffer* buffer,
167                            int buffer_length,
168                            const net::CompletionCallback& callback) {
169  DCHECK_CURRENTLY_ON(BrowserThread::IO);
170  TRACE_EVENT_ASYNC_BEGIN1("file_system_provider",
171                           "FileStreamWriter::Write",
172                           this,
173                           "buffer_length",
174                           buffer_length);
175
176  switch (state_) {
177    case NOT_INITIALIZED:
178      // Lazily initialize with the first call to Write().
179      Initialize(base::Bind(&FileStreamWriter::WriteAfterInitialized,
180                            weak_ptr_factory_.GetWeakPtr(),
181                            make_scoped_refptr(buffer),
182                            buffer_length,
183                            base::Bind(&FileStreamWriter::OnWriteCompleted,
184                                       weak_ptr_factory_.GetWeakPtr(),
185                                       callback)),
186                 base::Bind(&FileStreamWriter::OnWriteCompleted,
187                            weak_ptr_factory_.GetWeakPtr(),
188                            callback));
189      break;
190
191    case INITIALIZING:
192      NOTREACHED();
193      break;
194
195    case INITIALIZED:
196      WriteAfterInitialized(buffer,
197                            buffer_length,
198                            base::Bind(&FileStreamWriter::OnWriteCompleted,
199                                       weak_ptr_factory_.GetWeakPtr(),
200                                       callback));
201      break;
202
203    case FAILED:
204      NOTREACHED();
205      break;
206  }
207
208  return net::ERR_IO_PENDING;
209}
210
211int FileStreamWriter::Cancel(const net::CompletionCallback& callback) {
212  NOTIMPLEMENTED();
213  return net::ERR_FAILED;
214}
215
216int FileStreamWriter::Flush(const net::CompletionCallback& callback) {
217  if (state_ != INITIALIZED)
218    return net::ERR_FAILED;
219
220  return net::OK;
221}
222
223void FileStreamWriter::OnWriteFileCompleted(
224    int buffer_length,
225    const net::CompletionCallback& callback,
226    base::File::Error result) {
227  DCHECK_CURRENTLY_ON(BrowserThread::IO);
228  DCHECK_EQ(INITIALIZED, state_);
229
230  if (result != base::File::FILE_OK) {
231    state_ = FAILED;
232    callback.Run(net::FileErrorToNetError(result));
233    return;
234  }
235
236  current_offset_ += buffer_length;
237  callback.Run(buffer_length);
238}
239
240void FileStreamWriter::OnWriteCompleted(net::CompletionCallback callback,
241                                        int result) {
242  DCHECK_CURRENTLY_ON(BrowserThread::IO);
243  callback.Run(result);
244  TRACE_EVENT_ASYNC_END0(
245      "file_system_provider", "FileStreamWriter::Write", this);
246}
247
248void FileStreamWriter::WriteAfterInitialized(
249    scoped_refptr<net::IOBuffer> buffer,
250    int buffer_length,
251    const net::CompletionCallback& callback) {
252  DCHECK_CURRENTLY_ON(BrowserThread::IO);
253  DCHECK_EQ(INITIALIZED, state_);
254
255  BrowserThread::PostTask(
256      BrowserThread::UI,
257      FROM_HERE,
258      base::Bind(&WriteFileOnUIThread,
259                 file_system_,
260                 file_handle_,
261                 buffer,
262                 current_offset_,
263                 buffer_length,
264                 base::Bind(&OnWriteFileCompletedOnUIThread,
265                            base::Bind(&FileStreamWriter::OnWriteFileCompleted,
266                                       weak_ptr_factory_.GetWeakPtr(),
267                                       buffer_length,
268                                       callback))));
269}
270
271}  // namespace file_system_provider
272}  // namespace chromeos
273