| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "webkit/browser/fileapi/local_file_system_operation.h" |
| |
| #include "base/bind.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time.h" |
| #include "net/base/escape.h" |
| #include "net/url_request/url_request_context.h" |
| #include "webkit/browser/fileapi/async_file_util.h" |
| #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h" |
| #include "webkit/browser/fileapi/file_observers.h" |
| #include "webkit/browser/fileapi/file_system_context.h" |
| #include "webkit/browser/fileapi/file_system_file_util.h" |
| #include "webkit/browser/fileapi/file_system_mount_point_provider.h" |
| #include "webkit/browser/fileapi/file_system_operation_context.h" |
| #include "webkit/browser/fileapi/file_system_task_runners.h" |
| #include "webkit/browser/fileapi/file_system_url.h" |
| #include "webkit/browser/fileapi/file_writer_delegate.h" |
| #include "webkit/browser/fileapi/remove_operation_delegate.h" |
| #include "webkit/browser/fileapi/sandbox_file_stream_writer.h" |
| #include "webkit/browser/quota/quota_manager.h" |
| #include "webkit/common/blob/shareable_file_reference.h" |
| #include "webkit/common/fileapi/file_system_types.h" |
| #include "webkit/common/fileapi/file_system_util.h" |
| #include "webkit/common/quota/quota_types.h" |
| |
| using webkit_blob::ScopedFile; |
| using webkit_blob::ShareableFileReference; |
| |
| namespace fileapi { |
| |
| namespace { |
| void NopCloseFileCallback() {} |
| } |
| |
| LocalFileSystemOperation::LocalFileSystemOperation( |
| FileSystemContext* file_system_context, |
| scoped_ptr<FileSystemOperationContext> operation_context) |
| : file_system_context_(file_system_context), |
| operation_context_(operation_context.Pass()), |
| async_file_util_(NULL), |
| peer_handle_(base::kNullProcessHandle), |
| pending_operation_(kOperationNone), |
| weak_factory_(this) { |
| DCHECK(operation_context_.get()); |
| operation_context_->DetachUserDataThread(); |
| } |
| |
| LocalFileSystemOperation::~LocalFileSystemOperation() { |
| for (FileSystemURLSet::iterator iter = write_target_url_.begin(); |
| iter != write_target_url_.end(); ++iter) { |
| if (file_system_context_->GetUpdateObservers(iter->type())) { |
| file_system_context_->GetUpdateObservers(iter->type())->Notify( |
| &FileUpdateObserver::OnEndUpdate, MakeTuple(*iter)); |
| } |
| } |
| } |
| |
| void LocalFileSystemOperation::CreateFile(const FileSystemURL& url, |
| bool exclusive, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCreateFile)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| GetUsageAndQuotaThenRunTask( |
| url, |
| base::Bind(&LocalFileSystemOperation::DoCreateFile, |
| base::Unretained(this), url, callback, exclusive), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::CreateDirectory(const FileSystemURL& url, |
| bool exclusive, |
| bool recursive, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCreateDirectory)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| GetUsageAndQuotaThenRunTask( |
| url, |
| base::Bind(&LocalFileSystemOperation::DoCreateDirectory, |
| base::Unretained(this), url, callback, exclusive, recursive), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::Copy(const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCopy)); |
| |
| // Setting up this (dest) operation. |
| base::PlatformFileError result = SetUp(dest_url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| // Notify access_observer of access on src_url. |
| if (file_system_context_->GetAccessObservers(src_url.type())) { |
| file_system_context_->GetAccessObservers(src_url.type())->Notify( |
| &FileAccessObserver::OnAccess, MakeTuple(src_url)); |
| } |
| |
| DCHECK(!recursive_operation_delegate_); |
| recursive_operation_delegate_.reset( |
| new CopyOrMoveOperationDelegate( |
| file_system_context(), |
| src_url, dest_url, |
| CopyOrMoveOperationDelegate::OPERATION_COPY, |
| base::Bind(&LocalFileSystemOperation::DidFinishDelegatedOperation, |
| base::Unretained(this), callback))); |
| recursive_operation_delegate_->RunRecursively(); |
| } |
| |
| void LocalFileSystemOperation::Move(const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationMove)); |
| |
| // Setting up this (dest) operation. |
| base::PlatformFileError result = SetUp(dest_url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| // Notify update_observer of write access on src_url. |
| if (file_system_context_->GetUpdateObservers(src_url.type())) { |
| file_system_context_->GetUpdateObservers(src_url.type())->Notify( |
| &FileUpdateObserver::OnStartUpdate, MakeTuple(src_url)); |
| } |
| write_target_url_.insert(src_url); |
| |
| DCHECK(!recursive_operation_delegate_); |
| recursive_operation_delegate_.reset( |
| new CopyOrMoveOperationDelegate( |
| file_system_context(), |
| src_url, dest_url, |
| CopyOrMoveOperationDelegate::OPERATION_MOVE, |
| base::Bind(&LocalFileSystemOperation::DidFinishDelegatedOperation, |
| base::Unretained(this), callback))); |
| recursive_operation_delegate_->RunRecursively(); |
| } |
| |
| void LocalFileSystemOperation::DirectoryExists(const FileSystemURL& url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationDirectoryExists)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->GetFileInfo( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidDirectoryExists, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::FileExists(const FileSystemURL& url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationFileExists)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->GetFileInfo( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidFileExists, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::GetMetadata( |
| const FileSystemURL& url, const GetMetadataCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationGetMetadata)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result, base::PlatformFileInfo(), base::FilePath()); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->GetFileInfo( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidGetMetadata, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::ReadDirectory( |
| const FileSystemURL& url, const ReadDirectoryCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationReadDirectory)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result, std::vector<DirectoryEntry>(), false); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->ReadDirectory( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidReadDirectory, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::Remove(const FileSystemURL& url, |
| bool recursive, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationRemove)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| DCHECK(!recursive_operation_delegate_); |
| recursive_operation_delegate_.reset( |
| new RemoveOperationDelegate( |
| file_system_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidFinishDelegatedOperation, |
| base::Unretained(this), callback))); |
| if (recursive) |
| recursive_operation_delegate_->RunRecursively(); |
| else |
| recursive_operation_delegate_->Run(); |
| } |
| |
| void LocalFileSystemOperation::Write( |
| const net::URLRequestContext* url_request_context, |
| const FileSystemURL& url, |
| const GURL& blob_url, |
| int64 offset, |
| const WriteCallback& callback) { |
| GetWriteClosure(url_request_context, url, blob_url, offset, callback).Run(); |
| } |
| |
| void LocalFileSystemOperation::Truncate(const FileSystemURL& url, int64 length, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationTruncate)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| GetUsageAndQuotaThenRunTask( |
| url, |
| base::Bind(&LocalFileSystemOperation::DoTruncate, |
| base::Unretained(this), url, callback, length), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::TouchFile(const FileSystemURL& url, |
| const base::Time& last_access_time, |
| const base::Time& last_modified_time, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationTouchFile)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->Touch( |
| operation_context(), url, |
| last_access_time, last_modified_time, |
| base::Bind(&LocalFileSystemOperation::DidTouchFile, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::OpenFile(const FileSystemURL& url, |
| int file_flags, |
| base::ProcessHandle peer_handle, |
| const OpenFileCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationOpenFile)); |
| scoped_ptr<LocalFileSystemOperation> deleter(this); |
| |
| peer_handle_ = peer_handle; |
| |
| if (file_flags & ( |
| (base::PLATFORM_FILE_ENUMERATE | base::PLATFORM_FILE_TEMPORARY | |
| base::PLATFORM_FILE_HIDDEN))) { |
| callback.Run(base::PLATFORM_FILE_ERROR_FAILED, |
| base::kInvalidPlatformFileValue, |
| base::Closure(), |
| base::kNullProcessHandle); |
| return; |
| } |
| if (file_flags & |
| (base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_OPEN_ALWAYS | |
| base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_TRUNCATED | |
| base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE | |
| base::PLATFORM_FILE_DELETE_ON_CLOSE | |
| base::PLATFORM_FILE_WRITE_ATTRIBUTES)) { |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result, |
| base::kInvalidPlatformFileValue, |
| base::Closure(), |
| base::kNullProcessHandle); |
| return; |
| } |
| } else { |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result, |
| base::kInvalidPlatformFileValue, |
| base::Closure(), |
| base::kNullProcessHandle); |
| return; |
| } |
| } |
| GetUsageAndQuotaThenRunTask( |
| url, |
| base::Bind(&LocalFileSystemOperation::DoOpenFile, |
| base::Unretained(deleter.release()), |
| url, callback, file_flags), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, |
| base::kInvalidPlatformFileValue, |
| base::Closure(), |
| base::kNullProcessHandle)); |
| } |
| |
| // We can only get here on a write or truncate that's not yet completed. |
| // We don't support cancelling any other operation at this time. |
| void LocalFileSystemOperation::Cancel(const StatusCallback& cancel_callback) { |
| if (file_writer_delegate_) { |
| DCHECK_EQ(kOperationWrite, pending_operation_); |
| |
| // Writes are done without proxying through FileUtilProxy after the initial |
| // opening of the PlatformFile. All state changes are done on this thread, |
| // so we're guaranteed to be able to shut down atomically. |
| const bool delete_now = file_writer_delegate_->Cancel(); |
| |
| if (!write_callback_.is_null()) { |
| // Notify the failure status to the ongoing operation's callback. |
| write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0, false); |
| } |
| cancel_callback.Run(base::PLATFORM_FILE_OK); |
| write_callback_.Reset(); |
| |
| if (delete_now) { |
| delete this; |
| return; |
| } |
| } else { |
| DCHECK_EQ(kOperationTruncate, pending_operation_); |
| // We're cancelling a truncate operation, but we can't actually stop it |
| // since it's been proxied to another thread. We need to save the |
| // cancel_callback so that when the truncate returns, it can see that it's |
| // been cancelled, report it, and report that the cancel has succeeded. |
| DCHECK(cancel_callback_.is_null()); |
| cancel_callback_ = cancel_callback; |
| } |
| } |
| |
| LocalFileSystemOperation* |
| LocalFileSystemOperation::AsLocalFileSystemOperation() { |
| return this; |
| } |
| |
| void LocalFileSystemOperation::SyncGetPlatformPath(const FileSystemURL& url, |
| base::FilePath* platform_path) { |
| DCHECK(SetPendingOperationType(kOperationGetLocalPath)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| delete this; |
| return; |
| } |
| |
| FileSystemFileUtil* file_util = file_system_context()->GetFileUtil( |
| url.type()); |
| DCHECK(file_util); |
| file_util->GetLocalFilePath(operation_context(), url, platform_path); |
| |
| delete this; |
| } |
| |
| void LocalFileSystemOperation::CreateSnapshotFile( |
| const FileSystemURL& url, |
| const SnapshotFileCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_READ); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result, base::PlatformFileInfo(), base::FilePath(), NULL); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->CreateSnapshotFile( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidCreateSnapshotFile, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::CopyInForeignFile( |
| const base::FilePath& src_local_disk_file_path, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCopyInForeignFile)); |
| |
| base::PlatformFileError result = SetUp(dest_url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| GetUsageAndQuotaThenRunTask( |
| dest_url, |
| base::Bind(&LocalFileSystemOperation::DoCopyInForeignFile, |
| base::Unretained(this), src_local_disk_file_path, dest_url, |
| callback), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::RemoveFile( |
| const FileSystemURL& url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationRemove)); |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_NESTED); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->DeleteFile( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::RemoveDirectory( |
| const FileSystemURL& url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationRemove)); |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_NESTED); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| async_file_util_->DeleteDirectory( |
| operation_context(), url, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::CopyFileLocal( |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationCopy)); |
| DCHECK(src_url.IsInSameFileSystem(dest_url)); |
| |
| base::PlatformFileError result = SetUp(src_url, OPERATION_MODE_NESTED); |
| if (result == base::PLATFORM_FILE_OK) |
| result = SetUp(dest_url, OPERATION_MODE_NESTED); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| GetUsageAndQuotaThenRunTask( |
| dest_url, |
| base::Bind(&LocalFileSystemOperation::DoCopyFileLocal, |
| base::Unretained(this), src_url, dest_url, callback), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::MoveFileLocal( |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationMove)); |
| DCHECK(src_url.IsInSameFileSystem(dest_url)); |
| |
| base::PlatformFileError result = SetUp(src_url, OPERATION_MODE_NESTED); |
| if (result == base::PLATFORM_FILE_OK) |
| result = SetUp(dest_url, OPERATION_MODE_NESTED); |
| if (result != base::PLATFORM_FILE_OK) { |
| callback.Run(result); |
| delete this; |
| return; |
| } |
| |
| GetUsageAndQuotaThenRunTask( |
| dest_url, |
| base::Bind(&LocalFileSystemOperation::DoMoveFileLocal, |
| base::Unretained(this), src_url, dest_url, callback), |
| base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); |
| } |
| |
| void LocalFileSystemOperation::GetUsageAndQuotaThenRunTask( |
| const FileSystemURL& url, |
| const base::Closure& task, |
| const base::Closure& error_callback) { |
| quota::QuotaManagerProxy* quota_manager_proxy = |
| file_system_context()->quota_manager_proxy(); |
| if (!quota_manager_proxy || |
| !file_system_context()->GetQuotaUtil(url.type())) { |
| // If we don't have the quota manager or the requested filesystem type |
| // does not support quota, we should be able to let it go. |
| operation_context()->set_allowed_bytes_growth(kint64max); |
| task.Run(); |
| return; |
| } |
| |
| DCHECK(quota_manager_proxy); |
| DCHECK(quota_manager_proxy->quota_manager()); |
| quota_manager_proxy->quota_manager()->GetUsageAndQuota( |
| url.origin(), |
| FileSystemTypeToQuotaStorageType(url.type()), |
| base::Bind(&LocalFileSystemOperation::DidGetUsageAndQuotaAndRunTask, |
| weak_factory_.GetWeakPtr(), task, error_callback)); |
| } |
| |
| void LocalFileSystemOperation::DidGetUsageAndQuotaAndRunTask( |
| const base::Closure& task, |
| const base::Closure& error_callback, |
| quota::QuotaStatusCode status, |
| int64 usage, int64 quota) { |
| if (status != quota::kQuotaStatusOk) { |
| LOG(WARNING) << "Got unexpected quota error : " << status; |
| error_callback.Run(); |
| return; |
| } |
| |
| operation_context()->set_allowed_bytes_growth(quota - usage); |
| task.Run(); |
| } |
| |
| base::Closure LocalFileSystemOperation::GetWriteClosure( |
| const net::URLRequestContext* url_request_context, |
| const FileSystemURL& url, |
| const GURL& blob_url, |
| int64 offset, |
| const WriteCallback& callback) { |
| DCHECK(SetPendingOperationType(kOperationWrite)); |
| |
| base::PlatformFileError result = SetUp(url, OPERATION_MODE_WRITE); |
| if (result != base::PLATFORM_FILE_OK) { |
| return base::Bind(&LocalFileSystemOperation::DidFailWrite, |
| base::Owned(this), callback, result); |
| } |
| |
| scoped_ptr<FileStreamWriter> writer( |
| file_system_context()->CreateFileStreamWriter(url, offset)); |
| |
| if (!writer) { |
| // Write is not supported. |
| return base::Bind(&LocalFileSystemOperation::DidFailWrite, |
| base::Owned(this), callback, |
| base::PLATFORM_FILE_ERROR_SECURITY); |
| } |
| |
| DCHECK(blob_url.is_valid()); |
| file_writer_delegate_.reset(new FileWriterDelegate( |
| base::Bind(&LocalFileSystemOperation::DidWrite, |
| weak_factory_.GetWeakPtr(), url), |
| writer.Pass())); |
| |
| write_callback_ = callback; |
| scoped_ptr<net::URLRequest> blob_request(url_request_context->CreateRequest( |
| blob_url, file_writer_delegate_.get())); |
| |
| return base::Bind(&FileWriterDelegate::Start, |
| base::Unretained(file_writer_delegate_.get()), |
| base::Passed(&blob_request)); |
| } |
| |
| void LocalFileSystemOperation::DidFailWrite( |
| const WriteCallback& callback, |
| base::PlatformFileError result) { |
| callback.Run(result, 0, false); |
| } |
| |
| void LocalFileSystemOperation::DoCreateFile( |
| const FileSystemURL& url, |
| const StatusCallback& callback, |
| bool exclusive) { |
| async_file_util_->EnsureFileExists( |
| operation_context(), url, |
| base::Bind( |
| exclusive ? |
| &LocalFileSystemOperation::DidEnsureFileExistsExclusive : |
| &LocalFileSystemOperation::DidEnsureFileExistsNonExclusive, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoCreateDirectory( |
| const FileSystemURL& url, |
| const StatusCallback& callback, |
| bool exclusive, bool recursive) { |
| async_file_util_->CreateDirectory( |
| operation_context(), |
| url, exclusive, recursive, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoCopyFileLocal( |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| async_file_util_->CopyFileLocal( |
| operation_context(), src_url, dest_url, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoMoveFileLocal( |
| const FileSystemURL& src_url, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| async_file_util_->MoveFileLocal( |
| operation_context(), src_url, dest_url, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoCopyInForeignFile( |
| const base::FilePath& src_local_disk_file_path, |
| const FileSystemURL& dest_url, |
| const StatusCallback& callback) { |
| async_file_util_->CopyInForeignFile( |
| operation_context(), |
| src_local_disk_file_path, dest_url, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoTruncate(const FileSystemURL& url, |
| const StatusCallback& callback, |
| int64 length) { |
| async_file_util_->Truncate( |
| operation_context(), url, length, |
| base::Bind(&LocalFileSystemOperation::DidFinishFileOperation, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DoOpenFile(const FileSystemURL& url, |
| const OpenFileCallback& callback, |
| int file_flags) { |
| async_file_util_->CreateOrOpen( |
| operation_context(), url, file_flags, |
| base::Bind(&LocalFileSystemOperation::DidOpenFile, |
| base::Owned(this), callback)); |
| } |
| |
| void LocalFileSystemOperation::DidEnsureFileExistsExclusive( |
| const StatusCallback& callback, |
| base::PlatformFileError rv, bool created) { |
| if (rv == base::PLATFORM_FILE_OK && !created) { |
| callback.Run(base::PLATFORM_FILE_ERROR_EXISTS); |
| } else { |
| DidFinishFileOperation(callback, rv); |
| } |
| } |
| |
| void LocalFileSystemOperation::DidEnsureFileExistsNonExclusive( |
| const StatusCallback& callback, |
| base::PlatformFileError rv, bool /* created */) { |
| DidFinishFileOperation(callback, rv); |
| } |
| |
| void LocalFileSystemOperation::DidFinishFileOperation( |
| const StatusCallback& callback, |
| base::PlatformFileError rv) { |
| if (!cancel_callback_.is_null()) { |
| DCHECK_EQ(kOperationTruncate, pending_operation_); |
| |
| callback.Run(base::PLATFORM_FILE_ERROR_ABORT); |
| cancel_callback_.Run(base::PLATFORM_FILE_OK); |
| cancel_callback_.Reset(); |
| } else { |
| callback.Run(rv); |
| } |
| } |
| |
| void LocalFileSystemOperation::DidFinishDelegatedOperation( |
| const StatusCallback& callback, |
| base::PlatformFileError rv) { |
| // The callback might be held by the delegate and Owned() may not work, |
| // so just explicitly delete this now. |
| callback.Run(rv); |
| delete this; |
| } |
| |
| void LocalFileSystemOperation::DidDirectoryExists( |
| const StatusCallback& callback, |
| base::PlatformFileError rv, |
| const base::PlatformFileInfo& file_info, |
| const base::FilePath& unused) { |
| if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory) |
| rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; |
| callback.Run(rv); |
| } |
| |
| void LocalFileSystemOperation::DidFileExists( |
| const StatusCallback& callback, |
| base::PlatformFileError rv, |
| const base::PlatformFileInfo& file_info, |
| const base::FilePath& unused) { |
| if (rv == base::PLATFORM_FILE_OK && file_info.is_directory) |
| rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE; |
| callback.Run(rv); |
| } |
| |
| void LocalFileSystemOperation::DidGetMetadata( |
| const GetMetadataCallback& callback, |
| base::PlatformFileError rv, |
| const base::PlatformFileInfo& file_info, |
| const base::FilePath& platform_path) { |
| callback.Run(rv, file_info, platform_path); |
| } |
| |
| void LocalFileSystemOperation::DidReadDirectory( |
| const ReadDirectoryCallback& callback, |
| base::PlatformFileError rv, |
| const std::vector<DirectoryEntry>& entries, |
| bool has_more) { |
| callback.Run(rv, entries, has_more); |
| } |
| |
| void LocalFileSystemOperation::DidWrite( |
| const FileSystemURL& url, |
| base::PlatformFileError rv, |
| int64 bytes, |
| FileWriterDelegate::WriteProgressStatus write_status) { |
| if (write_callback_.is_null()) { |
| // If cancelled, callback is already invoked and set to null in Cancel(). |
| // We must not call it twice. Just shut down this operation object. |
| delete this; |
| return; |
| } |
| |
| const bool complete = ( |
| write_status != FileWriterDelegate::SUCCESS_IO_PENDING); |
| if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) { |
| operation_context()->change_observers()->Notify( |
| &FileChangeObserver::OnModifyFile, MakeTuple(url)); |
| } |
| |
| write_callback_.Run(rv, bytes, complete); |
| if (complete || rv != base::PLATFORM_FILE_OK) |
| delete this; |
| } |
| |
| void LocalFileSystemOperation::DidTouchFile(const StatusCallback& callback, |
| base::PlatformFileError rv) { |
| callback.Run(rv); |
| } |
| |
| void LocalFileSystemOperation::DidOpenFile( |
| const OpenFileCallback& callback, |
| base::PlatformFileError rv, |
| base::PassPlatformFile file, |
| bool unused) { |
| if (rv == base::PLATFORM_FILE_OK) |
| CHECK_NE(base::kNullProcessHandle, peer_handle_); |
| callback.Run(rv, file.ReleaseValue(), |
| base::Bind(&NopCloseFileCallback), |
| peer_handle_); |
| } |
| |
| void LocalFileSystemOperation::DidCreateSnapshotFile( |
| const SnapshotFileCallback& callback, |
| base::PlatformFileError result, |
| const base::PlatformFileInfo& file_info, |
| const base::FilePath& platform_path, |
| const scoped_refptr<ShareableFileReference>& file_ref) { |
| callback.Run(result, file_info, platform_path, file_ref); |
| } |
| |
| base::PlatformFileError LocalFileSystemOperation::SetUp( |
| const FileSystemURL& url, |
| OperationMode mode) { |
| DCHECK(url.is_valid()); |
| |
| async_file_util_ = file_system_context()->GetAsyncFileUtil(url.type()); |
| if (!async_file_util_) |
| return base::PLATFORM_FILE_ERROR_SECURITY; |
| |
| switch (mode) { |
| case OPERATION_MODE_READ: |
| if (file_system_context_->GetAccessObservers(url.type())) { |
| file_system_context_->GetAccessObservers(url.type())->Notify( |
| &FileAccessObserver::OnAccess, MakeTuple(url)); |
| } |
| break; |
| case OPERATION_MODE_WRITE: |
| if (file_system_context_->GetUpdateObservers(url.type())) { |
| file_system_context_->GetUpdateObservers(url.type())->Notify( |
| &FileUpdateObserver::OnStartUpdate, MakeTuple(url)); |
| } |
| write_target_url_.insert(url); |
| break; |
| case OPERATION_MODE_NESTED: |
| break; |
| } |
| |
| return base::PLATFORM_FILE_OK; |
| } |
| |
| bool LocalFileSystemOperation::SetPendingOperationType(OperationType type) { |
| if (pending_operation_ != kOperationNone) |
| return false; |
| pending_operation_ = type; |
| return true; |
| } |
| |
| } // namespace fileapi |