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.
5 #include "storage/browser/fileapi/file_system_operation_impl.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "net/base/escape.h"
12 #include "net/url_request/url_request.h"
13 #include "storage/browser/blob/shareable_file_reference.h"
14 #include "storage/browser/fileapi/async_file_util.h"
15 #include "storage/browser/fileapi/copy_or_move_operation_delegate.h"
16 #include "storage/browser/fileapi/file_observers.h"
17 #include "storage/browser/fileapi/file_system_backend.h"
18 #include "storage/browser/fileapi/file_system_context.h"
19 #include "storage/browser/fileapi/file_system_file_util.h"
20 #include "storage/browser/fileapi/file_system_operation_context.h"
21 #include "storage/browser/fileapi/file_system_url.h"
22 #include "storage/browser/fileapi/file_writer_delegate.h"
23 #include "storage/browser/fileapi/remove_operation_delegate.h"
24 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
25 #include "storage/browser/quota/quota_manager_proxy.h"
26 #include "storage/common/fileapi/file_system_types.h"
27 #include "storage/common/fileapi/file_system_util.h"
28 #include "storage/common/quota/quota_types.h"
30 using storage::ScopedFile
;
36 // Takes ownership and destruct on the target thread.
37 void Destruct(base::File file
) {}
40 scoped_refptr
<FileSystemContext
> context
,
41 base::WeakPtr
<FileSystemOperationImpl
> operation
,
42 const FileSystemOperationImpl::OpenFileCallback
& callback
,
44 const base::Closure
& on_close_callback
) {
46 context
->default_file_task_runner()->PostTask(
48 base::Bind(&Destruct
, base::Passed(&file
)));
51 callback
.Run(file
.Pass(), on_close_callback
);
56 FileSystemOperation
* FileSystemOperation::Create(
57 const FileSystemURL
& url
,
58 FileSystemContext
* file_system_context
,
59 scoped_ptr
<FileSystemOperationContext
> operation_context
) {
60 return new FileSystemOperationImpl(url
, file_system_context
,
61 operation_context
.Pass());
64 FileSystemOperationImpl::~FileSystemOperationImpl() {
67 void FileSystemOperationImpl::CreateFile(const FileSystemURL
& url
,
69 const StatusCallback
& callback
) {
70 DCHECK(SetPendingOperationType(kOperationCreateFile
));
71 GetUsageAndQuotaThenRunTask(
73 base::Bind(&FileSystemOperationImpl::DoCreateFile
,
74 weak_factory_
.GetWeakPtr(), url
, callback
, exclusive
),
75 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
78 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL
& url
,
81 const StatusCallback
& callback
) {
82 DCHECK(SetPendingOperationType(kOperationCreateDirectory
));
83 GetUsageAndQuotaThenRunTask(
85 base::Bind(&FileSystemOperationImpl::DoCreateDirectory
,
86 weak_factory_
.GetWeakPtr(), url
, callback
,
87 exclusive
, recursive
),
88 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
91 void FileSystemOperationImpl::Copy(
92 const FileSystemURL
& src_url
,
93 const FileSystemURL
& dest_url
,
94 CopyOrMoveOption option
,
95 const CopyProgressCallback
& progress_callback
,
96 const StatusCallback
& callback
) {
97 DCHECK(SetPendingOperationType(kOperationCopy
));
98 DCHECK(!recursive_operation_delegate_
);
100 // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
101 recursive_operation_delegate_
.reset(
102 new CopyOrMoveOperationDelegate(
103 file_system_context(),
105 CopyOrMoveOperationDelegate::OPERATION_COPY
,
108 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
109 weak_factory_
.GetWeakPtr(), callback
)));
110 recursive_operation_delegate_
->RunRecursively();
113 void FileSystemOperationImpl::Move(const FileSystemURL
& src_url
,
114 const FileSystemURL
& dest_url
,
115 CopyOrMoveOption option
,
116 const StatusCallback
& callback
) {
117 DCHECK(SetPendingOperationType(kOperationMove
));
118 DCHECK(!recursive_operation_delegate_
);
119 recursive_operation_delegate_
.reset(
120 new CopyOrMoveOperationDelegate(
121 file_system_context(),
123 CopyOrMoveOperationDelegate::OPERATION_MOVE
,
125 FileSystemOperation::CopyProgressCallback(),
126 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
127 weak_factory_
.GetWeakPtr(), callback
)));
128 recursive_operation_delegate_
->RunRecursively();
131 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL
& url
,
132 const StatusCallback
& callback
) {
133 DCHECK(SetPendingOperationType(kOperationDirectoryExists
));
134 async_file_util_
->GetFileInfo(
135 operation_context_
.Pass(), url
,
136 base::Bind(&FileSystemOperationImpl::DidDirectoryExists
,
137 weak_factory_
.GetWeakPtr(), callback
));
140 void FileSystemOperationImpl::FileExists(const FileSystemURL
& url
,
141 const StatusCallback
& callback
) {
142 DCHECK(SetPendingOperationType(kOperationFileExists
));
143 async_file_util_
->GetFileInfo(
144 operation_context_
.Pass(), url
,
145 base::Bind(&FileSystemOperationImpl::DidFileExists
,
146 weak_factory_
.GetWeakPtr(), callback
));
149 void FileSystemOperationImpl::GetMetadata(
150 const FileSystemURL
& url
, const GetMetadataCallback
& callback
) {
151 DCHECK(SetPendingOperationType(kOperationGetMetadata
));
152 async_file_util_
->GetFileInfo(operation_context_
.Pass(), url
, callback
);
155 void FileSystemOperationImpl::ReadDirectory(
156 const FileSystemURL
& url
, const ReadDirectoryCallback
& callback
) {
157 DCHECK(SetPendingOperationType(kOperationReadDirectory
));
158 async_file_util_
->ReadDirectory(
159 operation_context_
.Pass(), url
, callback
);
162 void FileSystemOperationImpl::Remove(const FileSystemURL
& url
,
164 const StatusCallback
& callback
) {
165 DCHECK(SetPendingOperationType(kOperationRemove
));
166 DCHECK(!recursive_operation_delegate_
);
169 // For recursive removal, try to delegate the operation to AsyncFileUtil
170 // first. If not supported, it is delegated to RemoveOperationDelegate
171 // in DidDeleteRecursively.
172 async_file_util_
->DeleteRecursively(
173 operation_context_
.Pass(), url
,
174 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively
,
175 weak_factory_
.GetWeakPtr(), url
, callback
));
179 recursive_operation_delegate_
.reset(
180 new RemoveOperationDelegate(
181 file_system_context(), url
,
182 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
183 weak_factory_
.GetWeakPtr(), callback
)));
184 recursive_operation_delegate_
->Run();
187 void FileSystemOperationImpl::Write(
188 const FileSystemURL
& url
,
189 scoped_ptr
<FileWriterDelegate
> writer_delegate
,
190 scoped_ptr
<net::URLRequest
> blob_request
,
191 const WriteCallback
& callback
) {
192 DCHECK(SetPendingOperationType(kOperationWrite
));
193 file_writer_delegate_
= writer_delegate
.Pass();
194 file_writer_delegate_
->Start(
196 base::Bind(&FileSystemOperationImpl::DidWrite
,
197 weak_factory_
.GetWeakPtr(), url
, callback
));
200 void FileSystemOperationImpl::Truncate(const FileSystemURL
& url
, int64 length
,
201 const StatusCallback
& callback
) {
202 DCHECK(SetPendingOperationType(kOperationTruncate
));
203 GetUsageAndQuotaThenRunTask(
205 base::Bind(&FileSystemOperationImpl::DoTruncate
,
206 weak_factory_
.GetWeakPtr(), url
, callback
, length
),
207 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
210 void FileSystemOperationImpl::TouchFile(const FileSystemURL
& url
,
211 const base::Time
& last_access_time
,
212 const base::Time
& last_modified_time
,
213 const StatusCallback
& callback
) {
214 DCHECK(SetPendingOperationType(kOperationTouchFile
));
215 async_file_util_
->Touch(
216 operation_context_
.Pass(), url
,
217 last_access_time
, last_modified_time
,
218 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
219 weak_factory_
.GetWeakPtr(), callback
));
222 void FileSystemOperationImpl::OpenFile(const FileSystemURL
& url
,
224 const OpenFileCallback
& callback
) {
225 DCHECK(SetPendingOperationType(kOperationOpenFile
));
228 (base::File::FLAG_TEMPORARY
| base::File::FLAG_HIDDEN
)) {
229 callback
.Run(base::File(base::File::FILE_ERROR_FAILED
),
233 GetUsageAndQuotaThenRunTask(
235 base::Bind(&FileSystemOperationImpl::DoOpenFile
,
236 weak_factory_
.GetWeakPtr(),
237 url
, callback
, file_flags
),
238 base::Bind(callback
, Passed(base::File(base::File::FILE_ERROR_FAILED
)),
242 // We can only get here on a write or truncate that's not yet completed.
243 // We don't support cancelling any other operation at this time.
244 void FileSystemOperationImpl::Cancel(const StatusCallback
& cancel_callback
) {
245 DCHECK(cancel_callback_
.is_null());
246 cancel_callback_
= cancel_callback
;
248 if (file_writer_delegate_
.get()) {
249 DCHECK_EQ(kOperationWrite
, pending_operation_
);
250 // This will call DidWrite() with ABORT status code.
251 file_writer_delegate_
->Cancel();
252 } else if (recursive_operation_delegate_
) {
253 // This will call DidFinishOperation() with ABORT status code.
254 recursive_operation_delegate_
->Cancel();
256 // For truncate we have no way to cancel the inflight operation (for now).
257 // Let it just run and dispatch cancel callback later.
258 DCHECK_EQ(kOperationTruncate
, pending_operation_
);
262 void FileSystemOperationImpl::CreateSnapshotFile(
263 const FileSystemURL
& url
,
264 const SnapshotFileCallback
& callback
) {
265 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile
));
266 async_file_util_
->CreateSnapshotFile(
267 operation_context_
.Pass(), url
, callback
);
270 void FileSystemOperationImpl::CopyInForeignFile(
271 const base::FilePath
& src_local_disk_file_path
,
272 const FileSystemURL
& dest_url
,
273 const StatusCallback
& callback
) {
274 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile
));
275 GetUsageAndQuotaThenRunTask(
277 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile
,
278 weak_factory_
.GetWeakPtr(), src_local_disk_file_path
, dest_url
,
280 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
283 void FileSystemOperationImpl::RemoveFile(
284 const FileSystemURL
& url
,
285 const StatusCallback
& callback
) {
286 DCHECK(SetPendingOperationType(kOperationRemove
));
287 async_file_util_
->DeleteFile(
288 operation_context_
.Pass(), url
,
289 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
290 weak_factory_
.GetWeakPtr(), callback
));
293 void FileSystemOperationImpl::RemoveDirectory(
294 const FileSystemURL
& url
,
295 const StatusCallback
& callback
) {
296 DCHECK(SetPendingOperationType(kOperationRemove
));
297 async_file_util_
->DeleteDirectory(
298 operation_context_
.Pass(), url
,
299 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
300 weak_factory_
.GetWeakPtr(), callback
));
303 void FileSystemOperationImpl::CopyFileLocal(
304 const FileSystemURL
& src_url
,
305 const FileSystemURL
& dest_url
,
306 CopyOrMoveOption option
,
307 const CopyFileProgressCallback
& progress_callback
,
308 const StatusCallback
& callback
) {
309 DCHECK(SetPendingOperationType(kOperationCopy
));
310 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
312 GetUsageAndQuotaThenRunTask(
314 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal
,
315 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, option
,
316 progress_callback
, callback
),
317 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
320 void FileSystemOperationImpl::MoveFileLocal(
321 const FileSystemURL
& src_url
,
322 const FileSystemURL
& dest_url
,
323 CopyOrMoveOption option
,
324 const StatusCallback
& callback
) {
325 DCHECK(SetPendingOperationType(kOperationMove
));
326 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
327 GetUsageAndQuotaThenRunTask(
329 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal
,
330 weak_factory_
.GetWeakPtr(),
331 src_url
, dest_url
, option
, callback
),
332 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
335 base::File::Error
FileSystemOperationImpl::SyncGetPlatformPath(
336 const FileSystemURL
& url
,
337 base::FilePath
* platform_path
) {
338 DCHECK(SetPendingOperationType(kOperationGetLocalPath
));
339 if (!file_system_context()->IsSandboxFileSystem(url
.type()))
340 return base::File::FILE_ERROR_INVALID_OPERATION
;
341 FileSystemFileUtil
* file_util
=
342 file_system_context()->sandbox_delegate()->sync_file_util();
343 file_util
->GetLocalFilePath(operation_context_
.get(), url
, platform_path
);
344 return base::File::FILE_OK
;
347 FileSystemOperationImpl::FileSystemOperationImpl(
348 const FileSystemURL
& url
,
349 FileSystemContext
* file_system_context
,
350 scoped_ptr
<FileSystemOperationContext
> operation_context
)
351 : file_system_context_(file_system_context
),
352 operation_context_(operation_context
.Pass()),
353 async_file_util_(NULL
),
354 pending_operation_(kOperationNone
),
355 weak_factory_(this) {
356 DCHECK(operation_context_
.get());
357 operation_context_
->DetachUserDataThread();
358 async_file_util_
= file_system_context_
->GetAsyncFileUtil(url
.type());
359 DCHECK(async_file_util_
);
362 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
363 const FileSystemURL
& url
,
364 const base::Closure
& task
,
365 const base::Closure
& error_callback
) {
366 storage::QuotaManagerProxy
* quota_manager_proxy
=
367 file_system_context()->quota_manager_proxy();
368 if (!quota_manager_proxy
||
369 !file_system_context()->GetQuotaUtil(url
.type())) {
370 // If we don't have the quota manager or the requested filesystem type
371 // does not support quota, we should be able to let it go.
372 operation_context_
->set_allowed_bytes_growth(kint64max
);
377 DCHECK(quota_manager_proxy
);
378 DCHECK(quota_manager_proxy
->quota_manager());
379 quota_manager_proxy
->quota_manager()->GetUsageAndQuota(
381 FileSystemTypeToQuotaStorageType(url
.type()),
382 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask
,
383 weak_factory_
.GetWeakPtr(), task
, error_callback
));
386 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
387 const base::Closure
& task
,
388 const base::Closure
& error_callback
,
389 storage::QuotaStatusCode status
,
392 if (status
!= storage::kQuotaStatusOk
) {
393 LOG(WARNING
) << "Got unexpected quota error : " << status
;
394 error_callback
.Run();
398 operation_context_
->set_allowed_bytes_growth(quota
- usage
);
402 void FileSystemOperationImpl::DoCreateFile(
403 const FileSystemURL
& url
,
404 const StatusCallback
& callback
,
406 async_file_util_
->EnsureFileExists(
407 operation_context_
.Pass(), url
,
410 &FileSystemOperationImpl::DidEnsureFileExistsExclusive
:
411 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive
,
412 weak_factory_
.GetWeakPtr(), callback
));
415 void FileSystemOperationImpl::DoCreateDirectory(
416 const FileSystemURL
& url
,
417 const StatusCallback
& callback
,
418 bool exclusive
, bool recursive
) {
419 async_file_util_
->CreateDirectory(
420 operation_context_
.Pass(),
421 url
, exclusive
, recursive
,
422 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
423 weak_factory_
.GetWeakPtr(), callback
));
426 void FileSystemOperationImpl::DoCopyFileLocal(
427 const FileSystemURL
& src_url
,
428 const FileSystemURL
& dest_url
,
429 CopyOrMoveOption option
,
430 const CopyFileProgressCallback
& progress_callback
,
431 const StatusCallback
& callback
) {
432 async_file_util_
->CopyFileLocal(
433 operation_context_
.Pass(), src_url
, dest_url
, option
, progress_callback
,
434 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
435 weak_factory_
.GetWeakPtr(), callback
));
438 void FileSystemOperationImpl::DoMoveFileLocal(
439 const FileSystemURL
& src_url
,
440 const FileSystemURL
& dest_url
,
441 CopyOrMoveOption option
,
442 const StatusCallback
& callback
) {
443 async_file_util_
->MoveFileLocal(
444 operation_context_
.Pass(), src_url
, dest_url
, option
,
445 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
446 weak_factory_
.GetWeakPtr(), callback
));
449 void FileSystemOperationImpl::DoCopyInForeignFile(
450 const base::FilePath
& src_local_disk_file_path
,
451 const FileSystemURL
& dest_url
,
452 const StatusCallback
& callback
) {
453 async_file_util_
->CopyInForeignFile(
454 operation_context_
.Pass(),
455 src_local_disk_file_path
, dest_url
,
456 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
457 weak_factory_
.GetWeakPtr(), callback
));
460 void FileSystemOperationImpl::DoTruncate(const FileSystemURL
& url
,
461 const StatusCallback
& callback
,
463 async_file_util_
->Truncate(
464 operation_context_
.Pass(), url
, length
,
465 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
466 weak_factory_
.GetWeakPtr(), callback
));
469 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL
& url
,
470 const OpenFileCallback
& callback
,
472 async_file_util_
->CreateOrOpen(
473 operation_context_
.Pass(), url
, file_flags
,
474 base::Bind(&DidOpenFile
,
475 file_system_context_
, weak_factory_
.GetWeakPtr(), callback
));
478 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
479 const StatusCallback
& callback
,
480 base::File::Error rv
, bool created
) {
481 if (rv
== base::File::FILE_OK
&& !created
) {
482 callback
.Run(base::File::FILE_ERROR_EXISTS
);
484 DidFinishOperation(callback
, rv
);
488 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
489 const StatusCallback
& callback
,
490 base::File::Error rv
, bool /* created */) {
491 DidFinishOperation(callback
, rv
);
494 void FileSystemOperationImpl::DidFinishOperation(
495 const StatusCallback
& callback
,
496 base::File::Error rv
) {
497 if (!cancel_callback_
.is_null()) {
498 StatusCallback cancel_callback
= cancel_callback_
;
501 // Return OK only if we succeeded to stop the operation.
502 cancel_callback
.Run(rv
== base::File::FILE_ERROR_ABORT
?
503 base::File::FILE_OK
:
504 base::File::FILE_ERROR_INVALID_OPERATION
);
510 void FileSystemOperationImpl::DidDirectoryExists(
511 const StatusCallback
& callback
,
512 base::File::Error rv
,
513 const base::File::Info
& file_info
) {
514 if (rv
== base::File::FILE_OK
&& !file_info
.is_directory
)
515 rv
= base::File::FILE_ERROR_NOT_A_DIRECTORY
;
519 void FileSystemOperationImpl::DidFileExists(
520 const StatusCallback
& callback
,
521 base::File::Error rv
,
522 const base::File::Info
& file_info
) {
523 if (rv
== base::File::FILE_OK
&& file_info
.is_directory
)
524 rv
= base::File::FILE_ERROR_NOT_A_FILE
;
528 void FileSystemOperationImpl::DidDeleteRecursively(
529 const FileSystemURL
& url
,
530 const StatusCallback
& callback
,
531 base::File::Error rv
) {
532 if (rv
== base::File::FILE_ERROR_INVALID_OPERATION
) {
533 // Recursive removal is not supported on this platform.
534 DCHECK(!recursive_operation_delegate_
);
535 recursive_operation_delegate_
.reset(
536 new RemoveOperationDelegate(
537 file_system_context(), url
,
538 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
539 weak_factory_
.GetWeakPtr(), callback
)));
540 recursive_operation_delegate_
->RunRecursively();
547 void FileSystemOperationImpl::DidWrite(
548 const FileSystemURL
& url
,
549 const WriteCallback
& write_callback
,
550 base::File::Error rv
,
552 FileWriterDelegate::WriteProgressStatus write_status
) {
553 const bool complete
= (
554 write_status
!= FileWriterDelegate::SUCCESS_IO_PENDING
);
555 if (complete
&& write_status
!= FileWriterDelegate::ERROR_WRITE_NOT_STARTED
) {
556 DCHECK(operation_context_
);
557 operation_context_
->change_observers()->Notify(
558 &FileChangeObserver::OnModifyFile
, MakeTuple(url
));
561 StatusCallback cancel_callback
= cancel_callback_
;
562 write_callback
.Run(rv
, bytes
, complete
);
563 if (!cancel_callback
.is_null())
564 cancel_callback
.Run(base::File::FILE_OK
);
567 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type
) {
568 if (pending_operation_
!= kOperationNone
)
570 pending_operation_
= type
;
574 } // namespace storage