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.
5 #include "base/files/file_proxy.h"
8 #include "base/bind_helpers.h"
9 #include "base/files/file.h"
10 #include "base/files/file_util.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"
18 void FileDeleter(base::File file
) {
27 FileHelper(FileProxy
* proxy
, File file
)
29 error_(File::FILE_ERROR_FAILED
),
30 task_runner_(proxy
->task_runner()),
31 proxy_(AsWeakPtr(proxy
)) {
36 proxy_
->SetFile(file_
.Pass());
37 else if (file_
.IsValid())
38 task_runner_
->PostTask(FROM_HERE
, Bind(&FileDeleter
, Passed(&file_
)));
46 scoped_refptr
<TaskRunner
> task_runner_
;
47 WeakPtr
<FileProxy
> proxy_
;
48 DISALLOW_COPY_AND_ASSIGN(FileHelper
);
53 class GenericFileHelper
: public FileHelper
{
55 GenericFileHelper(FileProxy
* proxy
, File file
)
56 : FileHelper(proxy
, file
.Pass()) {
61 error_
= File::FILE_OK
;
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
;
69 void SetLength(int64 length
) {
70 if (file_
.SetLength(length
))
71 error_
= File::FILE_OK
;
76 error_
= File::FILE_OK
;
79 void Reply(const FileProxy::StatusCallback
& callback
) {
81 if (!callback
.is_null())
86 DISALLOW_COPY_AND_ASSIGN(GenericFileHelper
);
89 class CreateOrOpenHelper
: public FileHelper
{
91 CreateOrOpenHelper(FileProxy
* proxy
, File file
)
92 : FileHelper(proxy
, file
.Pass()) {
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();
100 void Reply(const FileProxy::StatusCallback
& callback
) {
101 DCHECK(!callback
.is_null());
103 callback
.Run(error_
);
107 DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper
);
110 class CreateTemporaryHelper
: public FileHelper
{
112 CreateTemporaryHelper(FileProxy
* proxy
, File file
)
113 : FileHelper(proxy
, file
.Pass()) {
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
122 error_
= File::FILE_ERROR_FAILED
;
126 uint32 file_flags
= File::FLAG_WRITE
|
127 File::FLAG_TEMPORARY
|
128 File::FLAG_CREATE_ALWAYS
|
129 additional_file_flags
;
131 file_
.Initialize(file_path_
, file_flags
);
132 if (file_
.IsValid()) {
133 error_
= File::FILE_OK
;
135 error_
= file_
.error_details();
136 DeleteFile(file_path_
, false);
141 void Reply(const FileProxy::CreateTemporaryCallback
& callback
) {
142 DCHECK(!callback
.is_null());
144 callback
.Run(error_
, file_path_
);
149 DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper
);
152 class GetInfoHelper
: public FileHelper
{
154 GetInfoHelper(FileProxy
* proxy
, File file
)
155 : FileHelper(proxy
, file
.Pass()) {
159 if (file_
.GetInfo(&file_info_
))
160 error_
= File::FILE_OK
;
163 void Reply(const FileProxy::GetFileInfoCallback
& callback
) {
165 DCHECK(!callback
.is_null());
166 callback
.Run(error_
, file_info_
);
170 File::Info file_info_
;
171 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper
);
174 class ReadHelper
: public FileHelper
{
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
),
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
;
188 void Reply(const FileProxy::ReadCallback
& callback
) {
190 DCHECK(!callback
.is_null());
191 callback
.Run(error_
, buffer_
.get(), bytes_read_
);
195 scoped_ptr
<char[]> buffer_
;
198 DISALLOW_COPY_AND_ASSIGN(ReadHelper
);
201 class WriteHelper
: public FileHelper
{
203 WriteHelper(FileProxy
* proxy
,
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
),
210 memcpy(buffer_
.get(), buffer
, bytes_to_write
);
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
;
218 void Reply(const FileProxy::WriteCallback
& callback
) {
220 if (!callback
.is_null())
221 callback
.Run(error_
, bytes_written_
);
225 scoped_ptr
<char[]> buffer_
;
228 DISALLOW_COPY_AND_ASSIGN(WriteHelper
);
233 FileProxy::FileProxy(TaskRunner
* task_runner
) : task_runner_(task_runner
) {
236 FileProxy::~FileProxy() {
238 task_runner_
->PostTask(FROM_HERE
, Bind(&FileDeleter
, Passed(&file_
)));
241 bool FileProxy::CreateOrOpen(const FilePath
& file_path
,
243 const StatusCallback
& callback
) {
244 DCHECK(!file_
.IsValid());
245 CreateOrOpenHelper
* helper
= new CreateOrOpenHelper(this, File());
246 return task_runner_
->PostTaskAndReply(
248 Bind(&CreateOrOpenHelper::RunWork
, Unretained(helper
), file_path
,
250 Bind(&CreateOrOpenHelper::Reply
, Owned(helper
), callback
));
253 bool 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(
259 Bind(&CreateTemporaryHelper::RunWork
, Unretained(helper
),
260 additional_file_flags
),
261 Bind(&CreateTemporaryHelper::Reply
, Owned(helper
), callback
));
264 bool FileProxy::IsValid() const {
265 return file_
.IsValid();
268 void FileProxy::SetFile(File file
) {
269 DCHECK(!file_
.IsValid());
273 File
FileProxy::TakeFile() {
277 PlatformFile
FileProxy::GetPlatformFile() const {
278 return file_
.GetPlatformFile();
281 bool FileProxy::Close(const StatusCallback
& callback
) {
282 DCHECK(file_
.IsValid());
283 GenericFileHelper
* helper
= new GenericFileHelper(this, file_
.Pass());
284 return task_runner_
->PostTaskAndReply(
286 Bind(&GenericFileHelper::Close
, Unretained(helper
)),
287 Bind(&GenericFileHelper::Reply
, Owned(helper
), callback
));
290 bool FileProxy::GetInfo(const GetFileInfoCallback
& callback
) {
291 DCHECK(file_
.IsValid());
292 GetInfoHelper
* helper
= new GetInfoHelper(this, file_
.Pass());
293 return task_runner_
->PostTaskAndReply(
295 Bind(&GetInfoHelper::RunWork
, Unretained(helper
)),
296 Bind(&GetInfoHelper::Reply
, Owned(helper
), callback
));
299 bool FileProxy::Read(int64 offset
,
301 const ReadCallback
& callback
) {
302 DCHECK(file_
.IsValid());
303 if (bytes_to_read
< 0)
306 ReadHelper
* helper
= new ReadHelper(this, file_
.Pass(), bytes_to_read
);
307 return task_runner_
->PostTaskAndReply(
309 Bind(&ReadHelper::RunWork
, Unretained(helper
), offset
),
310 Bind(&ReadHelper::Reply
, Owned(helper
), callback
));
313 bool FileProxy::Write(int64 offset
,
316 const WriteCallback
& callback
) {
317 DCHECK(file_
.IsValid());
318 if (bytes_to_write
<= 0 || buffer
== NULL
)
321 WriteHelper
* helper
=
322 new WriteHelper(this, file_
.Pass(), buffer
, bytes_to_write
);
323 return task_runner_
->PostTaskAndReply(
325 Bind(&WriteHelper::RunWork
, Unretained(helper
), offset
),
326 Bind(&WriteHelper::Reply
, Owned(helper
), callback
));
329 bool 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(
336 Bind(&GenericFileHelper::SetTimes
, Unretained(helper
), last_access_time
,
338 Bind(&GenericFileHelper::Reply
, Owned(helper
), callback
));
341 bool FileProxy::SetLength(int64 length
, const StatusCallback
& callback
) {
342 DCHECK(file_
.IsValid());
343 GenericFileHelper
* helper
= new GenericFileHelper(this, file_
.Pass());
344 return task_runner_
->PostTaskAndReply(
346 Bind(&GenericFileHelper::SetLength
, Unretained(helper
), length
),
347 Bind(&GenericFileHelper::Reply
, Owned(helper
), callback
));
350 bool FileProxy::Flush(const StatusCallback
& callback
) {
351 DCHECK(file_
.IsValid());
352 GenericFileHelper
* helper
= new GenericFileHelper(this, file_
.Pass());
353 return task_runner_
->PostTaskAndReply(
355 Bind(&GenericFileHelper::Flush
, Unretained(helper
)),
356 Bind(&GenericFileHelper::Reply
, Owned(helper
), callback
));