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 "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
16namespace {
17
18void FileDeleter(base::File file) {
19}
20
21}  // namespace
22
23namespace base {
24
25class FileHelper {
26 public:
27   FileHelper(FileProxy* proxy, File file)
28      : file_(file.Pass()),
29        error_(File::FILE_ERROR_FAILED),
30        task_runner_(proxy->task_runner()),
31        proxy_(AsWeakPtr(proxy)) {
32   }
33
34   void PassFile() {
35     if (proxy_)
36       proxy_->SetFile(file_.Pass());
37     else if (file_.IsValid())
38       task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
39   }
40
41 protected:
42  File file_;
43  File::Error error_;
44
45 private:
46  scoped_refptr<TaskRunner> task_runner_;
47  WeakPtr<FileProxy> proxy_;
48  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
233FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
234}
235
236FileProxy::~FileProxy() {
237  if (file_.IsValid())
238    task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
239}
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
268void FileProxy::SetFile(File file) {
269  DCHECK(!file_.IsValid());
270  file_ = file.Pass();
271}
272
273File FileProxy::TakeFile() {
274  return file_.Pass();
275}
276
277PlatformFile FileProxy::GetPlatformFile() const {
278  return file_.GetPlatformFile();
279}
280
281bool 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
359}  // namespace base
360