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 ErrorBehavior error_behavior
,
92 const CopyProgressCallback
& progress_callback
,
93 const StatusCallback
& callback
) {
94 DCHECK(SetPendingOperationType(kOperationCopy
));
95 DCHECK(!recursive_operation_delegate_
);
97 recursive_operation_delegate_
.reset(new CopyOrMoveOperationDelegate(
98 file_system_context(), src_url
, dest_url
,
99 CopyOrMoveOperationDelegate::OPERATION_COPY
, option
, error_behavior
,
101 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
102 weak_factory_
.GetWeakPtr(), callback
)));
103 recursive_operation_delegate_
->RunRecursively();
106 void FileSystemOperationImpl::Move(const FileSystemURL
& src_url
,
107 const FileSystemURL
& dest_url
,
108 CopyOrMoveOption option
,
109 const StatusCallback
& callback
) {
110 DCHECK(SetPendingOperationType(kOperationMove
));
111 DCHECK(!recursive_operation_delegate_
);
112 recursive_operation_delegate_
.reset(new CopyOrMoveOperationDelegate(
113 file_system_context(), src_url
, dest_url
,
114 CopyOrMoveOperationDelegate::OPERATION_MOVE
, option
, ERROR_BEHAVIOR_ABORT
,
115 FileSystemOperation::CopyProgressCallback(),
116 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
117 weak_factory_
.GetWeakPtr(), callback
)));
118 recursive_operation_delegate_
->RunRecursively();
121 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL
& url
,
122 const StatusCallback
& callback
) {
123 DCHECK(SetPendingOperationType(kOperationDirectoryExists
));
124 async_file_util_
->GetFileInfo(
125 operation_context_
.Pass(), url
,
126 base::Bind(&FileSystemOperationImpl::DidDirectoryExists
,
127 weak_factory_
.GetWeakPtr(), callback
));
130 void FileSystemOperationImpl::FileExists(const FileSystemURL
& url
,
131 const StatusCallback
& callback
) {
132 DCHECK(SetPendingOperationType(kOperationFileExists
));
133 async_file_util_
->GetFileInfo(
134 operation_context_
.Pass(), url
,
135 base::Bind(&FileSystemOperationImpl::DidFileExists
,
136 weak_factory_
.GetWeakPtr(), callback
));
139 void FileSystemOperationImpl::GetMetadata(
140 const FileSystemURL
& url
, const GetMetadataCallback
& callback
) {
141 DCHECK(SetPendingOperationType(kOperationGetMetadata
));
142 async_file_util_
->GetFileInfo(operation_context_
.Pass(), url
, callback
);
145 void FileSystemOperationImpl::ReadDirectory(
146 const FileSystemURL
& url
, const ReadDirectoryCallback
& callback
) {
147 DCHECK(SetPendingOperationType(kOperationReadDirectory
));
148 async_file_util_
->ReadDirectory(
149 operation_context_
.Pass(), url
, callback
);
152 void FileSystemOperationImpl::Remove(const FileSystemURL
& url
,
154 const StatusCallback
& callback
) {
155 DCHECK(SetPendingOperationType(kOperationRemove
));
156 DCHECK(!recursive_operation_delegate_
);
159 // For recursive removal, try to delegate the operation to AsyncFileUtil
160 // first. If not supported, it is delegated to RemoveOperationDelegate
161 // in DidDeleteRecursively.
162 async_file_util_
->DeleteRecursively(
163 operation_context_
.Pass(), url
,
164 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively
,
165 weak_factory_
.GetWeakPtr(), url
, callback
));
169 recursive_operation_delegate_
.reset(
170 new RemoveOperationDelegate(
171 file_system_context(), url
,
172 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
173 weak_factory_
.GetWeakPtr(), callback
)));
174 recursive_operation_delegate_
->Run();
177 void FileSystemOperationImpl::Write(
178 const FileSystemURL
& url
,
179 scoped_ptr
<FileWriterDelegate
> writer_delegate
,
180 scoped_ptr
<net::URLRequest
> blob_request
,
181 const WriteCallback
& callback
) {
182 DCHECK(SetPendingOperationType(kOperationWrite
));
183 file_writer_delegate_
= writer_delegate
.Pass();
184 file_writer_delegate_
->Start(
186 base::Bind(&FileSystemOperationImpl::DidWrite
,
187 weak_factory_
.GetWeakPtr(), url
, callback
));
190 void FileSystemOperationImpl::Truncate(const FileSystemURL
& url
, int64 length
,
191 const StatusCallback
& callback
) {
192 DCHECK(SetPendingOperationType(kOperationTruncate
));
193 GetUsageAndQuotaThenRunTask(
195 base::Bind(&FileSystemOperationImpl::DoTruncate
,
196 weak_factory_
.GetWeakPtr(), url
, callback
, length
),
197 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
200 void FileSystemOperationImpl::TouchFile(const FileSystemURL
& url
,
201 const base::Time
& last_access_time
,
202 const base::Time
& last_modified_time
,
203 const StatusCallback
& callback
) {
204 DCHECK(SetPendingOperationType(kOperationTouchFile
));
205 async_file_util_
->Touch(
206 operation_context_
.Pass(), url
,
207 last_access_time
, last_modified_time
,
208 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
209 weak_factory_
.GetWeakPtr(), callback
));
212 void FileSystemOperationImpl::OpenFile(const FileSystemURL
& url
,
214 const OpenFileCallback
& callback
) {
215 DCHECK(SetPendingOperationType(kOperationOpenFile
));
218 (base::File::FLAG_TEMPORARY
| base::File::FLAG_HIDDEN
)) {
219 callback
.Run(base::File(base::File::FILE_ERROR_FAILED
),
223 GetUsageAndQuotaThenRunTask(
225 base::Bind(&FileSystemOperationImpl::DoOpenFile
,
226 weak_factory_
.GetWeakPtr(),
227 url
, callback
, file_flags
),
228 base::Bind(callback
, Passed(base::File(base::File::FILE_ERROR_FAILED
)),
232 // We can only get here on a write or truncate that's not yet completed.
233 // We don't support cancelling any other operation at this time.
234 void FileSystemOperationImpl::Cancel(const StatusCallback
& cancel_callback
) {
235 DCHECK(cancel_callback_
.is_null());
236 cancel_callback_
= cancel_callback
;
238 if (file_writer_delegate_
.get()) {
239 DCHECK_EQ(kOperationWrite
, pending_operation_
);
240 // This will call DidWrite() with ABORT status code.
241 file_writer_delegate_
->Cancel();
242 } else if (recursive_operation_delegate_
) {
243 // This will call DidFinishOperation() with ABORT status code.
244 recursive_operation_delegate_
->Cancel();
246 // For truncate we have no way to cancel the inflight operation (for now).
247 // Let it just run and dispatch cancel callback later.
248 DCHECK_EQ(kOperationTruncate
, pending_operation_
);
252 void FileSystemOperationImpl::CreateSnapshotFile(
253 const FileSystemURL
& url
,
254 const SnapshotFileCallback
& callback
) {
255 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile
));
256 async_file_util_
->CreateSnapshotFile(
257 operation_context_
.Pass(), url
, callback
);
260 void FileSystemOperationImpl::CopyInForeignFile(
261 const base::FilePath
& src_local_disk_file_path
,
262 const FileSystemURL
& dest_url
,
263 const StatusCallback
& callback
) {
264 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile
));
265 GetUsageAndQuotaThenRunTask(
267 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile
,
268 weak_factory_
.GetWeakPtr(), src_local_disk_file_path
, dest_url
,
270 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
273 void FileSystemOperationImpl::RemoveFile(
274 const FileSystemURL
& url
,
275 const StatusCallback
& callback
) {
276 DCHECK(SetPendingOperationType(kOperationRemove
));
277 async_file_util_
->DeleteFile(
278 operation_context_
.Pass(), url
,
279 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
280 weak_factory_
.GetWeakPtr(), callback
));
283 void FileSystemOperationImpl::RemoveDirectory(
284 const FileSystemURL
& url
,
285 const StatusCallback
& callback
) {
286 DCHECK(SetPendingOperationType(kOperationRemove
));
287 async_file_util_
->DeleteDirectory(
288 operation_context_
.Pass(), url
,
289 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
290 weak_factory_
.GetWeakPtr(), callback
));
293 void FileSystemOperationImpl::CopyFileLocal(
294 const FileSystemURL
& src_url
,
295 const FileSystemURL
& dest_url
,
296 CopyOrMoveOption option
,
297 const CopyFileProgressCallback
& progress_callback
,
298 const StatusCallback
& callback
) {
299 DCHECK(SetPendingOperationType(kOperationCopy
));
300 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
302 GetUsageAndQuotaThenRunTask(
304 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal
,
305 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, option
,
306 progress_callback
, callback
),
307 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
310 void FileSystemOperationImpl::MoveFileLocal(
311 const FileSystemURL
& src_url
,
312 const FileSystemURL
& dest_url
,
313 CopyOrMoveOption option
,
314 const StatusCallback
& callback
) {
315 DCHECK(SetPendingOperationType(kOperationMove
));
316 DCHECK(src_url
.IsInSameFileSystem(dest_url
));
317 GetUsageAndQuotaThenRunTask(
319 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal
,
320 weak_factory_
.GetWeakPtr(),
321 src_url
, dest_url
, option
, callback
),
322 base::Bind(callback
, base::File::FILE_ERROR_FAILED
));
325 base::File::Error
FileSystemOperationImpl::SyncGetPlatformPath(
326 const FileSystemURL
& url
,
327 base::FilePath
* platform_path
) {
328 DCHECK(SetPendingOperationType(kOperationGetLocalPath
));
329 if (!file_system_context()->IsSandboxFileSystem(url
.type()))
330 return base::File::FILE_ERROR_INVALID_OPERATION
;
331 FileSystemFileUtil
* file_util
=
332 file_system_context()->sandbox_delegate()->sync_file_util();
333 file_util
->GetLocalFilePath(operation_context_
.get(), url
, platform_path
);
334 return base::File::FILE_OK
;
337 FileSystemOperationImpl::FileSystemOperationImpl(
338 const FileSystemURL
& url
,
339 FileSystemContext
* file_system_context
,
340 scoped_ptr
<FileSystemOperationContext
> operation_context
)
341 : file_system_context_(file_system_context
),
342 operation_context_(operation_context
.Pass()),
343 async_file_util_(NULL
),
344 pending_operation_(kOperationNone
),
345 weak_factory_(this) {
346 DCHECK(operation_context_
.get());
347 operation_context_
->DetachUserDataThread();
348 async_file_util_
= file_system_context_
->GetAsyncFileUtil(url
.type());
349 DCHECK(async_file_util_
);
352 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
353 const FileSystemURL
& url
,
354 const base::Closure
& task
,
355 const base::Closure
& error_callback
) {
356 storage::QuotaManagerProxy
* quota_manager_proxy
=
357 file_system_context()->quota_manager_proxy();
358 if (!quota_manager_proxy
||
359 !file_system_context()->GetQuotaUtil(url
.type())) {
360 // If we don't have the quota manager or the requested filesystem type
361 // does not support quota, we should be able to let it go.
362 operation_context_
->set_allowed_bytes_growth(kint64max
);
367 DCHECK(quota_manager_proxy
);
368 DCHECK(quota_manager_proxy
->quota_manager());
369 quota_manager_proxy
->quota_manager()->GetUsageAndQuota(
371 FileSystemTypeToQuotaStorageType(url
.type()),
372 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask
,
373 weak_factory_
.GetWeakPtr(), task
, error_callback
));
376 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
377 const base::Closure
& task
,
378 const base::Closure
& error_callback
,
379 storage::QuotaStatusCode status
,
382 if (status
!= storage::kQuotaStatusOk
) {
383 LOG(WARNING
) << "Got unexpected quota error : " << status
;
384 error_callback
.Run();
388 operation_context_
->set_allowed_bytes_growth(quota
- usage
);
392 void FileSystemOperationImpl::DoCreateFile(
393 const FileSystemURL
& url
,
394 const StatusCallback
& callback
,
396 async_file_util_
->EnsureFileExists(
397 operation_context_
.Pass(), url
,
400 &FileSystemOperationImpl::DidEnsureFileExistsExclusive
:
401 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive
,
402 weak_factory_
.GetWeakPtr(), callback
));
405 void FileSystemOperationImpl::DoCreateDirectory(
406 const FileSystemURL
& url
,
407 const StatusCallback
& callback
,
408 bool exclusive
, bool recursive
) {
409 async_file_util_
->CreateDirectory(
410 operation_context_
.Pass(),
411 url
, exclusive
, recursive
,
412 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
413 weak_factory_
.GetWeakPtr(), callback
));
416 void FileSystemOperationImpl::DoCopyFileLocal(
417 const FileSystemURL
& src_url
,
418 const FileSystemURL
& dest_url
,
419 CopyOrMoveOption option
,
420 const CopyFileProgressCallback
& progress_callback
,
421 const StatusCallback
& callback
) {
422 async_file_util_
->CopyFileLocal(
423 operation_context_
.Pass(), src_url
, dest_url
, option
, progress_callback
,
424 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
425 weak_factory_
.GetWeakPtr(), callback
));
428 void FileSystemOperationImpl::DoMoveFileLocal(
429 const FileSystemURL
& src_url
,
430 const FileSystemURL
& dest_url
,
431 CopyOrMoveOption option
,
432 const StatusCallback
& callback
) {
433 async_file_util_
->MoveFileLocal(
434 operation_context_
.Pass(), src_url
, dest_url
, option
,
435 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
436 weak_factory_
.GetWeakPtr(), callback
));
439 void FileSystemOperationImpl::DoCopyInForeignFile(
440 const base::FilePath
& src_local_disk_file_path
,
441 const FileSystemURL
& dest_url
,
442 const StatusCallback
& callback
) {
443 async_file_util_
->CopyInForeignFile(
444 operation_context_
.Pass(),
445 src_local_disk_file_path
, dest_url
,
446 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
447 weak_factory_
.GetWeakPtr(), callback
));
450 void FileSystemOperationImpl::DoTruncate(const FileSystemURL
& url
,
451 const StatusCallback
& callback
,
453 async_file_util_
->Truncate(
454 operation_context_
.Pass(), url
, length
,
455 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
456 weak_factory_
.GetWeakPtr(), callback
));
459 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL
& url
,
460 const OpenFileCallback
& callback
,
462 async_file_util_
->CreateOrOpen(
463 operation_context_
.Pass(), url
, file_flags
,
464 base::Bind(&DidOpenFile
,
465 file_system_context_
, weak_factory_
.GetWeakPtr(), callback
));
468 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
469 const StatusCallback
& callback
,
470 base::File::Error rv
, bool created
) {
471 if (rv
== base::File::FILE_OK
&& !created
) {
472 callback
.Run(base::File::FILE_ERROR_EXISTS
);
474 DidFinishOperation(callback
, rv
);
478 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
479 const StatusCallback
& callback
,
480 base::File::Error rv
, bool /* created */) {
481 DidFinishOperation(callback
, rv
);
484 void FileSystemOperationImpl::DidFinishOperation(
485 const StatusCallback
& callback
,
486 base::File::Error rv
) {
487 if (!cancel_callback_
.is_null()) {
488 StatusCallback cancel_callback
= cancel_callback_
;
491 // Return OK only if we succeeded to stop the operation.
492 cancel_callback
.Run(rv
== base::File::FILE_ERROR_ABORT
?
493 base::File::FILE_OK
:
494 base::File::FILE_ERROR_INVALID_OPERATION
);
500 void FileSystemOperationImpl::DidDirectoryExists(
501 const StatusCallback
& callback
,
502 base::File::Error rv
,
503 const base::File::Info
& file_info
) {
504 if (rv
== base::File::FILE_OK
&& !file_info
.is_directory
)
505 rv
= base::File::FILE_ERROR_NOT_A_DIRECTORY
;
509 void FileSystemOperationImpl::DidFileExists(
510 const StatusCallback
& callback
,
511 base::File::Error rv
,
512 const base::File::Info
& file_info
) {
513 if (rv
== base::File::FILE_OK
&& file_info
.is_directory
)
514 rv
= base::File::FILE_ERROR_NOT_A_FILE
;
518 void FileSystemOperationImpl::DidDeleteRecursively(
519 const FileSystemURL
& url
,
520 const StatusCallback
& callback
,
521 base::File::Error rv
) {
522 if (rv
== base::File::FILE_ERROR_INVALID_OPERATION
) {
523 // Recursive removal is not supported on this platform.
524 DCHECK(!recursive_operation_delegate_
);
525 recursive_operation_delegate_
.reset(
526 new RemoveOperationDelegate(
527 file_system_context(), url
,
528 base::Bind(&FileSystemOperationImpl::DidFinishOperation
,
529 weak_factory_
.GetWeakPtr(), callback
)));
530 recursive_operation_delegate_
->RunRecursively();
537 void FileSystemOperationImpl::DidWrite(
538 const FileSystemURL
& url
,
539 const WriteCallback
& write_callback
,
540 base::File::Error rv
,
542 FileWriterDelegate::WriteProgressStatus write_status
) {
543 const bool complete
= (
544 write_status
!= FileWriterDelegate::SUCCESS_IO_PENDING
);
545 if (complete
&& write_status
!= FileWriterDelegate::ERROR_WRITE_NOT_STARTED
) {
546 DCHECK(operation_context_
);
547 operation_context_
->change_observers()->Notify(
548 &FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
551 StatusCallback cancel_callback
= cancel_callback_
;
552 write_callback
.Run(rv
, bytes
, complete
);
553 if (!cancel_callback
.is_null())
554 cancel_callback
.Run(base::File::FILE_OK
);
557 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type
) {
558 if (pending_operation_
!= kOperationNone
)
560 pending_operation_
= type
;
564 } // namespace storage