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. 4 5 #include "webkit/browser/fileapi/file_system_operation_impl.h" 6 7 #include "base/bind.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" 29 30 using webkit_blob::ScopedFile; 31 32 namespace fileapi { 33 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()); 40 } 41 42 FileSystemOperationImpl::~FileSystemOperationImpl() { 43 } 44 45 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url, 46 bool exclusive, 47 const StatusCallback& callback) { 48 DCHECK(SetPendingOperationType(kOperationCreateFile)); 49 GetUsageAndQuotaThenRunTask( 50 url, 51 base::Bind(&FileSystemOperationImpl::DoCreateFile, 52 weak_factory_.GetWeakPtr(), url, callback, exclusive), 53 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 54 } 55 56 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url, 57 bool exclusive, 58 bool recursive, 59 const StatusCallback& callback) { 60 DCHECK(SetPendingOperationType(kOperationCreateDirectory)); 61 GetUsageAndQuotaThenRunTask( 62 url, 63 base::Bind(&FileSystemOperationImpl::DoCreateDirectory, 64 weak_factory_.GetWeakPtr(), url, callback, 65 exclusive, recursive), 66 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 67 } 68 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_); 77 78 // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038). 79 recursive_operation_delegate_.reset( 80 new CopyOrMoveOperationDelegate( 81 file_system_context(), 82 src_url, dest_url, 83 CopyOrMoveOperationDelegate::OPERATION_COPY, 84 option, 85 progress_callback, 86 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 87 weak_factory_.GetWeakPtr(), callback))); 88 recursive_operation_delegate_->RunRecursively(); 89 } 90 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(), 100 src_url, dest_url, 101 CopyOrMoveOperationDelegate::OPERATION_MOVE, 102 option, 103 FileSystemOperation::CopyProgressCallback(), 104 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 105 weak_factory_.GetWeakPtr(), callback))); 106 recursive_operation_delegate_->RunRecursively(); 107 } 108 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)); 116 } 117 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)); 125 } 126 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); 131 } 132 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); 138 } 139 140 void FileSystemOperationImpl::Remove(const FileSystemURL& url, 141 bool recursive, 142 const StatusCallback& callback) { 143 DCHECK(SetPendingOperationType(kOperationRemove)); 144 DCHECK(!recursive_operation_delegate_); 145 146 if (recursive) { 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)); 154 return; 155 } 156 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(); 163 } 164 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( 173 blob_request.Pass(), 174 base::Bind(&FileSystemOperationImpl::DidWrite, 175 weak_factory_.GetWeakPtr(), url, callback)); 176 } 177 178 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length, 179 const StatusCallback& callback) { 180 DCHECK(SetPendingOperationType(kOperationTruncate)); 181 GetUsageAndQuotaThenRunTask( 182 url, 183 base::Bind(&FileSystemOperationImpl::DoTruncate, 184 weak_factory_.GetWeakPtr(), url, callback, length), 185 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 186 } 187 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)); 198 } 199 200 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url, 201 int file_flags, 202 const OpenFileCallback& callback) { 203 DCHECK(SetPendingOperationType(kOperationOpenFile)); 204 205 if (file_flags & 206 (base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN)) { 207 callback.Run(base::PLATFORM_FILE_ERROR_FAILED, 208 base::kInvalidPlatformFileValue, 209 base::Closure()); 210 return; 211 } 212 GetUsageAndQuotaThenRunTask( 213 url, 214 base::Bind(&FileSystemOperationImpl::DoOpenFile, 215 weak_factory_.GetWeakPtr(), 216 url, callback, file_flags), 217 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 218 base::kInvalidPlatformFileValue, 219 base::Closure())); 220 } 221 222 // We can only get here on a write or truncate that's not yet completed. 223 // We don't support cancelling any other operation at this time. 224 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) { 225 DCHECK(cancel_callback_.is_null()); 226 cancel_callback_ = cancel_callback; 227 228 if (file_writer_delegate_.get()) { 229 DCHECK_EQ(kOperationWrite, pending_operation_); 230 // This will call DidWrite() with ABORT status code. 231 file_writer_delegate_->Cancel(); 232 } else if (recursive_operation_delegate_) { 233 // This will call DidFinishOperation() with ABORT status code. 234 recursive_operation_delegate_->Cancel(); 235 } else { 236 // For truncate we have no way to cancel the inflight operation (for now). 237 // Let it just run and dispatch cancel callback later. 238 DCHECK_EQ(kOperationTruncate, pending_operation_); 239 } 240 } 241 242 void FileSystemOperationImpl::CreateSnapshotFile( 243 const FileSystemURL& url, 244 const SnapshotFileCallback& callback) { 245 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile)); 246 async_file_util_->CreateSnapshotFile( 247 operation_context_.Pass(), url, callback); 248 } 249 250 void FileSystemOperationImpl::CopyInForeignFile( 251 const base::FilePath& src_local_disk_file_path, 252 const FileSystemURL& dest_url, 253 const StatusCallback& callback) { 254 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile)); 255 GetUsageAndQuotaThenRunTask( 256 dest_url, 257 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile, 258 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url, 259 callback), 260 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 261 } 262 263 void FileSystemOperationImpl::RemoveFile( 264 const FileSystemURL& url, 265 const StatusCallback& callback) { 266 DCHECK(SetPendingOperationType(kOperationRemove)); 267 async_file_util_->DeleteFile( 268 operation_context_.Pass(), url, 269 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 270 weak_factory_.GetWeakPtr(), callback)); 271 } 272 273 void FileSystemOperationImpl::RemoveDirectory( 274 const FileSystemURL& url, 275 const StatusCallback& callback) { 276 DCHECK(SetPendingOperationType(kOperationRemove)); 277 async_file_util_->DeleteDirectory( 278 operation_context_.Pass(), url, 279 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 280 weak_factory_.GetWeakPtr(), callback)); 281 } 282 283 void FileSystemOperationImpl::CopyFileLocal( 284 const FileSystemURL& src_url, 285 const FileSystemURL& dest_url, 286 CopyOrMoveOption option, 287 const CopyFileProgressCallback& progress_callback, 288 const StatusCallback& callback) { 289 DCHECK(SetPendingOperationType(kOperationCopy)); 290 DCHECK(src_url.IsInSameFileSystem(dest_url)); 291 292 GetUsageAndQuotaThenRunTask( 293 dest_url, 294 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal, 295 weak_factory_.GetWeakPtr(), src_url, dest_url, option, 296 progress_callback, callback), 297 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 298 } 299 300 void FileSystemOperationImpl::MoveFileLocal( 301 const FileSystemURL& src_url, 302 const FileSystemURL& dest_url, 303 CopyOrMoveOption option, 304 const StatusCallback& callback) { 305 DCHECK(SetPendingOperationType(kOperationMove)); 306 DCHECK(src_url.IsInSameFileSystem(dest_url)); 307 GetUsageAndQuotaThenRunTask( 308 dest_url, 309 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal, 310 weak_factory_.GetWeakPtr(), 311 src_url, dest_url, option, callback), 312 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 313 } 314 315 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath( 316 const FileSystemURL& url, 317 base::FilePath* platform_path) { 318 DCHECK(SetPendingOperationType(kOperationGetLocalPath)); 319 if (!file_system_context()->IsSandboxFileSystem(url.type())) 320 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 321 FileSystemFileUtil* file_util = 322 file_system_context()->sandbox_delegate()->sync_file_util(); 323 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path); 324 return base::PLATFORM_FILE_OK; 325 } 326 327 FileSystemOperationImpl::FileSystemOperationImpl( 328 const FileSystemURL& url, 329 FileSystemContext* file_system_context, 330 scoped_ptr<FileSystemOperationContext> operation_context) 331 : file_system_context_(file_system_context), 332 operation_context_(operation_context.Pass()), 333 async_file_util_(NULL), 334 pending_operation_(kOperationNone), 335 weak_factory_(this) { 336 DCHECK(operation_context_.get()); 337 operation_context_->DetachUserDataThread(); 338 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type()); 339 DCHECK(async_file_util_); 340 } 341 342 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask( 343 const FileSystemURL& url, 344 const base::Closure& task, 345 const base::Closure& error_callback) { 346 quota::QuotaManagerProxy* quota_manager_proxy = 347 file_system_context()->quota_manager_proxy(); 348 if (!quota_manager_proxy || 349 !file_system_context()->GetQuotaUtil(url.type())) { 350 // If we don't have the quota manager or the requested filesystem type 351 // does not support quota, we should be able to let it go. 352 operation_context_->set_allowed_bytes_growth(kint64max); 353 task.Run(); 354 return; 355 } 356 357 DCHECK(quota_manager_proxy); 358 DCHECK(quota_manager_proxy->quota_manager()); 359 quota_manager_proxy->quota_manager()->GetUsageAndQuota( 360 url.origin(), 361 FileSystemTypeToQuotaStorageType(url.type()), 362 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask, 363 weak_factory_.GetWeakPtr(), task, error_callback)); 364 } 365 366 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask( 367 const base::Closure& task, 368 const base::Closure& error_callback, 369 quota::QuotaStatusCode status, 370 int64 usage, int64 quota) { 371 if (status != quota::kQuotaStatusOk) { 372 LOG(WARNING) << "Got unexpected quota error : " << status; 373 error_callback.Run(); 374 return; 375 } 376 377 operation_context_->set_allowed_bytes_growth(quota - usage); 378 task.Run(); 379 } 380 381 void FileSystemOperationImpl::DoCreateFile( 382 const FileSystemURL& url, 383 const StatusCallback& callback, 384 bool exclusive) { 385 async_file_util_->EnsureFileExists( 386 operation_context_.Pass(), url, 387 base::Bind( 388 exclusive ? 389 &FileSystemOperationImpl::DidEnsureFileExistsExclusive : 390 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive, 391 weak_factory_.GetWeakPtr(), callback)); 392 } 393 394 void FileSystemOperationImpl::DoCreateDirectory( 395 const FileSystemURL& url, 396 const StatusCallback& callback, 397 bool exclusive, bool recursive) { 398 async_file_util_->CreateDirectory( 399 operation_context_.Pass(), 400 url, exclusive, recursive, 401 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 402 weak_factory_.GetWeakPtr(), callback)); 403 } 404 405 void FileSystemOperationImpl::DoCopyFileLocal( 406 const FileSystemURL& src_url, 407 const FileSystemURL& dest_url, 408 CopyOrMoveOption option, 409 const CopyFileProgressCallback& progress_callback, 410 const StatusCallback& callback) { 411 async_file_util_->CopyFileLocal( 412 operation_context_.Pass(), src_url, dest_url, option, progress_callback, 413 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 414 weak_factory_.GetWeakPtr(), callback)); 415 } 416 417 void FileSystemOperationImpl::DoMoveFileLocal( 418 const FileSystemURL& src_url, 419 const FileSystemURL& dest_url, 420 CopyOrMoveOption option, 421 const StatusCallback& callback) { 422 async_file_util_->MoveFileLocal( 423 operation_context_.Pass(), src_url, dest_url, option, 424 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 425 weak_factory_.GetWeakPtr(), callback)); 426 } 427 428 void FileSystemOperationImpl::DoCopyInForeignFile( 429 const base::FilePath& src_local_disk_file_path, 430 const FileSystemURL& dest_url, 431 const StatusCallback& callback) { 432 async_file_util_->CopyInForeignFile( 433 operation_context_.Pass(), 434 src_local_disk_file_path, dest_url, 435 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 436 weak_factory_.GetWeakPtr(), callback)); 437 } 438 439 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url, 440 const StatusCallback& callback, 441 int64 length) { 442 async_file_util_->Truncate( 443 operation_context_.Pass(), url, length, 444 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 445 weak_factory_.GetWeakPtr(), callback)); 446 } 447 448 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url, 449 const OpenFileCallback& callback, 450 int file_flags) { 451 async_file_util_->CreateOrOpen( 452 operation_context_.Pass(), url, file_flags, 453 base::Bind(&FileSystemOperationImpl::DidOpenFile, 454 weak_factory_.GetWeakPtr(), callback)); 455 } 456 457 void FileSystemOperationImpl::DidEnsureFileExistsExclusive( 458 const StatusCallback& callback, 459 base::PlatformFileError rv, bool created) { 460 if (rv == base::PLATFORM_FILE_OK && !created) { 461 callback.Run(base::PLATFORM_FILE_ERROR_EXISTS); 462 } else { 463 DidFinishOperation(callback, rv); 464 } 465 } 466 467 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive( 468 const StatusCallback& callback, 469 base::PlatformFileError rv, bool /* created */) { 470 DidFinishOperation(callback, rv); 471 } 472 473 void FileSystemOperationImpl::DidFinishOperation( 474 const StatusCallback& callback, 475 base::PlatformFileError rv) { 476 if (!cancel_callback_.is_null()) { 477 StatusCallback cancel_callback = cancel_callback_; 478 callback.Run(rv); 479 480 // Return OK only if we succeeded to stop the operation. 481 cancel_callback.Run(rv == base::PLATFORM_FILE_ERROR_ABORT ? 482 base::PLATFORM_FILE_OK : 483 base::PLATFORM_FILE_ERROR_INVALID_OPERATION); 484 } else { 485 callback.Run(rv); 486 } 487 } 488 489 void FileSystemOperationImpl::DidDirectoryExists( 490 const StatusCallback& callback, 491 base::PlatformFileError rv, 492 const base::PlatformFileInfo& file_info) { 493 if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory) 494 rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 495 callback.Run(rv); 496 } 497 498 void FileSystemOperationImpl::DidFileExists( 499 const StatusCallback& callback, 500 base::PlatformFileError rv, 501 const base::PlatformFileInfo& file_info) { 502 if (rv == base::PLATFORM_FILE_OK && file_info.is_directory) 503 rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE; 504 callback.Run(rv); 505 } 506 507 void FileSystemOperationImpl::DidDeleteRecursively( 508 const FileSystemURL& url, 509 const StatusCallback& callback, 510 base::PlatformFileError rv) { 511 if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) { 512 // Recursive removal is not supported on this platform. 513 DCHECK(!recursive_operation_delegate_); 514 recursive_operation_delegate_.reset( 515 new RemoveOperationDelegate( 516 file_system_context(), url, 517 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 518 weak_factory_.GetWeakPtr(), callback))); 519 recursive_operation_delegate_->RunRecursively(); 520 return; 521 } 522 523 callback.Run(rv); 524 } 525 526 void FileSystemOperationImpl::DidWrite( 527 const FileSystemURL& url, 528 const WriteCallback& write_callback, 529 base::PlatformFileError rv, 530 int64 bytes, 531 FileWriterDelegate::WriteProgressStatus write_status) { 532 const bool complete = ( 533 write_status != FileWriterDelegate::SUCCESS_IO_PENDING); 534 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) { 535 DCHECK(operation_context_); 536 operation_context_->change_observers()->Notify( 537 &FileChangeObserver::OnModifyFile, MakeTuple(url)); 538 } 539 540 StatusCallback cancel_callback = cancel_callback_; 541 write_callback.Run(rv, bytes, complete); 542 if (!cancel_callback.is_null()) 543 cancel_callback.Run(base::PLATFORM_FILE_OK); 544 } 545 546 void FileSystemOperationImpl::DidOpenFile( 547 const OpenFileCallback& callback, 548 base::PlatformFileError rv, 549 base::PassPlatformFile file, 550 const base::Closure& on_close_callback) { 551 callback.Run(rv, file.ReleaseValue(), on_close_callback); 552 } 553 554 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) { 555 if (pending_operation_ != kOperationNone) 556 return false; 557 pending_operation_ = type; 558 return true; 559 } 560 561 } // namespace fileapi 562