1// Copyright (c) 2012 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 "storage/browser/fileapi/local_file_stream_writer.h"
6
7#include "base/callback.h"
8#include "base/message_loop/message_loop.h"
9#include "net/base/file_stream.h"
10#include "net/base/io_buffer.h"
11#include "net/base/net_errors.h"
12
13namespace storage {
14
15namespace {
16
17const int kOpenFlagsForWrite = base::File::FLAG_OPEN |
18                               base::File::FLAG_WRITE |
19                               base::File::FLAG_ASYNC;
20const int kCreateFlagsForWrite = base::File::FLAG_CREATE |
21                                 base::File::FLAG_WRITE |
22                                 base::File::FLAG_ASYNC;
23
24}  // namespace
25
26FileStreamWriter* FileStreamWriter::CreateForLocalFile(
27    base::TaskRunner* task_runner,
28    const base::FilePath& file_path,
29    int64 initial_offset,
30    OpenOrCreate open_or_create) {
31  return new LocalFileStreamWriter(
32      task_runner, file_path, initial_offset, open_or_create);
33}
34
35LocalFileStreamWriter::~LocalFileStreamWriter() {
36  // Invalidate weak pointers so that we won't receive any callbacks from
37  // in-flight stream operations, which might be triggered during the file close
38  // in the FileStream destructor.
39  weak_factory_.InvalidateWeakPtrs();
40
41  // FileStream's destructor closes the file safely, since we opened the file
42  // by its Open() method.
43}
44
45int LocalFileStreamWriter::Write(net::IOBuffer* buf, int buf_len,
46                                 const net::CompletionCallback& callback) {
47  DCHECK(!has_pending_operation_);
48  DCHECK(cancel_callback_.is_null());
49
50  has_pending_operation_ = true;
51  if (stream_impl_) {
52    int result = InitiateWrite(buf, buf_len, callback);
53    if (result != net::ERR_IO_PENDING)
54      has_pending_operation_ = false;
55    return result;
56  }
57  return InitiateOpen(callback,
58                      base::Bind(&LocalFileStreamWriter::ReadyToWrite,
59                                 weak_factory_.GetWeakPtr(),
60                                 make_scoped_refptr(buf), buf_len, callback));
61}
62
63int LocalFileStreamWriter::Cancel(const net::CompletionCallback& callback) {
64  if (!has_pending_operation_)
65    return net::ERR_UNEXPECTED;
66
67  DCHECK(!callback.is_null());
68  cancel_callback_ = callback;
69  return net::ERR_IO_PENDING;
70}
71
72int LocalFileStreamWriter::Flush(const net::CompletionCallback& callback) {
73  DCHECK(!has_pending_operation_);
74  DCHECK(cancel_callback_.is_null());
75
76  // Write() is not called yet, so there's nothing to flush.
77  if (!stream_impl_)
78    return net::OK;
79
80  has_pending_operation_ = true;
81  int result = InitiateFlush(callback);
82  if (result != net::ERR_IO_PENDING)
83    has_pending_operation_ = false;
84  return result;
85}
86
87LocalFileStreamWriter::LocalFileStreamWriter(base::TaskRunner* task_runner,
88                                             const base::FilePath& file_path,
89                                             int64 initial_offset,
90                                             OpenOrCreate open_or_create)
91    : file_path_(file_path),
92      open_or_create_(open_or_create),
93      initial_offset_(initial_offset),
94      task_runner_(task_runner),
95      has_pending_operation_(false),
96      weak_factory_(this) {}
97
98int LocalFileStreamWriter::InitiateOpen(
99    const net::CompletionCallback& error_callback,
100    const base::Closure& main_operation) {
101  DCHECK(has_pending_operation_);
102  DCHECK(!stream_impl_.get());
103
104  stream_impl_.reset(new net::FileStream(task_runner_));
105
106  int open_flags = 0;
107  switch (open_or_create_) {
108  case OPEN_EXISTING_FILE:
109    open_flags = kOpenFlagsForWrite;
110    break;
111  case CREATE_NEW_FILE:
112    open_flags = kCreateFlagsForWrite;
113    break;
114  }
115
116  return stream_impl_->Open(file_path_,
117                            open_flags,
118                            base::Bind(&LocalFileStreamWriter::DidOpen,
119                                       weak_factory_.GetWeakPtr(),
120                                       error_callback,
121                                       main_operation));
122}
123
124void LocalFileStreamWriter::DidOpen(
125    const net::CompletionCallback& error_callback,
126    const base::Closure& main_operation,
127    int result) {
128  DCHECK(has_pending_operation_);
129  DCHECK(stream_impl_.get());
130
131  if (CancelIfRequested())
132    return;
133
134  if (result != net::OK) {
135    has_pending_operation_ = false;
136    stream_impl_.reset(NULL);
137    error_callback.Run(result);
138    return;
139  }
140
141  InitiateSeek(error_callback, main_operation);
142}
143
144void LocalFileStreamWriter::InitiateSeek(
145    const net::CompletionCallback& error_callback,
146    const base::Closure& main_operation) {
147  DCHECK(has_pending_operation_);
148  DCHECK(stream_impl_.get());
149
150  if (initial_offset_ == 0) {
151    // No need to seek.
152    main_operation.Run();
153    return;
154  }
155
156  int result = stream_impl_->Seek(base::File::FROM_BEGIN, initial_offset_,
157                                  base::Bind(&LocalFileStreamWriter::DidSeek,
158                                             weak_factory_.GetWeakPtr(),
159                                             error_callback,
160                                             main_operation));
161  if (result != net::ERR_IO_PENDING) {
162    has_pending_operation_ = false;
163    error_callback.Run(result);
164  }
165}
166
167void LocalFileStreamWriter::DidSeek(
168    const net::CompletionCallback& error_callback,
169    const base::Closure& main_operation,
170    int64 result) {
171  DCHECK(has_pending_operation_);
172
173  if (CancelIfRequested())
174    return;
175
176  if (result != initial_offset_) {
177    // TODO(kinaba) add a more specific error code.
178    result = net::ERR_FAILED;
179  }
180
181  if (result < 0) {
182    has_pending_operation_ = false;
183    error_callback.Run(static_cast<int>(result));
184    return;
185  }
186
187  main_operation.Run();
188}
189
190void LocalFileStreamWriter::ReadyToWrite(
191    net::IOBuffer* buf, int buf_len,
192    const net::CompletionCallback& callback) {
193  DCHECK(has_pending_operation_);
194
195  int result = InitiateWrite(buf, buf_len, callback);
196  if (result != net::ERR_IO_PENDING) {
197    has_pending_operation_ = false;
198    callback.Run(result);
199  }
200}
201
202int LocalFileStreamWriter::InitiateWrite(
203    net::IOBuffer* buf, int buf_len,
204    const net::CompletionCallback& callback) {
205  DCHECK(has_pending_operation_);
206  DCHECK(stream_impl_.get());
207
208  return stream_impl_->Write(buf, buf_len,
209                             base::Bind(&LocalFileStreamWriter::DidWrite,
210                                        weak_factory_.GetWeakPtr(),
211                                        callback));
212}
213
214void LocalFileStreamWriter::DidWrite(const net::CompletionCallback& callback,
215                                     int result) {
216  DCHECK(has_pending_operation_);
217
218  if (CancelIfRequested())
219    return;
220  has_pending_operation_ = false;
221  callback.Run(result);
222}
223
224int LocalFileStreamWriter::InitiateFlush(
225    const net::CompletionCallback& callback) {
226  DCHECK(has_pending_operation_);
227  DCHECK(stream_impl_.get());
228
229  return stream_impl_->Flush(base::Bind(&LocalFileStreamWriter::DidFlush,
230                                        weak_factory_.GetWeakPtr(),
231                                        callback));
232}
233
234void LocalFileStreamWriter::DidFlush(const net::CompletionCallback& callback,
235                                     int result) {
236  DCHECK(has_pending_operation_);
237
238  if (CancelIfRequested())
239    return;
240  has_pending_operation_ = false;
241  callback.Run(result);
242}
243
244bool LocalFileStreamWriter::CancelIfRequested() {
245  DCHECK(has_pending_operation_);
246
247  if (cancel_callback_.is_null())
248    return false;
249
250  net::CompletionCallback pending_cancel = cancel_callback_;
251  has_pending_operation_ = false;
252  cancel_callback_.Reset();
253  pending_cancel.Run(net::OK);
254  return true;
255}
256
257}  // namespace storage
258