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 "chrome/browser/chromeos/drive/fileapi/async_file_util.h"
7 #include "base/callback.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
14 #include "chrome/browser/chromeos/drive/file_system_util.h"
15 #include "chrome/browser/chromeos/drive/fileapi/fileapi_worker.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "google_apis/drive/task_util.h"
18 #include "storage/browser/blob/shareable_file_reference.h"
19 #include "storage/browser/fileapi/file_system_operation_context.h"
20 #include "storage/browser/fileapi/file_system_url.h"
22 using content::BrowserThread
;
24 namespace google_apis
{
27 // Partial specialization of helper template from google_apis/drive/task_util.h
28 // to enable google_apis::CreateRelayCallback to work with CreateOrOpenCallback.
30 struct ComposedCallback
<void(base::File
, T2
)> {
32 const base::Callback
<void(const base::Closure
&)>& runner
,
33 const base::Callback
<void(base::File
, T2
)>& callback
,
34 base::File arg1
, T2 arg2
) {
35 runner
.Run(base::Bind(callback
, Passed(&arg1
), arg2
));
39 } // namespace internal
40 } // namespace google_apis
46 // Posts fileapi_internal::RunFileSystemCallback to UI thread.
47 // This function must be called on IO thread.
48 // The |on_error_callback| will be called (on error case) on IO thread.
49 void PostFileSystemCallback(
50 const fileapi_internal::FileSystemGetter
& file_system_getter
,
51 const base::Callback
<void(FileSystemInterface
*)>& function
,
52 const base::Closure
& on_error_callback
) {
53 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
55 BrowserThread::PostTask(
58 base::Bind(&fileapi_internal::RunFileSystemCallback
,
59 file_system_getter
, function
,
60 on_error_callback
.is_null() ?
62 base::Bind(&google_apis::RunTaskWithTaskRunner
,
63 base::ThreadTaskRunnerHandle::Get(),
67 // Runs CreateOrOpenFile callback based on the given |error| and |file|.
68 void RunCreateOrOpenFileCallback(
69 const AsyncFileUtil::CreateOrOpenCallback
& callback
,
71 const base::Closure
& close_callback_on_ui_thread
) {
72 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
74 // It is necessary to make a closure, which runs on file closing here.
75 // It will be provided as a FileSystem::OpenFileCallback's argument later.
76 // (crbug.com/259184).
79 base::Bind(&google_apis::RunTaskWithTaskRunner
,
80 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI
),
81 close_callback_on_ui_thread
));
84 // Runs CreateOrOpenFile when the error happens.
85 void RunCreateOrOpenFileCallbackOnError(
86 const AsyncFileUtil::CreateOrOpenCallback
& callback
,
87 base::File::Error error
) {
88 callback
.Run(base::File(error
), base::Closure());
91 // Runs EnsureFileExistsCallback based on the given |error|.
92 void RunEnsureFileExistsCallback(
93 const AsyncFileUtil::EnsureFileExistsCallback
& callback
,
94 base::File::Error error
) {
95 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
97 // Remember if the file is actually created or not.
98 bool created
= (error
== base::File::FILE_OK
);
100 // File::FILE_ERROR_EXISTS is not an actual error here.
101 if (error
== base::File::FILE_ERROR_EXISTS
)
102 error
= base::File::FILE_OK
;
104 callback
.Run(error
, created
);
107 // Runs |callback| with the arguments based on the given arguments.
108 void RunCreateSnapshotFileCallback(
109 const AsyncFileUtil::CreateSnapshotFileCallback
& callback
,
110 base::File::Error error
,
111 const base::File::Info
& file_info
,
112 const base::FilePath
& local_path
,
113 storage::ScopedFile::ScopeOutPolicy scope_out_policy
) {
114 // ShareableFileReference is thread *unsafe* class. So it is necessary to
115 // create the instance (by invoking GetOrCreate) on IO thread, though
116 // most drive file system related operations run on UI thread.
117 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
119 scoped_refptr
<storage::ShareableFileReference
> file_reference
=
120 storage::ShareableFileReference::GetOrCreate(storage::ScopedFile(
121 local_path
, scope_out_policy
, BrowserThread::GetBlockingPool()));
122 callback
.Run(error
, file_info
, local_path
, file_reference
);
127 AsyncFileUtil::AsyncFileUtil() {
130 AsyncFileUtil::~AsyncFileUtil() {
133 void AsyncFileUtil::CreateOrOpen(
134 scoped_ptr
<storage::FileSystemOperationContext
> context
,
135 const storage::FileSystemURL
& url
,
137 const CreateOrOpenCallback
& callback
) {
138 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
140 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
141 if (file_path
.empty()) {
142 callback
.Run(base::File(base::File::FILE_ERROR_NOT_FOUND
), base::Closure());
146 const fileapi_internal::FileSystemGetter getter
=
147 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
);
148 PostFileSystemCallback(
150 base::Bind(&fileapi_internal::OpenFile
,
151 file_path
, file_flags
,
152 google_apis::CreateRelayCallback(
153 base::Bind(&RunCreateOrOpenFileCallback
, callback
))),
154 base::Bind(&RunCreateOrOpenFileCallbackOnError
,
155 callback
, base::File::FILE_ERROR_FAILED
));
158 void AsyncFileUtil::EnsureFileExists(
159 scoped_ptr
<storage::FileSystemOperationContext
> context
,
160 const storage::FileSystemURL
& url
,
161 const EnsureFileExistsCallback
& callback
) {
162 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
164 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
165 if (file_path
.empty()) {
166 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
, false);
170 PostFileSystemCallback(
171 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
172 base::Bind(&fileapi_internal::CreateFile
,
173 file_path
, true /* is_exlusive */,
174 google_apis::CreateRelayCallback(
175 base::Bind(&RunEnsureFileExistsCallback
, callback
))),
176 base::Bind(callback
, base::File::FILE_ERROR_FAILED
, false));
179 void AsyncFileUtil::CreateDirectory(
180 scoped_ptr
<storage::FileSystemOperationContext
> context
,
181 const storage::FileSystemURL
& url
,
184 const StatusCallback
& callback
) {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
187 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
188 if (file_path
.empty()) {
189 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
193 PostFileSystemCallback(
194 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
195 base::Bind(&fileapi_internal::CreateDirectory
,
196 file_path
, exclusive
, recursive
,
197 google_apis::CreateRelayCallback(callback
)),
198 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
201 void AsyncFileUtil::GetFileInfo(
202 scoped_ptr
<storage::FileSystemOperationContext
> context
,
203 const storage::FileSystemURL
& url
,
204 const GetFileInfoCallback
& callback
) {
205 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
207 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
208 if (file_path
.empty()) {
209 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
, base::File::Info());
213 PostFileSystemCallback(
214 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
215 base::Bind(&fileapi_internal::GetFileInfo
,
216 file_path
, google_apis::CreateRelayCallback(callback
)),
217 base::Bind(callback
, base::File::FILE_ERROR_FAILED
,
218 base::File::Info()));
221 void AsyncFileUtil::ReadDirectory(
222 scoped_ptr
<storage::FileSystemOperationContext
> context
,
223 const storage::FileSystemURL
& url
,
224 const ReadDirectoryCallback
& callback
) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
227 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
228 if (file_path
.empty()) {
229 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
, EntryList(), false);
233 PostFileSystemCallback(
234 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
235 base::Bind(&fileapi_internal::ReadDirectory
,
236 file_path
, google_apis::CreateRelayCallback(callback
)),
237 base::Bind(callback
, base::File::FILE_ERROR_FAILED
,
238 EntryList(), false));
241 void AsyncFileUtil::Touch(
242 scoped_ptr
<storage::FileSystemOperationContext
> context
,
243 const storage::FileSystemURL
& url
,
244 const base::Time
& last_access_time
,
245 const base::Time
& last_modified_time
,
246 const StatusCallback
& callback
) {
247 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
249 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
250 if (file_path
.empty()) {
251 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
255 PostFileSystemCallback(
256 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
257 base::Bind(&fileapi_internal::TouchFile
,
258 file_path
, last_access_time
, last_modified_time
,
259 google_apis::CreateRelayCallback(callback
)),
260 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
263 void AsyncFileUtil::Truncate(
264 scoped_ptr
<storage::FileSystemOperationContext
> context
,
265 const storage::FileSystemURL
& url
,
267 const StatusCallback
& callback
) {
268 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
270 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
271 if (file_path
.empty()) {
272 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
276 PostFileSystemCallback(
277 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
278 base::Bind(&fileapi_internal::Truncate
,
279 file_path
, length
, google_apis::CreateRelayCallback(callback
)),
280 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
283 void AsyncFileUtil::CopyFileLocal(
284 scoped_ptr
<storage::FileSystemOperationContext
> context
,
285 const storage::FileSystemURL
& src_url
,
286 const storage::FileSystemURL
& dest_url
,
287 CopyOrMoveOption option
,
288 const CopyFileProgressCallback
& progress_callback
,
289 const StatusCallback
& callback
) {
290 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
292 base::FilePath src_path
= util::ExtractDrivePathFromFileSystemUrl(src_url
);
293 base::FilePath dest_path
= util::ExtractDrivePathFromFileSystemUrl(dest_url
);
294 if (src_path
.empty() || dest_path
.empty()) {
295 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
299 // TODO(kinaba): crbug.com/339794.
300 // Assumption here is that |src_url| and |dest_url| are always from the same
301 // profile. This indeed holds as long as we mount different profiles onto
302 // different mount point. Hence, using GetFileSystemFromUrl(dest_url) is safe.
303 // This will change after we introduce cross-profile sharing etc., and we
304 // need to deal with files from different profiles here.
305 PostFileSystemCallback(
306 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, dest_url
),
308 &fileapi_internal::Copy
,
311 option
== storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED
,
312 google_apis::CreateRelayCallback(callback
)),
313 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
316 void AsyncFileUtil::MoveFileLocal(
317 scoped_ptr
<storage::FileSystemOperationContext
> context
,
318 const storage::FileSystemURL
& src_url
,
319 const storage::FileSystemURL
& dest_url
,
320 CopyOrMoveOption option
,
321 const StatusCallback
& callback
) {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
324 base::FilePath src_path
= util::ExtractDrivePathFromFileSystemUrl(src_url
);
325 base::FilePath dest_path
= util::ExtractDrivePathFromFileSystemUrl(dest_url
);
326 if (src_path
.empty() || dest_path
.empty()) {
327 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
331 // TODO(kinaba): see the comment in CopyFileLocal(). |src_url| and |dest_url|
332 // always return the same FileSystem by GetFileSystemFromUrl, but we need to
333 // change it in order to support cross-profile file sharing etc.
334 PostFileSystemCallback(
335 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, dest_url
),
336 base::Bind(&fileapi_internal::Move
,
338 google_apis::CreateRelayCallback(callback
)),
339 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
342 void AsyncFileUtil::CopyInForeignFile(
343 scoped_ptr
<storage::FileSystemOperationContext
> context
,
344 const base::FilePath
& src_file_path
,
345 const storage::FileSystemURL
& dest_url
,
346 const StatusCallback
& callback
) {
347 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
349 base::FilePath dest_path
= util::ExtractDrivePathFromFileSystemUrl(dest_url
);
350 if (dest_path
.empty()) {
351 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
355 PostFileSystemCallback(
356 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, dest_url
),
357 base::Bind(&fileapi_internal::CopyInForeignFile
,
358 src_file_path
, dest_path
,
359 google_apis::CreateRelayCallback(callback
)),
360 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
363 void AsyncFileUtil::DeleteFile(
364 scoped_ptr
<storage::FileSystemOperationContext
> context
,
365 const storage::FileSystemURL
& url
,
366 const StatusCallback
& callback
) {
367 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
369 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
370 if (file_path
.empty()) {
371 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
375 PostFileSystemCallback(
376 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
377 base::Bind(&fileapi_internal::Remove
,
378 file_path
, false /* not recursive */,
379 google_apis::CreateRelayCallback(callback
)),
380 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
383 void AsyncFileUtil::DeleteDirectory(
384 scoped_ptr
<storage::FileSystemOperationContext
> context
,
385 const storage::FileSystemURL
& url
,
386 const StatusCallback
& callback
) {
387 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
389 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
390 if (file_path
.empty()) {
391 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
395 PostFileSystemCallback(
396 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
397 base::Bind(&fileapi_internal::Remove
,
398 file_path
, false /* not recursive */,
399 google_apis::CreateRelayCallback(callback
)),
400 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
403 void AsyncFileUtil::DeleteRecursively(
404 scoped_ptr
<storage::FileSystemOperationContext
> context
,
405 const storage::FileSystemURL
& url
,
406 const StatusCallback
& callback
) {
407 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
409 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
410 if (file_path
.empty()) {
411 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
415 PostFileSystemCallback(
416 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
417 base::Bind(&fileapi_internal::Remove
,
418 file_path
, true /* recursive */,
419 google_apis::CreateRelayCallback(callback
)),
420 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
423 void AsyncFileUtil::CreateSnapshotFile(
424 scoped_ptr
<storage::FileSystemOperationContext
> context
,
425 const storage::FileSystemURL
& url
,
426 const CreateSnapshotFileCallback
& callback
) {
427 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
429 base::FilePath file_path
= util::ExtractDrivePathFromFileSystemUrl(url
);
430 if (file_path
.empty()) {
431 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
,
434 scoped_refptr
<storage::ShareableFileReference
>());
438 PostFileSystemCallback(
439 base::Bind(&fileapi_internal::GetFileSystemFromUrl
, url
),
440 base::Bind(&fileapi_internal::CreateSnapshotFile
,
442 google_apis::CreateRelayCallback(
443 base::Bind(&RunCreateSnapshotFileCallback
, callback
))),
445 base::File::FILE_ERROR_FAILED
,
448 scoped_refptr
<storage::ShareableFileReference
>()));
451 } // namespace internal