Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / fileapi / file_system_operation_impl.cc
blobbf259672068ccf63d561f49054b69d746aaf71a7
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.
5 #include "storage/browser/fileapi/file_system_operation_impl.h"
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 "storage/browser/blob/shareable_file_reference.h"
14 #include "storage/browser/fileapi/async_file_util.h"
15 #include "storage/browser/fileapi/copy_or_move_operation_delegate.h"
16 #include "storage/browser/fileapi/file_observers.h"
17 #include "storage/browser/fileapi/file_system_backend.h"
18 #include "storage/browser/fileapi/file_system_context.h"
19 #include "storage/browser/fileapi/file_system_file_util.h"
20 #include "storage/browser/fileapi/remove_operation_delegate.h"
21 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
22 #include "storage/browser/quota/quota_manager_proxy.h"
23 #include "storage/common/fileapi/file_system_types.h"
24 #include "storage/common/fileapi/file_system_util.h"
26 using storage::ScopedFile;
28 namespace storage {
30 namespace {
32 // Takes ownership and destruct on the target thread.
33 void Destruct(base::File file) {}
35 void DidOpenFile(
36 scoped_refptr<FileSystemContext> context,
37 base::WeakPtr<FileSystemOperationImpl> operation,
38 const FileSystemOperationImpl::OpenFileCallback& callback,
39 base::File file,
40 const base::Closure& on_close_callback) {
41 if (!operation) {
42 context->default_file_task_runner()->PostTask(
43 FROM_HERE,
44 base::Bind(&Destruct, base::Passed(&file)));
45 return;
47 callback.Run(file.Pass(), on_close_callback);
50 } // namespace
52 FileSystemOperation* FileSystemOperation::Create(
53 const FileSystemURL& url,
54 FileSystemContext* file_system_context,
55 scoped_ptr<FileSystemOperationContext> operation_context) {
56 return new FileSystemOperationImpl(url, file_system_context,
57 operation_context.Pass());
60 FileSystemOperationImpl::~FileSystemOperationImpl() {
63 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
64 bool exclusive,
65 const StatusCallback& callback) {
66 DCHECK(SetPendingOperationType(kOperationCreateFile));
67 GetUsageAndQuotaThenRunTask(
68 url,
69 base::Bind(&FileSystemOperationImpl::DoCreateFile,
70 weak_factory_.GetWeakPtr(), url, callback, exclusive),
71 base::Bind(callback, base::File::FILE_ERROR_FAILED));
74 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
75 bool exclusive,
76 bool recursive,
77 const StatusCallback& callback) {
78 DCHECK(SetPendingOperationType(kOperationCreateDirectory));
79 GetUsageAndQuotaThenRunTask(
80 url,
81 base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
82 weak_factory_.GetWeakPtr(), url, callback,
83 exclusive, recursive),
84 base::Bind(callback, base::File::FILE_ERROR_FAILED));
87 void FileSystemOperationImpl::Copy(
88 const FileSystemURL& src_url,
89 const FileSystemURL& dest_url,
90 CopyOrMoveOption option,
91 const CopyProgressCallback& progress_callback,
92 const StatusCallback& callback) {
93 DCHECK(SetPendingOperationType(kOperationCopy));
94 DCHECK(!recursive_operation_delegate_);
96 // TODO(hidehiko): Support |progress_callback|. (crbug.com/278038).
97 recursive_operation_delegate_.reset(
98 new CopyOrMoveOperationDelegate(
99 file_system_context(),
100 src_url, dest_url,
101 CopyOrMoveOperationDelegate::OPERATION_COPY,
102 option,
103 progress_callback,
104 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
105 weak_factory_.GetWeakPtr(), callback)));
106 recursive_operation_delegate_->RunRecursively();
109 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
110 const FileSystemURL& dest_url,
111 CopyOrMoveOption option,
112 const StatusCallback& callback) {
113 DCHECK(SetPendingOperationType(kOperationMove));
114 DCHECK(!recursive_operation_delegate_);
115 recursive_operation_delegate_.reset(
116 new CopyOrMoveOperationDelegate(
117 file_system_context(),
118 src_url, dest_url,
119 CopyOrMoveOperationDelegate::OPERATION_MOVE,
120 option,
121 FileSystemOperation::CopyProgressCallback(),
122 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
123 weak_factory_.GetWeakPtr(), callback)));
124 recursive_operation_delegate_->RunRecursively();
127 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
128 const StatusCallback& callback) {
129 DCHECK(SetPendingOperationType(kOperationDirectoryExists));
130 async_file_util_->GetFileInfo(
131 operation_context_.Pass(), url,
132 base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
133 weak_factory_.GetWeakPtr(), callback));
136 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
137 const StatusCallback& callback) {
138 DCHECK(SetPendingOperationType(kOperationFileExists));
139 async_file_util_->GetFileInfo(
140 operation_context_.Pass(), url,
141 base::Bind(&FileSystemOperationImpl::DidFileExists,
142 weak_factory_.GetWeakPtr(), callback));
145 void FileSystemOperationImpl::GetMetadata(
146 const FileSystemURL& url, const GetMetadataCallback& callback) {
147 DCHECK(SetPendingOperationType(kOperationGetMetadata));
148 async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
151 void FileSystemOperationImpl::ReadDirectory(
152 const FileSystemURL& url, const ReadDirectoryCallback& callback) {
153 DCHECK(SetPendingOperationType(kOperationReadDirectory));
154 async_file_util_->ReadDirectory(
155 operation_context_.Pass(), url, callback);
158 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
159 bool recursive,
160 const StatusCallback& callback) {
161 DCHECK(SetPendingOperationType(kOperationRemove));
162 DCHECK(!recursive_operation_delegate_);
164 if (recursive) {
165 // For recursive removal, try to delegate the operation to AsyncFileUtil
166 // first. If not supported, it is delegated to RemoveOperationDelegate
167 // in DidDeleteRecursively.
168 async_file_util_->DeleteRecursively(
169 operation_context_.Pass(), url,
170 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
171 weak_factory_.GetWeakPtr(), url, callback));
172 return;
175 recursive_operation_delegate_.reset(
176 new RemoveOperationDelegate(
177 file_system_context(), url,
178 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
179 weak_factory_.GetWeakPtr(), callback)));
180 recursive_operation_delegate_->Run();
183 void FileSystemOperationImpl::Write(
184 const FileSystemURL& url,
185 scoped_ptr<FileWriterDelegate> writer_delegate,
186 scoped_ptr<net::URLRequest> blob_request,
187 const WriteCallback& callback) {
188 DCHECK(SetPendingOperationType(kOperationWrite));
189 file_writer_delegate_ = writer_delegate.Pass();
190 file_writer_delegate_->Start(
191 blob_request.Pass(),
192 base::Bind(&FileSystemOperationImpl::DidWrite,
193 weak_factory_.GetWeakPtr(), url, callback));
196 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
197 const StatusCallback& callback) {
198 DCHECK(SetPendingOperationType(kOperationTruncate));
199 GetUsageAndQuotaThenRunTask(
200 url,
201 base::Bind(&FileSystemOperationImpl::DoTruncate,
202 weak_factory_.GetWeakPtr(), url, callback, length),
203 base::Bind(callback, base::File::FILE_ERROR_FAILED));
206 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
207 const base::Time& last_access_time,
208 const base::Time& last_modified_time,
209 const StatusCallback& callback) {
210 DCHECK(SetPendingOperationType(kOperationTouchFile));
211 async_file_util_->Touch(
212 operation_context_.Pass(), url,
213 last_access_time, last_modified_time,
214 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
215 weak_factory_.GetWeakPtr(), callback));
218 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
219 int file_flags,
220 const OpenFileCallback& callback) {
221 DCHECK(SetPendingOperationType(kOperationOpenFile));
223 if (file_flags &
224 (base::File::FLAG_TEMPORARY | base::File::FLAG_HIDDEN)) {
225 callback.Run(base::File(base::File::FILE_ERROR_FAILED),
226 base::Closure());
227 return;
229 GetUsageAndQuotaThenRunTask(
230 url,
231 base::Bind(&FileSystemOperationImpl::DoOpenFile,
232 weak_factory_.GetWeakPtr(),
233 url, callback, file_flags),
234 base::Bind(callback, Passed(base::File(base::File::FILE_ERROR_FAILED)),
235 base::Closure()));
238 // We can only get here on a write or truncate that's not yet completed.
239 // We don't support cancelling any other operation at this time.
240 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
241 DCHECK(cancel_callback_.is_null());
242 cancel_callback_ = cancel_callback;
244 if (file_writer_delegate_.get()) {
245 DCHECK_EQ(kOperationWrite, pending_operation_);
246 // This will call DidWrite() with ABORT status code.
247 file_writer_delegate_->Cancel();
248 } else if (recursive_operation_delegate_) {
249 // This will call DidFinishOperation() with ABORT status code.
250 recursive_operation_delegate_->Cancel();
251 } else {
252 // For truncate we have no way to cancel the inflight operation (for now).
253 // Let it just run and dispatch cancel callback later.
254 DCHECK_EQ(kOperationTruncate, pending_operation_);
258 void FileSystemOperationImpl::CreateSnapshotFile(
259 const FileSystemURL& url,
260 const SnapshotFileCallback& callback) {
261 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
262 async_file_util_->CreateSnapshotFile(
263 operation_context_.Pass(), url, callback);
266 void FileSystemOperationImpl::CopyInForeignFile(
267 const base::FilePath& src_local_disk_file_path,
268 const FileSystemURL& dest_url,
269 const StatusCallback& callback) {
270 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
271 GetUsageAndQuotaThenRunTask(
272 dest_url,
273 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
274 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
275 callback),
276 base::Bind(callback, base::File::FILE_ERROR_FAILED));
279 void FileSystemOperationImpl::RemoveFile(
280 const FileSystemURL& url,
281 const StatusCallback& callback) {
282 DCHECK(SetPendingOperationType(kOperationRemove));
283 async_file_util_->DeleteFile(
284 operation_context_.Pass(), url,
285 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
286 weak_factory_.GetWeakPtr(), callback));
289 void FileSystemOperationImpl::RemoveDirectory(
290 const FileSystemURL& url,
291 const StatusCallback& callback) {
292 DCHECK(SetPendingOperationType(kOperationRemove));
293 async_file_util_->DeleteDirectory(
294 operation_context_.Pass(), url,
295 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
296 weak_factory_.GetWeakPtr(), callback));
299 void FileSystemOperationImpl::CopyFileLocal(
300 const FileSystemURL& src_url,
301 const FileSystemURL& dest_url,
302 CopyOrMoveOption option,
303 const CopyFileProgressCallback& progress_callback,
304 const StatusCallback& callback) {
305 DCHECK(SetPendingOperationType(kOperationCopy));
306 DCHECK(src_url.IsInSameFileSystem(dest_url));
308 GetUsageAndQuotaThenRunTask(
309 dest_url,
310 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
311 weak_factory_.GetWeakPtr(), src_url, dest_url, option,
312 progress_callback, callback),
313 base::Bind(callback, base::File::FILE_ERROR_FAILED));
316 void FileSystemOperationImpl::MoveFileLocal(
317 const FileSystemURL& src_url,
318 const FileSystemURL& dest_url,
319 CopyOrMoveOption option,
320 const StatusCallback& callback) {
321 DCHECK(SetPendingOperationType(kOperationMove));
322 DCHECK(src_url.IsInSameFileSystem(dest_url));
323 GetUsageAndQuotaThenRunTask(
324 dest_url,
325 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
326 weak_factory_.GetWeakPtr(),
327 src_url, dest_url, option, callback),
328 base::Bind(callback, base::File::FILE_ERROR_FAILED));
331 base::File::Error FileSystemOperationImpl::SyncGetPlatformPath(
332 const FileSystemURL& url,
333 base::FilePath* platform_path) {
334 DCHECK(SetPendingOperationType(kOperationGetLocalPath));
335 if (!file_system_context()->IsSandboxFileSystem(url.type()))
336 return base::File::FILE_ERROR_INVALID_OPERATION;
337 FileSystemFileUtil* file_util =
338 file_system_context()->sandbox_delegate()->sync_file_util();
339 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
340 return base::File::FILE_OK;
343 FileSystemOperationImpl::FileSystemOperationImpl(
344 const FileSystemURL& url,
345 FileSystemContext* file_system_context,
346 scoped_ptr<FileSystemOperationContext> operation_context)
347 : file_system_context_(file_system_context),
348 operation_context_(operation_context.Pass()),
349 async_file_util_(NULL),
350 pending_operation_(kOperationNone),
351 weak_factory_(this) {
352 DCHECK(operation_context_.get());
353 operation_context_->DetachUserDataThread();
354 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
355 DCHECK(async_file_util_);
358 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
359 const FileSystemURL& url,
360 const base::Closure& task,
361 const base::Closure& error_callback) {
362 storage::QuotaManagerProxy* quota_manager_proxy =
363 file_system_context()->quota_manager_proxy();
364 if (!quota_manager_proxy ||
365 !file_system_context()->GetQuotaUtil(url.type())) {
366 // If we don't have the quota manager or the requested filesystem type
367 // does not support quota, we should be able to let it go.
368 operation_context_->set_allowed_bytes_growth(kint64max);
369 task.Run();
370 return;
373 DCHECK(quota_manager_proxy);
374 DCHECK(quota_manager_proxy->quota_manager());
375 quota_manager_proxy->quota_manager()->GetUsageAndQuota(
376 url.origin(),
377 FileSystemTypeToQuotaStorageType(url.type()),
378 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
379 weak_factory_.GetWeakPtr(), task, error_callback));
382 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
383 const base::Closure& task,
384 const base::Closure& error_callback,
385 storage::QuotaStatusCode status,
386 int64 usage,
387 int64 quota) {
388 if (status != storage::kQuotaStatusOk) {
389 LOG(WARNING) << "Got unexpected quota error : " << status;
390 error_callback.Run();
391 return;
394 operation_context_->set_allowed_bytes_growth(quota - usage);
395 task.Run();
398 void FileSystemOperationImpl::DoCreateFile(
399 const FileSystemURL& url,
400 const StatusCallback& callback,
401 bool exclusive) {
402 async_file_util_->EnsureFileExists(
403 operation_context_.Pass(), url,
404 base::Bind(
405 exclusive ?
406 &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
407 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
408 weak_factory_.GetWeakPtr(), callback));
411 void FileSystemOperationImpl::DoCreateDirectory(
412 const FileSystemURL& url,
413 const StatusCallback& callback,
414 bool exclusive, bool recursive) {
415 async_file_util_->CreateDirectory(
416 operation_context_.Pass(),
417 url, exclusive, recursive,
418 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
419 weak_factory_.GetWeakPtr(), callback));
422 void FileSystemOperationImpl::DoCopyFileLocal(
423 const FileSystemURL& src_url,
424 const FileSystemURL& dest_url,
425 CopyOrMoveOption option,
426 const CopyFileProgressCallback& progress_callback,
427 const StatusCallback& callback) {
428 async_file_util_->CopyFileLocal(
429 operation_context_.Pass(), src_url, dest_url, option, progress_callback,
430 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
431 weak_factory_.GetWeakPtr(), callback));
434 void FileSystemOperationImpl::DoMoveFileLocal(
435 const FileSystemURL& src_url,
436 const FileSystemURL& dest_url,
437 CopyOrMoveOption option,
438 const StatusCallback& callback) {
439 async_file_util_->MoveFileLocal(
440 operation_context_.Pass(), src_url, dest_url, option,
441 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
442 weak_factory_.GetWeakPtr(), callback));
445 void FileSystemOperationImpl::DoCopyInForeignFile(
446 const base::FilePath& src_local_disk_file_path,
447 const FileSystemURL& dest_url,
448 const StatusCallback& callback) {
449 async_file_util_->CopyInForeignFile(
450 operation_context_.Pass(),
451 src_local_disk_file_path, dest_url,
452 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
453 weak_factory_.GetWeakPtr(), callback));
456 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
457 const StatusCallback& callback,
458 int64 length) {
459 async_file_util_->Truncate(
460 operation_context_.Pass(), url, length,
461 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
462 weak_factory_.GetWeakPtr(), callback));
465 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
466 const OpenFileCallback& callback,
467 int file_flags) {
468 async_file_util_->CreateOrOpen(
469 operation_context_.Pass(), url, file_flags,
470 base::Bind(&DidOpenFile,
471 file_system_context_, weak_factory_.GetWeakPtr(), callback));
474 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
475 const StatusCallback& callback,
476 base::File::Error rv, bool created) {
477 if (rv == base::File::FILE_OK && !created) {
478 callback.Run(base::File::FILE_ERROR_EXISTS);
479 } else {
480 DidFinishOperation(callback, rv);
484 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
485 const StatusCallback& callback,
486 base::File::Error rv, bool /* created */) {
487 DidFinishOperation(callback, rv);
490 void FileSystemOperationImpl::DidFinishOperation(
491 const StatusCallback& callback,
492 base::File::Error rv) {
493 if (!cancel_callback_.is_null()) {
494 StatusCallback cancel_callback = cancel_callback_;
495 callback.Run(rv);
497 // Return OK only if we succeeded to stop the operation.
498 cancel_callback.Run(rv == base::File::FILE_ERROR_ABORT ?
499 base::File::FILE_OK :
500 base::File::FILE_ERROR_INVALID_OPERATION);
501 } else {
502 callback.Run(rv);
506 void FileSystemOperationImpl::DidDirectoryExists(
507 const StatusCallback& callback,
508 base::File::Error rv,
509 const base::File::Info& file_info) {
510 if (rv == base::File::FILE_OK && !file_info.is_directory)
511 rv = base::File::FILE_ERROR_NOT_A_DIRECTORY;
512 callback.Run(rv);
515 void FileSystemOperationImpl::DidFileExists(
516 const StatusCallback& callback,
517 base::File::Error rv,
518 const base::File::Info& file_info) {
519 if (rv == base::File::FILE_OK && file_info.is_directory)
520 rv = base::File::FILE_ERROR_NOT_A_FILE;
521 callback.Run(rv);
524 void FileSystemOperationImpl::DidDeleteRecursively(
525 const FileSystemURL& url,
526 const StatusCallback& callback,
527 base::File::Error rv) {
528 if (rv == base::File::FILE_ERROR_INVALID_OPERATION) {
529 // Recursive removal is not supported on this platform.
530 DCHECK(!recursive_operation_delegate_);
531 recursive_operation_delegate_.reset(
532 new RemoveOperationDelegate(
533 file_system_context(), url,
534 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
535 weak_factory_.GetWeakPtr(), callback)));
536 recursive_operation_delegate_->RunRecursively();
537 return;
540 callback.Run(rv);
543 void FileSystemOperationImpl::DidWrite(
544 const FileSystemURL& url,
545 const WriteCallback& write_callback,
546 base::File::Error rv,
547 int64 bytes,
548 FileWriterDelegate::WriteProgressStatus write_status) {
549 const bool complete = (
550 write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
551 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
552 DCHECK(operation_context_);
553 operation_context_->change_observers()->Notify(
554 &FileChangeObserver::OnModifyFile, base::MakeTuple(url));
557 StatusCallback cancel_callback = cancel_callback_;
558 write_callback.Run(rv, bytes, complete);
559 if (!cancel_callback.is_null())
560 cancel_callback.Run(base::File::FILE_OK);
563 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
564 if (pending_operation_ != kOperationNone)
565 return false;
566 pending_operation_ = type;
567 return true;
570 } // namespace storage