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_stream_writer.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 FileSystemOperationImpl::FileSystemOperationImpl( 35 const FileSystemURL& url, 36 FileSystemContext* file_system_context, 37 scoped_ptr<FileSystemOperationContext> operation_context) 38 : file_system_context_(file_system_context), 39 operation_context_(operation_context.Pass()), 40 async_file_util_(NULL), 41 peer_handle_(base::kNullProcessHandle), 42 pending_operation_(kOperationNone) { 43 DCHECK(operation_context_.get()); 44 operation_context_->DetachUserDataThread(); 45 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type()); 46 DCHECK(async_file_util_); 47 } 48 49 FileSystemOperationImpl::~FileSystemOperationImpl() { 50 } 51 52 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url, 53 bool exclusive, 54 const StatusCallback& callback) { 55 DCHECK(SetPendingOperationType(kOperationCreateFile)); 56 GetUsageAndQuotaThenRunTask( 57 url, 58 base::Bind(&FileSystemOperationImpl::DoCreateFile, 59 AsWeakPtr(), url, callback, exclusive), 60 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 61 } 62 63 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url, 64 bool exclusive, 65 bool recursive, 66 const StatusCallback& callback) { 67 DCHECK(SetPendingOperationType(kOperationCreateDirectory)); 68 GetUsageAndQuotaThenRunTask( 69 url, 70 base::Bind(&FileSystemOperationImpl::DoCreateDirectory, 71 AsWeakPtr(), url, callback, exclusive, recursive), 72 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 73 } 74 75 void FileSystemOperationImpl::Copy(const FileSystemURL& src_url, 76 const FileSystemURL& dest_url, 77 const StatusCallback& callback) { 78 DCHECK(SetPendingOperationType(kOperationCopy)); 79 DCHECK(!recursive_operation_delegate_); 80 recursive_operation_delegate_.reset( 81 new CopyOrMoveOperationDelegate( 82 file_system_context(), 83 src_url, dest_url, 84 CopyOrMoveOperationDelegate::OPERATION_COPY, 85 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 86 AsWeakPtr(), callback))); 87 recursive_operation_delegate_->RunRecursively(); 88 } 89 90 void FileSystemOperationImpl::Move(const FileSystemURL& src_url, 91 const FileSystemURL& dest_url, 92 const StatusCallback& callback) { 93 DCHECK(SetPendingOperationType(kOperationMove)); 94 DCHECK(!recursive_operation_delegate_); 95 recursive_operation_delegate_.reset( 96 new CopyOrMoveOperationDelegate( 97 file_system_context(), 98 src_url, dest_url, 99 CopyOrMoveOperationDelegate::OPERATION_MOVE, 100 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 101 AsWeakPtr(), callback))); 102 recursive_operation_delegate_->RunRecursively(); 103 } 104 105 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url, 106 const StatusCallback& callback) { 107 DCHECK(SetPendingOperationType(kOperationDirectoryExists)); 108 async_file_util_->GetFileInfo( 109 operation_context_.Pass(), url, 110 base::Bind(&FileSystemOperationImpl::DidDirectoryExists, 111 AsWeakPtr(), callback)); 112 } 113 114 void FileSystemOperationImpl::FileExists(const FileSystemURL& url, 115 const StatusCallback& callback) { 116 DCHECK(SetPendingOperationType(kOperationFileExists)); 117 async_file_util_->GetFileInfo( 118 operation_context_.Pass(), url, 119 base::Bind(&FileSystemOperationImpl::DidFileExists, 120 AsWeakPtr(), callback)); 121 } 122 123 void FileSystemOperationImpl::GetMetadata( 124 const FileSystemURL& url, const GetMetadataCallback& callback) { 125 DCHECK(SetPendingOperationType(kOperationGetMetadata)); 126 async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback); 127 } 128 129 void FileSystemOperationImpl::ReadDirectory( 130 const FileSystemURL& url, const ReadDirectoryCallback& callback) { 131 DCHECK(SetPendingOperationType(kOperationReadDirectory)); 132 async_file_util_->ReadDirectory( 133 operation_context_.Pass(), url, callback); 134 } 135 136 void FileSystemOperationImpl::Remove(const FileSystemURL& url, 137 bool recursive, 138 const StatusCallback& callback) { 139 DCHECK(SetPendingOperationType(kOperationRemove)); 140 DCHECK(!recursive_operation_delegate_); 141 142 if (recursive) { 143 // For recursive removal, try to delegate the operation to AsyncFileUtil 144 // first. If not supported, it is delegated to RemoveOperationDelegate 145 // in DidDeleteRecursively. 146 async_file_util_->DeleteRecursively( 147 operation_context_.Pass(), url, 148 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively, 149 AsWeakPtr(), url, callback)); 150 return; 151 } 152 153 recursive_operation_delegate_.reset( 154 new RemoveOperationDelegate( 155 file_system_context(), url, 156 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 157 AsWeakPtr(), callback))); 158 recursive_operation_delegate_->Run(); 159 } 160 161 void FileSystemOperationImpl::Write( 162 const FileSystemURL& url, 163 scoped_ptr<FileWriterDelegate> writer_delegate, 164 scoped_ptr<net::URLRequest> blob_request, 165 const WriteCallback& callback) { 166 DCHECK(SetPendingOperationType(kOperationWrite)); 167 file_writer_delegate_ = writer_delegate.Pass(); 168 file_writer_delegate_->Start( 169 blob_request.Pass(), 170 base::Bind(&FileSystemOperationImpl::DidWrite, AsWeakPtr(), 171 url, callback)); 172 } 173 174 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length, 175 const StatusCallback& callback) { 176 DCHECK(SetPendingOperationType(kOperationTruncate)); 177 GetUsageAndQuotaThenRunTask( 178 url, 179 base::Bind(&FileSystemOperationImpl::DoTruncate, 180 AsWeakPtr(), url, callback, length), 181 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 182 } 183 184 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url, 185 const base::Time& last_access_time, 186 const base::Time& last_modified_time, 187 const StatusCallback& callback) { 188 DCHECK(SetPendingOperationType(kOperationTouchFile)); 189 async_file_util_->Touch( 190 operation_context_.Pass(), url, 191 last_access_time, last_modified_time, 192 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 193 AsWeakPtr(), callback)); 194 } 195 196 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url, 197 int file_flags, 198 base::ProcessHandle peer_handle, 199 const OpenFileCallback& callback) { 200 DCHECK(SetPendingOperationType(kOperationOpenFile)); 201 peer_handle_ = peer_handle; 202 203 if (file_flags & ( 204 (base::PLATFORM_FILE_ENUMERATE | base::PLATFORM_FILE_TEMPORARY | 205 base::PLATFORM_FILE_HIDDEN))) { 206 callback.Run(base::PLATFORM_FILE_ERROR_FAILED, 207 base::kInvalidPlatformFileValue, 208 base::Closure(), 209 base::kNullProcessHandle); 210 return; 211 } 212 GetUsageAndQuotaThenRunTask( 213 url, 214 base::Bind(&FileSystemOperationImpl::DoOpenFile, 215 AsWeakPtr(), 216 url, callback, file_flags), 217 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 218 base::kInvalidPlatformFileValue, 219 base::Closure(), 220 base::kNullProcessHandle)); 221 } 222 223 // We can only get here on a write or truncate that's not yet completed. 224 // We don't support cancelling any other operation at this time. 225 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) { 226 DCHECK(cancel_callback_.is_null()); 227 cancel_callback_ = cancel_callback; 228 229 if (file_writer_delegate_.get()) { 230 DCHECK_EQ(kOperationWrite, pending_operation_); 231 // This will call DidWrite() with ABORT status code. 232 file_writer_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 FileSystemOperationImpl* FileSystemOperationImpl::AsFileSystemOperationImpl() { 241 return this; 242 } 243 244 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath( 245 const FileSystemURL& url, 246 base::FilePath* platform_path) { 247 DCHECK(SetPendingOperationType(kOperationGetLocalPath)); 248 FileSystemFileUtil* file_util = file_system_context()->GetFileUtil( 249 url.type()); 250 if (!file_util) 251 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 252 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path); 253 return base::PLATFORM_FILE_OK; 254 } 255 256 void FileSystemOperationImpl::CreateSnapshotFile( 257 const FileSystemURL& url, 258 const SnapshotFileCallback& callback) { 259 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile)); 260 async_file_util_->CreateSnapshotFile( 261 operation_context_.Pass(), url, callback); 262 } 263 264 void FileSystemOperationImpl::CopyInForeignFile( 265 const base::FilePath& src_local_disk_file_path, 266 const FileSystemURL& dest_url, 267 const StatusCallback& callback) { 268 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile)); 269 GetUsageAndQuotaThenRunTask( 270 dest_url, 271 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile, 272 AsWeakPtr(), src_local_disk_file_path, dest_url, 273 callback), 274 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 275 } 276 277 void FileSystemOperationImpl::RemoveFile( 278 const FileSystemURL& url, 279 const StatusCallback& callback) { 280 DCHECK(SetPendingOperationType(kOperationRemove)); 281 async_file_util_->DeleteFile( 282 operation_context_.Pass(), url, 283 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 284 AsWeakPtr(), callback)); 285 } 286 287 void FileSystemOperationImpl::RemoveDirectory( 288 const FileSystemURL& url, 289 const StatusCallback& callback) { 290 DCHECK(SetPendingOperationType(kOperationRemove)); 291 async_file_util_->DeleteDirectory( 292 operation_context_.Pass(), url, 293 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 294 AsWeakPtr(), callback)); 295 } 296 297 void FileSystemOperationImpl::CopyFileLocal( 298 const FileSystemURL& src_url, 299 const FileSystemURL& dest_url, 300 const StatusCallback& callback) { 301 DCHECK(SetPendingOperationType(kOperationCopy)); 302 DCHECK(src_url.IsInSameFileSystem(dest_url)); 303 GetUsageAndQuotaThenRunTask( 304 dest_url, 305 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal, 306 AsWeakPtr(), src_url, dest_url, callback), 307 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 308 } 309 310 void FileSystemOperationImpl::MoveFileLocal( 311 const FileSystemURL& src_url, 312 const FileSystemURL& dest_url, 313 const StatusCallback& callback) { 314 DCHECK(SetPendingOperationType(kOperationMove)); 315 DCHECK(src_url.IsInSameFileSystem(dest_url)); 316 GetUsageAndQuotaThenRunTask( 317 dest_url, 318 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal, 319 AsWeakPtr(), src_url, dest_url, callback), 320 base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED)); 321 } 322 323 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask( 324 const FileSystemURL& url, 325 const base::Closure& task, 326 const base::Closure& error_callback) { 327 quota::QuotaManagerProxy* quota_manager_proxy = 328 file_system_context()->quota_manager_proxy(); 329 if (!quota_manager_proxy || 330 !file_system_context()->GetQuotaUtil(url.type())) { 331 // If we don't have the quota manager or the requested filesystem type 332 // does not support quota, we should be able to let it go. 333 operation_context_->set_allowed_bytes_growth(kint64max); 334 task.Run(); 335 return; 336 } 337 338 DCHECK(quota_manager_proxy); 339 DCHECK(quota_manager_proxy->quota_manager()); 340 quota_manager_proxy->quota_manager()->GetUsageAndQuota( 341 url.origin(), 342 FileSystemTypeToQuotaStorageType(url.type()), 343 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask, 344 AsWeakPtr(), task, error_callback)); 345 } 346 347 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask( 348 const base::Closure& task, 349 const base::Closure& error_callback, 350 quota::QuotaStatusCode status, 351 int64 usage, int64 quota) { 352 if (status != quota::kQuotaStatusOk) { 353 LOG(WARNING) << "Got unexpected quota error : " << status; 354 error_callback.Run(); 355 return; 356 } 357 358 operation_context_->set_allowed_bytes_growth(quota - usage); 359 task.Run(); 360 } 361 362 void FileSystemOperationImpl::DoCreateFile( 363 const FileSystemURL& url, 364 const StatusCallback& callback, 365 bool exclusive) { 366 async_file_util_->EnsureFileExists( 367 operation_context_.Pass(), url, 368 base::Bind( 369 exclusive ? 370 &FileSystemOperationImpl::DidEnsureFileExistsExclusive : 371 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive, 372 AsWeakPtr(), callback)); 373 } 374 375 void FileSystemOperationImpl::DoCreateDirectory( 376 const FileSystemURL& url, 377 const StatusCallback& callback, 378 bool exclusive, bool recursive) { 379 async_file_util_->CreateDirectory( 380 operation_context_.Pass(), 381 url, exclusive, recursive, 382 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 383 AsWeakPtr(), callback)); 384 } 385 386 void FileSystemOperationImpl::DoCopyFileLocal( 387 const FileSystemURL& src_url, 388 const FileSystemURL& dest_url, 389 const StatusCallback& callback) { 390 async_file_util_->CopyFileLocal( 391 operation_context_.Pass(), src_url, dest_url, 392 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 393 AsWeakPtr(), callback)); 394 } 395 396 void FileSystemOperationImpl::DoMoveFileLocal( 397 const FileSystemURL& src_url, 398 const FileSystemURL& dest_url, 399 const StatusCallback& callback) { 400 async_file_util_->MoveFileLocal( 401 operation_context_.Pass(), src_url, dest_url, 402 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 403 AsWeakPtr(), callback)); 404 } 405 406 void FileSystemOperationImpl::DoCopyInForeignFile( 407 const base::FilePath& src_local_disk_file_path, 408 const FileSystemURL& dest_url, 409 const StatusCallback& callback) { 410 async_file_util_->CopyInForeignFile( 411 operation_context_.Pass(), 412 src_local_disk_file_path, dest_url, 413 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 414 AsWeakPtr(), callback)); 415 } 416 417 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url, 418 const StatusCallback& callback, 419 int64 length) { 420 async_file_util_->Truncate( 421 operation_context_.Pass(), url, length, 422 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 423 AsWeakPtr(), callback)); 424 } 425 426 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url, 427 const OpenFileCallback& callback, 428 int file_flags) { 429 async_file_util_->CreateOrOpen( 430 operation_context_.Pass(), url, file_flags, 431 base::Bind(&FileSystemOperationImpl::DidOpenFile, 432 AsWeakPtr(), callback)); 433 } 434 435 void FileSystemOperationImpl::DidEnsureFileExistsExclusive( 436 const StatusCallback& callback, 437 base::PlatformFileError rv, bool created) { 438 if (rv == base::PLATFORM_FILE_OK && !created) { 439 callback.Run(base::PLATFORM_FILE_ERROR_EXISTS); 440 } else { 441 DidFinishOperation(callback, rv); 442 } 443 } 444 445 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive( 446 const StatusCallback& callback, 447 base::PlatformFileError rv, bool /* created */) { 448 DidFinishOperation(callback, rv); 449 } 450 451 void FileSystemOperationImpl::DidFinishOperation( 452 const StatusCallback& callback, 453 base::PlatformFileError rv) { 454 if (!cancel_callback_.is_null()) { 455 DCHECK_EQ(kOperationTruncate, pending_operation_); 456 457 StatusCallback cancel_callback = cancel_callback_; 458 callback.Run(base::PLATFORM_FILE_ERROR_ABORT); 459 cancel_callback.Run(base::PLATFORM_FILE_OK); 460 } else { 461 callback.Run(rv); 462 } 463 } 464 465 void FileSystemOperationImpl::DidDirectoryExists( 466 const StatusCallback& callback, 467 base::PlatformFileError rv, 468 const base::PlatformFileInfo& file_info) { 469 if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory) 470 rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; 471 callback.Run(rv); 472 } 473 474 void FileSystemOperationImpl::DidFileExists( 475 const StatusCallback& callback, 476 base::PlatformFileError rv, 477 const base::PlatformFileInfo& file_info) { 478 if (rv == base::PLATFORM_FILE_OK && file_info.is_directory) 479 rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE; 480 callback.Run(rv); 481 } 482 483 void FileSystemOperationImpl::DidDeleteRecursively( 484 const FileSystemURL& url, 485 const StatusCallback& callback, 486 base::PlatformFileError rv) { 487 if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) { 488 // Recursive removal is not supported on this platform. 489 DCHECK(!recursive_operation_delegate_); 490 recursive_operation_delegate_.reset( 491 new RemoveOperationDelegate( 492 file_system_context(), url, 493 base::Bind(&FileSystemOperationImpl::DidFinishOperation, 494 AsWeakPtr(), callback))); 495 recursive_operation_delegate_->RunRecursively(); 496 return; 497 } 498 499 callback.Run(rv); 500 } 501 502 void FileSystemOperationImpl::DidWrite( 503 const FileSystemURL& url, 504 const WriteCallback& write_callback, 505 base::PlatformFileError rv, 506 int64 bytes, 507 FileWriterDelegate::WriteProgressStatus write_status) { 508 const bool complete = ( 509 write_status != FileWriterDelegate::SUCCESS_IO_PENDING); 510 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) { 511 DCHECK(operation_context_); 512 operation_context_->change_observers()->Notify( 513 &FileChangeObserver::OnModifyFile, MakeTuple(url)); 514 } 515 516 StatusCallback cancel_callback = cancel_callback_; 517 write_callback.Run(rv, bytes, complete); 518 if (!cancel_callback.is_null()) 519 cancel_callback.Run(base::PLATFORM_FILE_OK); 520 } 521 522 void FileSystemOperationImpl::DidOpenFile( 523 const OpenFileCallback& callback, 524 base::PlatformFileError rv, 525 base::PassPlatformFile file, 526 const base::Closure& on_close_callback) { 527 if (rv == base::PLATFORM_FILE_OK) 528 CHECK_NE(base::kNullProcessHandle, peer_handle_); 529 callback.Run(rv, file.ReleaseValue(), on_close_callback, peer_handle_); 530 } 531 532 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) { 533 if (pending_operation_ != kOperationNone) 534 return false; 535 pending_operation_ = type; 536 return true; 537 } 538 539 } // namespace fileapi 540