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 "base/files/file_util_proxy.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/file_util.h"
10 #include "base/location.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/task_runner.h"
13 #include "base/task_runner_util.h"
14
15 namespace base {
16
17 namespace {
18
CallWithTranslatedParameter(const FileUtilProxy::StatusCallback & callback,bool value)19 void CallWithTranslatedParameter(const FileUtilProxy::StatusCallback& callback,
20 bool value) {
21 DCHECK(!callback.is_null());
22 callback.Run(value ? PLATFORM_FILE_OK : PLATFORM_FILE_ERROR_FAILED);
23 }
24
25 // Helper classes or routines for individual methods.
26 class CreateOrOpenHelper {
27 public:
CreateOrOpenHelper(TaskRunner * task_runner,const FileUtilProxy::CloseTask & close_task)28 CreateOrOpenHelper(TaskRunner* task_runner,
29 const FileUtilProxy::CloseTask& close_task)
30 : task_runner_(task_runner),
31 close_task_(close_task),
32 file_handle_(kInvalidPlatformFileValue),
33 created_(false),
34 error_(PLATFORM_FILE_OK) {}
35
~CreateOrOpenHelper()36 ~CreateOrOpenHelper() {
37 if (file_handle_ != kInvalidPlatformFileValue) {
38 task_runner_->PostTask(
39 FROM_HERE,
40 base::Bind(base::IgnoreResult(close_task_), file_handle_));
41 }
42 }
43
RunWork(const FileUtilProxy::CreateOrOpenTask & task)44 void RunWork(const FileUtilProxy::CreateOrOpenTask& task) {
45 error_ = task.Run(&file_handle_, &created_);
46 }
47
Reply(const FileUtilProxy::CreateOrOpenCallback & callback)48 void Reply(const FileUtilProxy::CreateOrOpenCallback& callback) {
49 DCHECK(!callback.is_null());
50 callback.Run(error_, PassPlatformFile(&file_handle_), created_);
51 }
52
53 private:
54 scoped_refptr<TaskRunner> task_runner_;
55 FileUtilProxy::CloseTask close_task_;
56 PlatformFile file_handle_;
57 bool created_;
58 PlatformFileError error_;
59 DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
60 };
61
62 class CreateTemporaryHelper {
63 public:
CreateTemporaryHelper(TaskRunner * task_runner)64 explicit CreateTemporaryHelper(TaskRunner* task_runner)
65 : task_runner_(task_runner),
66 file_handle_(kInvalidPlatformFileValue),
67 error_(PLATFORM_FILE_OK) {}
68
~CreateTemporaryHelper()69 ~CreateTemporaryHelper() {
70 if (file_handle_ != kInvalidPlatformFileValue) {
71 FileUtilProxy::Close(
72 task_runner_.get(), file_handle_, FileUtilProxy::StatusCallback());
73 }
74 }
75
RunWork(int additional_file_flags)76 void RunWork(int additional_file_flags) {
77 // TODO(darin): file_util should have a variant of CreateTemporaryFile
78 // that returns a FilePath and a PlatformFile.
79 base::CreateTemporaryFile(&file_path_);
80
81 int file_flags =
82 PLATFORM_FILE_WRITE |
83 PLATFORM_FILE_TEMPORARY |
84 PLATFORM_FILE_CREATE_ALWAYS |
85 additional_file_flags;
86
87 error_ = PLATFORM_FILE_OK;
88 file_handle_ = CreatePlatformFile(file_path_, file_flags, NULL, &error_);
89 }
90
Reply(const FileUtilProxy::CreateTemporaryCallback & callback)91 void Reply(const FileUtilProxy::CreateTemporaryCallback& callback) {
92 DCHECK(!callback.is_null());
93 callback.Run(error_, PassPlatformFile(&file_handle_), file_path_);
94 }
95
96 private:
97 scoped_refptr<TaskRunner> task_runner_;
98 PlatformFile file_handle_;
99 FilePath file_path_;
100 PlatformFileError error_;
101 DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
102 };
103
104 class GetFileInfoHelper {
105 public:
GetFileInfoHelper()106 GetFileInfoHelper()
107 : error_(PLATFORM_FILE_OK) {}
108
RunWorkForFilePath(const FilePath & file_path)109 void RunWorkForFilePath(const FilePath& file_path) {
110 if (!PathExists(file_path)) {
111 error_ = PLATFORM_FILE_ERROR_NOT_FOUND;
112 return;
113 }
114 if (!GetFileInfo(file_path, &file_info_))
115 error_ = PLATFORM_FILE_ERROR_FAILED;
116 }
117
RunWorkForPlatformFile(PlatformFile file)118 void RunWorkForPlatformFile(PlatformFile file) {
119 if (!GetPlatformFileInfo(file, &file_info_))
120 error_ = PLATFORM_FILE_ERROR_FAILED;
121 }
122
Reply(const FileUtilProxy::GetFileInfoCallback & callback)123 void Reply(const FileUtilProxy::GetFileInfoCallback& callback) {
124 if (!callback.is_null()) {
125 callback.Run(error_, file_info_);
126 }
127 }
128
129 private:
130 PlatformFileError error_;
131 PlatformFileInfo file_info_;
132 DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper);
133 };
134
135 class ReadHelper {
136 public:
ReadHelper(int bytes_to_read)137 explicit ReadHelper(int bytes_to_read)
138 : buffer_(new char[bytes_to_read]),
139 bytes_to_read_(bytes_to_read),
140 bytes_read_(0) {}
141
RunWork(PlatformFile file,int64 offset)142 void RunWork(PlatformFile file, int64 offset) {
143 bytes_read_ = ReadPlatformFile(file, offset, buffer_.get(), bytes_to_read_);
144 }
145
Reply(const FileUtilProxy::ReadCallback & callback)146 void Reply(const FileUtilProxy::ReadCallback& callback) {
147 if (!callback.is_null()) {
148 PlatformFileError error =
149 (bytes_read_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK;
150 callback.Run(error, buffer_.get(), bytes_read_);
151 }
152 }
153
154 private:
155 scoped_ptr<char[]> buffer_;
156 int bytes_to_read_;
157 int bytes_read_;
158 DISALLOW_COPY_AND_ASSIGN(ReadHelper);
159 };
160
161 class WriteHelper {
162 public:
WriteHelper(const char * buffer,int bytes_to_write)163 WriteHelper(const char* buffer, int bytes_to_write)
164 : buffer_(new char[bytes_to_write]),
165 bytes_to_write_(bytes_to_write),
166 bytes_written_(0) {
167 memcpy(buffer_.get(), buffer, bytes_to_write);
168 }
169
RunWork(PlatformFile file,int64 offset)170 void RunWork(PlatformFile file, int64 offset) {
171 bytes_written_ = WritePlatformFile(file, offset, buffer_.get(),
172 bytes_to_write_);
173 }
174
Reply(const FileUtilProxy::WriteCallback & callback)175 void Reply(const FileUtilProxy::WriteCallback& callback) {
176 if (!callback.is_null()) {
177 PlatformFileError error =
178 (bytes_written_ < 0) ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK;
179 callback.Run(error, bytes_written_);
180 }
181 }
182
183 private:
184 scoped_ptr<char[]> buffer_;
185 int bytes_to_write_;
186 int bytes_written_;
187 DISALLOW_COPY_AND_ASSIGN(WriteHelper);
188 };
189
CreateOrOpenAdapter(const FilePath & file_path,int file_flags,PlatformFile * file_handle,bool * created)190 PlatformFileError CreateOrOpenAdapter(
191 const FilePath& file_path, int file_flags,
192 PlatformFile* file_handle, bool* created) {
193 DCHECK(file_handle);
194 DCHECK(created);
195 if (!DirectoryExists(file_path.DirName())) {
196 // If its parent does not exist, should return NOT_FOUND error.
197 return PLATFORM_FILE_ERROR_NOT_FOUND;
198 }
199 PlatformFileError error = PLATFORM_FILE_OK;
200 *file_handle = CreatePlatformFile(file_path, file_flags, created, &error);
201 return error;
202 }
203
CloseAdapter(PlatformFile file_handle)204 PlatformFileError CloseAdapter(PlatformFile file_handle) {
205 if (!ClosePlatformFile(file_handle)) {
206 return PLATFORM_FILE_ERROR_FAILED;
207 }
208 return PLATFORM_FILE_OK;
209 }
210
DeleteAdapter(const FilePath & file_path,bool recursive)211 PlatformFileError DeleteAdapter(const FilePath& file_path, bool recursive) {
212 if (!PathExists(file_path)) {
213 return PLATFORM_FILE_ERROR_NOT_FOUND;
214 }
215 if (!base::DeleteFile(file_path, recursive)) {
216 if (!recursive && !base::IsDirectoryEmpty(file_path)) {
217 return PLATFORM_FILE_ERROR_NOT_EMPTY;
218 }
219 return PLATFORM_FILE_ERROR_FAILED;
220 }
221 return PLATFORM_FILE_OK;
222 }
223
224 } // namespace
225
226 // static
CreateOrOpen(TaskRunner * task_runner,const FilePath & file_path,int file_flags,const CreateOrOpenCallback & callback)227 bool FileUtilProxy::CreateOrOpen(
228 TaskRunner* task_runner,
229 const FilePath& file_path, int file_flags,
230 const CreateOrOpenCallback& callback) {
231 return RelayCreateOrOpen(
232 task_runner,
233 base::Bind(&CreateOrOpenAdapter, file_path, file_flags),
234 base::Bind(&CloseAdapter),
235 callback);
236 }
237
238 // static
CreateTemporary(TaskRunner * task_runner,int additional_file_flags,const CreateTemporaryCallback & callback)239 bool FileUtilProxy::CreateTemporary(
240 TaskRunner* task_runner,
241 int additional_file_flags,
242 const CreateTemporaryCallback& callback) {
243 CreateTemporaryHelper* helper = new CreateTemporaryHelper(task_runner);
244 return task_runner->PostTaskAndReply(
245 FROM_HERE,
246 Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
247 additional_file_flags),
248 Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
249 }
250
251 // static
Close(TaskRunner * task_runner,base::PlatformFile file_handle,const StatusCallback & callback)252 bool FileUtilProxy::Close(
253 TaskRunner* task_runner,
254 base::PlatformFile file_handle,
255 const StatusCallback& callback) {
256 return RelayClose(
257 task_runner,
258 base::Bind(&CloseAdapter),
259 file_handle, callback);
260 }
261
262 // Retrieves the information about a file. It is invalid to pass NULL for the
263 // callback.
GetFileInfo(TaskRunner * task_runner,const FilePath & file_path,const GetFileInfoCallback & callback)264 bool FileUtilProxy::GetFileInfo(
265 TaskRunner* task_runner,
266 const FilePath& file_path,
267 const GetFileInfoCallback& callback) {
268 GetFileInfoHelper* helper = new GetFileInfoHelper;
269 return task_runner->PostTaskAndReply(
270 FROM_HERE,
271 Bind(&GetFileInfoHelper::RunWorkForFilePath,
272 Unretained(helper), file_path),
273 Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
274 }
275
276 // static
GetFileInfoFromPlatformFile(TaskRunner * task_runner,PlatformFile file,const GetFileInfoCallback & callback)277 bool FileUtilProxy::GetFileInfoFromPlatformFile(
278 TaskRunner* task_runner,
279 PlatformFile file,
280 const GetFileInfoCallback& callback) {
281 GetFileInfoHelper* helper = new GetFileInfoHelper;
282 return task_runner->PostTaskAndReply(
283 FROM_HERE,
284 Bind(&GetFileInfoHelper::RunWorkForPlatformFile,
285 Unretained(helper), file),
286 Bind(&GetFileInfoHelper::Reply, Owned(helper), callback));
287 }
288
289 // static
DeleteFile(TaskRunner * task_runner,const FilePath & file_path,bool recursive,const StatusCallback & callback)290 bool FileUtilProxy::DeleteFile(TaskRunner* task_runner,
291 const FilePath& file_path,
292 bool recursive,
293 const StatusCallback& callback) {
294 return base::PostTaskAndReplyWithResult(
295 task_runner, FROM_HERE,
296 Bind(&DeleteAdapter, file_path, recursive),
297 callback);
298 }
299
300 // static
Read(TaskRunner * task_runner,PlatformFile file,int64 offset,int bytes_to_read,const ReadCallback & callback)301 bool FileUtilProxy::Read(
302 TaskRunner* task_runner,
303 PlatformFile file,
304 int64 offset,
305 int bytes_to_read,
306 const ReadCallback& callback) {
307 if (bytes_to_read < 0) {
308 return false;
309 }
310 ReadHelper* helper = new ReadHelper(bytes_to_read);
311 return task_runner->PostTaskAndReply(
312 FROM_HERE,
313 Bind(&ReadHelper::RunWork, Unretained(helper), file, offset),
314 Bind(&ReadHelper::Reply, Owned(helper), callback));
315 }
316
317 // static
Write(TaskRunner * task_runner,PlatformFile file,int64 offset,const char * buffer,int bytes_to_write,const WriteCallback & callback)318 bool FileUtilProxy::Write(
319 TaskRunner* task_runner,
320 PlatformFile file,
321 int64 offset,
322 const char* buffer,
323 int bytes_to_write,
324 const WriteCallback& callback) {
325 if (bytes_to_write <= 0 || buffer == NULL) {
326 return false;
327 }
328 WriteHelper* helper = new WriteHelper(buffer, bytes_to_write);
329 return task_runner->PostTaskAndReply(
330 FROM_HERE,
331 Bind(&WriteHelper::RunWork, Unretained(helper), file, offset),
332 Bind(&WriteHelper::Reply, Owned(helper), callback));
333 }
334
335 // static
Touch(TaskRunner * task_runner,PlatformFile file,const Time & last_access_time,const Time & last_modified_time,const StatusCallback & callback)336 bool FileUtilProxy::Touch(
337 TaskRunner* task_runner,
338 PlatformFile file,
339 const Time& last_access_time,
340 const Time& last_modified_time,
341 const StatusCallback& callback) {
342 return base::PostTaskAndReplyWithResult(
343 task_runner,
344 FROM_HERE,
345 Bind(&TouchPlatformFile, file,
346 last_access_time, last_modified_time),
347 Bind(&CallWithTranslatedParameter, callback));
348 }
349
350 // static
Touch(TaskRunner * task_runner,const FilePath & file_path,const Time & last_access_time,const Time & last_modified_time,const StatusCallback & callback)351 bool FileUtilProxy::Touch(
352 TaskRunner* task_runner,
353 const FilePath& file_path,
354 const Time& last_access_time,
355 const Time& last_modified_time,
356 const StatusCallback& callback) {
357 return base::PostTaskAndReplyWithResult(
358 task_runner,
359 FROM_HERE,
360 Bind(&TouchFile, file_path, last_access_time, last_modified_time),
361 Bind(&CallWithTranslatedParameter, callback));
362 }
363
364 // static
Truncate(TaskRunner * task_runner,PlatformFile file,int64 length,const StatusCallback & callback)365 bool FileUtilProxy::Truncate(
366 TaskRunner* task_runner,
367 PlatformFile file,
368 int64 length,
369 const StatusCallback& callback) {
370 return base::PostTaskAndReplyWithResult(
371 task_runner,
372 FROM_HERE,
373 Bind(&TruncatePlatformFile, file, length),
374 Bind(&CallWithTranslatedParameter, callback));
375 }
376
377 // static
Flush(TaskRunner * task_runner,PlatformFile file,const StatusCallback & callback)378 bool FileUtilProxy::Flush(
379 TaskRunner* task_runner,
380 PlatformFile file,
381 const StatusCallback& callback) {
382 return base::PostTaskAndReplyWithResult(
383 task_runner,
384 FROM_HERE,
385 Bind(&FlushPlatformFile, file),
386 Bind(&CallWithTranslatedParameter, callback));
387 }
388
389 // static
RelayCreateOrOpen(TaskRunner * task_runner,const CreateOrOpenTask & open_task,const CloseTask & close_task,const CreateOrOpenCallback & callback)390 bool FileUtilProxy::RelayCreateOrOpen(
391 TaskRunner* task_runner,
392 const CreateOrOpenTask& open_task,
393 const CloseTask& close_task,
394 const CreateOrOpenCallback& callback) {
395 CreateOrOpenHelper* helper = new CreateOrOpenHelper(
396 task_runner, close_task);
397 return task_runner->PostTaskAndReply(
398 FROM_HERE,
399 Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), open_task),
400 Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback));
401 }
402
403 // static
RelayClose(TaskRunner * task_runner,const CloseTask & close_task,PlatformFile file_handle,const StatusCallback & callback)404 bool FileUtilProxy::RelayClose(
405 TaskRunner* task_runner,
406 const CloseTask& close_task,
407 PlatformFile file_handle,
408 const StatusCallback& callback) {
409 return base::PostTaskAndReplyWithResult(
410 task_runner, FROM_HERE, Bind(close_task, file_handle), callback);
411 }
412
413 } // namespace base
414