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_proxy.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::File::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::File::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::File::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::File::FLAG_TEMPORARY | base::File::FLAG_HIDDEN)) { 207 callback.Run(base::File(base::File::FILE_ERROR_FAILED), 208 base::Closure()); 209 return; 210 } 211 GetUsageAndQuotaThenRunTask( 212 url, 213 base::Bind(&FileSystemOperationImpl::DoOpenFile, 214 weak_factory_.GetWeakPtr(), 215 url, callback, file_flags), 216 base::Bind(callback, Passed(base::File(base::File::FILE_ERROR_FAILED)), 217 base::Closure())); 218 } 219 220 // We can only get here on a write or truncate that's not yet completed. 221 // We don't support cancelling any other operation at this time. 222 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) { 223 DCHECK(cancel_callback_.is_null()); 224 cancel_callback_ = cancel_callback; 225 226 if (file_writer_delegate_.get()) { 227 DCHECK_EQ(kOperationWrite, pending_operation_); 228 // This will call DidWrite() with ABORT status code. 229 file_writer_delegate_->Cancel(); 230 } else if (recursive_operation_delegate_) { 231 // This will call DidFinishOperation() with ABORT status code. 232 recursive_operation_delegate_->Cancel(); 233 } else { 234 // For truncate we have no way to cancel the inflight operation (for now). 235 // Let it just run and dispatch cancel callback later. 236 DCHECK_EQ(kOperationTruncate, pending_operation_); 237 } 238 } 239 240 void FileSystemOperationImpl::CreateSnapshotFile( 241 const FileSystemURL& url, 242 const SnapshotFileCallback& callback) { 243 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile)); 244 async_file_util_->CreateSnapshotFile( 245 operation_context_.Pass(), url, callback); 246 } 247 248 void FileSystemOperationImpl::CopyInForeignFile( 249 const base::FilePath& src_local_disk_file_path, 250 const FileSystemURL& dest_url, 251 const StatusCallback& callback) { 252 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile)); 253 GetUsageAndQuotaThenRunTask( 254 dest_url, 255 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile, 256 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url, 257 callback), 258 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 259 } 260 261 void FileSystemOperationImpl::RemoveFile( 262 const FileSystemURL& url, 263 const StatusCallback& callback) { 264 DCHECK(SetPendingOperationType(kOperationRemove)); 265 async_file_util_->DeleteFile( 266 operation_context_.Pass(), url, 267 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 268 weak_factory_.GetWeakPtr(), callback)); 269 } 270 271 void FileSystemOperationImpl::RemoveDirectory( 272 const FileSystemURL& url, 273 const StatusCallback& callback) { 274 DCHECK(SetPendingOperationType(kOperationRemove)); 275 async_file_util_->DeleteDirectory( 276 operation_context_.Pass(), url, 277 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 278 weak_factory_.GetWeakPtr(), callback)); 279 } 280 281 void FileSystemOperationImpl::CopyFileLocal( 282 const FileSystemURL& src_url, 283 const FileSystemURL& dest_url, 284 CopyOrMoveOption option, 285 const CopyFileProgressCallback& progress_callback, 286 const StatusCallback& callback) { 287 DCHECK(SetPendingOperationType(kOperationCopy)); 288 DCHECK(src_url.IsInSameFileSystem(dest_url)); 289 290 GetUsageAndQuotaThenRunTask( 291 dest_url, 292 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal, 293 weak_factory_.GetWeakPtr(), src_url, dest_url, option, 294 progress_callback, callback), 295 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 296 } 297 298 void FileSystemOperationImpl::MoveFileLocal( 299 const FileSystemURL& src_url, 300 const FileSystemURL& dest_url, 301 CopyOrMoveOption option, 302 const StatusCallback& callback) { 303 DCHECK(SetPendingOperationType(kOperationMove)); 304 DCHECK(src_url.IsInSameFileSystem(dest_url)); 305 GetUsageAndQuotaThenRunTask( 306 dest_url, 307 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal, 308 weak_factory_.GetWeakPtr(), 309 src_url, dest_url, option, callback), 310 base::Bind(callback, base::File::FILE_ERROR_FAILED)); 311 } 312 313 base::File::Error FileSystemOperationImpl::SyncGetPlatformPath( 314 const FileSystemURL& url, 315 base::FilePath* platform_path) { 316 DCHECK(SetPendingOperationType(kOperationGetLocalPath)); 317 if (!file_system_context()->IsSandboxFileSystem(url.type())) 318 return base::File::FILE_ERROR_INVALID_OPERATION; 319 FileSystemFileUtil* file_util = 320 file_system_context()->sandbox_delegate()->sync_file_util(); 321 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path); 322 return base::File::FILE_OK; 323 } 324 325 FileSystemOperationImpl::FileSystemOperationImpl( 326 const FileSystemURL& url, 327 FileSystemContext* file_system_context, 328 scoped_ptr<FileSystemOperationContext> operation_context) 329 : file_system_context_(file_system_context), 330 operation_context_(operation_context.Pass()), 331 async_file_util_(NULL), 332 pending_operation_(kOperationNone), 333 weak_factory_(this) { 334 DCHECK(operation_context_.get()); 335 operation_context_->DetachUserDataThread(); 336 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type()); 337 DCHECK(async_file_util_); 338 } 339 340 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask( 341 const FileSystemURL& url, 342 const base::Closure& task, 343 const base::Closure& error_callback) { 344 quota::QuotaManagerProxy* quota_manager_proxy = 345 file_system_context()->quota_manager_proxy(); 346 if (!quota_manager_proxy || 347 !file_system_context()->GetQuotaUtil(url.type())) { 348 // If we don't have the quota manager or the requested filesystem type 349 // does not support quota, we should be able to let it go. 350 operation_context_->set_allowed_bytes_growth(kint64max); 351 task.Run(); 352 return; 353 } 354 355 DCHECK(quota_manager_proxy); 356 DCHECK(quota_manager_proxy->quota_manager()); 357 quota_manager_proxy->quota_manager()->GetUsageAndQuota( 358 url.origin(), 359 FileSystemTypeToQuotaStorageType(url.type()), 360 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask, 361 weak_factory_.GetWeakPtr(), task, error_callback)); 362 } 363 364 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask( 365 const base::Closure& task, 366 const base::Closure& error_callback, 367 quota::QuotaStatusCode status, 368 int64 usage, int64 quota) { 369 if (status != quota::kQuotaStatusOk) { 370 LOG(WARNING) << "Got unexpected quota error : " << status; 371 error_callback.Run(); 372 return; 373 } 374 375 operation_context_->set_allowed_bytes_growth(quota - usage); 376 task.Run(); 377 } 378 379 void FileSystemOperationImpl::DoCreateFile( 380 const FileSystemURL& url, 381 const StatusCallback& callback, 382 bool exclusive) { 383 async_file_util_->EnsureFileExists( 384 operation_context_.Pass(), url, 385 base::Bind( 386 exclusive ? 387 &FileSystemOperationImpl::DidEnsureFileExistsExclusive : 388 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive, 389 weak_factory_.GetWeakPtr(), callback)); 390 } 391 392 void FileSystemOperationImpl::DoCreateDirectory( 393 const FileSystemURL& url, 394 const StatusCallback& callback, 395 bool exclusive, bool recursive) { 396 async_file_util_->CreateDirectory( 397 operation_context_.Pass(), 398 url, exclusive, recursive, 399 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 400 weak_factory_.GetWeakPtr(), callback)); 401 } 402 403 void FileSystemOperationImpl::DoCopyFileLocal( 404 const FileSystemURL& src_url, 405 const FileSystemURL& dest_url, 406 CopyOrMoveOption option, 407 const CopyFileProgressCallback& progress_callback, 408 const StatusCallback& callback) { 409 async_file_util_->CopyFileLocal( 410 operation_context_.Pass(), src_url, dest_url, option, progress_callback, 411 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 412 weak_factory_.GetWeakPtr(), callback)); 413 } 414 415 void FileSystemOperationImpl::DoMoveFileLocal( 416 const FileSystemURL& src_url, 417 const FileSystemURL& dest_url, 418 CopyOrMoveOption option, 419 const StatusCallback& callback) { 420 async_file_util_->MoveFileLocal( 421 operation_context_.Pass(), src_url, dest_url, option, 422 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 423 weak_factory_.GetWeakPtr(), callback)); 424 } 425 426 void FileSystemOperationImpl::DoCopyInForeignFile( 427 const base::FilePath& src_local_disk_file_path, 428 const FileSystemURL& dest_url, 429 const StatusCallback& callback) { 430 async_file_util_->CopyInForeignFile( 431 operation_context_.Pass(), 432 src_local_disk_file_path, dest_url, 433 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 434 weak_factory_.GetWeakPtr(), callback)); 435 } 436 437 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url, 438 const StatusCallback& callback, 439 int64 length) { 440 async_file_util_->Truncate( 441 operation_context_.Pass(), url, length, 442 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 443 weak_factory_.GetWeakPtr(), callback)); 444 } 445 446 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url, 447 const OpenFileCallback& callback, 448 int file_flags) { 449 async_file_util_->CreateOrOpen( 450 operation_context_.Pass(), url, file_flags, 451 base::Bind(&FileSystemOperationImpl::DidOpenFile, 452 weak_factory_.GetWeakPtr(), callback)); 453 } 454 455 void FileSystemOperationImpl::DidEnsureFileExistsExclusive( 456 const StatusCallback& callback, 457 base::File::Error rv, bool created) { 458 if (rv == base::File::FILE_OK && !created) { 459 callback.Run(base::File::FILE_ERROR_EXISTS); 460 } else { 461 DidFinishOperation(callback, rv); 462 } 463 } 464 465 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive( 466 const StatusCallback& callback, 467 base::File::Error rv, bool /* created */) { 468 DidFinishOperation(callback, rv); 469 } 470 471 void FileSystemOperationImpl::DidFinishOperation( 472 const StatusCallback& callback, 473 base::File::Error rv) { 474 if (!cancel_callback_.is_null()) { 475 StatusCallback cancel_callback = cancel_callback_; 476 callback.Run(rv); 477 478 // Return OK only if we succeeded to stop the operation. 479 cancel_callback.Run(rv == base::File::FILE_ERROR_ABORT ? 480 base::File::FILE_OK : 481 base::File::FILE_ERROR_INVALID_OPERATION); 482 } else { 483 callback.Run(rv); 484 } 485 } 486 487 void FileSystemOperationImpl::DidDirectoryExists( 488 const StatusCallback& callback, 489 base::File::Error rv, 490 const base::File::Info& file_info) { 491 if (rv == base::File::FILE_OK && !file_info.is_directory) 492 rv = base::File::FILE_ERROR_NOT_A_DIRECTORY; 493 callback.Run(rv); 494 } 495 496 void FileSystemOperationImpl::DidFileExists( 497 const StatusCallback& callback, 498 base::File::Error rv, 499 const base::File::Info& file_info) { 500 if (rv == base::File::FILE_OK && file_info.is_directory) 501 rv = base::File::FILE_ERROR_NOT_A_FILE; 502 callback.Run(rv); 503 } 504 505 void FileSystemOperationImpl::DidDeleteRecursively( 506 const FileSystemURL& url, 507 const StatusCallback& callback, 508 base::File::Error rv) { 509 if (rv == base::File::FILE_ERROR_INVALID_OPERATION) { 510 // Recursive removal is not supported on this platform. 511 DCHECK(!recursive_operation_delegate_); 512 recursive_operation_delegate_.reset( 513 new RemoveOperationDelegate( 514 file_system_context(), url, 515 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 516 weak_factory_.GetWeakPtr(), callback))); 517 recursive_operation_delegate_->RunRecursively(); 518 return; 519 } 520 521 callback.Run(rv); 522 } 523 524 void FileSystemOperationImpl::DidWrite( 525 const FileSystemURL& url, 526 const WriteCallback& write_callback, 527 base::File::Error rv, 528 int64 bytes, 529 FileWriterDelegate::WriteProgressStatus write_status) { 530 const bool complete = ( 531 write_status != FileWriterDelegate::SUCCESS_IO_PENDING); 532 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) { 533 DCHECK(operation_context_); 534 operation_context_->change_observers()->Notify( 535 &FileChangeObserver::OnModifyFile, MakeTuple(url)); 536 } 537 538 StatusCallback cancel_callback = cancel_callback_; 539 write_callback.Run(rv, bytes, complete); 540 if (!cancel_callback.is_null()) 541 cancel_callback.Run(base::File::FILE_OK); 542 } 543 544 void FileSystemOperationImpl::DidOpenFile( 545 const OpenFileCallback& callback, 546 base::File file, 547 const base::Closure& on_close_callback) { 548 callback.Run(file.Pass(), on_close_callback); 549 } 550 551 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) { 552 if (pending_operation_ != kOperationNone) 553 return false; 554 pending_operation_ = type; 555 return true; 556 } 557 558 } // namespace fileapi 559