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 "chrome/browser/google_apis/task_util.h"
15#include "content/public/browser/browser_thread.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
96// Runs |callback| with the arguments based on the given arguments.
97void RunCreateSnapshotFileCallback(
98    const AsyncFileUtil::CreateSnapshotFileCallback& callback,
99    base::PlatformFileError error,
100    const base::PlatformFileInfo& file_info,
101    const base::FilePath& local_path,
102    webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy) {
103  // ShareableFileReference is thread *unsafe* class. So it is necessary to
104  // create the instance (by invoking GetOrCreate) on IO thread, though
105  // most drive file system related operations run on UI thread.
106  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
107
108  scoped_refptr<webkit_blob::ShareableFileReference> file_reference =
109      webkit_blob::ShareableFileReference::GetOrCreate(webkit_blob::ScopedFile(
110          local_path,
111          scope_out_policy,
112          BrowserThread::GetBlockingPool()));
113  callback.Run(error, file_info, local_path, file_reference);
114}
115
116}  // namespace
117
118AsyncFileUtil::AsyncFileUtil(const FileSystemGetter& file_system_getter)
119    : file_system_getter_(file_system_getter) {
120}
121
122AsyncFileUtil::~AsyncFileUtil() {
123}
124
125void AsyncFileUtil::CreateOrOpen(
126    scoped_ptr<fileapi::FileSystemOperationContext> context,
127    const fileapi::FileSystemURL& url,
128    int file_flags,
129    const CreateOrOpenCallback& callback) {
130  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
131
132  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
133  if (file_path.empty()) {
134    base::PlatformFile platform_file = base::kInvalidPlatformFileValue;
135    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
136                 base::PassPlatformFile(&platform_file),
137                 base::Closure());
138    return;
139  }
140
141  PostFileSystemCallback(
142      file_system_getter_,
143      base::Bind(&fileapi_internal::OpenFile,
144                 file_path, file_flags,
145                 google_apis::CreateRelayCallback(
146                     base::Bind(&RunCreateOrOpenFileCallback,
147                                file_system_getter_, file_path, callback))),
148      base::Bind(&RunCreateOrOpenFileCallbackOnError,
149                 callback, base::PLATFORM_FILE_ERROR_FAILED));
150}
151
152void AsyncFileUtil::EnsureFileExists(
153    scoped_ptr<fileapi::FileSystemOperationContext> context,
154    const fileapi::FileSystemURL& url,
155    const EnsureFileExistsCallback& callback) {
156  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
157
158  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
159  if (file_path.empty()) {
160    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, false);
161    return;
162  }
163
164  PostFileSystemCallback(
165      file_system_getter_,
166      base::Bind(&fileapi_internal::CreateFile,
167                 file_path, true /* is_exlusive */,
168                 google_apis::CreateRelayCallback(
169                     base::Bind(&RunEnsureFileExistsCallback, callback))),
170      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, false));
171}
172
173void AsyncFileUtil::CreateDirectory(
174    scoped_ptr<fileapi::FileSystemOperationContext> context,
175    const fileapi::FileSystemURL& url,
176    bool exclusive,
177    bool recursive,
178    const StatusCallback& callback) {
179  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
180
181  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
182  if (file_path.empty()) {
183    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
184    return;
185  }
186
187  PostFileSystemCallback(
188      file_system_getter_,
189      base::Bind(&fileapi_internal::CreateDirectory,
190                 file_path, exclusive, recursive,
191                 google_apis::CreateRelayCallback(callback)),
192      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
193}
194
195void AsyncFileUtil::GetFileInfo(
196    scoped_ptr<fileapi::FileSystemOperationContext> context,
197    const fileapi::FileSystemURL& url,
198    const GetFileInfoCallback& callback) {
199  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
200
201  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
202  if (file_path.empty()) {
203    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, base::PlatformFileInfo());
204    return;
205  }
206
207  PostFileSystemCallback(
208      file_system_getter_,
209      base::Bind(&fileapi_internal::GetFileInfo,
210                 file_path, google_apis::CreateRelayCallback(callback)),
211      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
212                 base::PlatformFileInfo()));
213}
214
215void AsyncFileUtil::ReadDirectory(
216    scoped_ptr<fileapi::FileSystemOperationContext> context,
217    const fileapi::FileSystemURL& url,
218    const ReadDirectoryCallback& callback) {
219  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
220
221  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
222  if (file_path.empty()) {
223    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND, EntryList(), false);
224    return;
225  }
226
227  PostFileSystemCallback(
228      file_system_getter_,
229      base::Bind(&fileapi_internal::ReadDirectory,
230                 file_path, google_apis::CreateRelayCallback(callback)),
231      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
232                 EntryList(), false));
233}
234
235void AsyncFileUtil::Touch(
236    scoped_ptr<fileapi::FileSystemOperationContext> context,
237    const fileapi::FileSystemURL& url,
238    const base::Time& last_access_time,
239    const base::Time& last_modified_time,
240    const StatusCallback& callback) {
241  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
242
243  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
244  if (file_path.empty()) {
245    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
246    return;
247  }
248
249  PostFileSystemCallback(
250      file_system_getter_,
251      base::Bind(&fileapi_internal::TouchFile,
252                 file_path, last_access_time, last_modified_time,
253                 google_apis::CreateRelayCallback(callback)),
254      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
255}
256
257void AsyncFileUtil::Truncate(
258    scoped_ptr<fileapi::FileSystemOperationContext> context,
259    const fileapi::FileSystemURL& url,
260    int64 length,
261    const StatusCallback& callback) {
262  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
263
264  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
265  if (file_path.empty()) {
266    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
267    return;
268  }
269
270  PostFileSystemCallback(
271      file_system_getter_,
272      base::Bind(&fileapi_internal::Truncate,
273                 file_path, length, google_apis::CreateRelayCallback(callback)),
274      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
275}
276
277void AsyncFileUtil::CopyFileLocal(
278    scoped_ptr<fileapi::FileSystemOperationContext> context,
279    const fileapi::FileSystemURL& src_url,
280    const fileapi::FileSystemURL& dest_url,
281    const StatusCallback& callback) {
282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
283
284  base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
285  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
286  if (src_path.empty() || dest_path.empty()) {
287    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
288    return;
289  }
290
291  PostFileSystemCallback(
292      file_system_getter_,
293      base::Bind(&fileapi_internal::Copy,
294                 src_path, dest_path,
295                 google_apis::CreateRelayCallback(callback)),
296      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
297}
298
299void AsyncFileUtil::MoveFileLocal(
300    scoped_ptr<fileapi::FileSystemOperationContext> context,
301    const fileapi::FileSystemURL& src_url,
302    const fileapi::FileSystemURL& dest_url,
303    const StatusCallback& callback) {
304  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
305
306  base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
307  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
308  if (src_path.empty() || dest_path.empty()) {
309    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
310    return;
311  }
312
313  PostFileSystemCallback(
314      file_system_getter_,
315      base::Bind(&fileapi_internal::Move,
316                 src_path, dest_path,
317                 google_apis::CreateRelayCallback(callback)),
318      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
319}
320
321void AsyncFileUtil::CopyInForeignFile(
322    scoped_ptr<fileapi::FileSystemOperationContext> context,
323    const base::FilePath& src_file_path,
324    const fileapi::FileSystemURL& dest_url,
325    const StatusCallback& callback) {
326  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
327
328  base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
329  if (dest_path.empty()) {
330    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
331    return;
332  }
333
334  PostFileSystemCallback(
335      file_system_getter_,
336      base::Bind(&fileapi_internal::CopyInForeignFile,
337                 src_file_path, dest_path,
338                 google_apis::CreateRelayCallback(callback)),
339      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
340}
341
342void AsyncFileUtil::DeleteFile(
343    scoped_ptr<fileapi::FileSystemOperationContext> context,
344    const fileapi::FileSystemURL& url,
345    const StatusCallback& callback) {
346  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
347
348  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
349  if (file_path.empty()) {
350    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
351    return;
352  }
353
354  PostFileSystemCallback(
355      file_system_getter_,
356      base::Bind(&fileapi_internal::Remove,
357                 file_path, false /* not recursive */,
358                 google_apis::CreateRelayCallback(callback)),
359      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
360}
361
362void AsyncFileUtil::DeleteDirectory(
363    scoped_ptr<fileapi::FileSystemOperationContext> context,
364    const fileapi::FileSystemURL& url,
365    const StatusCallback& callback) {
366  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
367
368  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
369  if (file_path.empty()) {
370    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
371    return;
372  }
373
374  PostFileSystemCallback(
375      file_system_getter_,
376      base::Bind(&fileapi_internal::Remove,
377                 file_path, false /* not recursive */,
378                 google_apis::CreateRelayCallback(callback)),
379      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
380}
381
382void AsyncFileUtil::DeleteRecursively(
383    scoped_ptr<fileapi::FileSystemOperationContext> context,
384    const fileapi::FileSystemURL& url,
385    const StatusCallback& callback) {
386  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
387
388  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
389  if (file_path.empty()) {
390    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND);
391    return;
392  }
393
394  PostFileSystemCallback(
395      file_system_getter_,
396      base::Bind(&fileapi_internal::Remove,
397                 file_path, true /* recursive */,
398                 google_apis::CreateRelayCallback(callback)),
399      base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
400}
401
402void AsyncFileUtil::CreateSnapshotFile(
403    scoped_ptr<fileapi::FileSystemOperationContext> context,
404    const fileapi::FileSystemURL& url,
405    const CreateSnapshotFileCallback& callback) {
406  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
407
408  base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
409  if (file_path.empty()) {
410    callback.Run(base::PLATFORM_FILE_ERROR_NOT_FOUND,
411                 base::PlatformFileInfo(),
412                 base::FilePath(),
413                 scoped_refptr<webkit_blob::ShareableFileReference>());
414    return;
415  }
416
417  PostFileSystemCallback(
418      file_system_getter_,
419      base::Bind(&fileapi_internal::CreateSnapshotFile,
420                 file_path,
421                 google_apis::CreateRelayCallback(
422                     base::Bind(&RunCreateSnapshotFileCallback, callback))),
423      base::Bind(callback,
424                 base::PLATFORM_FILE_ERROR_FAILED,
425                 base::PlatformFileInfo(),
426                 base::FilePath(),
427                 scoped_refptr<webkit_blob::ShareableFileReference>()));
428}
429
430}  // namespace internal
431}  // namespace drive
432