| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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/copy_or_move_operation_delegate.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/files/file_path.h" | |
| 9 #include "net/base/io_buffer.h" | |
| 10 #include "net/base/net_errors.h" | |
| 11 #include "webkit/browser/blob/file_stream_reader.h" | |
| 12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" | |
| 13 #include "webkit/browser/fileapi/file_stream_writer.h" | |
| 14 #include "webkit/browser/fileapi/file_system_context.h" | |
| 15 #include "webkit/browser/fileapi/file_system_operation_runner.h" | |
| 16 #include "webkit/browser/fileapi/file_system_url.h" | |
| 17 #include "webkit/browser/fileapi/recursive_operation_delegate.h" | |
| 18 #include "webkit/common/blob/shareable_file_reference.h" | |
| 19 #include "webkit/common/fileapi/file_system_util.h" | |
| 20 | |
| 21 namespace storage { | |
| 22 | |
| 23 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB. | |
| 24 | |
| 25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl { | |
| 26 public: | |
| 27 virtual ~CopyOrMoveImpl() {} | |
| 28 virtual void Run( | |
| 29 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0; | |
| 30 virtual void Cancel() = 0; | |
| 31 | |
| 32 protected: | |
| 33 CopyOrMoveImpl() {} | |
| 34 | |
| 35 private: | |
| 36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl); | |
| 37 }; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Copies a file on a (same) file system. Just delegate the operation to | |
| 42 // |operation_runner|. | |
| 43 class CopyOrMoveOnSameFileSystemImpl | |
| 44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { | |
| 45 public: | |
| 46 CopyOrMoveOnSameFileSystemImpl( | |
| 47 FileSystemOperationRunner* operation_runner, | |
| 48 CopyOrMoveOperationDelegate::OperationType operation_type, | |
| 49 const FileSystemURL& src_url, | |
| 50 const FileSystemURL& dest_url, | |
| 51 CopyOrMoveOperationDelegate::CopyOrMoveOption option, | |
| 52 const FileSystemOperation::CopyFileProgressCallback& | |
| 53 file_progress_callback) | |
| 54 : operation_runner_(operation_runner), | |
| 55 operation_type_(operation_type), | |
| 56 src_url_(src_url), | |
| 57 dest_url_(dest_url), | |
| 58 option_(option), | |
| 59 file_progress_callback_(file_progress_callback) { | |
| 60 } | |
| 61 | |
| 62 virtual void Run( | |
| 63 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { | |
| 64 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) { | |
| 65 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback); | |
| 66 } else { | |
| 67 operation_runner_->CopyFileLocal( | |
| 68 src_url_, dest_url_, option_, file_progress_callback_, callback); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 virtual void Cancel() OVERRIDE { | |
| 73 // We can do nothing for the copy/move operation on a local file system. | |
| 74 // Assuming the operation is quickly done, it should be ok to just wait | |
| 75 // for the completion. | |
| 76 } | |
| 77 | |
| 78 private: | |
| 79 FileSystemOperationRunner* operation_runner_; | |
| 80 CopyOrMoveOperationDelegate::OperationType operation_type_; | |
| 81 FileSystemURL src_url_; | |
| 82 FileSystemURL dest_url_; | |
| 83 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; | |
| 84 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; | |
| 85 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl); | |
| 86 }; | |
| 87 | |
| 88 // Specifically for cross file system copy/move operation, this class creates | |
| 89 // a snapshot file, validates it if necessary, runs copying process, | |
| 90 // validates the created file, and removes source file for move (noop for | |
| 91 // copy). | |
| 92 class SnapshotCopyOrMoveImpl | |
| 93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { | |
| 94 public: | |
| 95 SnapshotCopyOrMoveImpl( | |
| 96 FileSystemOperationRunner* operation_runner, | |
| 97 CopyOrMoveOperationDelegate::OperationType operation_type, | |
| 98 const FileSystemURL& src_url, | |
| 99 const FileSystemURL& dest_url, | |
| 100 CopyOrMoveOperationDelegate::CopyOrMoveOption option, | |
| 101 CopyOrMoveFileValidatorFactory* validator_factory, | |
| 102 const FileSystemOperation::CopyFileProgressCallback& | |
| 103 file_progress_callback) | |
| 104 : operation_runner_(operation_runner), | |
| 105 operation_type_(operation_type), | |
| 106 src_url_(src_url), | |
| 107 dest_url_(dest_url), | |
| 108 option_(option), | |
| 109 validator_factory_(validator_factory), | |
| 110 file_progress_callback_(file_progress_callback), | |
| 111 cancel_requested_(false), | |
| 112 weak_factory_(this) { | |
| 113 } | |
| 114 | |
| 115 virtual void Run( | |
| 116 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { | |
| 117 file_progress_callback_.Run(0); | |
| 118 operation_runner_->CreateSnapshotFile( | |
| 119 src_url_, | |
| 120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot, | |
| 121 weak_factory_.GetWeakPtr(), callback)); | |
| 122 } | |
| 123 | |
| 124 virtual void Cancel() OVERRIDE { | |
| 125 cancel_requested_ = true; | |
| 126 } | |
| 127 | |
| 128 private: | |
| 129 void RunAfterCreateSnapshot( | |
| 130 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 131 base::File::Error error, | |
| 132 const base::File::Info& file_info, | |
| 133 const base::FilePath& platform_path, | |
| 134 const scoped_refptr<storage::ShareableFileReference>& file_ref) { | |
| 135 if (cancel_requested_) | |
| 136 error = base::File::FILE_ERROR_ABORT; | |
| 137 | |
| 138 if (error != base::File::FILE_OK) { | |
| 139 callback.Run(error); | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 // For now we assume CreateSnapshotFile always return a valid local file | |
| 144 // path. | |
| 145 DCHECK(!platform_path.empty()); | |
| 146 | |
| 147 if (!validator_factory_) { | |
| 148 // No validation is needed. | |
| 149 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback, | |
| 150 base::File::FILE_OK); | |
| 151 return; | |
| 152 } | |
| 153 | |
| 154 // Run pre write validation. | |
| 155 PreWriteValidation( | |
| 156 platform_path, | |
| 157 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation, | |
| 158 weak_factory_.GetWeakPtr(), | |
| 159 platform_path, file_info, file_ref, callback)); | |
| 160 } | |
| 161 | |
| 162 void RunAfterPreWriteValidation( | |
| 163 const base::FilePath& platform_path, | |
| 164 const base::File::Info& file_info, | |
| 165 const scoped_refptr<storage::ShareableFileReference>& file_ref, | |
| 166 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 167 base::File::Error error) { | |
| 168 if (cancel_requested_) | |
| 169 error = base::File::FILE_ERROR_ABORT; | |
| 170 | |
| 171 if (error != base::File::FILE_OK) { | |
| 172 callback.Run(error); | |
| 173 return; | |
| 174 } | |
| 175 | |
| 176 // |file_ref| is unused but necessary to keep the file alive until | |
| 177 // CopyInForeignFile() is completed. | |
| 178 operation_runner_->CopyInForeignFile( | |
| 179 platform_path, dest_url_, | |
| 180 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile, | |
| 181 weak_factory_.GetWeakPtr(), file_info, file_ref, callback)); | |
| 182 } | |
| 183 | |
| 184 void RunAfterCopyInForeignFile( | |
| 185 const base::File::Info& file_info, | |
| 186 const scoped_refptr<storage::ShareableFileReference>& file_ref, | |
| 187 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 188 base::File::Error error) { | |
| 189 if (cancel_requested_) | |
| 190 error = base::File::FILE_ERROR_ABORT; | |
| 191 | |
| 192 if (error != base::File::FILE_OK) { | |
| 193 callback.Run(error); | |
| 194 return; | |
| 195 } | |
| 196 | |
| 197 file_progress_callback_.Run(file_info.size); | |
| 198 | |
| 199 if (option_ == FileSystemOperation::OPTION_NONE) { | |
| 200 RunAfterTouchFile(callback, base::File::FILE_OK); | |
| 201 return; | |
| 202 } | |
| 203 | |
| 204 operation_runner_->TouchFile( | |
| 205 dest_url_, base::Time::Now() /* last_access */, | |
| 206 file_info.last_modified, | |
| 207 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile, | |
| 208 weak_factory_.GetWeakPtr(), callback)); | |
| 209 } | |
| 210 | |
| 211 void RunAfterTouchFile( | |
| 212 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 213 base::File::Error error) { | |
| 214 // Even if TouchFile is failed, just ignore it. | |
| 215 | |
| 216 if (cancel_requested_) { | |
| 217 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 218 return; | |
| 219 } | |
| 220 | |
| 221 // |validator_| is NULL when the destination filesystem does not do | |
| 222 // validation. | |
| 223 if (!validator_) { | |
| 224 // No validation is needed. | |
| 225 RunAfterPostWriteValidation(callback, base::File::FILE_OK); | |
| 226 return; | |
| 227 } | |
| 228 | |
| 229 PostWriteValidation( | |
| 230 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation, | |
| 231 weak_factory_.GetWeakPtr(), callback)); | |
| 232 } | |
| 233 | |
| 234 void RunAfterPostWriteValidation( | |
| 235 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 236 base::File::Error error) { | |
| 237 if (cancel_requested_) { | |
| 238 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 239 return; | |
| 240 } | |
| 241 | |
| 242 if (error != base::File::FILE_OK) { | |
| 243 // Failed to validate. Remove the destination file. | |
| 244 operation_runner_->Remove( | |
| 245 dest_url_, true /* recursive */, | |
| 246 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError, | |
| 247 weak_factory_.GetWeakPtr(), error, callback)); | |
| 248 return; | |
| 249 } | |
| 250 | |
| 251 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { | |
| 252 callback.Run(base::File::FILE_OK); | |
| 253 return; | |
| 254 } | |
| 255 | |
| 256 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); | |
| 257 | |
| 258 // Remove the source for finalizing move operation. | |
| 259 operation_runner_->Remove( | |
| 260 src_url_, true /* recursive */, | |
| 261 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove, | |
| 262 weak_factory_.GetWeakPtr(), callback)); | |
| 263 } | |
| 264 | |
| 265 void RunAfterRemoveSourceForMove( | |
| 266 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 267 base::File::Error error) { | |
| 268 if (cancel_requested_) | |
| 269 error = base::File::FILE_ERROR_ABORT; | |
| 270 | |
| 271 if (error == base::File::FILE_ERROR_NOT_FOUND) | |
| 272 error = base::File::FILE_OK; | |
| 273 callback.Run(error); | |
| 274 } | |
| 275 | |
| 276 void DidRemoveDestForError( | |
| 277 base::File::Error prior_error, | |
| 278 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 279 base::File::Error error) { | |
| 280 if (error != base::File::FILE_OK) { | |
| 281 VLOG(1) << "Error removing destination file after validation error: " | |
| 282 << error; | |
| 283 } | |
| 284 callback.Run(prior_error); | |
| 285 } | |
| 286 | |
| 287 // Runs pre-write validation. | |
| 288 void PreWriteValidation( | |
| 289 const base::FilePath& platform_path, | |
| 290 const CopyOrMoveOperationDelegate::StatusCallback& callback) { | |
| 291 DCHECK(validator_factory_); | |
| 292 validator_.reset( | |
| 293 validator_factory_->CreateCopyOrMoveFileValidator( | |
| 294 src_url_, platform_path)); | |
| 295 validator_->StartPreWriteValidation(callback); | |
| 296 } | |
| 297 | |
| 298 // Runs post-write validation. | |
| 299 void PostWriteValidation( | |
| 300 const CopyOrMoveOperationDelegate::StatusCallback& callback) { | |
| 301 operation_runner_->CreateSnapshotFile( | |
| 302 dest_url_, | |
| 303 base::Bind( | |
| 304 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile, | |
| 305 weak_factory_.GetWeakPtr(), callback)); | |
| 306 } | |
| 307 | |
| 308 void PostWriteValidationAfterCreateSnapshotFile( | |
| 309 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 310 base::File::Error error, | |
| 311 const base::File::Info& file_info, | |
| 312 const base::FilePath& platform_path, | |
| 313 const scoped_refptr<storage::ShareableFileReference>& file_ref) { | |
| 314 if (cancel_requested_) | |
| 315 error = base::File::FILE_ERROR_ABORT; | |
| 316 | |
| 317 if (error != base::File::FILE_OK) { | |
| 318 callback.Run(error); | |
| 319 return; | |
| 320 } | |
| 321 | |
| 322 DCHECK(validator_); | |
| 323 // Note: file_ref passed here to keep the file alive until after | |
| 324 // the StartPostWriteValidation operation finishes. | |
| 325 validator_->StartPostWriteValidation( | |
| 326 platform_path, | |
| 327 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation, | |
| 328 weak_factory_.GetWeakPtr(), file_ref, callback)); | |
| 329 } | |
| 330 | |
| 331 // |file_ref| is unused; it is passed here to make sure the reference is | |
| 332 // alive until after post-write validation is complete. | |
| 333 void DidPostWriteValidation( | |
| 334 const scoped_refptr<storage::ShareableFileReference>& file_ref, | |
| 335 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 336 base::File::Error error) { | |
| 337 callback.Run(error); | |
| 338 } | |
| 339 | |
| 340 FileSystemOperationRunner* operation_runner_; | |
| 341 CopyOrMoveOperationDelegate::OperationType operation_type_; | |
| 342 FileSystemURL src_url_; | |
| 343 FileSystemURL dest_url_; | |
| 344 | |
| 345 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; | |
| 346 CopyOrMoveFileValidatorFactory* validator_factory_; | |
| 347 scoped_ptr<CopyOrMoveFileValidator> validator_; | |
| 348 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; | |
| 349 bool cancel_requested_; | |
| 350 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_; | |
| 351 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); | |
| 352 }; | |
| 353 | |
| 354 // The size of buffer for StreamCopyHelper. | |
| 355 const int kReadBufferSize = 32768; | |
| 356 | |
| 357 // To avoid too many progress callbacks, it should be called less | |
| 358 // frequently than 50ms. | |
| 359 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50; | |
| 360 | |
| 361 // Specifically for cross file system copy/move operation, this class uses | |
| 362 // stream reader and writer for copying. Validator is not supported, so if | |
| 363 // necessary SnapshotCopyOrMoveImpl should be used. | |
| 364 class StreamCopyOrMoveImpl | |
| 365 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { | |
| 366 public: | |
| 367 StreamCopyOrMoveImpl( | |
| 368 FileSystemOperationRunner* operation_runner, | |
| 369 CopyOrMoveOperationDelegate::OperationType operation_type, | |
| 370 const FileSystemURL& src_url, | |
| 371 const FileSystemURL& dest_url, | |
| 372 CopyOrMoveOperationDelegate::CopyOrMoveOption option, | |
| 373 scoped_ptr<storage::FileStreamReader> reader, | |
| 374 scoped_ptr<FileStreamWriter> writer, | |
| 375 const FileSystemOperation::CopyFileProgressCallback& | |
| 376 file_progress_callback) | |
| 377 : operation_runner_(operation_runner), | |
| 378 operation_type_(operation_type), | |
| 379 src_url_(src_url), | |
| 380 dest_url_(dest_url), | |
| 381 option_(option), | |
| 382 reader_(reader.Pass()), | |
| 383 writer_(writer.Pass()), | |
| 384 file_progress_callback_(file_progress_callback), | |
| 385 cancel_requested_(false), | |
| 386 weak_factory_(this) {} | |
| 387 | |
| 388 virtual void Run( | |
| 389 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { | |
| 390 // Reader can be created even if the entry does not exist or the entry is | |
| 391 // a directory. To check errors before destination file creation, | |
| 392 // check metadata first. | |
| 393 operation_runner_->GetMetadata( | |
| 394 src_url_, | |
| 395 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource, | |
| 396 weak_factory_.GetWeakPtr(), callback)); | |
| 397 } | |
| 398 | |
| 399 virtual void Cancel() OVERRIDE { | |
| 400 cancel_requested_ = true; | |
| 401 if (copy_helper_) | |
| 402 copy_helper_->Cancel(); | |
| 403 } | |
| 404 | |
| 405 private: | |
| 406 void RunAfterGetMetadataForSource( | |
| 407 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 408 base::File::Error error, | |
| 409 const base::File::Info& file_info) { | |
| 410 if (cancel_requested_) | |
| 411 error = base::File::FILE_ERROR_ABORT; | |
| 412 | |
| 413 if (error != base::File::FILE_OK) { | |
| 414 callback.Run(error); | |
| 415 return; | |
| 416 } | |
| 417 | |
| 418 if (file_info.is_directory) { | |
| 419 // If not a directory, failed with appropriate error code. | |
| 420 callback.Run(base::File::FILE_ERROR_NOT_A_FILE); | |
| 421 return; | |
| 422 } | |
| 423 | |
| 424 // To use FileStreamWriter, we need to ensure the destination file exists. | |
| 425 operation_runner_->CreateFile( | |
| 426 dest_url_, | |
| 427 true /* exclusive */, | |
| 428 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination, | |
| 429 weak_factory_.GetWeakPtr(), | |
| 430 callback, | |
| 431 file_info.last_modified)); | |
| 432 } | |
| 433 | |
| 434 void RunAfterCreateFileForDestination( | |
| 435 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 436 const base::Time& last_modified, | |
| 437 base::File::Error error) { | |
| 438 if (cancel_requested_) | |
| 439 error = base::File::FILE_ERROR_ABORT; | |
| 440 | |
| 441 if (error != base::File::FILE_OK && | |
| 442 error != base::File::FILE_ERROR_EXISTS) { | |
| 443 callback.Run(error); | |
| 444 return; | |
| 445 } | |
| 446 | |
| 447 if (error == base::File::FILE_ERROR_EXISTS) { | |
| 448 operation_runner_->Truncate( | |
| 449 dest_url_, | |
| 450 0 /* length */, | |
| 451 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination, | |
| 452 weak_factory_.GetWeakPtr(), | |
| 453 callback, | |
| 454 last_modified)); | |
| 455 return; | |
| 456 } | |
| 457 RunAfterTruncateForDestination( | |
| 458 callback, last_modified, base::File::FILE_OK); | |
| 459 } | |
| 460 | |
| 461 void RunAfterTruncateForDestination( | |
| 462 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 463 const base::Time& last_modified, | |
| 464 base::File::Error error) { | |
| 465 if (cancel_requested_) | |
| 466 error = base::File::FILE_ERROR_ABORT; | |
| 467 | |
| 468 if (error != base::File::FILE_OK) { | |
| 469 callback.Run(error); | |
| 470 return; | |
| 471 } | |
| 472 | |
| 473 const bool need_flush = dest_url_.mount_option().copy_sync_option() == | |
| 474 storage::COPY_SYNC_OPTION_SYNC; | |
| 475 | |
| 476 DCHECK(!copy_helper_); | |
| 477 copy_helper_.reset( | |
| 478 new CopyOrMoveOperationDelegate::StreamCopyHelper( | |
| 479 reader_.Pass(), writer_.Pass(), | |
| 480 need_flush, | |
| 481 kReadBufferSize, | |
| 482 file_progress_callback_, | |
| 483 base::TimeDelta::FromMilliseconds( | |
| 484 kMinProgressCallbackInvocationSpanInMilliseconds))); | |
| 485 copy_helper_->Run( | |
| 486 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy, | |
| 487 weak_factory_.GetWeakPtr(), callback, last_modified)); | |
| 488 } | |
| 489 | |
| 490 void RunAfterStreamCopy( | |
| 491 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 492 const base::Time& last_modified, | |
| 493 base::File::Error error) { | |
| 494 if (cancel_requested_) | |
| 495 error = base::File::FILE_ERROR_ABORT; | |
| 496 | |
| 497 if (error != base::File::FILE_OK) { | |
| 498 callback.Run(error); | |
| 499 return; | |
| 500 } | |
| 501 | |
| 502 if (option_ == FileSystemOperation::OPTION_NONE) { | |
| 503 RunAfterTouchFile(callback, base::File::FILE_OK); | |
| 504 return; | |
| 505 } | |
| 506 | |
| 507 operation_runner_->TouchFile( | |
| 508 dest_url_, base::Time::Now() /* last_access */, last_modified, | |
| 509 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile, | |
| 510 weak_factory_.GetWeakPtr(), callback)); | |
| 511 } | |
| 512 | |
| 513 void RunAfterTouchFile( | |
| 514 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 515 base::File::Error error) { | |
| 516 // Even if TouchFile is failed, just ignore it. | |
| 517 if (cancel_requested_) { | |
| 518 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 519 return; | |
| 520 } | |
| 521 | |
| 522 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { | |
| 523 callback.Run(base::File::FILE_OK); | |
| 524 return; | |
| 525 } | |
| 526 | |
| 527 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); | |
| 528 | |
| 529 // Remove the source for finalizing move operation. | |
| 530 operation_runner_->Remove( | |
| 531 src_url_, false /* recursive */, | |
| 532 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove, | |
| 533 weak_factory_.GetWeakPtr(), callback)); | |
| 534 } | |
| 535 | |
| 536 void RunAfterRemoveForMove( | |
| 537 const CopyOrMoveOperationDelegate::StatusCallback& callback, | |
| 538 base::File::Error error) { | |
| 539 if (cancel_requested_) | |
| 540 error = base::File::FILE_ERROR_ABORT; | |
| 541 if (error == base::File::FILE_ERROR_NOT_FOUND) | |
| 542 error = base::File::FILE_OK; | |
| 543 callback.Run(error); | |
| 544 } | |
| 545 | |
| 546 FileSystemOperationRunner* operation_runner_; | |
| 547 CopyOrMoveOperationDelegate::OperationType operation_type_; | |
| 548 FileSystemURL src_url_; | |
| 549 FileSystemURL dest_url_; | |
| 550 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; | |
| 551 scoped_ptr<storage::FileStreamReader> reader_; | |
| 552 scoped_ptr<FileStreamWriter> writer_; | |
| 553 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; | |
| 554 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_; | |
| 555 bool cancel_requested_; | |
| 556 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_; | |
| 557 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl); | |
| 558 }; | |
| 559 | |
| 560 } // namespace | |
| 561 | |
| 562 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper( | |
| 563 scoped_ptr<storage::FileStreamReader> reader, | |
| 564 scoped_ptr<FileStreamWriter> writer, | |
| 565 bool need_flush, | |
| 566 int buffer_size, | |
| 567 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback, | |
| 568 const base::TimeDelta& min_progress_callback_invocation_span) | |
| 569 : reader_(reader.Pass()), | |
| 570 writer_(writer.Pass()), | |
| 571 need_flush_(need_flush), | |
| 572 file_progress_callback_(file_progress_callback), | |
| 573 io_buffer_(new net::IOBufferWithSize(buffer_size)), | |
| 574 num_copied_bytes_(0), | |
| 575 previous_flush_offset_(0), | |
| 576 min_progress_callback_invocation_span_( | |
| 577 min_progress_callback_invocation_span), | |
| 578 cancel_requested_(false), | |
| 579 weak_factory_(this) { | |
| 580 } | |
| 581 | |
| 582 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() { | |
| 583 } | |
| 584 | |
| 585 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run( | |
| 586 const StatusCallback& callback) { | |
| 587 file_progress_callback_.Run(0); | |
| 588 last_progress_callback_invocation_time_ = base::Time::Now(); | |
| 589 Read(callback); | |
| 590 } | |
| 591 | |
| 592 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() { | |
| 593 cancel_requested_ = true; | |
| 594 } | |
| 595 | |
| 596 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read( | |
| 597 const StatusCallback& callback) { | |
| 598 int result = reader_->Read( | |
| 599 io_buffer_.get(), io_buffer_->size(), | |
| 600 base::Bind(&StreamCopyHelper::DidRead, | |
| 601 weak_factory_.GetWeakPtr(), callback)); | |
| 602 if (result != net::ERR_IO_PENDING) | |
| 603 DidRead(callback, result); | |
| 604 } | |
| 605 | |
| 606 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead( | |
| 607 const StatusCallback& callback, int result) { | |
| 608 if (cancel_requested_) { | |
| 609 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 610 return; | |
| 611 } | |
| 612 | |
| 613 if (result < 0) { | |
| 614 callback.Run(NetErrorToFileError(result)); | |
| 615 return; | |
| 616 } | |
| 617 | |
| 618 if (result == 0) { | |
| 619 // Here is the EOF. | |
| 620 if (need_flush_) | |
| 621 Flush(callback, true /* is_eof */); | |
| 622 else | |
| 623 callback.Run(base::File::FILE_OK); | |
| 624 return; | |
| 625 } | |
| 626 | |
| 627 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result)); | |
| 628 } | |
| 629 | |
| 630 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write( | |
| 631 const StatusCallback& callback, | |
| 632 scoped_refptr<net::DrainableIOBuffer> buffer) { | |
| 633 DCHECK_GT(buffer->BytesRemaining(), 0); | |
| 634 | |
| 635 int result = writer_->Write( | |
| 636 buffer.get(), buffer->BytesRemaining(), | |
| 637 base::Bind(&StreamCopyHelper::DidWrite, | |
| 638 weak_factory_.GetWeakPtr(), callback, buffer)); | |
| 639 if (result != net::ERR_IO_PENDING) | |
| 640 DidWrite(callback, buffer, result); | |
| 641 } | |
| 642 | |
| 643 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite( | |
| 644 const StatusCallback& callback, | |
| 645 scoped_refptr<net::DrainableIOBuffer> buffer, | |
| 646 int result) { | |
| 647 if (cancel_requested_) { | |
| 648 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 649 return; | |
| 650 } | |
| 651 | |
| 652 if (result < 0) { | |
| 653 callback.Run(NetErrorToFileError(result)); | |
| 654 return; | |
| 655 } | |
| 656 | |
| 657 buffer->DidConsume(result); | |
| 658 num_copied_bytes_ += result; | |
| 659 | |
| 660 // Check the elapsed time since last |file_progress_callback_| invocation. | |
| 661 base::Time now = base::Time::Now(); | |
| 662 if (now - last_progress_callback_invocation_time_ >= | |
| 663 min_progress_callback_invocation_span_) { | |
| 664 file_progress_callback_.Run(num_copied_bytes_); | |
| 665 last_progress_callback_invocation_time_ = now; | |
| 666 } | |
| 667 | |
| 668 if (buffer->BytesRemaining() > 0) { | |
| 669 Write(callback, buffer); | |
| 670 return; | |
| 671 } | |
| 672 | |
| 673 if (need_flush_ && | |
| 674 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) { | |
| 675 Flush(callback, false /* not is_eof */); | |
| 676 } else { | |
| 677 Read(callback); | |
| 678 } | |
| 679 } | |
| 680 | |
| 681 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush( | |
| 682 const StatusCallback& callback, bool is_eof) { | |
| 683 int result = writer_->Flush( | |
| 684 base::Bind(&StreamCopyHelper::DidFlush, | |
| 685 weak_factory_.GetWeakPtr(), callback, is_eof)); | |
| 686 if (result != net::ERR_IO_PENDING) | |
| 687 DidFlush(callback, is_eof, result); | |
| 688 } | |
| 689 | |
| 690 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush( | |
| 691 const StatusCallback& callback, bool is_eof, int result) { | |
| 692 if (cancel_requested_) { | |
| 693 callback.Run(base::File::FILE_ERROR_ABORT); | |
| 694 return; | |
| 695 } | |
| 696 | |
| 697 previous_flush_offset_ = num_copied_bytes_; | |
| 698 if (is_eof) | |
| 699 callback.Run(NetErrorToFileError(result)); | |
| 700 else | |
| 701 Read(callback); | |
| 702 } | |
| 703 | |
| 704 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( | |
| 705 FileSystemContext* file_system_context, | |
| 706 const FileSystemURL& src_root, | |
| 707 const FileSystemURL& dest_root, | |
| 708 OperationType operation_type, | |
| 709 CopyOrMoveOption option, | |
| 710 const CopyProgressCallback& progress_callback, | |
| 711 const StatusCallback& callback) | |
| 712 : RecursiveOperationDelegate(file_system_context), | |
| 713 src_root_(src_root), | |
| 714 dest_root_(dest_root), | |
| 715 operation_type_(operation_type), | |
| 716 option_(option), | |
| 717 progress_callback_(progress_callback), | |
| 718 callback_(callback), | |
| 719 weak_factory_(this) { | |
| 720 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_); | |
| 721 } | |
| 722 | |
| 723 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() { | |
| 724 STLDeleteElements(&running_copy_set_); | |
| 725 } | |
| 726 | |
| 727 void CopyOrMoveOperationDelegate::Run() { | |
| 728 // Not supported; this should never be called. | |
| 729 NOTREACHED(); | |
| 730 } | |
| 731 | |
| 732 void CopyOrMoveOperationDelegate::RunRecursively() { | |
| 733 // Perform light-weight checks first. | |
| 734 | |
| 735 // It is an error to try to copy/move an entry into its child. | |
| 736 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) { | |
| 737 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION); | |
| 738 return; | |
| 739 } | |
| 740 | |
| 741 if (same_file_system_ && src_root_.path() == dest_root_.path()) { | |
| 742 // In JS API this should return error, but we return success because Pepper | |
| 743 // wants to return success and we have a code path that returns error in | |
| 744 // Blink for JS (http://crbug.com/329517). | |
| 745 callback_.Run(base::File::FILE_OK); | |
| 746 return; | |
| 747 } | |
| 748 | |
| 749 // Start to process the source directory recursively. | |
| 750 // TODO(kinuko): This could be too expensive for same_file_system_==true | |
| 751 // and operation==MOVE case, probably we can just rename the root directory. | |
| 752 // http://crbug.com/172187 | |
| 753 StartRecursiveOperation(src_root_, callback_); | |
| 754 } | |
| 755 | |
| 756 void CopyOrMoveOperationDelegate::ProcessFile( | |
| 757 const FileSystemURL& src_url, | |
| 758 const StatusCallback& callback) { | |
| 759 if (!progress_callback_.is_null()) { | |
| 760 progress_callback_.Run( | |
| 761 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0); | |
| 762 } | |
| 763 | |
| 764 FileSystemURL dest_url = CreateDestURL(src_url); | |
| 765 CopyOrMoveImpl* impl = NULL; | |
| 766 if (same_file_system_ && | |
| 767 (file_system_context() | |
| 768 ->GetFileSystemBackend(src_url.type()) | |
| 769 ->HasInplaceCopyImplementation(src_url.type()) || | |
| 770 operation_type_ == OperationType::OPERATION_MOVE)) { | |
| 771 impl = new CopyOrMoveOnSameFileSystemImpl( | |
| 772 operation_runner(), operation_type_, src_url, dest_url, option_, | |
| 773 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, | |
| 774 weak_factory_.GetWeakPtr(), src_url)); | |
| 775 } else { | |
| 776 // Cross filesystem case. | |
| 777 base::File::Error error = base::File::FILE_ERROR_FAILED; | |
| 778 CopyOrMoveFileValidatorFactory* validator_factory = | |
| 779 file_system_context()->GetCopyOrMoveFileValidatorFactory( | |
| 780 dest_root_.type(), &error); | |
| 781 if (error != base::File::FILE_OK) { | |
| 782 callback.Run(error); | |
| 783 return; | |
| 784 } | |
| 785 | |
| 786 if (!validator_factory) { | |
| 787 scoped_ptr<storage::FileStreamReader> reader = | |
| 788 file_system_context()->CreateFileStreamReader( | |
| 789 src_url, 0, base::Time()); | |
| 790 scoped_ptr<FileStreamWriter> writer = | |
| 791 file_system_context()->CreateFileStreamWriter(dest_url, 0); | |
| 792 if (reader && writer) { | |
| 793 impl = new StreamCopyOrMoveImpl( | |
| 794 operation_runner(), operation_type_, src_url, dest_url, option_, | |
| 795 reader.Pass(), writer.Pass(), | |
| 796 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, | |
| 797 weak_factory_.GetWeakPtr(), src_url)); | |
| 798 } | |
| 799 } | |
| 800 | |
| 801 if (!impl) { | |
| 802 impl = new SnapshotCopyOrMoveImpl( | |
| 803 operation_runner(), operation_type_, src_url, dest_url, option_, | |
| 804 validator_factory, | |
| 805 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, | |
| 806 weak_factory_.GetWeakPtr(), src_url)); | |
| 807 } | |
| 808 } | |
| 809 | |
| 810 // Register the running task. | |
| 811 running_copy_set_.insert(impl); | |
| 812 impl->Run(base::Bind( | |
| 813 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile, | |
| 814 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl)); | |
| 815 } | |
| 816 | |
| 817 void CopyOrMoveOperationDelegate::ProcessDirectory( | |
| 818 const FileSystemURL& src_url, | |
| 819 const StatusCallback& callback) { | |
| 820 if (src_url == src_root_) { | |
| 821 // The src_root_ looks to be a directory. | |
| 822 // Try removing the dest_root_ to see if it exists and/or it is an | |
| 823 // empty directory. | |
| 824 // We do not invoke |progress_callback_| for source root, because it is | |
| 825 // already called in ProcessFile(). | |
| 826 operation_runner()->RemoveDirectory( | |
| 827 dest_root_, | |
| 828 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot, | |
| 829 weak_factory_.GetWeakPtr(), callback)); | |
| 830 return; | |
| 831 } | |
| 832 | |
| 833 if (!progress_callback_.is_null()) { | |
| 834 progress_callback_.Run( | |
| 835 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0); | |
| 836 } | |
| 837 | |
| 838 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback); | |
| 839 } | |
| 840 | |
| 841 void CopyOrMoveOperationDelegate::PostProcessDirectory( | |
| 842 const FileSystemURL& src_url, | |
| 843 const StatusCallback& callback) { | |
| 844 if (option_ == FileSystemOperation::OPTION_NONE) { | |
| 845 PostProcessDirectoryAfterTouchFile( | |
| 846 src_url, callback, base::File::FILE_OK); | |
| 847 return; | |
| 848 } | |
| 849 | |
| 850 operation_runner()->GetMetadata( | |
| 851 src_url, | |
| 852 base::Bind( | |
| 853 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata, | |
| 854 weak_factory_.GetWeakPtr(), src_url, callback)); | |
| 855 } | |
| 856 | |
| 857 void CopyOrMoveOperationDelegate::OnCancel() { | |
| 858 // Request to cancel all running Copy/Move file. | |
| 859 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin(); | |
| 860 iter != running_copy_set_.end(); ++iter) | |
| 861 (*iter)->Cancel(); | |
| 862 } | |
| 863 | |
| 864 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile( | |
| 865 const FileSystemURL& src_url, | |
| 866 const FileSystemURL& dest_url, | |
| 867 const StatusCallback& callback, | |
| 868 CopyOrMoveImpl* impl, | |
| 869 base::File::Error error) { | |
| 870 running_copy_set_.erase(impl); | |
| 871 delete impl; | |
| 872 | |
| 873 if (!progress_callback_.is_null() && error == base::File::FILE_OK) { | |
| 874 progress_callback_.Run( | |
| 875 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0); | |
| 876 } | |
| 877 | |
| 878 callback.Run(error); | |
| 879 } | |
| 880 | |
| 881 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot( | |
| 882 const StatusCallback& callback, | |
| 883 base::File::Error error) { | |
| 884 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) { | |
| 885 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION); | |
| 886 return; | |
| 887 } | |
| 888 if (error != base::File::FILE_OK && | |
| 889 error != base::File::FILE_ERROR_NOT_FOUND) { | |
| 890 callback_.Run(error); | |
| 891 return; | |
| 892 } | |
| 893 | |
| 894 ProcessDirectoryInternal(src_root_, dest_root_, callback); | |
| 895 } | |
| 896 | |
| 897 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal( | |
| 898 const FileSystemURL& src_url, | |
| 899 const FileSystemURL& dest_url, | |
| 900 const StatusCallback& callback) { | |
| 901 // If operation_type == Move we may need to record directories and | |
| 902 // restore directory timestamps in the end, though it may have | |
| 903 // negative performance impact. | |
| 904 // See http://crbug.com/171284 for more details. | |
| 905 operation_runner()->CreateDirectory( | |
| 906 dest_url, false /* exclusive */, false /* recursive */, | |
| 907 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory, | |
| 908 weak_factory_.GetWeakPtr(), src_url, dest_url, callback)); | |
| 909 } | |
| 910 | |
| 911 void CopyOrMoveOperationDelegate::DidCreateDirectory( | |
| 912 const FileSystemURL& src_url, | |
| 913 const FileSystemURL& dest_url, | |
| 914 const StatusCallback& callback, | |
| 915 base::File::Error error) { | |
| 916 if (!progress_callback_.is_null() && error == base::File::FILE_OK) { | |
| 917 progress_callback_.Run( | |
| 918 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0); | |
| 919 } | |
| 920 | |
| 921 callback.Run(error); | |
| 922 } | |
| 923 | |
| 924 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata( | |
| 925 const FileSystemURL& src_url, | |
| 926 const StatusCallback& callback, | |
| 927 base::File::Error error, | |
| 928 const base::File::Info& file_info) { | |
| 929 if (error != base::File::FILE_OK) { | |
| 930 // Ignore the error, and run post process which should run after TouchFile. | |
| 931 PostProcessDirectoryAfterTouchFile( | |
| 932 src_url, callback, base::File::FILE_OK); | |
| 933 return; | |
| 934 } | |
| 935 | |
| 936 operation_runner()->TouchFile( | |
| 937 CreateDestURL(src_url), base::Time::Now() /* last access */, | |
| 938 file_info.last_modified, | |
| 939 base::Bind( | |
| 940 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile, | |
| 941 weak_factory_.GetWeakPtr(), src_url, callback)); | |
| 942 } | |
| 943 | |
| 944 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile( | |
| 945 const FileSystemURL& src_url, | |
| 946 const StatusCallback& callback, | |
| 947 base::File::Error error) { | |
| 948 // Even if the TouchFile is failed, just ignore it. | |
| 949 | |
| 950 if (operation_type_ == OPERATION_COPY) { | |
| 951 callback.Run(base::File::FILE_OK); | |
| 952 return; | |
| 953 } | |
| 954 | |
| 955 DCHECK_EQ(OPERATION_MOVE, operation_type_); | |
| 956 | |
| 957 // All files and subdirectories in the directory should be moved here, | |
| 958 // so remove the source directory for finalizing move operation. | |
| 959 operation_runner()->Remove( | |
| 960 src_url, false /* recursive */, | |
| 961 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove, | |
| 962 weak_factory_.GetWeakPtr(), callback)); | |
| 963 } | |
| 964 | |
| 965 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove( | |
| 966 const StatusCallback& callback, | |
| 967 base::File::Error error) { | |
| 968 if (error == base::File::FILE_ERROR_NOT_FOUND) | |
| 969 error = base::File::FILE_OK; | |
| 970 callback.Run(error); | |
| 971 } | |
| 972 | |
| 973 void CopyOrMoveOperationDelegate::OnCopyFileProgress( | |
| 974 const FileSystemURL& src_url, int64 size) { | |
| 975 if (!progress_callback_.is_null()) { | |
| 976 progress_callback_.Run( | |
| 977 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size); | |
| 978 } | |
| 979 } | |
| 980 | |
| 981 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL( | |
| 982 const FileSystemURL& src_url) const { | |
| 983 DCHECK_EQ(src_root_.type(), src_url.type()); | |
| 984 DCHECK_EQ(src_root_.origin(), src_url.origin()); | |
| 985 | |
| 986 base::FilePath relative = dest_root_.virtual_path(); | |
| 987 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(), | |
| 988 &relative); | |
| 989 return file_system_context()->CreateCrackedFileSystemURL( | |
| 990 dest_root_.origin(), | |
| 991 dest_root_.mount_type(), | |
| 992 relative); | |
| 993 } | |
| 994 | |
| 995 } // namespace storage | |
| OLD | NEW |