1// Copyright 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 "chrome/browser/chromeos/drive/async_file_util.h"
6
7#include "base/callback.h"
8#include "base/files/file_path.h"
9#include "base/logging.h"
10#include "base/platform_file.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "chrome/browser/chromeos/drive/file_system_util.h"
13#include "chrome/browser/chromeos/drive/fileapi_worker.h"
14#include "content/public/browser/browser_thread.h"
15#include "google_apis/drive/task_util.h"
16#include "webkit/browser/fileapi/file_system_operation_context.h"
17#include "webkit/browser/fileapi/file_system_url.h"
18#include "webkit/common/blob/shareable_file_reference.h"
19
20using content::BrowserThread;
21
22namespace drive {
23namespace internal {
24namespace {
25
26// Posts fileapi_internal::RunFileSystemCallback to UI thread.
27// This function must be called on IO thread.
28// The |on_error_callback| will be called (on error case) on IO thread.
29void PostFileSystemCallback(
30    const fileapi_internal::FileSystemGetter& file_system_getter,
31    const base::Callback<void(FileSystemInterface*)>& function,
32    const base::Closure& on_error_callback) {
33  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
34
35  BrowserThread::PostTask(
36      BrowserThread::UI,
37      FROM_HERE,
38      base::Bind(&fileapi_internal::RunFileSystemCallback,
39                 file_system_getter, function,
40                 on_error_callback.is_null() ?
41                 base::Closure() :
42                 base::Bind(&google_apis::RunTaskOnThread,
43                            base::MessageLoopProxy::current(),
44                            on_error_callback)));
45}
46
47// Runs CreateOrOpenFile callback based on the given |error| and |file|.
48void RunCreateOrOpenFileCallback(
49    const AsyncFileUtil::FileSystemGetter& file_system_getter,
50    const base::FilePath& file_path,
51    const AsyncFileUtil::CreateOrOpenCallback& callback,
52    base::PlatformFileError error,
53    base::PlatformFile file,
54    const base::Closure& close_callback_on_ui_thread) {
55  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
56
57  // It is necessary to make a closure, which runs on file closing here.
58  // It will be provided as a FileSystem::OpenFileCallback's argument later.
59  // (crbug.com/259184).
60  callback.Run(
61      error, base::PassPlatformFile(&file),
62      base::Bind(&google_apis::RunTaskOnThread,
63                 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
64                 close_callback_on_ui_thread));
65}
66
67// Runs CreateOrOpenFile when the error happens.
68void RunCreateOrOpenFileCallbackOnError(
69    const AsyncFileUtil::CreateOrOpenCallback& callback,
70    base::PlatformFileError error) {
71  // Because the |callback| takes PassPlatformFile as its argument, and
72  // it is necessary to guarantee the pointer passed to PassPlatformFile is
73  // alive during the |callback| invocation, here we prepare a thin adapter
74  // to have PlatformFile on stack frame.
75  base::PlatformFile file = base::kInvalidPlatformFileValue;
76  callback.Run(error, base::PassPlatformFile(&file), base::Closure());
77}
78
79// Runs EnsureFileExistsCallback based on the given |error|.
80void RunEnsureFileExistsCallback(
81    const AsyncFileUtil::EnsureFileExistsCallback& callback,
82    base::PlatformFileError error) {
83  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
84
85  // Remember if the file is actually created or not.
86  bool created = (error == base::PLATFORM_FILE_OK);
87
88  // PLATFORM_FILE_ERROR_EXISTS is not an actual error here.
89  if (error == base::PLATFORM_FILE_ERROR_EXISTS)
90    error = base::PLATFORM_FILE_OK;
91
92  callback.Run(error, created);
93}
94
95// Runs |callback| with the arguments based on the given arguments.
96void RunCreateSnapshotFileCallback(
97    const AsyncFileUtil::CreateSnapshotFileCallback& callback,
98    base::PlatformFileError error,
99    const base::PlatformFileInfo& file_info,
100    const base::FilePath& local_path,
101    webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy) {
102  // ShareableFileReference is thread *unsafe* class. So it is necessary to
103  // create the instance (by invoking GetOrCreate) on IO thread, though
104  // most drive file system related operations run on UI thread.
105  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
106
107  scoped_refptr<webkit_blob::ShareableFileReference> file_reference =
108      webkit_blob::ShareableFileReference::GetOrCreate(webkit_blob::ScopedFile(
109          local_path,
110          scope_out_policy,
111          BrowserThread::GetBlockingPool()));
112  callback.Run(error, file_info, local_path, file_reference);
113}
114
115}  // namespace
116
117AsyncFileUtil::AsyncFileUtil(const FileSystemGetter& file_system_getter)
118    : file_system_getter_(file_system_getter) {
119}
120
121AsyncFileUtil::~AsyncFileUtil() {
122}
123
124void AsyncFileUtil::CreateOrOpen(
125    scoped_ptr<fileapi::FileSystemOperationContext> context,
126    const fileapi::FileSystemURL& url,
127    int file_flags,
128    const CreateOrOpenCallback& callback) {
129  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
130
131  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
132  if (file_path.empty()) {
133    base::PlatformFile platform_file = base::kInvalidPlatformFileValue;
134    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
135                 base::PassPlatformFile(&platform_file),
136                 base::Closure());
137    return;
138  }
139
140  PostFileSystemCallback(
141      file_system_getter_,
142      base::Bind(&fileapi_internal::OpenFile,
143                 file_path, file_flags,
144                 google_apis::CreateRelayCallback(
145                     base::Bind(&RunCreateOrOpenFileCallback,
146                                file_system_getter_, file_path, callback))),
147      base::Bind(&RunCreateOrOpenFileCallbackOnError,
148                 callback, base::PLATFORM_FILE_ERROR_FAILED));
149}
150
151void AsyncFileUtil::EnsureFileExists(
152    scoped_ptr<fileapi::FileSystemOperationContext> context,
153    const fileapi::FileSystemURL& url,
154    const EnsureFileExistsCallback& callback) {
155  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
156
157  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
158  if (file_path.empty()) {
159    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, false);
160    return;
161  }
162
163  PostFileSystemCallback(
164      file_system_getter_,
165      base::Bind(&fileapi_internal::CreateFile,
166                 file_path, true /* is_exlusive */,
167                 google_apis::CreateRelayCallback(
168                     base::Bind(&RunEnsureFileExistsCallback, callback))),
169      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, false));
170}
171
172void AsyncFileUtil::CreateDirectory(
173    scoped_ptr<fileapi::FileSystemOperationContext> context,
174    const fileapi::FileSystemURL& url,
175    bool exclusive,
176    bool recursive,
177    const StatusCallback& callback) {
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
179
180  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
181  if (file_path.empty()) {
182    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
183    return;
184  }
185
186  PostFileSystemCallback(
187      file_system_getter_,
188      base::Bind(&fileapi_internal::CreateDirectory,
189                 file_path, exclusive, recursive,
190                 google_apis::CreateRelayCallback(callback)),
191      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
192}
193
194void AsyncFileUtil::GetFileInfo(
195    scoped_ptr<fileapi::FileSystemOperationContext> context,
196    const fileapi::FileSystemURL& url,
197    const GetFileInfoCallback& callback) {
198  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
199
200  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
201  if (file_path.empty()) {
202    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, base::PlatformFileInfo());
203    return;
204  }
205
206  PostFileSystemCallback(
207      file_system_getter_,
208      base::Bind(&fileapi_internal::GetFileInfo,
209                 file_path, google_apis::CreateRelayCallback(callback)),
210      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
211                 base::PlatformFileInfo()));
212}
213
214void AsyncFileUtil::ReadDirectory(
215    scoped_ptr<fileapi::FileSystemOperationContext> context,
216    const fileapi::FileSystemURL& url,
217    const ReadDirectoryCallback& callback) {
218  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
219
220  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
221  if (file_path.empty()) {
222    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, EntryList(), false);
223    return;
224  }
225
226  PostFileSystemCallback(
227      file_system_getter_,
228      base::Bind(&fileapi_internal::ReadDirectory,
229                 file_path, google_apis::CreateRelayCallback(callback)),
230      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
231                 EntryList(), false));
232}
233
234void AsyncFileUtil::Touch(
235    scoped_ptr<fileapi::FileSystemOperationContext> context,
236    const fileapi::FileSystemURL& url,
237    const base::Time& last_access_time,
238    const base::Time& last_modified_time,
239    const StatusCallback& callback) {
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241
242  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
243  if (file_path.empty()) {
244    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
245    return;
246  }
247
248  PostFileSystemCallback(
249      file_system_getter_,
250      base::Bind(&fileapi_internal::TouchFile,
251                 file_path, last_access_time, last_modified_time,
252                 google_apis::CreateRelayCallback(callback)),
253      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
254}
255
256void AsyncFileUtil::Truncate(
257    scoped_ptr<fileapi::FileSystemOperationContext> context,
258    const fileapi::FileSystemURL& url,
259    int64 length,
260    const StatusCallback& callback) {
261  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
262
263  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
264  if (file_path.empty()) {
265    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
266    return;
267  }
268
269  PostFileSystemCallback(
270      file_system_getter_,
271      base::Bind(&fileapi_internal::Truncate,
272                 file_path, length, google_apis::CreateRelayCallback(callback)),
273      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
274}
275
276void AsyncFileUtil::CopyFileLocal(
277    scoped_ptr<fileapi::FileSystemOperationContext> context,
278    const fileapi::FileSystemURL& src_url,
279    const fileapi::FileSystemURL& dest_url,
280    CopyOrMoveOption option,
281    const CopyFileProgressCallback& progress_callback,
282    const StatusCallback& callback) {
283  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
284
285  base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
286  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
287  if (src_path.empty() || dest_path.empty()) {
288    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
289    return;
290  }
291
292  PostFileSystemCallback(
293      file_system_getter_,
294      base::Bind(
295          &fileapi_internal::Copy,
296          src_path, dest_path,
297          option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
298          google_apis::CreateRelayCallback(callback)),
299      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
300}
301
302void AsyncFileUtil::MoveFileLocal(
303    scoped_ptr<fileapi::FileSystemOperationContext> context,
304    const fileapi::FileSystemURL& src_url,
305    const fileapi::FileSystemURL& dest_url,
306    CopyOrMoveOption option,
307    const StatusCallback& callback) {
308  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
309
310  base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
311  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
312  if (src_path.empty() || dest_path.empty()) {
313    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
314    return;
315  }
316
317  PostFileSystemCallback(
318      file_system_getter_,
319      base::Bind(
320          &fileapi_internal::Move,
321          src_path, dest_path,
322          option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
323          google_apis::CreateRelayCallback(callback)),
324      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
325}
326
327void AsyncFileUtil::CopyInForeignFile(
328    scoped_ptr<fileapi::FileSystemOperationContext> context,
329    const base::FilePath& src_file_path,
330    const fileapi::FileSystemURL& dest_url,
331    const StatusCallback& callback) {
332  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
333
334  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
335  if (dest_path.empty()) {
336    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
337    return;
338  }
339
340  PostFileSystemCallback(
341      file_system_getter_,
342      base::Bind(&fileapi_internal::CopyInForeignFile,
343                 src_file_path, dest_path,
344                 google_apis::CreateRelayCallback(callback)),
345      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
346}
347
348void AsyncFileUtil::DeleteFile(
349    scoped_ptr<fileapi::FileSystemOperationContext> context,
350    const fileapi::FileSystemURL& url,
351    const StatusCallback& callback) {
352  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
353
354  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
355  if (file_path.empty()) {
356    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
357    return;
358  }
359
360  PostFileSystemCallback(
361      file_system_getter_,
362      base::Bind(&fileapi_internal::Remove,
363                 file_path, false /* not recursive */,
364                 google_apis::CreateRelayCallback(callback)),
365      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
366}
367
368void AsyncFileUtil::DeleteDirectory(
369    scoped_ptr<fileapi::FileSystemOperationContext> context,
370    const fileapi::FileSystemURL& url,
371    const StatusCallback& callback) {
372  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
373
374  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
375  if (file_path.empty()) {
376    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
377    return;
378  }
379
380  PostFileSystemCallback(
381      file_system_getter_,
382      base::Bind(&fileapi_internal::Remove,
383                 file_path, false /* not recursive */,
384                 google_apis::CreateRelayCallback(callback)),
385      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
386}
387
388void AsyncFileUtil::DeleteRecursively(
389    scoped_ptr<fileapi::FileSystemOperationContext> context,
390    const fileapi::FileSystemURL& url,
391    const StatusCallback& callback) {
392  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
393
394  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
395  if (file_path.empty()) {
396    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
397    return;
398  }
399
400  PostFileSystemCallback(
401      file_system_getter_,
402      base::Bind(&fileapi_internal::Remove,
403                 file_path, true /* recursive */,
404                 google_apis::CreateRelayCallback(callback)),
405      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
406}
407
408void AsyncFileUtil::CreateSnapshotFile(
409    scoped_ptr<fileapi::FileSystemOperationContext> context,
410    const fileapi::FileSystemURL& url,
411    const CreateSnapshotFileCallback& callback) {
412  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
413
414  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
415  if (file_path.empty()) {
416    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
417                 base::PlatformFileInfo(),
418                 base::FilePath(),
419                 scoped_refptr<webkit_blob::ShareableFileReference>());
420    return;
421  }
422
423  PostFileSystemCallback(
424      file_system_getter_,
425      base::Bind(&fileapi_internal::CreateSnapshotFile,
426                 file_path,
427                 google_apis::CreateRelayCallback(
428                     base::Bind(&RunCreateSnapshotFileCallback, callback))),
429      base::Bind(callback,
430                 base::PLATFORM_FILE_ERROR_FAILED,
431                 base::PlatformFileInfo(),
432                 base::FilePath(),
433                 scoped_refptr<webkit_blob::ShareableFileReference>()));
434}
435
436}  // namespace internal
437}  // namespace drive
438