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 "webkit/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 "webkit/browser/fileapi/async_file_util.h"
14 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
15 #include "webkit/browser/fileapi/file_observers.h"
16 #include "webkit/browser/fileapi/file_system_backend.h"
17 #include "webkit/browser/fileapi/file_system_context.h"
18 #include "webkit/browser/fileapi/file_system_file_util.h"
19 #include "webkit/browser/fileapi/file_system_operation_context.h"
20 #include "webkit/browser/fileapi/file_system_url.h"
21 #include "webkit/browser/fileapi/file_writer_delegate.h"
22 #include "webkit/browser/fileapi/remove_operation_delegate.h"
23 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
24 #include "webkit/browser/quota/quota_manager.h"
25 #include "webkit/common/blob/shareable_file_reference.h"
26 #include "webkit/common/fileapi/file_system_types.h"
27 #include "webkit/common/fileapi/file_system_util.h"
28 #include "webkit/common/quota/quota_types.h"
30 using webkit_blob::ScopedFile;
34 FileSystemOperation* FileSystemOperation::Create(
35 const FileSystemURL& url,
36 FileSystemContext* file_system_context,
37 scoped_ptr<FileSystemOperationContext> operation_context) {
38 return new FileSystemOperationImpl(url, file_system_context,
39 operation_context.Pass());
42 FileSystemOperationImpl::~FileSystemOperationImpl() {
45 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
47 const StatusCallback& callback) {
48 DCHECK(SetPendingOperationType(kOperationCreateFile));
49 GetUsageAndQuotaThenRunTask(
51 base::Bind(&FileSystemOperationImpl::DoCreateFile,
52 weak_factory_.GetWeakPtr(), url, callback, exclusive),
53 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
56 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
59 const StatusCallback& callback) {
60 DCHECK(SetPendingOperationType(kOperationCreateDirectory));
61 GetUsageAndQuotaThenRunTask(
63 base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
64 weak_factory_.GetWeakPtr(), url, callback,
65 exclusive, recursive),
66 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
69 void FileSystemOperationImpl::Copy(
70 const FileSystemURL& src_url,
71 const FileSystemURL& dest_url,
72 CopyOrMoveOption option,
73 const CopyProgressCallback& progress_callback,
74 const StatusCallback& callback) {
75 DCHECK(SetPendingOperationType(kOperationCopy));
76 DCHECK(!recursive_operation_delegate_);
78 // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
79 recursive_operation_delegate_.reset(
80 new CopyOrMoveOperationDelegate(
81 file_system_context(),
83 CopyOrMoveOperationDelegate::OPERATION_COPY,
86 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
87 weak_factory_.GetWeakPtr(), callback)));
88 recursive_operation_delegate_->RunRecursively();
91 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
92 const FileSystemURL& dest_url,
93 CopyOrMoveOption option,
94 const StatusCallback& callback) {
95 DCHECK(SetPendingOperationType(kOperationMove));
96 DCHECK(!recursive_operation_delegate_);
97 recursive_operation_delegate_.reset(
98 new CopyOrMoveOperationDelegate(
99 file_system_context(),
101 CopyOrMoveOperationDelegate::OPERATION_MOVE,
103 FileSystemOperation::CopyProgressCallback(),
104 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
105 weak_factory_.GetWeakPtr(), callback)));
106 recursive_operation_delegate_->RunRecursively();
109 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
110 const StatusCallback& callback) {
111 DCHECK(SetPendingOperationType(kOperationDirectoryExists));
112 async_file_util_->GetFileInfo(
113 operation_context_.Pass(), url,
114 base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
115 weak_factory_.GetWeakPtr(), callback));
118 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
119 const StatusCallback& callback) {
120 DCHECK(SetPendingOperationType(kOperationFileExists));
121 async_file_util_->GetFileInfo(
122 operation_context_.Pass(), url,
123 base::Bind(&FileSystemOperationImpl::DidFileExists,
124 weak_factory_.GetWeakPtr(), callback));
127 void FileSystemOperationImpl::GetMetadata(
128 const FileSystemURL& url, const GetMetadataCallback& callback) {
129 DCHECK(SetPendingOperationType(kOperationGetMetadata));
130 async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
133 void FileSystemOperationImpl::ReadDirectory(
134 const FileSystemURL& url, const ReadDirectoryCallback& callback) {
135 DCHECK(SetPendingOperationType(kOperationReadDirectory));
136 async_file_util_->ReadDirectory(
137 operation_context_.Pass(), url, callback);
140 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
142 const StatusCallback& callback) {
143 DCHECK(SetPendingOperationType(kOperationRemove));
144 DCHECK(!recursive_operation_delegate_);
147 // For recursive removal, try to delegate the operation to AsyncFileUtil
148 // first. If not supported, it is delegated to RemoveOperationDelegate
149 // in DidDeleteRecursively.
150 async_file_util_->DeleteRecursively(
151 operation_context_.Pass(), url,
152 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
153 weak_factory_.GetWeakPtr(), url, callback));
157 recursive_operation_delegate_.reset(
158 new RemoveOperationDelegate(
159 file_system_context(), url,
160 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
161 weak_factory_.GetWeakPtr(), callback)));
162 recursive_operation_delegate_->Run();
165 void FileSystemOperationImpl::Write(
166 const FileSystemURL& url,
167 scoped_ptr<FileWriterDelegate> writer_delegate,
168 scoped_ptr<net::URLRequest> blob_request,
169 const WriteCallback& callback) {
170 DCHECK(SetPendingOperationType(kOperationWrite));
171 file_writer_delegate_ = writer_delegate.Pass();
172 file_writer_delegate_->Start(
174 base::Bind(&FileSystemOperationImpl::DidWrite,
175 weak_factory_.GetWeakPtr(), url, callback));
178 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
179 const StatusCallback& callback) {
180 DCHECK(SetPendingOperationType(kOperationTruncate));
181 GetUsageAndQuotaThenRunTask(
183 base::Bind(&FileSystemOperationImpl::DoTruncate,
184 weak_factory_.GetWeakPtr(), url, callback, length),
185 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
188 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
189 const base::Time& last_access_time,
190 const base::Time& last_modified_time,
191 const StatusCallback& callback) {
192 DCHECK(SetPendingOperationType(kOperationTouchFile));
193 async_file_util_->Touch(
194 operation_context_.Pass(), url,
195 last_access_time, last_modified_time,
196 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
197 weak_factory_.GetWeakPtr(), callback));
200 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
202 base::ProcessHandle peer_handle,
203 const OpenFileCallback& callback) {
204 DCHECK(SetPendingOperationType(kOperationOpenFile));
205 peer_handle_ = peer_handle;
208 (base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN)) {
209 callback.Run(base::PLATFORM_FILE_ERROR_FAILED,
210 base::kInvalidPlatformFileValue,
212 base::kNullProcessHandle);
215 GetUsageAndQuotaThenRunTask(
217 base::Bind(&FileSystemOperationImpl::DoOpenFile,
218 weak_factory_.GetWeakPtr(),
219 url, callback, file_flags),
220 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
221 base::kInvalidPlatformFileValue,
223 base::kNullProcessHandle));
226 // We can only get here on a write or truncate that's not yet completed.
227 // We don't support cancelling any other operation at this time.
228 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
229 DCHECK(cancel_callback_.is_null());
230 cancel_callback_ = cancel_callback;
232 if (file_writer_delegate_.get()) {
233 DCHECK_EQ(kOperationWrite, pending_operation_);
234 // This will call DidWrite() with ABORT status code.
235 file_writer_delegate_->Cancel();
236 } else if (recursive_operation_delegate_) {
237 // This will call DidFinishOperation() with ABORT status code.
238 recursive_operation_delegate_->Cancel();
240 // For truncate we have no way to cancel the inflight operation (for now).
241 // Let it just run and dispatch cancel callback later.
242 DCHECK_EQ(kOperationTruncate, pending_operation_);
246 void FileSystemOperationImpl::CreateSnapshotFile(
247 const FileSystemURL& url,
248 const SnapshotFileCallback& callback) {
249 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
250 async_file_util_->CreateSnapshotFile(
251 operation_context_.Pass(), url, callback);
254 void FileSystemOperationImpl::CopyInForeignFile(
255 const base::FilePath& src_local_disk_file_path,
256 const FileSystemURL& dest_url,
257 const StatusCallback& callback) {
258 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
259 GetUsageAndQuotaThenRunTask(
261 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
262 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
264 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
267 void FileSystemOperationImpl::RemoveFile(
268 const FileSystemURL& url,
269 const StatusCallback& callback) {
270 DCHECK(SetPendingOperationType(kOperationRemove));
271 async_file_util_->DeleteFile(
272 operation_context_.Pass(), url,
273 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
274 weak_factory_.GetWeakPtr(), callback));
277 void FileSystemOperationImpl::RemoveDirectory(
278 const FileSystemURL& url,
279 const StatusCallback& callback) {
280 DCHECK(SetPendingOperationType(kOperationRemove));
281 async_file_util_->DeleteDirectory(
282 operation_context_.Pass(), url,
283 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
284 weak_factory_.GetWeakPtr(), callback));
287 void FileSystemOperationImpl::CopyFileLocal(
288 const FileSystemURL& src_url,
289 const FileSystemURL& dest_url,
290 CopyOrMoveOption option,
291 const CopyFileProgressCallback& progress_callback,
292 const StatusCallback& callback) {
293 DCHECK(SetPendingOperationType(kOperationCopy));
294 DCHECK(src_url.IsInSameFileSystem(dest_url));
296 GetUsageAndQuotaThenRunTask(
298 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
299 weak_factory_.GetWeakPtr(), src_url, dest_url, option,
300 progress_callback, callback),
301 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
304 void FileSystemOperationImpl::MoveFileLocal(
305 const FileSystemURL& src_url,
306 const FileSystemURL& dest_url,
307 CopyOrMoveOption option,
308 const StatusCallback& callback) {
309 DCHECK(SetPendingOperationType(kOperationMove));
310 DCHECK(src_url.IsInSameFileSystem(dest_url));
311 GetUsageAndQuotaThenRunTask(
313 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
314 weak_factory_.GetWeakPtr(),
315 src_url, dest_url, option, callback),
316 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
319 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
320 const FileSystemURL& url,
321 base::FilePath* platform_path) {
322 DCHECK(SetPendingOperationType(kOperationGetLocalPath));
323 if (!file_system_context()->IsSandboxFileSystem(url.type()))
324 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
325 FileSystemFileUtil* file_util =
326 file_system_context()->sandbox_delegate()->sync_file_util();
327 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
328 return base::PLATFORM_FILE_OK;
331 FileSystemOperationImpl::FileSystemOperationImpl(
332 const FileSystemURL& url,
333 FileSystemContext* file_system_context,
334 scoped_ptr<FileSystemOperationContext> operation_context)
335 : file_system_context_(file_system_context),
336 operation_context_(operation_context.Pass()),
337 async_file_util_(NULL),
338 peer_handle_(base::kNullProcessHandle),
339 pending_operation_(kOperationNone),
340 weak_factory_(this) {
341 DCHECK(operation_context_.get());
342 operation_context_->DetachUserDataThread();
343 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
344 DCHECK(async_file_util_);
347 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
348 const FileSystemURL& url,
349 const base::Closure& task,
350 const base::Closure& error_callback) {
351 quota::QuotaManagerProxy* quota_manager_proxy =
352 file_system_context()->quota_manager_proxy();
353 if (!quota_manager_proxy ||
354 !file_system_context()->GetQuotaUtil(url.type())) {
355 // If we don't have the quota manager or the requested filesystem type
356 // does not support quota, we should be able to let it go.
357 operation_context_->set_allowed_bytes_growth(kint64max);
362 DCHECK(quota_manager_proxy);
363 DCHECK(quota_manager_proxy->quota_manager());
364 quota_manager_proxy->quota_manager()->GetUsageAndQuota(
366 FileSystemTypeToQuotaStorageType(url.type()),
367 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
368 weak_factory_.GetWeakPtr(), task, error_callback));
371 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
372 const base::Closure& task,
373 const base::Closure& error_callback,
374 quota::QuotaStatusCode status,
375 int64 usage, int64 quota) {
376 if (status != quota::kQuotaStatusOk) {
377 LOG(WARNING) << "Got unexpected quota error : " << status;
378 error_callback.Run();
382 operation_context_->set_allowed_bytes_growth(quota - usage);
386 void FileSystemOperationImpl::DoCreateFile(
387 const FileSystemURL& url,
388 const StatusCallback& callback,
390 async_file_util_->EnsureFileExists(
391 operation_context_.Pass(), url,
394 &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
395 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
396 weak_factory_.GetWeakPtr(), callback));
399 void FileSystemOperationImpl::DoCreateDirectory(
400 const FileSystemURL& url,
401 const StatusCallback& callback,
402 bool exclusive, bool recursive) {
403 async_file_util_->CreateDirectory(
404 operation_context_.Pass(),
405 url, exclusive, recursive,
406 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
407 weak_factory_.GetWeakPtr(), callback));
410 void FileSystemOperationImpl::DoCopyFileLocal(
411 const FileSystemURL& src_url,
412 const FileSystemURL& dest_url,
413 CopyOrMoveOption option,
414 const CopyFileProgressCallback& progress_callback,
415 const StatusCallback& callback) {
416 async_file_util_->CopyFileLocal(
417 operation_context_.Pass(), src_url, dest_url, option, progress_callback,
418 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
419 weak_factory_.GetWeakPtr(), callback));
422 void FileSystemOperationImpl::DoMoveFileLocal(
423 const FileSystemURL& src_url,
424 const FileSystemURL& dest_url,
425 CopyOrMoveOption option,
426 const StatusCallback& callback) {
427 async_file_util_->MoveFileLocal(
428 operation_context_.Pass(), src_url, dest_url, option,
429 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
430 weak_factory_.GetWeakPtr(), callback));
433 void FileSystemOperationImpl::DoCopyInForeignFile(
434 const base::FilePath& src_local_disk_file_path,
435 const FileSystemURL& dest_url,
436 const StatusCallback& callback) {
437 async_file_util_->CopyInForeignFile(
438 operation_context_.Pass(),
439 src_local_disk_file_path, dest_url,
440 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
441 weak_factory_.GetWeakPtr(), callback));
444 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
445 const StatusCallback& callback,
447 async_file_util_->Truncate(
448 operation_context_.Pass(), url, length,
449 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
450 weak_factory_.GetWeakPtr(), callback));
453 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
454 const OpenFileCallback& callback,
456 async_file_util_->CreateOrOpen(
457 operation_context_.Pass(), url, file_flags,
458 base::Bind(&FileSystemOperationImpl::DidOpenFile,
459 weak_factory_.GetWeakPtr(), callback));
462 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
463 const StatusCallback& callback,
464 base::PlatformFileError rv, bool created) {
465 if (rv == base::PLATFORM_FILE_OK && !created) {
466 callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
468 DidFinishOperation(callback, rv);
472 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
473 const StatusCallback& callback,
474 base::PlatformFileError rv, bool /* created */) {
475 DidFinishOperation(callback, rv);
478 void FileSystemOperationImpl::DidFinishOperation(
479 const StatusCallback& callback,
480 base::PlatformFileError rv) {
481 if (!cancel_callback_.is_null()) {
482 StatusCallback cancel_callback = cancel_callback_;
485 // Return OK only if we succeeded to stop the operation.
486 cancel_callback.Run(rv == base::PLATFORM_FILE_ERROR_ABORT ?
487 base::PLATFORM_FILE_OK :
488 base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
494 void FileSystemOperationImpl::DidDirectoryExists(
495 const StatusCallback& callback,
496 base::PlatformFileError rv,
497 const base::PlatformFileInfo& file_info) {
498 if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory)
499 rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
503 void FileSystemOperationImpl::DidFileExists(
504 const StatusCallback& callback,
505 base::PlatformFileError rv,
506 const base::PlatformFileInfo& file_info) {
507 if (rv == base::PLATFORM_FILE_OK && file_info.is_directory)
508 rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
512 void FileSystemOperationImpl::DidDeleteRecursively(
513 const FileSystemURL& url,
514 const StatusCallback& callback,
515 base::PlatformFileError rv) {
516 if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) {
517 // Recursive removal is not supported on this platform.
518 DCHECK(!recursive_operation_delegate_);
519 recursive_operation_delegate_.reset(
520 new RemoveOperationDelegate(
521 file_system_context(), url,
522 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
523 weak_factory_.GetWeakPtr(), callback)));
524 recursive_operation_delegate_->RunRecursively();
531 void FileSystemOperationImpl::DidWrite(
532 const FileSystemURL& url,
533 const WriteCallback& write_callback,
534 base::PlatformFileError rv,
536 FileWriterDelegate::WriteProgressStatus write_status) {
537 const bool complete = (
538 write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
539 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
540 DCHECK(operation_context_);
541 operation_context_->change_observers()->Notify(
542 &FileChangeObserver::OnModifyFile, MakeTuple(url));
545 StatusCallback cancel_callback = cancel_callback_;
546 write_callback.Run(rv, bytes, complete);
547 if (!cancel_callback.is_null())
548 cancel_callback.Run(base::PLATFORM_FILE_OK);
551 void FileSystemOperationImpl::DidOpenFile(
552 const OpenFileCallback& callback,
553 base::PlatformFileError rv,
554 base::PassPlatformFile file,
555 const base::Closure& on_close_callback) {
556 if (rv == base::PLATFORM_FILE_OK)
557 CHECK_NE(base::kNullProcessHandle, peer_handle_);
558 callback.Run(rv, file.ReleaseValue(), on_close_callback, peer_handle_);
561 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
562 if (pending_operation_ != kOperationNone)
564 pending_operation_ = type;
568 } // namespace fileapi