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 "chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h"
7 #include <sys/statvfs.h>
9 #include "base/posix/eintr_wrapper.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/task_runner_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/drive/drive.pb.h"
16 #include "chrome/browser/chromeos/drive/file_system_interface.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
19 #include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
20 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
21 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
22 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
23 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/common/extensions/api/file_manager_private.h"
27 #include "chrome/common/extensions/api/file_manager_private_internal.h"
28 #include "chromeos/disks/disk_mount_manager.h"
29 #include "content/public/browser/child_process_security_policy.h"
30 #include "content/public/browser/render_process_host.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "net/base/escape.h"
33 #include "storage/browser/fileapi/file_system_context.h"
34 #include "storage/browser/fileapi/file_system_file_util.h"
35 #include "storage/browser/fileapi/file_system_operation_context.h"
36 #include "storage/browser/fileapi/file_system_operation_runner.h"
37 #include "storage/browser/fileapi/file_system_url.h"
38 #include "storage/common/fileapi/file_system_info.h"
39 #include "storage/common/fileapi/file_system_types.h"
40 #include "storage/common/fileapi/file_system_util.h"
42 using chromeos::disks::DiskMountManager;
43 using content::BrowserThread;
44 using content::ChildProcessSecurityPolicy;
45 using file_manager::util::EntryDefinition;
46 using file_manager::util::FileDefinition;
47 using storage::FileSystemURL;
49 namespace extensions {
52 // Retrieves total and remaining available size on |mount_path|.
53 void GetSizeStatsOnBlockingPool(const std::string& mount_path,
55 uint64* remaining_size) {
56 struct statvfs stat = {}; // Zero-clear
57 if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
58 *total_size = static_cast<uint64>(stat.f_blocks) * stat.f_frsize;
59 *remaining_size = static_cast<uint64>(stat.f_bavail) * stat.f_frsize;
63 // Retrieves the maximum file name length of the file system of |path|.
64 // Returns 0 if it could not be queried.
65 size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
66 struct statvfs stat = {};
67 if (HANDLE_EINTR(statvfs(path.c_str(), &stat)) != 0) {
68 // The filesystem seems not supporting statvfs(). Assume it to be a commonly
69 // used bound 255, and log the failure.
70 LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
73 return stat.f_namemax;
76 // Returns EventRouter for the |profile_id| if available.
77 file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
80 // |profile_id| needs to be checked with ProfileManager::IsValidProfile
82 Profile* profile = reinterpret_cast<Profile*>(profile_id);
83 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
86 return file_manager::EventRouterFactory::GetForProfile(profile);
89 // Notifies the copy progress to extensions via event router.
90 void NotifyCopyProgress(
92 storage::FileSystemOperationRunner::OperationID operation_id,
93 storage::FileSystemOperation::CopyProgressType type,
94 const FileSystemURL& source_url,
95 const FileSystemURL& destination_url,
97 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
99 file_manager::EventRouter* event_router =
100 GetEventRouterByProfileId(profile_id);
102 event_router->OnCopyProgress(
104 source_url.ToGURL(), destination_url.ToGURL(), size);
108 // Callback invoked periodically on progress update of Copy().
111 storage::FileSystemOperationRunner::OperationID* operation_id,
112 storage::FileSystemOperation::CopyProgressType type,
113 const FileSystemURL& source_url,
114 const FileSystemURL& destination_url,
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118 BrowserThread::PostTask(
119 BrowserThread::UI, FROM_HERE,
120 base::Bind(&NotifyCopyProgress,
121 profile_id, *operation_id, type,
122 source_url, destination_url, size));
125 // Notifies the copy completion to extensions via event router.
126 void NotifyCopyCompletion(
128 storage::FileSystemOperationRunner::OperationID operation_id,
129 const FileSystemURL& source_url,
130 const FileSystemURL& destination_url,
131 base::File::Error error) {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134 file_manager::EventRouter* event_router =
135 GetEventRouterByProfileId(profile_id);
137 event_router->OnCopyCompleted(
139 source_url.ToGURL(), destination_url.ToGURL(), error);
142 // Callback invoked upon completion of Copy() (regardless of succeeded or
144 void OnCopyCompleted(
146 storage::FileSystemOperationRunner::OperationID* operation_id,
147 const FileSystemURL& source_url,
148 const FileSystemURL& destination_url,
149 base::File::Error error) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
152 BrowserThread::PostTask(
153 BrowserThread::UI, FROM_HERE,
154 base::Bind(&NotifyCopyCompletion,
155 profile_id, *operation_id,
156 source_url, destination_url, error));
159 // Starts the copy operation via FileSystemOperationRunner.
160 storage::FileSystemOperationRunner::OperationID StartCopyOnIOThread(
162 scoped_refptr<storage::FileSystemContext> file_system_context,
163 const FileSystemURL& source_url,
164 const FileSystemURL& destination_url) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
167 // Note: |operation_id| is owned by the callback for
168 // FileSystemOperationRunner::Copy(). It is always called in the next message
169 // loop or later, so at least during this invocation it should alive.
170 storage::FileSystemOperationRunner::OperationID* operation_id =
171 new storage::FileSystemOperationRunner::OperationID;
172 *operation_id = file_system_context->operation_runner()->Copy(
175 storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
176 base::Bind(&OnCopyProgress, profile_id, base::Unretained(operation_id)),
177 base::Bind(&OnCopyCompleted,
179 base::Owned(operation_id),
182 return *operation_id;
185 void OnCopyCancelled(base::File::Error error) {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
188 // We just ignore the status if the copy is actually cancelled or not,
189 // because failing cancellation means the operation is not running now.
190 DLOG_IF(WARNING, error != base::File::FILE_OK)
191 << "Failed to cancel copy: " << error;
194 // Cancels the running copy operation identified by |operation_id|.
195 void CancelCopyOnIOThread(
196 scoped_refptr<storage::FileSystemContext> file_system_context,
197 storage::FileSystemOperationRunner::OperationID operation_id) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
200 file_system_context->operation_runner()->Cancel(
201 operation_id, base::Bind(&OnCopyCancelled));
206 void FileManagerPrivateRequestFileSystemFunction::DidFail(
207 base::File::Error error_code) {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210 SetError(base::StringPrintf("File error %d", static_cast<int>(error_code)));
215 FileManagerPrivateRequestFileSystemFunction::SetupFileSystemAccessPermissions(
216 scoped_refptr<storage::FileSystemContext> file_system_context,
219 scoped_refptr<const extensions::Extension> extension) {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 if (!extension.get())
225 // Make sure that only component extension can access the entire
226 // local file system.
227 if (extension_->location() != extensions::Manifest::COMPONENT) {
228 NOTREACHED() << "Private method access by non-component extension "
233 storage::ExternalFileSystemBackend* backend =
234 file_system_context->external_backend();
238 // Grant full access to File API from this component extension.
239 backend->GrantFullAccessToExtension(extension_->id());
241 // Grant R/W file permissions to the renderer hosting component
242 // extension for all paths exposed by our local file system backend.
243 std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
244 for (size_t i = 0; i < root_dirs.size(); ++i) {
245 ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
246 child_id, root_dirs[i]);
249 // Grant R/W permissions to profile-specific directories (Drive, Downloads)
250 // from other profiles. Those directories may not be mounted at this moment
251 // yet, so we need to do this separately from the above loop over
252 // GetRootDirectories().
253 const std::vector<Profile*>& profiles =
254 g_browser_process->profile_manager()->GetLoadedProfiles();
255 for (size_t i = 0; i < profiles.size(); ++i) {
256 if (!profiles[i]->IsOffTheRecord()) {
257 file_manager::util::SetupProfileFileAccessPermissions(child_id,
265 bool FileManagerPrivateRequestFileSystemFunction::RunAsync() {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267 using extensions::api::file_manager_private::RequestFileSystem::Params;
268 const scoped_ptr<Params> params(Params::Create(*args_));
269 EXTENSION_FUNCTION_VALIDATE(params);
271 if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
274 set_log_on_completion(true);
276 using file_manager::VolumeManager;
277 using file_manager::VolumeInfo;
278 VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
282 VolumeInfo volume_info;
283 if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info)) {
284 DidFail(base::File::FILE_ERROR_NOT_FOUND);
288 scoped_refptr<storage::FileSystemContext> file_system_context =
289 file_manager::util::GetFileSystemContextForRenderViewHost(
290 GetProfile(), render_view_host());
292 // Set up file permission access.
293 const int child_id = render_view_host()->GetProcess()->GetID();
294 if (!SetupFileSystemAccessPermissions(
295 file_system_context, child_id, GetProfile(), extension())) {
296 DidFail(base::File::FILE_ERROR_SECURITY);
300 FileDefinition file_definition;
301 if (!file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
304 volume_info.mount_path,
305 &file_definition.virtual_path)) {
306 DidFail(base::File::FILE_ERROR_INVALID_OPERATION);
309 file_definition.is_directory = true;
311 file_manager::util::ConvertFileDefinitionToEntryDefinition(
316 &FileManagerPrivateRequestFileSystemFunction::OnEntryDefinition,
321 void FileManagerPrivateRequestFileSystemFunction::OnEntryDefinition(
322 const EntryDefinition& entry_definition) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
325 if (entry_definition.error != base::File::FILE_OK) {
326 DidFail(entry_definition.error);
330 if (!entry_definition.is_directory) {
331 DidFail(base::File::FILE_ERROR_NOT_A_DIRECTORY);
335 base::DictionaryValue* dict = new base::DictionaryValue();
337 dict->SetString("name", entry_definition.file_system_name);
338 dict->SetString("root_url", entry_definition.file_system_root_url);
339 dict->SetInteger("error", drive::FILE_ERROR_OK);
343 void FileWatchFunctionBase::Respond(bool success) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 SetResult(new base::FundamentalValue(success));
347 SendResponse(success);
350 bool FileWatchFunctionBase::RunAsync() {
351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 if (!render_view_host() || !render_view_host()->GetProcess())
356 // First param is url of a file to watch.
358 if (!args_->GetString(0, &url) || url.empty())
361 scoped_refptr<storage::FileSystemContext> file_system_context =
362 file_manager::util::GetFileSystemContextForRenderViewHost(
363 GetProfile(), render_view_host());
365 FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
366 base::FilePath local_path = file_watch_url.path();
367 base::FilePath virtual_path = file_watch_url.virtual_path();
368 if (local_path.empty()) {
372 PerformFileWatchOperation(local_path, virtual_path, extension_id());
377 void FileManagerPrivateAddFileWatchFunction::PerformFileWatchOperation(
378 const base::FilePath& local_path,
379 const base::FilePath& virtual_path,
380 const std::string& extension_id) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 file_manager::EventRouter* event_router =
384 file_manager::EventRouterFactory::GetForProfile(GetProfile());
385 event_router->AddFileWatch(
389 base::Bind(&FileManagerPrivateAddFileWatchFunction::Respond, this));
392 void FileManagerPrivateRemoveFileWatchFunction::PerformFileWatchOperation(
393 const base::FilePath& local_path,
394 const base::FilePath& unused,
395 const std::string& extension_id) {
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398 file_manager::EventRouter* event_router =
399 file_manager::EventRouterFactory::GetForProfile(GetProfile());
400 event_router->RemoveFileWatch(local_path, extension_id);
404 bool FileManagerPrivateGetSizeStatsFunction::RunAsync() {
405 using extensions::api::file_manager_private::GetSizeStats::Params;
406 const scoped_ptr<Params> params(Params::Create(*args_));
407 EXTENSION_FUNCTION_VALIDATE(params);
409 using file_manager::VolumeManager;
410 using file_manager::VolumeInfo;
411 VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
415 VolumeInfo volume_info;
416 if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
419 if (volume_info.type == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
420 drive::FileSystemInterface* file_system =
421 drive::util::GetFileSystemByProfile(GetProfile());
423 // |file_system| is NULL if Drive is disabled.
424 // If stats couldn't be gotten for drive, result should be left
425 // undefined. See comments in GetDriveAvailableSpaceCallback().
430 file_system->GetAvailableSpace(
431 base::Bind(&FileManagerPrivateGetSizeStatsFunction::
432 GetDriveAvailableSpaceCallback,
435 uint64* total_size = new uint64(0);
436 uint64* remaining_size = new uint64(0);
437 BrowserThread::PostBlockingPoolTaskAndReply(
439 base::Bind(&GetSizeStatsOnBlockingPool,
440 volume_info.mount_path.value(),
443 base::Bind(&FileManagerPrivateGetSizeStatsFunction::
444 GetSizeStatsCallback,
446 base::Owned(total_size),
447 base::Owned(remaining_size)));
452 void FileManagerPrivateGetSizeStatsFunction::GetDriveAvailableSpaceCallback(
453 drive::FileError error,
456 if (error == drive::FILE_ERROR_OK) {
457 const uint64 bytes_total_unsigned = bytes_total;
458 const uint64 bytes_remaining_unsigned = bytes_total - bytes_used;
459 GetSizeStatsCallback(&bytes_total_unsigned,
460 &bytes_remaining_unsigned);
462 // If stats couldn't be gotten for drive, result should be left undefined.
467 void FileManagerPrivateGetSizeStatsFunction::GetSizeStatsCallback(
468 const uint64* total_size,
469 const uint64* remaining_size) {
470 base::DictionaryValue* sizes = new base::DictionaryValue();
473 sizes->SetDouble("totalSize", static_cast<double>(*total_size));
474 sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
479 bool FileManagerPrivateValidatePathNameLengthFunction::RunAsync() {
480 using extensions::api::file_manager_private::ValidatePathNameLength::Params;
481 const scoped_ptr<Params> params(Params::Create(*args_));
482 EXTENSION_FUNCTION_VALIDATE(params);
484 scoped_refptr<storage::FileSystemContext> file_system_context =
485 file_manager::util::GetFileSystemContextForRenderViewHost(
486 GetProfile(), render_view_host());
488 storage::FileSystemURL filesystem_url(
489 file_system_context->CrackURL(GURL(params->parent_directory_url)));
490 if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
493 // No explicit limit on the length of Drive file names.
494 if (filesystem_url.type() == storage::kFileSystemTypeDrive) {
495 SetResult(new base::FundamentalValue(true));
500 base::PostTaskAndReplyWithResult(
501 BrowserThread::GetBlockingPool(),
503 base::Bind(&GetFileNameMaxLengthOnBlockingPool,
504 filesystem_url.path().AsUTF8Unsafe()),
505 base::Bind(&FileManagerPrivateValidatePathNameLengthFunction::
506 OnFilePathLimitRetrieved,
507 this, params->name.size()));
511 void FileManagerPrivateValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
512 size_t current_length,
514 SetResult(new base::FundamentalValue(current_length <= max_length));
518 bool FileManagerPrivateFormatVolumeFunction::RunAsync() {
519 using extensions::api::file_manager_private::FormatVolume::Params;
520 const scoped_ptr<Params> params(Params::Create(*args_));
521 EXTENSION_FUNCTION_VALIDATE(params);
523 using file_manager::VolumeManager;
524 using file_manager::VolumeInfo;
525 VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
529 VolumeInfo volume_info;
530 if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
533 DiskMountManager::GetInstance()->FormatMountedDevice(
534 volume_info.mount_path.AsUTF8Unsafe());
539 bool FileManagerPrivateStartCopyFunction::RunAsync() {
540 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
542 using extensions::api::file_manager_private::StartCopy::Params;
543 const scoped_ptr<Params> params(Params::Create(*args_));
544 EXTENSION_FUNCTION_VALIDATE(params);
546 if (params->source_url.empty() || params->parent.empty() ||
547 params->new_name.empty()) {
548 // Error code in format of DOMError.name.
549 SetError("EncodingError");
553 scoped_refptr<storage::FileSystemContext> file_system_context =
554 file_manager::util::GetFileSystemContextForRenderViewHost(
555 GetProfile(), render_view_host());
557 // |parent| may have a trailing slash if it is a root directory.
558 std::string destination_url_string = params->parent;
559 if (destination_url_string[destination_url_string.size() - 1] != '/')
560 destination_url_string += '/';
561 destination_url_string += net::EscapePath(params->new_name);
563 storage::FileSystemURL source_url(
564 file_system_context->CrackURL(GURL(params->source_url)));
565 storage::FileSystemURL destination_url(
566 file_system_context->CrackURL(GURL(destination_url_string)));
568 if (!source_url.is_valid() || !destination_url.is_valid()) {
569 // Error code in format of DOMError.name.
570 SetError("EncodingError");
574 return BrowserThread::PostTaskAndReplyWithResult(
577 base::Bind(&StartCopyOnIOThread,
582 base::Bind(&FileManagerPrivateStartCopyFunction::RunAfterStartCopy,
586 void FileManagerPrivateStartCopyFunction::RunAfterStartCopy(
588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
590 SetResult(new base::FundamentalValue(operation_id));
594 bool FileManagerPrivateCancelCopyFunction::RunAsync() {
595 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
597 using extensions::api::file_manager_private::CancelCopy::Params;
598 const scoped_ptr<Params> params(Params::Create(*args_));
599 EXTENSION_FUNCTION_VALIDATE(params);
601 scoped_refptr<storage::FileSystemContext> file_system_context =
602 file_manager::util::GetFileSystemContextForRenderViewHost(
603 GetProfile(), render_view_host());
605 // We don't much take care about the result of cancellation.
606 BrowserThread::PostTask(
609 base::Bind(&CancelCopyOnIOThread, file_system_context, params->copy_id));
614 bool FileManagerPrivateInternalResolveIsolatedEntriesFunction::RunAsync() {
615 using extensions::api::file_manager_private_internal::ResolveIsolatedEntries::
617 const scoped_ptr<Params> params(Params::Create(*args_));
618 EXTENSION_FUNCTION_VALIDATE(params);
620 scoped_refptr<storage::FileSystemContext> file_system_context =
621 file_manager::util::GetFileSystemContextForRenderViewHost(
622 GetProfile(), render_view_host());
623 DCHECK(file_system_context.get());
625 const storage::ExternalFileSystemBackend* external_backend =
626 file_system_context->external_backend();
627 DCHECK(external_backend);
629 file_manager::util::FileDefinitionList file_definition_list;
630 for (size_t i = 0; i < params->urls.size(); ++i) {
631 FileSystemURL fileSystemUrl =
632 file_system_context->CrackURL(GURL(params->urls[i]));
633 DCHECK(external_backend->CanHandleType(fileSystemUrl.type()));
635 FileDefinition file_definition;
637 file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
640 fileSystemUrl.path(),
641 &file_definition.virtual_path);
644 // The API only supports isolated files.
645 file_definition.is_directory = false;
646 file_definition_list.push_back(file_definition);
649 file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
652 file_definition_list, // Safe, since copied internally.
654 &FileManagerPrivateInternalResolveIsolatedEntriesFunction::
655 RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList,
660 void FileManagerPrivateInternalResolveIsolatedEntriesFunction::
661 RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList(scoped_ptr<
662 file_manager::util::EntryDefinitionList> entry_definition_list) {
663 using extensions::api::file_manager_private_internal::EntryDescription;
664 std::vector<linked_ptr<EntryDescription> > entries;
666 for (size_t i = 0; i < entry_definition_list->size(); ++i) {
667 if (entry_definition_list->at(i).error != base::File::FILE_OK)
669 linked_ptr<EntryDescription> entry(new EntryDescription);
670 entry->file_system_name = entry_definition_list->at(i).file_system_name;
671 entry->file_system_root = entry_definition_list->at(i).file_system_root_url;
672 entry->file_full_path =
673 "/" + entry_definition_list->at(i).full_path.AsUTF8Unsafe();
674 entry->file_is_directory = entry_definition_list->at(i).is_directory;
675 entries.push_back(entry);
678 results_ = extensions::api::file_manager_private_internal::
679 ResolveIsolatedEntries::Results::Create(entries);
682 } // namespace extensions