1 // Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
9 #include "base/debug/trace_event.h"
10 #include "base/files/file.h"
11 #include "chrome/browser/chromeos/file_system_provider/notification_manager.h"
12 #include "chrome/browser/chromeos/file_system_provider/operations/abort.h"
13 #include "chrome/browser/chromeos/file_system_provider/operations/add_watcher.h"
14 #include "chrome/browser/chromeos/file_system_provider/operations/close_file.h"
15 #include "chrome/browser/chromeos/file_system_provider/operations/copy_entry.h"
16 #include "chrome/browser/chromeos/file_system_provider/operations/create_directory.h"
17 #include "chrome/browser/chromeos/file_system_provider/operations/create_file.h"
18 #include "chrome/browser/chromeos/file_system_provider/operations/delete_entry.h"
19 #include "chrome/browser/chromeos/file_system_provider/operations/get_metadata.h"
20 #include "chrome/browser/chromeos/file_system_provider/operations/move_entry.h"
21 #include "chrome/browser/chromeos/file_system_provider/operations/open_file.h"
22 #include "chrome/browser/chromeos/file_system_provider/operations/read_directory.h"
23 #include "chrome/browser/chromeos/file_system_provider/operations/read_file.h"
24 #include "chrome/browser/chromeos/file_system_provider/operations/remove_watcher.h"
25 #include "chrome/browser/chromeos/file_system_provider/operations/truncate.h"
26 #include "chrome/browser/chromeos/file_system_provider/operations/unmount.h"
27 #include "chrome/browser/chromeos/file_system_provider/operations/write_file.h"
28 #include "chrome/browser/chromeos/file_system_provider/request_manager.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/common/extensions/api/file_system_provider.h"
31 #include "extensions/browser/event_router.h"
38 namespace file_system_provider
{
41 // Discards the result of Abort() when called from the destructor.
42 void EmptyStatusCallback(base::File::Error
/* result */) {
45 // Discards the error code and always calls the callback with a success.
46 void AlwaysSuccessCallback(
47 const storage::AsyncFileUtil::StatusCallback
& callback
,
48 base::File::Error
/* result */) {
49 callback
.Run(base::File::FILE_OK
);
54 AutoUpdater::AutoUpdater(const base::Closure
& update_callback
)
55 : update_callback_(update_callback
),
56 created_callbacks_(0),
57 pending_callbacks_(0) {
60 base::Closure
AutoUpdater::CreateCallback() {
63 return base::Bind(&AutoUpdater::OnPendingCallback
, this);
66 void AutoUpdater::OnPendingCallback() {
67 DCHECK_LT(0, pending_callbacks_
);
68 if (--pending_callbacks_
== 0)
69 update_callback_
.Run();
72 AutoUpdater::~AutoUpdater() {
73 // If no callbacks are created, then we need to invoke updating in the
75 if (!created_callbacks_
)
76 update_callback_
.Run();
77 else if (pending_callbacks_
)
78 LOG(ERROR
) << "Not all callbacks called. This may happen on shutdown.";
81 ProvidedFileSystem::ProvidedFileSystem(
83 const ProvidedFileSystemInfo
& file_system_info
)
85 event_router_(extensions::EventRouter::Get(profile
)), // May be NULL.
86 file_system_info_(file_system_info
),
87 notification_manager_(
88 new NotificationManager(profile_
, file_system_info_
)),
89 request_manager_(new RequestManager(notification_manager_
.get())),
90 weak_ptr_factory_(this) {
93 ProvidedFileSystem::~ProvidedFileSystem() {
94 const std::vector
<int> request_ids
= request_manager_
->GetActiveRequestIds();
95 for (size_t i
= 0; i
< request_ids
.size(); ++i
) {
96 Abort(request_ids
[i
], base::Bind(&EmptyStatusCallback
));
100 void ProvidedFileSystem::SetEventRouterForTesting(
101 extensions::EventRouter
* event_router
) {
102 event_router_
= event_router
;
105 void ProvidedFileSystem::SetNotificationManagerForTesting(
106 scoped_ptr
<NotificationManagerInterface
> notification_manager
) {
107 notification_manager_
= notification_manager
.Pass();
108 request_manager_
.reset(new RequestManager(notification_manager_
.get()));
111 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::RequestUnmount(
112 const storage::AsyncFileUtil::StatusCallback
& callback
) {
113 const int request_id
= request_manager_
->CreateRequest(
115 scoped_ptr
<RequestManager::HandlerInterface
>(
116 new operations::Unmount(event_router_
, file_system_info_
, callback
)));
118 callback
.Run(base::File::FILE_ERROR_SECURITY
);
119 return AbortCallback();
123 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
126 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::GetMetadata(
127 const base::FilePath
& entry_path
,
128 MetadataFieldMask fields
,
129 const GetMetadataCallback
& callback
) {
130 const int request_id
= request_manager_
->CreateRequest(
132 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::GetMetadata(
133 event_router_
, file_system_info_
, entry_path
, fields
, callback
)));
135 callback
.Run(make_scoped_ptr
<EntryMetadata
>(NULL
),
136 base::File::FILE_ERROR_SECURITY
);
137 return AbortCallback();
141 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
144 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::ReadDirectory(
145 const base::FilePath
& directory_path
,
146 const storage::AsyncFileUtil::ReadDirectoryCallback
& callback
) {
147 const int request_id
= request_manager_
->CreateRequest(
149 scoped_ptr
<RequestManager::HandlerInterface
>(
150 new operations::ReadDirectory(
151 event_router_
, file_system_info_
, directory_path
, callback
)));
153 callback
.Run(base::File::FILE_ERROR_SECURITY
,
154 storage::AsyncFileUtil::EntryList(),
155 false /* has_more */);
156 return AbortCallback();
160 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
163 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::ReadFile(
165 net::IOBuffer
* buffer
,
168 const ReadChunkReceivedCallback
& callback
) {
170 "file_system_provider", "ProvidedFileSystem::ReadFile", "length", length
);
171 const int request_id
= request_manager_
->CreateRequest(
173 make_scoped_ptr
<RequestManager::HandlerInterface
>(
174 new operations::ReadFile(event_router_
,
182 callback
.Run(0 /* chunk_length */,
183 false /* has_more */,
184 base::File::FILE_ERROR_SECURITY
);
185 return AbortCallback();
189 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
192 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::OpenFile(
193 const base::FilePath
& file_path
,
195 const OpenFileCallback
& callback
) {
196 const int request_id
= request_manager_
->CreateRequest(
198 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::OpenFile(
199 event_router_
, file_system_info_
, file_path
, mode
, callback
)));
201 callback
.Run(0 /* file_handle */, base::File::FILE_ERROR_SECURITY
);
202 return AbortCallback();
206 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
209 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::CloseFile(
211 const storage::AsyncFileUtil::StatusCallback
& callback
) {
212 const int request_id
= request_manager_
->CreateRequest(
214 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::CloseFile(
215 event_router_
, file_system_info_
, file_handle
, callback
)));
217 callback
.Run(base::File::FILE_ERROR_SECURITY
);
218 return AbortCallback();
222 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
225 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::CreateDirectory(
226 const base::FilePath
& directory_path
,
228 const storage::AsyncFileUtil::StatusCallback
& callback
) {
229 const int request_id
= request_manager_
->CreateRequest(
231 scoped_ptr
<RequestManager::HandlerInterface
>(
232 new operations::CreateDirectory(event_router_
,
238 callback
.Run(base::File::FILE_ERROR_SECURITY
);
239 return AbortCallback();
243 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
246 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::DeleteEntry(
247 const base::FilePath
& entry_path
,
249 const storage::AsyncFileUtil::StatusCallback
& callback
) {
250 const int request_id
= request_manager_
->CreateRequest(
252 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::DeleteEntry(
253 event_router_
, file_system_info_
, entry_path
, recursive
, callback
)));
255 callback
.Run(base::File::FILE_ERROR_SECURITY
);
256 return AbortCallback();
260 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
263 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::CreateFile(
264 const base::FilePath
& file_path
,
265 const storage::AsyncFileUtil::StatusCallback
& callback
) {
266 const int request_id
= request_manager_
->CreateRequest(
268 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::CreateFile(
269 event_router_
, file_system_info_
, file_path
, callback
)));
271 callback
.Run(base::File::FILE_ERROR_SECURITY
);
272 return AbortCallback();
276 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
279 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::CopyEntry(
280 const base::FilePath
& source_path
,
281 const base::FilePath
& target_path
,
282 const storage::AsyncFileUtil::StatusCallback
& callback
) {
283 const int request_id
= request_manager_
->CreateRequest(
285 scoped_ptr
<RequestManager::HandlerInterface
>(
286 new operations::CopyEntry(event_router_
,
292 callback
.Run(base::File::FILE_ERROR_SECURITY
);
293 return AbortCallback();
297 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
300 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::WriteFile(
302 net::IOBuffer
* buffer
,
305 const storage::AsyncFileUtil::StatusCallback
& callback
) {
306 TRACE_EVENT1("file_system_provider",
307 "ProvidedFileSystem::WriteFile",
310 const int request_id
= request_manager_
->CreateRequest(
312 make_scoped_ptr
<RequestManager::HandlerInterface
>(
313 new operations::WriteFile(event_router_
,
316 make_scoped_refptr(buffer
),
321 callback
.Run(base::File::FILE_ERROR_SECURITY
);
322 return AbortCallback();
326 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
329 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::MoveEntry(
330 const base::FilePath
& source_path
,
331 const base::FilePath
& target_path
,
332 const storage::AsyncFileUtil::StatusCallback
& callback
) {
333 const int request_id
= request_manager_
->CreateRequest(
335 scoped_ptr
<RequestManager::HandlerInterface
>(
336 new operations::MoveEntry(event_router_
,
342 callback
.Run(base::File::FILE_ERROR_SECURITY
);
343 return AbortCallback();
347 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
350 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::Truncate(
351 const base::FilePath
& file_path
,
353 const storage::AsyncFileUtil::StatusCallback
& callback
) {
354 const int request_id
= request_manager_
->CreateRequest(
356 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::Truncate(
357 event_router_
, file_system_info_
, file_path
, length
, callback
)));
359 callback
.Run(base::File::FILE_ERROR_SECURITY
);
360 return AbortCallback();
364 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
367 ProvidedFileSystem::AbortCallback
ProvidedFileSystem::AddWatcher(
369 const base::FilePath
& entry_path
,
372 const storage::AsyncFileUtil::StatusCallback
& callback
,
373 const storage::WatcherManager::NotificationCallback
&
374 notification_callback
) {
375 // TODO(mtomasz): Wrap the entire method body with an asynchronous queue to
377 if (persistent
&& (!file_system_info_
.supports_notify_tag() ||
378 !notification_callback
.is_null())) {
379 OnAddWatcherCompleted(entry_path
,
383 base::File::FILE_ERROR_INVALID_OPERATION
);
384 return AbortCallback();
387 // Create a candidate subscriber. This could be done in OnAddWatcherCompleted,
388 // but base::Bind supports only up to 7 arguments.
389 Subscriber subscriber
;
390 subscriber
.origin
= origin
;
391 subscriber
.persistent
= persistent
;
392 subscriber
.notification_callback
= notification_callback
;
394 const WatcherKey
key(entry_path
, recursive
);
395 const Watchers::const_iterator it
= watchers_
.find(key
);
396 if (it
!= watchers_
.end()) {
398 it
->second
.subscribers
.find(origin
) != it
->second
.subscribers
.end();
399 OnAddWatcherCompleted(
404 exists
? base::File::FILE_ERROR_EXISTS
: base::File::FILE_OK
);
405 return AbortCallback();
408 const int request_id
= request_manager_
->CreateRequest(
410 scoped_ptr
<RequestManager::HandlerInterface
>(new operations::AddWatcher(
415 base::Bind(&ProvidedFileSystem::OnAddWatcherCompleted
,
416 weak_ptr_factory_
.GetWeakPtr(),
423 OnAddWatcherCompleted(entry_path
,
427 base::File::FILE_ERROR_SECURITY
);
428 return AbortCallback();
432 &ProvidedFileSystem::Abort
, weak_ptr_factory_
.GetWeakPtr(), request_id
);
435 void ProvidedFileSystem::RemoveWatcher(
437 const base::FilePath
& entry_path
,
439 const storage::AsyncFileUtil::StatusCallback
& callback
) {
440 const WatcherKey
key(entry_path
, recursive
);
441 const Watchers::iterator it
= watchers_
.find(key
);
442 if (it
== watchers_
.end() ||
443 it
->second
.subscribers
.find(origin
) == it
->second
.subscribers
.end()) {
444 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
448 // Delete the subscriber in advance, since the list of watchers is owned by
449 // the C++ layer, not by the extension.
450 it
->second
.subscribers
.erase(origin
);
452 FOR_EACH_OBSERVER(ProvidedFileSystemObserver
,
454 OnWatcherListChanged(file_system_info_
, watchers_
));
456 // If there are other subscribers, then do not remove the obsererver, but
457 // simply return a success.
458 if (it
->second
.subscribers
.size()) {
459 callback
.Run(base::File::FILE_OK
);
463 // Delete the watcher in advance.
466 // Even if the extension returns an error, the callback is called with base::
467 // File::FILE_OK. The reason for that is that the entry is not watche anymore
468 // anyway, as it's removed in advance.
469 const int request_id
= request_manager_
->CreateRequest(
471 scoped_ptr
<RequestManager::HandlerInterface
>(
472 new operations::RemoveWatcher(
477 base::Bind(&AlwaysSuccessCallback
, callback
))));
479 callback
.Run(base::File::FILE_OK
);
482 const ProvidedFileSystemInfo
& ProvidedFileSystem::GetFileSystemInfo() const {
483 return file_system_info_
;
486 RequestManager
* ProvidedFileSystem::GetRequestManager() {
487 return request_manager_
.get();
490 Watchers
* ProvidedFileSystem::GetWatchers() {
494 void ProvidedFileSystem::AddObserver(ProvidedFileSystemObserver
* observer
) {
496 observers_
.AddObserver(observer
);
499 void ProvidedFileSystem::RemoveObserver(ProvidedFileSystemObserver
* observer
) {
501 observers_
.RemoveObserver(observer
);
504 void ProvidedFileSystem::Notify(
505 const base::FilePath
& entry_path
,
507 storage::WatcherManager::ChangeType change_type
,
508 scoped_ptr
<ProvidedFileSystemObserver::Changes
> changes
,
509 const std::string
& tag
,
510 const storage::AsyncFileUtil::StatusCallback
& callback
) {
511 const WatcherKey
key(entry_path
, recursive
);
512 const auto& watcher_it
= watchers_
.find(key
);
513 if (watcher_it
== watchers_
.end()) {
514 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
518 // The tag must be provided if and only if it's explicitly supported.
519 if (file_system_info_
.supports_notify_tag() == tag
.empty()) {
520 callback
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
524 // It's illegal to provide a tag which is not unique.
525 if (!tag
.empty() && tag
== watcher_it
->second
.last_tag
) {
526 callback
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
530 // The object is owned by AutoUpdated, so the reference is valid as long as
531 // callbacks created with AutoUpdater::CreateCallback().
532 const ProvidedFileSystemObserver::Changes
& changes_ref
= *changes
.get();
534 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
535 base::Bind(&ProvidedFileSystem::OnNotifyCompleted
,
536 weak_ptr_factory_
.GetWeakPtr(), entry_path
, recursive
,
537 change_type
, base::Passed(&changes
), tag
, callback
)));
539 // Call all notification callbacks (if any).
540 for (const auto& subscriber_it
: watcher_it
->second
.subscribers
) {
541 const storage::WatcherManager::NotificationCallback
& notification_callback
=
542 subscriber_it
.second
.notification_callback
;
543 if (!notification_callback
.is_null())
544 notification_callback
.Run(change_type
);
547 // Notify all observers.
548 FOR_EACH_OBSERVER(ProvidedFileSystemObserver
,
550 OnWatcherChanged(file_system_info_
,
554 auto_updater
->CreateCallback()));
557 base::WeakPtr
<ProvidedFileSystemInterface
> ProvidedFileSystem::GetWeakPtr() {
558 return weak_ptr_factory_
.GetWeakPtr();
561 void ProvidedFileSystem::Abort(
562 int operation_request_id
,
563 const storage::AsyncFileUtil::StatusCallback
& callback
) {
564 request_manager_
->RejectRequest(operation_request_id
,
565 make_scoped_ptr(new RequestValue()),
566 base::File::FILE_ERROR_ABORT
);
567 if (!request_manager_
->CreateRequest(
569 scoped_ptr
<RequestManager::HandlerInterface
>(
570 new operations::Abort(event_router_
,
572 operation_request_id
,
574 callback
.Run(base::File::FILE_ERROR_SECURITY
);
578 void ProvidedFileSystem::OnAddWatcherCompleted(
579 const base::FilePath
& entry_path
,
581 const Subscriber
& subscriber
,
582 const storage::AsyncFileUtil::StatusCallback
& callback
,
583 base::File::Error result
) {
584 if (result
!= base::File::FILE_OK
) {
585 callback
.Run(result
);
589 const WatcherKey
key(entry_path
, recursive
);
590 const Watchers::iterator it
= watchers_
.find(key
);
591 if (it
!= watchers_
.end()) {
592 callback
.Run(base::File::FILE_OK
);
596 // TODO(mtomasz): Add a queue to prevent races.
597 Watcher
* const watcher
= &watchers_
[key
];
598 watcher
->entry_path
= entry_path
;
599 watcher
->recursive
= recursive
;
600 watcher
->subscribers
[subscriber
.origin
] = subscriber
;
602 FOR_EACH_OBSERVER(ProvidedFileSystemObserver
,
604 OnWatcherListChanged(file_system_info_
, watchers_
));
606 callback
.Run(base::File::FILE_OK
);
609 void ProvidedFileSystem::OnNotifyCompleted(
610 const base::FilePath
& entry_path
,
612 storage::WatcherManager::ChangeType change_type
,
613 scoped_ptr
<ProvidedFileSystemObserver::Changes
> /* changes */,
614 const std::string
& tag
,
615 const storage::AsyncFileUtil::StatusCallback
& callback
) {
616 const WatcherKey
key(entry_path
, recursive
);
617 const Watchers::iterator it
= watchers_
.find(key
);
618 // Check if the entry is still watched.
619 if (it
== watchers_
.end()) {
620 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
624 // TODO(mtomasz): Add an async queue around notify and other watcher related
625 // methods so there is no race.
627 it
->second
.last_tag
= tag
;
629 FOR_EACH_OBSERVER(ProvidedFileSystemObserver
,
631 OnWatcherTagUpdated(file_system_info_
, it
->second
));
633 // If the watched entry is deleted, then remove the watcher.
634 if (change_type
== storage::WatcherManager::DELETED
) {
635 // Make a copy, since the |it| iterator will get invalidated on the last
637 Subscribers subscribers
= it
->second
.subscribers
;
638 for (const auto& subscriber_it
: subscribers
) {
639 RemoveWatcher(subscriber_it
.second
.origin
,
642 base::Bind(&EmptyStatusCallback
));
646 callback
.Run(base::File::FILE_OK
);
649 } // namespace file_system_provider
650 } // namespace chromeos