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 |