blob: 291b98d1117fbc190a15e5b8b8e31d70ab1b3b74 [file] [log] [blame]
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +09001// 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 "base/files/file_proxy.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/file_util.h"
10#include "base/files/file.h"
11#include "base/location.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/task_runner.h"
14#include "base/task_runner_util.h"
15
rvargas@chromium.orge60d9672014-05-06 09:55:35 +090016namespace {
17
18void FileDeleter(base::File file) {
19}
20
21} // namespace
22
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +090023namespace base {
24
25class FileHelper {
26 public:
27 FileHelper(FileProxy* proxy, File file)
28 : file_(file.Pass()),
rvargas@chromium.orge60d9672014-05-06 09:55:35 +090029 error_(File::FILE_ERROR_FAILED),
30 task_runner_(proxy->task_runner()),
31 proxy_(AsWeakPtr(proxy)) {
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +090032 }
33
34 void PassFile() {
35 if (proxy_)
36 proxy_->SetFile(file_.Pass());
rvargas@chromium.orge60d9672014-05-06 09:55:35 +090037 else if (file_.IsValid())
38 task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +090039 }
40
41 protected:
42 File file_;
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +090043 File::Error error_;
44
45 private:
rvargas@chromium.orge60d9672014-05-06 09:55:35 +090046 scoped_refptr<TaskRunner> task_runner_;
47 WeakPtr<FileProxy> proxy_;
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +090048 DISALLOW_COPY_AND_ASSIGN(FileHelper);
49};
50
51namespace {
52
53class GenericFileHelper : public FileHelper {
54 public:
55 GenericFileHelper(FileProxy* proxy, File file)
56 : FileHelper(proxy, file.Pass()) {
57 }
58
59 void Close() {
60 file_.Close();
61 error_ = File::FILE_OK;
62 }
63
64 void SetTimes(Time last_access_time, Time last_modified_time) {
65 bool rv = file_.SetTimes(last_access_time, last_modified_time);
66 error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
67 }
68
69 void SetLength(int64 length) {
70 if (file_.SetLength(length))
71 error_ = File::FILE_OK;
72 }
73
74 void Flush() {
75 if (file_.Flush())
76 error_ = File::FILE_OK;
77 }
78
79 void Reply(const FileProxy::StatusCallback& callback) {
80 PassFile();
81 if (!callback.is_null())
82 callback.Run(error_);
83 }
84
85 private:
86 DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
87};
88
89class CreateOrOpenHelper : public FileHelper {
90 public:
91 CreateOrOpenHelper(FileProxy* proxy, File file)
92 : FileHelper(proxy, file.Pass()) {
93 }
94
95 void RunWork(const FilePath& file_path, int file_flags) {
96 file_.Initialize(file_path, file_flags);
97 error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
98 }
99
100 void Reply(const FileProxy::StatusCallback& callback) {
101 DCHECK(!callback.is_null());
102 PassFile();
103 callback.Run(error_);
104 }
105
106 private:
107 DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
108};
109
110class CreateTemporaryHelper : public FileHelper {
111 public:
112 CreateTemporaryHelper(FileProxy* proxy, File file)
113 : FileHelper(proxy, file.Pass()) {
114 }
115
116 void RunWork(uint32 additional_file_flags) {
117 // TODO(darin): file_util should have a variant of CreateTemporaryFile
118 // that returns a FilePath and a File.
119 if (!CreateTemporaryFile(&file_path_)) {
120 // TODO(davidben): base::CreateTemporaryFile should preserve the error
121 // code.
122 error_ = File::FILE_ERROR_FAILED;
123 return;
124 }
125
126 uint32 file_flags = File::FLAG_WRITE |
127 File::FLAG_TEMPORARY |
128 File::FLAG_CREATE_ALWAYS |
129 additional_file_flags;
130
131 file_.Initialize(file_path_, file_flags);
132 if (file_.IsValid()) {
133 error_ = File::FILE_OK;
134 } else {
135 error_ = file_.error_details();
136 DeleteFile(file_path_, false);
137 file_path_.clear();
138 }
139 }
140
141 void Reply(const FileProxy::CreateTemporaryCallback& callback) {
142 DCHECK(!callback.is_null());
143 PassFile();
144 callback.Run(error_, file_path_);
145 }
146
147 private:
148 FilePath file_path_;
149 DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
150};
151
152class GetInfoHelper : public FileHelper {
153 public:
154 GetInfoHelper(FileProxy* proxy, File file)
155 : FileHelper(proxy, file.Pass()) {
156 }
157
158 void RunWork() {
159 if (file_.GetInfo(&file_info_))
160 error_ = File::FILE_OK;
161 }
162
163 void Reply(const FileProxy::GetFileInfoCallback& callback) {
164 PassFile();
165 DCHECK(!callback.is_null());
166 callback.Run(error_, file_info_);
167 }
168
169 private:
170 File::Info file_info_;
171 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
172};
173
174class ReadHelper : public FileHelper {
175 public:
176 ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
177 : FileHelper(proxy, file.Pass()),
178 buffer_(new char[bytes_to_read]),
179 bytes_to_read_(bytes_to_read),
180 bytes_read_(0) {
181 }
182
183 void RunWork(int64 offset) {
184 bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
185 error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
186 }
187
188 void Reply(const FileProxy::ReadCallback& callback) {
189 PassFile();
190 DCHECK(!callback.is_null());
191 callback.Run(error_, buffer_.get(), bytes_read_);
192 }
193
194 private:
195 scoped_ptr<char[]> buffer_;
196 int bytes_to_read_;
197 int bytes_read_;
198 DISALLOW_COPY_AND_ASSIGN(ReadHelper);
199};
200
201class WriteHelper : public FileHelper {
202 public:
203 WriteHelper(FileProxy* proxy,
204 File file,
205 const char* buffer, int bytes_to_write)
206 : FileHelper(proxy, file.Pass()),
207 buffer_(new char[bytes_to_write]),
208 bytes_to_write_(bytes_to_write),
209 bytes_written_(0) {
210 memcpy(buffer_.get(), buffer, bytes_to_write);
211 }
212
213 void RunWork(int64 offset) {
214 bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
215 error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
216 }
217
218 void Reply(const FileProxy::WriteCallback& callback) {
219 PassFile();
220 if (!callback.is_null())
221 callback.Run(error_, bytes_written_);
222 }
223
224 private:
225 scoped_ptr<char[]> buffer_;
226 int bytes_to_write_;
227 int bytes_written_;
228 DISALLOW_COPY_AND_ASSIGN(WriteHelper);
229};
230
231} // namespace
232
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +0900233FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
234}
235
236FileProxy::~FileProxy() {
rvargas@chromium.orge60d9672014-05-06 09:55:35 +0900237 if (file_.IsValid())
238 task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +0900239}
240
241bool FileProxy::CreateOrOpen(const FilePath& file_path,
242 uint32 file_flags,
243 const StatusCallback& callback) {
244 DCHECK(!file_.IsValid());
245 CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
246 return task_runner_->PostTaskAndReply(
247 FROM_HERE,
248 Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
249 file_flags),
250 Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback));
251}
252
253bool FileProxy::CreateTemporary(uint32 additional_file_flags,
254 const CreateTemporaryCallback& callback) {
255 DCHECK(!file_.IsValid());
256 CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
257 return task_runner_->PostTaskAndReply(
258 FROM_HERE,
259 Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
260 additional_file_flags),
261 Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
262}
263
264bool FileProxy::IsValid() const {
265 return file_.IsValid();
266}
267
rvargas@chromium.orgbea68922014-05-16 04:40:40 +0900268void FileProxy::SetFile(File file) {
269 DCHECK(!file_.IsValid());
270 file_ = file.Pass();
271}
272
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +0900273File FileProxy::TakeFile() {
274 return file_.Pass();
275}
276
rvargas@chromium.orgbea68922014-05-16 04:40:40 +0900277PlatformFile FileProxy::GetPlatformFile() const {
278 return file_.GetPlatformFile();
279}
280
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +0900281bool FileProxy::Close(const StatusCallback& callback) {
282 DCHECK(file_.IsValid());
283 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
284 return task_runner_->PostTaskAndReply(
285 FROM_HERE,
286 Bind(&GenericFileHelper::Close, Unretained(helper)),
287 Bind(&GenericFileHelper::Reply, Owned(helper), callback));
288}
289
290bool FileProxy::GetInfo(const GetFileInfoCallback& callback) {
291 DCHECK(file_.IsValid());
292 GetInfoHelper* helper = new GetInfoHelper(this, file_.Pass());
293 return task_runner_->PostTaskAndReply(
294 FROM_HERE,
295 Bind(&GetInfoHelper::RunWork, Unretained(helper)),
296 Bind(&GetInfoHelper::Reply, Owned(helper), callback));
297}
298
299bool FileProxy::Read(int64 offset,
300 int bytes_to_read,
301 const ReadCallback& callback) {
302 DCHECK(file_.IsValid());
303 if (bytes_to_read < 0)
304 return false;
305
306 ReadHelper* helper = new ReadHelper(this, file_.Pass(), bytes_to_read);
307 return task_runner_->PostTaskAndReply(
308 FROM_HERE,
309 Bind(&ReadHelper::RunWork, Unretained(helper), offset),
310 Bind(&ReadHelper::Reply, Owned(helper), callback));
311}
312
313bool FileProxy::Write(int64 offset,
314 const char* buffer,
315 int bytes_to_write,
316 const WriteCallback& callback) {
317 DCHECK(file_.IsValid());
318 if (bytes_to_write <= 0 || buffer == NULL)
319 return false;
320
321 WriteHelper* helper =
322 new WriteHelper(this, file_.Pass(), buffer, bytes_to_write);
323 return task_runner_->PostTaskAndReply(
324 FROM_HERE,
325 Bind(&WriteHelper::RunWork, Unretained(helper), offset),
326 Bind(&WriteHelper::Reply, Owned(helper), callback));
327}
328
329bool FileProxy::SetTimes(Time last_access_time,
330 Time last_modified_time,
331 const StatusCallback& callback) {
332 DCHECK(file_.IsValid());
333 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
334 return task_runner_->PostTaskAndReply(
335 FROM_HERE,
336 Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time,
337 last_modified_time),
338 Bind(&GenericFileHelper::Reply, Owned(helper), callback));
339}
340
341bool FileProxy::SetLength(int64 length, const StatusCallback& callback) {
342 DCHECK(file_.IsValid());
343 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
344 return task_runner_->PostTaskAndReply(
345 FROM_HERE,
346 Bind(&GenericFileHelper::SetLength, Unretained(helper), length),
347 Bind(&GenericFileHelper::Reply, Owned(helper), callback));
348}
349
350bool FileProxy::Flush(const StatusCallback& callback) {
351 DCHECK(file_.IsValid());
352 GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
353 return task_runner_->PostTaskAndReply(
354 FROM_HERE,
355 Bind(&GenericFileHelper::Flush, Unretained(helper)),
356 Bind(&GenericFileHelper::Reply, Owned(helper), callback));
357}
358
rvargas@chromium.orgc40e3b12014-03-15 14:19:31 +0900359} // namespace base