1// Copyright (c) 2013 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 "storage/browser/fileapi/async_file_util_adapter.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/sequenced_task_runner.h"
11#include "base/task_runner_util.h"
12#include "base/thread_task_runner_handle.h"
13#include "storage/browser/fileapi/file_system_context.h"
14#include "storage/browser/fileapi/file_system_file_util.h"
15#include "storage/browser/fileapi/file_system_operation_context.h"
16#include "storage/browser/fileapi/file_system_url.h"
17#include "storage/common/blob/shareable_file_reference.h"
18#include "storage/common/fileapi/file_system_util.h"
19
20using base::Bind;
21using base::Callback;
22using base::Owned;
23using base::Unretained;
24using storage::ShareableFileReference;
25
26namespace storage {
27
28namespace {
29
30class EnsureFileExistsHelper {
31 public:
32  EnsureFileExistsHelper() : error_(base::File::FILE_OK), created_(false) {}
33
34  void RunWork(FileSystemFileUtil* file_util,
35               FileSystemOperationContext* context,
36               const FileSystemURL& url) {
37    error_ = file_util->EnsureFileExists(context, url, &created_);
38  }
39
40  void Reply(const AsyncFileUtil::EnsureFileExistsCallback& callback) {
41    callback.Run(error_, created_);
42  }
43
44 private:
45  base::File::Error error_;
46  bool created_;
47  DISALLOW_COPY_AND_ASSIGN(EnsureFileExistsHelper);
48};
49
50class GetFileInfoHelper {
51 public:
52  GetFileInfoHelper()
53      : error_(base::File::FILE_OK) {}
54
55  void GetFileInfo(FileSystemFileUtil* file_util,
56                   FileSystemOperationContext* context,
57                   const FileSystemURL& url) {
58    error_ = file_util->GetFileInfo(context, url, &file_info_, &platform_path_);
59  }
60
61  void CreateSnapshotFile(FileSystemFileUtil* file_util,
62                          FileSystemOperationContext* context,
63                          const FileSystemURL& url) {
64    scoped_file_ = file_util->CreateSnapshotFile(
65        context, url, &error_, &file_info_, &platform_path_);
66  }
67
68  void ReplyFileInfo(const AsyncFileUtil::GetFileInfoCallback& callback) {
69    callback.Run(error_, file_info_);
70  }
71
72  void ReplySnapshotFile(
73      const AsyncFileUtil::CreateSnapshotFileCallback& callback) {
74    callback.Run(error_, file_info_, platform_path_,
75                  ShareableFileReference::GetOrCreate(scoped_file_.Pass()));
76  }
77
78 private:
79  base::File::Error error_;
80  base::File::Info file_info_;
81  base::FilePath platform_path_;
82  storage::ScopedFile scoped_file_;
83  DISALLOW_COPY_AND_ASSIGN(GetFileInfoHelper);
84};
85
86void ReadDirectoryHelper(FileSystemFileUtil* file_util,
87                         FileSystemOperationContext* context,
88                         const FileSystemURL& url,
89                         base::SingleThreadTaskRunner* origin_loop,
90                         const AsyncFileUtil::ReadDirectoryCallback& callback) {
91  base::File::Info file_info;
92  base::FilePath platform_path;
93  base::File::Error error = file_util->GetFileInfo(
94      context, url, &file_info, &platform_path);
95
96  if (error == base::File::FILE_OK && !file_info.is_directory)
97    error = base::File::FILE_ERROR_NOT_A_DIRECTORY;
98
99  std::vector<DirectoryEntry> entries;
100  if (error != base::File::FILE_OK) {
101    origin_loop->PostTask(
102        FROM_HERE, base::Bind(callback, error, entries, false /* has_more */));
103    return;
104  }
105
106  // Note: Increasing this value may make some tests in LayoutTests meaningless.
107  // (Namely, read-directory-many.html and read-directory-sync-many.html are
108  // assuming that they are reading much more entries than this constant.)
109  const size_t kResultChunkSize = 100;
110
111  scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum(
112      file_util->CreateFileEnumerator(context, url));
113
114  base::FilePath current;
115  while (!(current = file_enum->Next()).empty()) {
116    DirectoryEntry entry;
117    entry.is_directory = file_enum->IsDirectory();
118    entry.name = VirtualPath::BaseName(current).value();
119    entry.size = file_enum->Size();
120    entry.last_modified_time = file_enum->LastModifiedTime();
121    entries.push_back(entry);
122
123    if (entries.size() == kResultChunkSize) {
124      origin_loop->PostTask(
125          FROM_HERE, base::Bind(callback, base::File::FILE_OK, entries,
126                                true /* has_more */));
127      entries.clear();
128    }
129  }
130  origin_loop->PostTask(
131      FROM_HERE, base::Bind(callback, base::File::FILE_OK, entries,
132                            false /* has_more */));
133}
134
135void RunCreateOrOpenCallback(
136    FileSystemOperationContext* context,
137    const AsyncFileUtil::CreateOrOpenCallback& callback,
138    base::File file) {
139  callback.Run(file.Pass(), base::Closure());
140}
141
142}  // namespace
143
144AsyncFileUtilAdapter::AsyncFileUtilAdapter(
145    FileSystemFileUtil* sync_file_util)
146    : sync_file_util_(sync_file_util) {
147  DCHECK(sync_file_util_.get());
148}
149
150AsyncFileUtilAdapter::~AsyncFileUtilAdapter() {
151}
152
153void AsyncFileUtilAdapter::CreateOrOpen(
154    scoped_ptr<FileSystemOperationContext> context,
155    const FileSystemURL& url,
156    int file_flags,
157    const CreateOrOpenCallback& callback) {
158  FileSystemOperationContext* context_ptr = context.release();
159  base::PostTaskAndReplyWithResult(
160      context_ptr->task_runner(),
161      FROM_HERE,
162      Bind(&FileSystemFileUtil::CreateOrOpen, Unretained(sync_file_util_.get()),
163           context_ptr, url, file_flags),
164      Bind(&RunCreateOrOpenCallback, base::Owned(context_ptr), callback));
165}
166
167void AsyncFileUtilAdapter::EnsureFileExists(
168    scoped_ptr<FileSystemOperationContext> context,
169    const FileSystemURL& url,
170    const EnsureFileExistsCallback& callback) {
171  EnsureFileExistsHelper* helper = new EnsureFileExistsHelper;
172  FileSystemOperationContext* context_ptr = context.release();
173  const bool success = context_ptr->task_runner()->PostTaskAndReply(
174      FROM_HERE,
175      Bind(&EnsureFileExistsHelper::RunWork, Unretained(helper),
176           sync_file_util_.get(), base::Owned(context_ptr), url),
177      Bind(&EnsureFileExistsHelper::Reply, Owned(helper), callback));
178  DCHECK(success);
179}
180
181void AsyncFileUtilAdapter::CreateDirectory(
182    scoped_ptr<FileSystemOperationContext> context,
183    const FileSystemURL& url,
184    bool exclusive,
185    bool recursive,
186    const StatusCallback& callback) {
187  FileSystemOperationContext* context_ptr = context.release();
188  const bool success = base::PostTaskAndReplyWithResult(
189      context_ptr->task_runner(), FROM_HERE,
190      Bind(&FileSystemFileUtil::CreateDirectory,
191           Unretained(sync_file_util_.get()),
192           base::Owned(context_ptr), url, exclusive, recursive),
193      callback);
194  DCHECK(success);
195}
196
197void AsyncFileUtilAdapter::GetFileInfo(
198    scoped_ptr<FileSystemOperationContext> context,
199    const FileSystemURL& url,
200    const GetFileInfoCallback& callback) {
201  FileSystemOperationContext* context_ptr = context.release();
202  GetFileInfoHelper* helper = new GetFileInfoHelper;
203  const bool success = context_ptr->task_runner()->PostTaskAndReply(
204      FROM_HERE,
205      Bind(&GetFileInfoHelper::GetFileInfo, Unretained(helper),
206           sync_file_util_.get(), base::Owned(context_ptr), url),
207      Bind(&GetFileInfoHelper::ReplyFileInfo, Owned(helper), callback));
208  DCHECK(success);
209}
210
211void AsyncFileUtilAdapter::ReadDirectory(
212    scoped_ptr<FileSystemOperationContext> context,
213    const FileSystemURL& url,
214    const ReadDirectoryCallback& callback) {
215  FileSystemOperationContext* context_ptr = context.release();
216  const bool success = context_ptr->task_runner()->PostTask(
217      FROM_HERE,
218      Bind(&ReadDirectoryHelper,
219           sync_file_util_.get(), base::Owned(context_ptr), url,
220           base::ThreadTaskRunnerHandle::Get(), callback));
221  DCHECK(success);
222}
223
224void AsyncFileUtilAdapter::Touch(
225    scoped_ptr<FileSystemOperationContext> context,
226    const FileSystemURL& url,
227    const base::Time& last_access_time,
228    const base::Time& last_modified_time,
229    const StatusCallback& callback) {
230  FileSystemOperationContext* context_ptr = context.release();
231  const bool success = base::PostTaskAndReplyWithResult(
232      context_ptr->task_runner(), FROM_HERE,
233      Bind(&FileSystemFileUtil::Touch, Unretained(sync_file_util_.get()),
234           base::Owned(context_ptr), url,
235           last_access_time, last_modified_time),
236      callback);
237  DCHECK(success);
238}
239
240void AsyncFileUtilAdapter::Truncate(
241    scoped_ptr<FileSystemOperationContext> context,
242    const FileSystemURL& url,
243    int64 length,
244    const StatusCallback& callback) {
245  FileSystemOperationContext* context_ptr = context.release();
246  const bool success = base::PostTaskAndReplyWithResult(
247      context_ptr->task_runner(), FROM_HERE,
248      Bind(&FileSystemFileUtil::Truncate, Unretained(sync_file_util_.get()),
249           base::Owned(context_ptr), url, length),
250      callback);
251  DCHECK(success);
252}
253
254void AsyncFileUtilAdapter::CopyFileLocal(
255    scoped_ptr<FileSystemOperationContext> context,
256    const FileSystemURL& src_url,
257    const FileSystemURL& dest_url,
258    CopyOrMoveOption option,
259    const CopyFileProgressCallback& progress_callback,
260    const StatusCallback& callback) {
261  // TODO(hidehiko): Support progress_callback.
262  FileSystemOperationContext* context_ptr = context.release();
263  const bool success = base::PostTaskAndReplyWithResult(
264      context_ptr->task_runner(), FROM_HERE,
265      Bind(&FileSystemFileUtil::CopyOrMoveFile,
266           Unretained(sync_file_util_.get()), base::Owned(context_ptr),
267           src_url, dest_url, option, true /* copy */),
268      callback);
269  DCHECK(success);
270}
271
272void AsyncFileUtilAdapter::MoveFileLocal(
273    scoped_ptr<FileSystemOperationContext> context,
274    const FileSystemURL& src_url,
275    const FileSystemURL& dest_url,
276    CopyOrMoveOption option,
277    const StatusCallback& callback) {
278  FileSystemOperationContext* context_ptr = context.release();
279  const bool success = base::PostTaskAndReplyWithResult(
280      context_ptr->task_runner(), FROM_HERE,
281      Bind(&FileSystemFileUtil::CopyOrMoveFile,
282           Unretained(sync_file_util_.get()), base::Owned(context_ptr),
283           src_url, dest_url, option, false /* copy */),
284      callback);
285  DCHECK(success);
286}
287
288void AsyncFileUtilAdapter::CopyInForeignFile(
289      scoped_ptr<FileSystemOperationContext> context,
290      const base::FilePath& src_file_path,
291      const FileSystemURL& dest_url,
292      const StatusCallback& callback) {
293  FileSystemOperationContext* context_ptr = context.release();
294  const bool success = base::PostTaskAndReplyWithResult(
295      context_ptr->task_runner(), FROM_HERE,
296      Bind(&FileSystemFileUtil::CopyInForeignFile,
297           Unretained(sync_file_util_.get()),
298           base::Owned(context_ptr), src_file_path, dest_url),
299      callback);
300  DCHECK(success);
301}
302
303void AsyncFileUtilAdapter::DeleteFile(
304    scoped_ptr<FileSystemOperationContext> context,
305    const FileSystemURL& url,
306    const StatusCallback& callback) {
307  FileSystemOperationContext* context_ptr = context.release();
308  const bool success = base::PostTaskAndReplyWithResult(
309      context_ptr->task_runner(), FROM_HERE,
310      Bind(&FileSystemFileUtil::DeleteFile,
311           Unretained(sync_file_util_.get()),
312           base::Owned(context_ptr), url),
313      callback);
314  DCHECK(success);
315}
316
317void AsyncFileUtilAdapter::DeleteDirectory(
318    scoped_ptr<FileSystemOperationContext> context,
319    const FileSystemURL& url,
320    const StatusCallback& callback) {
321  FileSystemOperationContext* context_ptr = context.release();
322  const bool success = base::PostTaskAndReplyWithResult(
323      context_ptr->task_runner(), FROM_HERE,
324      Bind(&FileSystemFileUtil::DeleteDirectory,
325           Unretained(sync_file_util_.get()),
326           base::Owned(context_ptr), url),
327      callback);
328  DCHECK(success);
329}
330
331void AsyncFileUtilAdapter::DeleteRecursively(
332    scoped_ptr<FileSystemOperationContext> context,
333    const FileSystemURL& url,
334    const StatusCallback& callback) {
335  callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
336}
337
338void AsyncFileUtilAdapter::CreateSnapshotFile(
339    scoped_ptr<FileSystemOperationContext> context,
340    const FileSystemURL& url,
341    const CreateSnapshotFileCallback& callback) {
342  FileSystemOperationContext* context_ptr = context.release();
343  GetFileInfoHelper* helper = new GetFileInfoHelper;
344  const bool success = context_ptr->task_runner()->PostTaskAndReply(
345      FROM_HERE,
346      Bind(&GetFileInfoHelper::CreateSnapshotFile, Unretained(helper),
347           sync_file_util_.get(), base::Owned(context_ptr), url),
348      Bind(&GetFileInfoHelper::ReplySnapshotFile, Owned(helper), callback));
349  DCHECK(success);
350}
351
352}  // namespace storage
353