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/remove_operation_delegate.h"
21 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
22 #include "storage/browser/quota/quota_manager_proxy.h"
23 #include "storage/common/fileapi/file_system_types.h"
24 #include "storage/common/fileapi/file_system_util.h"
26 using storage::ScopedFile
;
32 // Takes ownership and destruct on the target thread.
33 void Destruct(base::File file
) {}
36 scoped_refptr
<FileSystemContext
> context
,
37 base::WeakPtr
<FileSystemOperationImpl
> operation
,
38 const FileSystemOperationImpl::OpenFileCallback
& callback
,
40 const base::Closure
& on_close_callback
) {
42 context
->default_file_task_runner()->PostTask(
44 base::Bind(&Destruct
, base::Passed(&file
)));
47 callback
.Run(file
.Pass(), on_close_callback
);
52 FileSystemOperation
* FileSystemOperation::Create(
53 const FileSystemURL
& url
,
54 FileSystemContext
* file_system_context
,
55 scoped_ptr
<FileSystemOperationContext
> operation_context
) {
56 return new FileSystemOperationImpl(url
, file_system_context
,
57 operation_context
.Pass());
60 FileSystemOperationImpl::~FileSystemOperationImpl() {
63 void FileSystemOperationImpl::CreateFile(const FileSystemURL
& url
,
65 const StatusCallback
& callback
) {
66 DCHECK(SetPendingOperationType(kOperationCreateFile
));
67 GetUsageAndQuotaThenRunTask(
69 base::Bind(&FileSystemOperationImpl::DoCreateFile
,
70 weak_factory_
.GetWeakPtr(), url
, callback
, exclusive
),
71 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
74 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL
& url
,
77 const StatusCallback
& callback
) {
78 DCHECK(SetPendingOperationType(kOperationCreateDirectory
));
79 GetUsageAndQuotaThenRunTask(
81 base::Bind(&FileSystemOperationImpl::DoCreateDirectory
,
82 weak_factory_
.GetWeakPtr(), url
, callback
,
83 exclusive
, recursive
),
84 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
87 void FileSystemOperationImpl::Copy(
88 const FileSystemURL
& src_url
,
89 const FileSystemURL
& dest_url
,
90 CopyOrMoveOption option
,
91 const CopyProgressCallback
& progress_callback
,
92 const StatusCallback
& callback
) {
93 DCHECK(SetPendingOperationType(kOperationCopy
));
94 DCHECK(!recursive_operation_delegate_
);
96 // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
97 recursive_operation_delegate_
.reset(
98 new CopyOrMoveOperationDelegate(
99 file_system_context(),
101 CopyOrMoveOperationDelegate::OPERATION_COPY
,
104 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
105 weak_factory_
.GetWeakPtr(), callback
)));
106 recursive_operation_delegate_
->RunRecursively();
109 void FileSystemOperationImpl::Move(const FileSystemURL
& src_url
,
110 const FileSystemURL
& dest_url
,
111 CopyOrMoveOption option
,
112 const StatusCallback
& callback
) {
113 DCHECK(SetPendingOperationType(kOperationMove
));
114 DCHECK(!recursive_operation_delegate_
);
115 recursive_operation_delegate_
.reset(
116 new CopyOrMoveOperationDelegate(
117 file_system_context(),
119 CopyOrMoveOperationDelegate::OPERATION_MOVE
,
121 FileSystemOperation::CopyProgressCallback(),
122 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
123 weak_factory_
.GetWeakPtr(), callback
)));
124 recursive_operation_delegate_
->RunRecursively();
127 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL
& url
,
128 const StatusCallback
& callback
) {
129 DCHECK(SetPendingOperationType(kOperationDirectoryExists
));
130 async_file_util_
->GetFileInfo(
131 operation_context_
.Pass(), url
,
132 base::Bind(&FileSystemOperationImpl::DidDirectoryExists
,
133 weak_factory_
.GetWeakPtr(), callback
));
136 void FileSystemOperationImpl::FileExists(const FileSystemURL
& url
,
137 const StatusCallback
& callback
) {
138 DCHECK(SetPendingOperationType(kOperationFileExists
));
139 async_file_util_
->GetFileInfo(
140 operation_context_
.Pass(), url
,
141 base::Bind(&FileSystemOperationImpl::DidFileExists
,
142 weak_factory_
.GetWeakPtr(), callback
));
145 void FileSystemOperationImpl::GetMetadata(
146 const FileSystemURL
& url
, const GetMetadataCallback
& callback
) {
147 DCHECK(SetPendingOperationType(kOperationGetMetadata
));
148 async_file_util_
->GetFileInfo(operation_context_
.Pass(), url
, callback
);
151 void FileSystemOperationImpl::ReadDirectory(
152 const FileSystemURL
& url
, const ReadDirectoryCallback
& callback
) {
153 DCHECK(SetPendingOperationType(kOperationReadDirectory
));
154 async_file_util_
->ReadDirectory(
155 operation_context_
.Pass(), url
, callback
);
158 void FileSystemOperationImpl::Remove(const FileSystemURL
& url
,
160 const StatusCallback
& callback
) {
161 DCHECK(SetPendingOperationType(kOperationRemove
));
162 DCHECK(!recursive_operation_delegate_
);
165 // For recursive removal, try to delegate the operation to AsyncFileUtil
166 // first. If not supported, it is delegated to RemoveOperationDelegate
167 // in DidDeleteRecursively.
168 async_file_util_
->DeleteRecursively(
169 operation_context_
.Pass(), url
,
170 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively
,
171 weak_factory_
.GetWeakPtr(), url
, callback
));
175 recursive_operation_delegate_
.reset(
176 new RemoveOperationDelegate(
177 file_system_context(), url
,
178 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
179 weak_factory_
.GetWeakPtr(), callback
)));
180 recursive_operation_delegate_
->Run();
183 void FileSystemOperationImpl::Write(
184 const FileSystemURL
& url
,
185 scoped_ptr
<FileWriterDelegate
> writer_delegate
,
186 scoped_ptr
<net::URLRequest
> blob_request
,
187 const WriteCallback
& callback
) {
188 DCHECK(SetPendingOperationType(kOperationWrite
));
189 file_writer_delegate_
= writer_delegate
.Pass();
190 file_writer_delegate_
->Start(
192 base::Bind(&FileSystemOperationImpl::DidWrite
,
193 weak_factory_
.GetWeakPtr(), url
, callback
));
196 void FileSystemOperationImpl::Truncate(const FileSystemURL
& url
, int64 length
,
197 const StatusCallback
& callback
) {
198 DCHECK(SetPendingOperationType(kOperationTruncate
));
199 GetUsageAndQuotaThenRunTask(
201 base::Bind(&FileSystemOperationImpl::DoTruncate
,
202 weak_factory_
.GetWeakPtr(), url
, callback
, length
),
203 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
206 void FileSystemOperationImpl::TouchFile(const FileSystemURL
& url
,
207 const base::Time
& last_access_time
,
208 const base::Time
& last_modified_time
,
209 const StatusCallback
& callback
) {
210 DCHECK(SetPendingOperationType(kOperationTouchFile
));
211 async_file_util_
->Touch(
212 operation_context_
.Pass(), url
,
213 last_access_time
, last_modified_time
,
214 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
215 weak_factory_
.GetWeakPtr(), callback
));
218 void FileSystemOperationImpl::OpenFile(const FileSystemURL
& url
,
220 const OpenFileCallback
& callback
) {
221 DCHECK(SetPendingOperationType(kOperationOpenFile
));
224 (base::File::FLAG_TEMPORARY
| base::File::FLAG_HIDDEN
)) {
225 callback
.Run(base::File(base::File::FILE_ERROR_FAILED
),
229 GetUsageAndQuotaThenRunTask(
231 base::Bind(&FileSystemOperationImpl::DoOpenFile
,
232 weak_factory_
.GetWeakPtr(),
233 url
, callback
, file_flags
),
234 base::Bind(callback
, Passed(base::File(base::File::FILE_ERROR_FAILED
)),
238 // We can only get here on a write or truncate that's not yet completed.
239 // We don't support cancelling any other operation at this time.
240 void FileSystemOperationImpl::Cancel(const StatusCallback
& cancel_callback
) {
241 DCHECK(cancel_callback_
.is_null());
242 cancel_callback_
= cancel_callback
;
244 if (file_writer_delegate_
.get()) {
245 DCHECK_EQ(kOperationWrite
, pending_operation_
);
246 // This will call DidWrite() with ABORT status code.
247 file_writer_delegate_
->Cancel();
248 } else if (recursive_operation_delegate_
) {
249 // This will call DidFinishOperation() with ABORT status code.
250 recursive_operation_delegate_
->Cancel();
252 // For truncate we have no way to cancel the inflight operation (for now).
253 // Let it just run and dispatch cancel callback later.
254 DCHECK_EQ(kOperationTruncate
, pending_operation_
);
258 void FileSystemOperationImpl::CreateSnapshotFile(
259 const FileSystemURL
& url
,
260 const SnapshotFileCallback
& callback
) {
261 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile
));
262 async_file_util_
->CreateSnapshotFile(
263 operation_context_
.Pass(), url
, callback
);
266 void FileSystemOperationImpl::CopyInForeignFile(
267 const base::FilePath
& src_local_disk_file_path
,
268 const FileSystemURL
& dest_url
,
269 const StatusCallback
& callback
) {
270 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile
));
271 GetUsageAndQuotaThenRunTask(
273 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile
,
274 weak_factory_
.GetWeakPtr(), src_local_disk_file_path
, dest_url
,
276 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
279 void FileSystemOperationImpl::RemoveFile(
280 const FileSystemURL
& url
,
281 const StatusCallback
& callback
) {
282 DCHECK(SetPendingOperationType(kOperationRemove
));
283 async_file_util_
->DeleteFile(
284 operation_context_
.Pass(), url
,
285 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
286 weak_factory_
.GetWeakPtr(), callback
));
289 void FileSystemOperationImpl::RemoveDirectory(
290 const FileSystemURL
& url
,
291 const StatusCallback
& callback
) {
292 DCHECK(SetPendingOperationType(kOperationRemove
));
293 async_file_util_
->DeleteDirectory(
294 operation_context_
.Pass(), url
,
295 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
296 weak_factory_
.GetWeakPtr(), callback
));
299 void FileSystemOperationImpl::CopyFileLocal(
300 const FileSystemURL
& src_url
,
301 const FileSystemURL
& dest_url
,
302 CopyOrMoveOption option
,
303 const CopyFileProgressCallback
& progress_callback
,
304 const StatusCallback
& callback
) {
305 DCHECK(SetPendingOperationType(kOperationCopy
));
306 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
308 GetUsageAndQuotaThenRunTask(
310 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal
,
311 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, option
,
312 progress_callback
, callback
),
313 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
316 void FileSystemOperationImpl::MoveFileLocal(
317 const FileSystemURL
& src_url
,
318 const FileSystemURL
& dest_url
,
319 CopyOrMoveOption option
,
320 const StatusCallback
& callback
) {
321 DCHECK(SetPendingOperationType(kOperationMove
));
322 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
323 GetUsageAndQuotaThenRunTask(
325 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal
,
326 weak_factory_
.GetWeakPtr(),
327 src_url
, dest_url
, option
, callback
),
328 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
331 base::File::Error
FileSystemOperationImpl::SyncGetPlatformPath(
332 const FileSystemURL
& url
,
333 base::FilePath
* platform_path
) {
334 DCHECK(SetPendingOperationType(kOperationGetLocalPath
));
335 if (!file_system_context()->IsSandboxFileSystem(url
.type()))
336 return base::File::FILE_ERROR_INVALID_OPERATION
;
337 FileSystemFileUtil
* file_util
=
338 file_system_context()->sandbox_delegate()->sync_file_util();
339 file_util
->GetLocalFilePath(operation_context_
.get(), url
, platform_path
);
340 return base::File::FILE_OK
;
343 FileSystemOperationImpl::FileSystemOperationImpl(
344 const FileSystemURL
& url
,
345 FileSystemContext
* file_system_context
,
346 scoped_ptr
<FileSystemOperationContext
> operation_context
)
347 : file_system_context_(file_system_context
),
348 operation_context_(operation_context
.Pass()),
349 async_file_util_(NULL
),
350 pending_operation_(kOperationNone
),
351 weak_factory_(this) {
352 DCHECK(operation_context_
.get());
353 operation_context_
->DetachUserDataThread();
354 async_file_util_
= file_system_context_
->GetAsyncFileUtil(url
.type());
355 DCHECK(async_file_util_
);
358 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
359 const FileSystemURL
& url
,
360 const base::Closure
& task
,
361 const base::Closure
& error_callback
) {
362 storage::QuotaManagerProxy
* quota_manager_proxy
=
363 file_system_context()->quota_manager_proxy();
364 if (!quota_manager_proxy
||
365 !file_system_context()->GetQuotaUtil(url
.type())) {
366 // If we don't have the quota manager or the requested filesystem type
367 // does not support quota, we should be able to let it go.
368 operation_context_
->set_allowed_bytes_growth(kint64max
);
373 DCHECK(quota_manager_proxy
);
374 DCHECK(quota_manager_proxy
->quota_manager());
375 quota_manager_proxy
->quota_manager()->GetUsageAndQuota(
377 FileSystemTypeToQuotaStorageType(url
.type()),
378 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask
,
379 weak_factory_
.GetWeakPtr(), task
, error_callback
));
382 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
383 const base::Closure
& task
,
384 const base::Closure
& error_callback
,
385 storage::QuotaStatusCode status
,
388 if (status
!= storage::kQuotaStatusOk
) {
389 LOG(WARNING
) << "Got unexpected quota error : " << status
;
390 error_callback
.Run();
394 operation_context_
->set_allowed_bytes_growth(quota
- usage
);
398 void FileSystemOperationImpl::DoCreateFile(
399 const FileSystemURL
& url
,
400 const StatusCallback
& callback
,
402 async_file_util_
->EnsureFileExists(
403 operation_context_
.Pass(), url
,
406 &FileSystemOperationImpl::DidEnsureFileExistsExclusive
:
407 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive
,
408 weak_factory_
.GetWeakPtr(), callback
));
411 void FileSystemOperationImpl::DoCreateDirectory(
412 const FileSystemURL
& url
,
413 const StatusCallback
& callback
,
414 bool exclusive
, bool recursive
) {
415 async_file_util_
->CreateDirectory(
416 operation_context_
.Pass(),
417 url
, exclusive
, recursive
,
418 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
419 weak_factory_
.GetWeakPtr(), callback
));
422 void FileSystemOperationImpl::DoCopyFileLocal(
423 const FileSystemURL
& src_url
,
424 const FileSystemURL
& dest_url
,
425 CopyOrMoveOption option
,
426 const CopyFileProgressCallback
& progress_callback
,
427 const StatusCallback
& callback
) {
428 async_file_util_
->CopyFileLocal(
429 operation_context_
.Pass(), src_url
, dest_url
, option
, progress_callback
,
430 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
431 weak_factory_
.GetWeakPtr(), callback
));
434 void FileSystemOperationImpl::DoMoveFileLocal(
435 const FileSystemURL
& src_url
,
436 const FileSystemURL
& dest_url
,
437 CopyOrMoveOption option
,
438 const StatusCallback
& callback
) {
439 async_file_util_
->MoveFileLocal(
440 operation_context_
.Pass(), src_url
, dest_url
, option
,
441 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
442 weak_factory_
.GetWeakPtr(), callback
));
445 void FileSystemOperationImpl::DoCopyInForeignFile(
446 const base::FilePath
& src_local_disk_file_path
,
447 const FileSystemURL
& dest_url
,
448 const StatusCallback
& callback
) {
449 async_file_util_
->CopyInForeignFile(
450 operation_context_
.Pass(),
451 src_local_disk_file_path
, dest_url
,
452 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
453 weak_factory_
.GetWeakPtr(), callback
));
456 void FileSystemOperationImpl::DoTruncate(const FileSystemURL
& url
,
457 const StatusCallback
& callback
,
459 async_file_util_
->Truncate(
460 operation_context_
.Pass(), url
, length
,
461 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
462 weak_factory_
.GetWeakPtr(), callback
));
465 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL
& url
,
466 const OpenFileCallback
& callback
,
468 async_file_util_
->CreateOrOpen(
469 operation_context_
.Pass(), url
, file_flags
,
470 base::Bind(&DidOpenFile
,
471 file_system_context_
, weak_factory_
.GetWeakPtr(), callback
));
474 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
475 const StatusCallback
& callback
,
476 base::File::Error rv
, bool created
) {
477 if (rv
== base::File::FILE_OK
&& !created
) {
478 callback
.Run(base::File::FILE_ERROR_EXISTS
);
480 DidFinishOperation(callback
, rv
);
484 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
485 const StatusCallback
& callback
,
486 base::File::Error rv
, bool /* created */) {
487 DidFinishOperation(callback
, rv
);
490 void FileSystemOperationImpl::DidFinishOperation(
491 const StatusCallback
& callback
,
492 base::File::Error rv
) {
493 if (!cancel_callback_
.is_null()) {
494 StatusCallback cancel_callback
= cancel_callback_
;
497 // Return OK only if we succeeded to stop the operation.
498 cancel_callback
.Run(rv
== base::File::FILE_ERROR_ABORT
?
499 base::File::FILE_OK
:
500 base::File::FILE_ERROR_INVALID_OPERATION
);
506 void FileSystemOperationImpl::DidDirectoryExists(
507 const StatusCallback
& callback
,
508 base::File::Error rv
,
509 const base::File::Info
& file_info
) {
510 if (rv
== base::File::FILE_OK
&& !file_info
.is_directory
)
511 rv
= base::File::FILE_ERROR_NOT_A_DIRECTORY
;
515 void FileSystemOperationImpl::DidFileExists(
516 const StatusCallback
& callback
,
517 base::File::Error rv
,
518 const base::File::Info
& file_info
) {
519 if (rv
== base::File::FILE_OK
&& file_info
.is_directory
)
520 rv
= base::File::FILE_ERROR_NOT_A_FILE
;
524 void FileSystemOperationImpl::DidDeleteRecursively(
525 const FileSystemURL
& url
,
526 const StatusCallback
& callback
,
527 base::File::Error rv
) {
528 if (rv
== base::File::FILE_ERROR_INVALID_OPERATION
) {
529 // Recursive removal is not supported on this platform.
530 DCHECK(!recursive_operation_delegate_
);
531 recursive_operation_delegate_
.reset(
532 new RemoveOperationDelegate(
533 file_system_context(), url
,
534 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
535 weak_factory_
.GetWeakPtr(), callback
)));
536 recursive_operation_delegate_
->RunRecursively();
543 void FileSystemOperationImpl::DidWrite(
544 const FileSystemURL
& url
,
545 const WriteCallback
& write_callback
,
546 base::File::Error rv
,
548 FileWriterDelegate::WriteProgressStatus write_status
) {
549 const bool complete
= (
550 write_status
!= FileWriterDelegate::SUCCESS_IO_PENDING
);
551 if (complete
&& write_status
!= FileWriterDelegate::ERROR_WRITE_NOT_STARTED
) {
552 DCHECK(operation_context_
);
553 operation_context_
->change_observers()->Notify(
554 &FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
557 StatusCallback cancel_callback
= cancel_callback_
;
558 write_callback
.Run(rv
, bytes
, complete
);
559 if (!cancel_callback
.is_null())
560 cancel_callback
.Run(base::File::FILE_OK
);
563 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type
) {
564 if (pending_operation_
!= kOperationNone
)
566 pending_operation_
= type
;
570 } // namespace storage