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