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
13 namespace storage {
14
15 namespace {
16
17 const int kOpenFlagsForWrite = base::File::FLAG_OPEN |
18 base::File::FLAG_WRITE |
19 base::File::FLAG_ASYNC;
20 const int kCreateFlagsForWrite = base::File::FLAG_CREATE |
21 base::File::FLAG_WRITE |
22 base::File::FLAG_ASYNC;
23
24 } // namespace
25
CreateForLocalFile(base::TaskRunner * task_runner,const base::FilePath & file_path,int64 initial_offset,OpenOrCreate open_or_create)26 FileStreamWriter* 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
~LocalFileStreamWriter()35 LocalFileStreamWriter::~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
Write(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)45 int 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
Cancel(const net::CompletionCallback & callback)63 int 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
Flush(const net::CompletionCallback & callback)72 int 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
LocalFileStreamWriter(base::TaskRunner * task_runner,const base::FilePath & file_path,int64 initial_offset,OpenOrCreate open_or_create)87 LocalFileStreamWriter::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
InitiateOpen(const net::CompletionCallback & error_callback,const base::Closure & main_operation)98 int 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
DidOpen(const net::CompletionCallback & error_callback,const base::Closure & main_operation,int result)124 void 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
InitiateSeek(const net::CompletionCallback & error_callback,const base::Closure & main_operation)144 void 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
DidSeek(const net::CompletionCallback & error_callback,const base::Closure & main_operation,int64 result)167 void 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
ReadyToWrite(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)190 void 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
InitiateWrite(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)202 int 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
DidWrite(const net::CompletionCallback & callback,int result)214 void 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
InitiateFlush(const net::CompletionCallback & callback)224 int 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
DidFlush(const net::CompletionCallback & callback,int result)234 void 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
CancelIfRequested()244 bool 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