Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / storage / browser / fileapi / file_system_operation_impl.cc
blob30d8561aff199cd6021b6006db4fca2dce88926e
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 ErrorBehavior error_behavior,
92 const CopyProgressCallback& progress_callback,
93 const StatusCallback& callback) {
94 DCHECK(SetPendingOperationType(kOperationCopy));
95 DCHECK(!recursive_operation_delegate_);
97 recursive_operation_delegate_.reset(new CopyOrMoveOperationDelegate(
98 file_system_context(), src_url, dest_url,
99 CopyOrMoveOperationDelegate::OPERATION_COPY, option, error_behavior,
100 progress_callback,
101 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
102 weak_factory_.GetWeakPtr(), callback)));
103 recursive_operation_delegate_->RunRecursively();
106 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
107 const FileSystemURL& dest_url,
108 CopyOrMoveOption option,
109 const StatusCallback& callback) {
110 DCHECK(SetPendingOperationType(kOperationMove));
111 DCHECK(!recursive_operation_delegate_);
112 recursive_operation_delegate_.reset(new CopyOrMoveOperationDelegate(
113 file_system_context(), src_url, dest_url,
114 CopyOrMoveOperationDelegate::OPERATION_MOVE, option, ERROR_BEHAVIOR_ABORT,
115 FileSystemOperation::CopyProgressCallback(),
116 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
117 weak_factory_.GetWeakPtr(), callback)));
118 recursive_operation_delegate_->RunRecursively();
121 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
122 const StatusCallback& callback) {
123 DCHECK(SetPendingOperationType(kOperationDirectoryExists));
124 async_file_util_->GetFileInfo(
125 operation_context_.Pass(), url,
126 base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
127 weak_factory_.GetWeakPtr(), callback));
130 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
131 const StatusCallback& callback) {
132 DCHECK(SetPendingOperationType(kOperationFileExists));
133 async_file_util_->GetFileInfo(
134 operation_context_.Pass(), url,
135 base::Bind(&FileSystemOperationImpl::DidFileExists,
136 weak_factory_.GetWeakPtr(), callback));
139 void FileSystemOperationImpl::GetMetadata(
140 const FileSystemURL& url, const GetMetadataCallback& callback) {
141 DCHECK(SetPendingOperationType(kOperationGetMetadata));
142 async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
145 void FileSystemOperationImpl::ReadDirectory(
146 const FileSystemURL& url, const ReadDirectoryCallback& callback) {
147 DCHECK(SetPendingOperationType(kOperationReadDirectory));
148 async_file_util_->ReadDirectory(
149 operation_context_.Pass(), url, callback);
152 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
153 bool recursive,
154 const StatusCallback& callback) {
155 DCHECK(SetPendingOperationType(kOperationRemove));
156 DCHECK(!recursive_operation_delegate_);
158 if (recursive) {
159 // For recursive removal, try to delegate the operation to AsyncFileUtil
160 // first. If not supported, it is delegated to RemoveOperationDelegate
161 // in DidDeleteRecursively.
162 async_file_util_->DeleteRecursively(
163 operation_context_.Pass(), url,
164 base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
165 weak_factory_.GetWeakPtr(), url, callback));
166 return;
169 recursive_operation_delegate_.reset(
170 new RemoveOperationDelegate(
171 file_system_context(), url,
172 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
173 weak_factory_.GetWeakPtr(), callback)));
174 recursive_operation_delegate_->Run();
177 void FileSystemOperationImpl::Write(
178 const FileSystemURL& url,
179 scoped_ptr<FileWriterDelegate> writer_delegate,
180 scoped_ptr<net::URLRequest> blob_request,
181 const WriteCallback& callback) {
182 DCHECK(SetPendingOperationType(kOperationWrite));
183 file_writer_delegate_ = writer_delegate.Pass();
184 file_writer_delegate_->Start(
185 blob_request.Pass(),
186 base::Bind(&FileSystemOperationImpl::DidWrite,
187 weak_factory_.GetWeakPtr(), url, callback));
190 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
191 const StatusCallback& callback) {
192 DCHECK(SetPendingOperationType(kOperationTruncate));
193 GetUsageAndQuotaThenRunTask(
194 url,
195 base::Bind(&FileSystemOperationImpl::DoTruncate,
196 weak_factory_.GetWeakPtr(), url, callback, length),
197 base::Bind(callback, base::File::FILE_ERROR_FAILED));
200 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
201 const base::Time& last_access_time,
202 const base::Time& last_modified_time,
203 const StatusCallback& callback) {
204 DCHECK(SetPendingOperationType(kOperationTouchFile));
205 async_file_util_->Touch(
206 operation_context_.Pass(), url,
207 last_access_time, last_modified_time,
208 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
209 weak_factory_.GetWeakPtr(), callback));
212 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
213 int file_flags,
214 const OpenFileCallback& callback) {
215 DCHECK(SetPendingOperationType(kOperationOpenFile));
217 if (file_flags &
218 (base::File::FLAG_TEMPORARY | base::File::FLAG_HIDDEN)) {
219 callback.Run(base::File(base::File::FILE_ERROR_FAILED),
220 base::Closure());
221 return;
223 GetUsageAndQuotaThenRunTask(
224 url,
225 base::Bind(&FileSystemOperationImpl::DoOpenFile,
226 weak_factory_.GetWeakPtr(),
227 url, callback, file_flags),
228 base::Bind(callback, Passed(base::File(base::File::FILE_ERROR_FAILED)),
229 base::Closure()));
232 // We can only get here on a write or truncate that's not yet completed.
233 // We don't support cancelling any other operation at this time.
234 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
235 DCHECK(cancel_callback_.is_null());
236 cancel_callback_ = cancel_callback;
238 if (file_writer_delegate_.get()) {
239 DCHECK_EQ(kOperationWrite, pending_operation_);
240 // This will call DidWrite() with ABORT status code.
241 file_writer_delegate_->Cancel();
242 } else if (recursive_operation_delegate_) {
243 // This will call DidFinishOperation() with ABORT status code.
244 recursive_operation_delegate_->Cancel();
245 } else {
246 // For truncate we have no way to cancel the inflight operation (for now).
247 // Let it just run and dispatch cancel callback later.
248 DCHECK_EQ(kOperationTruncate, pending_operation_);
252 void FileSystemOperationImpl::CreateSnapshotFile(
253 const FileSystemURL& url,
254 const SnapshotFileCallback& callback) {
255 DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
256 async_file_util_->CreateSnapshotFile(
257 operation_context_.Pass(), url, callback);
260 void FileSystemOperationImpl::CopyInForeignFile(
261 const base::FilePath& src_local_disk_file_path,
262 const FileSystemURL& dest_url,
263 const StatusCallback& callback) {
264 DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
265 GetUsageAndQuotaThenRunTask(
266 dest_url,
267 base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
268 weak_factory_.GetWeakPtr(), src_local_disk_file_path, dest_url,
269 callback),
270 base::Bind(callback, base::File::FILE_ERROR_FAILED));
273 void FileSystemOperationImpl::RemoveFile(
274 const FileSystemURL& url,
275 const StatusCallback& callback) {
276 DCHECK(SetPendingOperationType(kOperationRemove));
277 async_file_util_->DeleteFile(
278 operation_context_.Pass(), url,
279 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
280 weak_factory_.GetWeakPtr(), callback));
283 void FileSystemOperationImpl::RemoveDirectory(
284 const FileSystemURL& url,
285 const StatusCallback& callback) {
286 DCHECK(SetPendingOperationType(kOperationRemove));
287 async_file_util_->DeleteDirectory(
288 operation_context_.Pass(), url,
289 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
290 weak_factory_.GetWeakPtr(), callback));
293 void FileSystemOperationImpl::CopyFileLocal(
294 const FileSystemURL& src_url,
295 const FileSystemURL& dest_url,
296 CopyOrMoveOption option,
297 const CopyFileProgressCallback& progress_callback,
298 const StatusCallback& callback) {
299 DCHECK(SetPendingOperationType(kOperationCopy));
300 DCHECK(src_url.IsInSameFileSystem(dest_url));
302 GetUsageAndQuotaThenRunTask(
303 dest_url,
304 base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
305 weak_factory_.GetWeakPtr(), src_url, dest_url, option,
306 progress_callback, callback),
307 base::Bind(callback, base::File::FILE_ERROR_FAILED));
310 void FileSystemOperationImpl::MoveFileLocal(
311 const FileSystemURL& src_url,
312 const FileSystemURL& dest_url,
313 CopyOrMoveOption option,
314 const StatusCallback& callback) {
315 DCHECK(SetPendingOperationType(kOperationMove));
316 DCHECK(src_url.IsInSameFileSystem(dest_url));
317 GetUsageAndQuotaThenRunTask(
318 dest_url,
319 base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
320 weak_factory_.GetWeakPtr(),
321 src_url, dest_url, option, callback),
322 base::Bind(callback, base::File::FILE_ERROR_FAILED));
325 base::File::Error FileSystemOperationImpl::SyncGetPlatformPath(
326 const FileSystemURL& url,
327 base::FilePath* platform_path) {
328 DCHECK(SetPendingOperationType(kOperationGetLocalPath));
329 if (!file_system_context()->IsSandboxFileSystem(url.type()))
330 return base::File::FILE_ERROR_INVALID_OPERATION;
331 FileSystemFileUtil* file_util =
332 file_system_context()->sandbox_delegate()->sync_file_util();
333 file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
334 return base::File::FILE_OK;
337 FileSystemOperationImpl::FileSystemOperationImpl(
338 const FileSystemURL& url,
339 FileSystemContext* file_system_context,
340 scoped_ptr<FileSystemOperationContext> operation_context)
341 : file_system_context_(file_system_context),
342 operation_context_(operation_context.Pass()),
343 async_file_util_(NULL),
344 pending_operation_(kOperationNone),
345 weak_factory_(this) {
346 DCHECK(operation_context_.get());
347 operation_context_->DetachUserDataThread();
348 async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
349 DCHECK(async_file_util_);
352 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
353 const FileSystemURL& url,
354 const base::Closure& task,
355 const base::Closure& error_callback) {
356 storage::QuotaManagerProxy* quota_manager_proxy =
357 file_system_context()->quota_manager_proxy();
358 if (!quota_manager_proxy ||
359 !file_system_context()->GetQuotaUtil(url.type())) {
360 // If we don't have the quota manager or the requested filesystem type
361 // does not support quota, we should be able to let it go.
362 operation_context_->set_allowed_bytes_growth(kint64max);
363 task.Run();
364 return;
367 DCHECK(quota_manager_proxy);
368 DCHECK(quota_manager_proxy->quota_manager());
369 quota_manager_proxy->quota_manager()->GetUsageAndQuota(
370 url.origin(),
371 FileSystemTypeToQuotaStorageType(url.type()),
372 base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
373 weak_factory_.GetWeakPtr(), task, error_callback));
376 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
377 const base::Closure& task,
378 const base::Closure& error_callback,
379 storage::QuotaStatusCode status,
380 int64 usage,
381 int64 quota) {
382 if (status != storage::kQuotaStatusOk) {
383 LOG(WARNING) << "Got unexpected quota error : " << status;
384 error_callback.Run();
385 return;
388 operation_context_->set_allowed_bytes_growth(quota - usage);
389 task.Run();
392 void FileSystemOperationImpl::DoCreateFile(
393 const FileSystemURL& url,
394 const StatusCallback& callback,
395 bool exclusive) {
396 async_file_util_->EnsureFileExists(
397 operation_context_.Pass(), url,
398 base::Bind(
399 exclusive ?
400 &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
401 &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
402 weak_factory_.GetWeakPtr(), callback));
405 void FileSystemOperationImpl::DoCreateDirectory(
406 const FileSystemURL& url,
407 const StatusCallback& callback,
408 bool exclusive, bool recursive) {
409 async_file_util_->CreateDirectory(
410 operation_context_.Pass(),
411 url, exclusive, recursive,
412 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
413 weak_factory_.GetWeakPtr(), callback));
416 void FileSystemOperationImpl::DoCopyFileLocal(
417 const FileSystemURL& src_url,
418 const FileSystemURL& dest_url,
419 CopyOrMoveOption option,
420 const CopyFileProgressCallback& progress_callback,
421 const StatusCallback& callback) {
422 async_file_util_->CopyFileLocal(
423 operation_context_.Pass(), src_url, dest_url, option, progress_callback,
424 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
425 weak_factory_.GetWeakPtr(), callback));
428 void FileSystemOperationImpl::DoMoveFileLocal(
429 const FileSystemURL& src_url,
430 const FileSystemURL& dest_url,
431 CopyOrMoveOption option,
432 const StatusCallback& callback) {
433 async_file_util_->MoveFileLocal(
434 operation_context_.Pass(), src_url, dest_url, option,
435 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
436 weak_factory_.GetWeakPtr(), callback));
439 void FileSystemOperationImpl::DoCopyInForeignFile(
440 const base::FilePath& src_local_disk_file_path,
441 const FileSystemURL& dest_url,
442 const StatusCallback& callback) {
443 async_file_util_->CopyInForeignFile(
444 operation_context_.Pass(),
445 src_local_disk_file_path, dest_url,
446 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
447 weak_factory_.GetWeakPtr(), callback));
450 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
451 const StatusCallback& callback,
452 int64 length) {
453 async_file_util_->Truncate(
454 operation_context_.Pass(), url, length,
455 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
456 weak_factory_.GetWeakPtr(), callback));
459 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
460 const OpenFileCallback& callback,
461 int file_flags) {
462 async_file_util_->CreateOrOpen(
463 operation_context_.Pass(), url, file_flags,
464 base::Bind(&DidOpenFile,
465 file_system_context_, weak_factory_.GetWeakPtr(), callback));
468 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
469 const StatusCallback& callback,
470 base::File::Error rv, bool created) {
471 if (rv == base::File::FILE_OK && !created) {
472 callback.Run(base::File::FILE_ERROR_EXISTS);
473 } else {
474 DidFinishOperation(callback, rv);
478 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
479 const StatusCallback& callback,
480 base::File::Error rv, bool /* created */) {
481 DidFinishOperation(callback, rv);
484 void FileSystemOperationImpl::DidFinishOperation(
485 const StatusCallback& callback,
486 base::File::Error rv) {
487 if (!cancel_callback_.is_null()) {
488 StatusCallback cancel_callback = cancel_callback_;
489 callback.Run(rv);
491 // Return OK only if we succeeded to stop the operation.
492 cancel_callback.Run(rv == base::File::FILE_ERROR_ABORT ?
493 base::File::FILE_OK :
494 base::File::FILE_ERROR_INVALID_OPERATION);
495 } else {
496 callback.Run(rv);
500 void FileSystemOperationImpl::DidDirectoryExists(
501 const StatusCallback& callback,
502 base::File::Error rv,
503 const base::File::Info& file_info) {
504 if (rv == base::File::FILE_OK && !file_info.is_directory)
505 rv = base::File::FILE_ERROR_NOT_A_DIRECTORY;
506 callback.Run(rv);
509 void FileSystemOperationImpl::DidFileExists(
510 const StatusCallback& callback,
511 base::File::Error rv,
512 const base::File::Info& file_info) {
513 if (rv == base::File::FILE_OK && file_info.is_directory)
514 rv = base::File::FILE_ERROR_NOT_A_FILE;
515 callback.Run(rv);
518 void FileSystemOperationImpl::DidDeleteRecursively(
519 const FileSystemURL& url,
520 const StatusCallback& callback,
521 base::File::Error rv) {
522 if (rv == base::File::FILE_ERROR_INVALID_OPERATION) {
523 // Recursive removal is not supported on this platform.
524 DCHECK(!recursive_operation_delegate_);
525 recursive_operation_delegate_.reset(
526 new RemoveOperationDelegate(
527 file_system_context(), url,
528 base::Bind(&FileSystemOperationImpl::DidFinishOperation,
529 weak_factory_.GetWeakPtr(), callback)));
530 recursive_operation_delegate_->RunRecursively();
531 return;
534 callback.Run(rv);
537 void FileSystemOperationImpl::DidWrite(
538 const FileSystemURL& url,
539 const WriteCallback& write_callback,
540 base::File::Error rv,
541 int64 bytes,
542 FileWriterDelegate::WriteProgressStatus write_status) {
543 const bool complete = (
544 write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
545 if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
546 DCHECK(operation_context_);
547 operation_context_->change_observers()->Notify(
548 &FileChangeObserver::OnModifyFile, base::MakeTuple(url));
551 StatusCallback cancel_callback = cancel_callback_;
552 write_callback.Run(rv, bytes, complete);
553 if (!cancel_callback.is_null())
554 cancel_callback.Run(base::File::FILE_OK);
557 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
558 if (pending_operation_ != kOperationNone)
559 return false;
560 pending_operation_ = type;
561 return true;
564 } // namespace storage