• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 namespace {
17 
FileDeleter(base::File file)18 void FileDeleter(base::File file) {
19 }
20 
21 }  // namespace
22 
23 namespace base {
24 
25 class FileHelper {
26  public:
FileHelper(FileProxy * proxy,File file)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 
PassFile()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 
51 namespace {
52 
53 class GenericFileHelper : public FileHelper {
54  public:
GenericFileHelper(FileProxy * proxy,File file)55   GenericFileHelper(FileProxy* proxy, File file)
56       : FileHelper(proxy, file.Pass()) {
57   }
58 
Close()59   void Close() {
60     file_.Close();
61     error_ = File::FILE_OK;
62   }
63 
SetTimes(Time last_access_time,Time last_modified_time)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 
SetLength(int64 length)69   void SetLength(int64 length) {
70     if (file_.SetLength(length))
71       error_ = File::FILE_OK;
72   }
73 
Flush()74   void Flush() {
75     if (file_.Flush())
76       error_ = File::FILE_OK;
77   }
78 
Reply(const FileProxy::StatusCallback & callback)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 
89 class CreateOrOpenHelper : public FileHelper {
90  public:
CreateOrOpenHelper(FileProxy * proxy,File file)91   CreateOrOpenHelper(FileProxy* proxy, File file)
92       : FileHelper(proxy, file.Pass()) {
93   }
94 
RunWork(const FilePath & file_path,int file_flags)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 
Reply(const FileProxy::StatusCallback & callback)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 
110 class CreateTemporaryHelper : public FileHelper {
111  public:
CreateTemporaryHelper(FileProxy * proxy,File file)112   CreateTemporaryHelper(FileProxy* proxy, File file)
113       : FileHelper(proxy, file.Pass()) {
114   }
115 
RunWork(uint32 additional_file_flags)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 
Reply(const FileProxy::CreateTemporaryCallback & callback)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 
152 class GetInfoHelper : public FileHelper {
153  public:
GetInfoHelper(FileProxy * proxy,File file)154   GetInfoHelper(FileProxy* proxy, File file)
155       : FileHelper(proxy, file.Pass()) {
156   }
157 
RunWork()158   void RunWork() {
159     if (file_.GetInfo(&file_info_))
160       error_  = File::FILE_OK;
161   }
162 
Reply(const FileProxy::GetFileInfoCallback & callback)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 
174 class ReadHelper : public FileHelper {
175  public:
ReadHelper(FileProxy * proxy,File file,int bytes_to_read)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 
RunWork(int64 offset)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 
Reply(const FileProxy::ReadCallback & callback)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 
201 class WriteHelper : public FileHelper {
202  public:
WriteHelper(FileProxy * proxy,File file,const char * buffer,int bytes_to_write)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 
RunWork(int64 offset)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 
Reply(const FileProxy::WriteCallback & callback)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 
FileProxy(TaskRunner * task_runner)233 FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
234 }
235 
~FileProxy()236 FileProxy::~FileProxy() {
237   if (file_.IsValid())
238     task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
239 }
240 
CreateOrOpen(const FilePath & file_path,uint32 file_flags,const StatusCallback & callback)241 bool 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 
CreateTemporary(uint32 additional_file_flags,const CreateTemporaryCallback & 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(
258       FROM_HERE,
259       Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
260            additional_file_flags),
261       Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
262 }
263 
IsValid() const264 bool FileProxy::IsValid() const {
265   return file_.IsValid();
266 }
267 
SetFile(File file)268 void FileProxy::SetFile(File file) {
269   DCHECK(!file_.IsValid());
270   file_ = file.Pass();
271 }
272 
TakeFile()273 File FileProxy::TakeFile() {
274   return file_.Pass();
275 }
276 
GetPlatformFile() const277 PlatformFile FileProxy::GetPlatformFile() const {
278   return file_.GetPlatformFile();
279 }
280 
Close(const StatusCallback & callback)281 bool 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 
GetInfo(const GetFileInfoCallback & 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(
294       FROM_HERE,
295       Bind(&GetInfoHelper::RunWork, Unretained(helper)),
296       Bind(&GetInfoHelper::Reply, Owned(helper), callback));
297 }
298 
Read(int64 offset,int bytes_to_read,const ReadCallback & callback)299 bool 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 
Write(int64 offset,const char * buffer,int bytes_to_write,const WriteCallback & callback)313 bool 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 
SetTimes(Time last_access_time,Time last_modified_time,const StatusCallback & 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(
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 
SetLength(int64 length,const StatusCallback & 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(
345       FROM_HERE,
346       Bind(&GenericFileHelper::SetLength, Unretained(helper), length),
347       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
348 }
349 
Flush(const StatusCallback & 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(
354       FROM_HERE,
355       Bind(&GenericFileHelper::Flush, Unretained(helper)),
356       Bind(&GenericFileHelper::Reply, Owned(helper), callback));
357 }
358 
359 }  // namespace base
360