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/file_browser_private_api.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_browser_private.h"
27 #include "chromeos/disks/disk_mount_manager.h"
28 #include "content/public/browser/child_process_security_policy.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "webkit/browser/fileapi/file_system_context.h"
32 #include "webkit/browser/fileapi/file_system_file_util.h"
33 #include "webkit/browser/fileapi/file_system_operation_context.h"
34 #include "webkit/browser/fileapi/file_system_operation_runner.h"
35 #include "webkit/browser/fileapi/file_system_url.h"
36 #include "webkit/common/fileapi/file_system_info.h"
37 #include "webkit/common/fileapi/file_system_types.h"
38 #include "webkit/common/fileapi/file_system_util.h"
40 using chromeos::disks::DiskMountManager;
41 using content::BrowserThread;
42 using content::ChildProcessSecurityPolicy;
43 using fileapi::FileSystemURL;
45 namespace extensions {
48 // Retrieves total and remaining available size on |mount_path|.
49 void GetSizeStatsOnBlockingPool(const std::string& mount_path,
51 uint64* remaining_size) {
52 struct statvfs stat = {}; // Zero-clear
53 if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
54 *total_size = static_cast<uint64>(stat.f_blocks) * stat.f_frsize;
55 *remaining_size = static_cast<uint64>(stat.f_bavail) * stat.f_frsize;
59 // Retrieves the maximum file name length of the file system of |path|.
60 // Returns 0 if it could not be queried.
61 size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
62 struct statvfs stat = {};
63 if (HANDLE_EINTR(statvfs(path.c_str(), &stat)) != 0) {
64 // The filesystem seems not supporting statvfs(). Assume it to be a commonly
65 // used bound 255, and log the failure.
66 LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
69 return stat.f_namemax;
72 // Returns EventRouter for the |profile_id| if available.
73 file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76 // |profile_id| needs to be checked with ProfileManager::IsValidProfile
78 Profile* profile = reinterpret_cast<Profile*>(profile_id);
79 if (!g_browser_process->profile_manager()->IsValidProfile(profile))
82 return file_manager::FileBrowserPrivateAPI::Get(profile)->event_router();
85 // Notifies the copy progress to extensions via event router.
86 void NotifyCopyProgress(
88 fileapi::FileSystemOperationRunner::OperationID operation_id,
89 fileapi::FileSystemOperation::CopyProgressType type,
90 const FileSystemURL& source_url,
91 const FileSystemURL& destination_url,
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95 file_manager::EventRouter* event_router =
96 GetEventRouterByProfileId(profile_id);
98 event_router->OnCopyProgress(
100 source_url.ToGURL(), destination_url.ToGURL(), size);
104 // Callback invoked periodically on progress update of Copy().
107 fileapi::FileSystemOperationRunner::OperationID* operation_id,
108 fileapi::FileSystemOperation::CopyProgressType type,
109 const FileSystemURL& source_url,
110 const FileSystemURL& destination_url,
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
114 BrowserThread::PostTask(
115 BrowserThread::UI, FROM_HERE,
116 base::Bind(&NotifyCopyProgress,
117 profile_id, *operation_id, type,
118 source_url, destination_url, size));
121 // Notifies the copy completion to extensions via event router.
122 void NotifyCopyCompletion(
124 fileapi::FileSystemOperationRunner::OperationID operation_id,
125 const FileSystemURL& source_url,
126 const FileSystemURL& destination_url,
127 base::File::Error error) {
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130 file_manager::EventRouter* event_router =
131 GetEventRouterByProfileId(profile_id);
133 event_router->OnCopyCompleted(
135 source_url.ToGURL(), destination_url.ToGURL(), error);
138 // Callback invoked upon completion of Copy() (regardless of succeeded or
140 void OnCopyCompleted(
142 fileapi::FileSystemOperationRunner::OperationID* operation_id,
143 const FileSystemURL& source_url,
144 const FileSystemURL& destination_url,
145 base::File::Error error) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
148 BrowserThread::PostTask(
149 BrowserThread::UI, FROM_HERE,
150 base::Bind(&NotifyCopyCompletion,
151 profile_id, *operation_id,
152 source_url, destination_url, error));
155 // Starts the copy operation via FileSystemOperationRunner.
156 fileapi::FileSystemOperationRunner::OperationID StartCopyOnIOThread(
158 scoped_refptr<fileapi::FileSystemContext> file_system_context,
159 const FileSystemURL& source_url,
160 const FileSystemURL& destination_url) {
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
163 // Note: |operation_id| is owned by the callback for
164 // FileSystemOperationRunner::Copy(). It is always called in the next message
165 // loop or later, so at least during this invocation it should alive.
166 fileapi::FileSystemOperationRunner::OperationID* operation_id =
167 new fileapi::FileSystemOperationRunner::OperationID;
168 *operation_id = file_system_context->operation_runner()->Copy(
169 source_url, destination_url,
170 fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
171 base::Bind(&OnCopyProgress,
172 profile_id, base::Unretained(operation_id)),
173 base::Bind(&OnCopyCompleted,
174 profile_id, base::Owned(operation_id),
175 source_url, destination_url));
176 return *operation_id;
179 void OnCopyCancelled(base::File::Error error) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
182 // We just ignore the status if the copy is actually cancelled or not,
183 // because failing cancellation means the operation is not running now.
184 DLOG_IF(WARNING, error != base::File::FILE_OK)
185 << "Failed to cancel copy: " << error;
188 // Cancels the running copy operation identified by |operation_id|.
189 void CancelCopyOnIOThread(
190 scoped_refptr<fileapi::FileSystemContext> file_system_context,
191 fileapi::FileSystemOperationRunner::OperationID operation_id) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
194 file_system_context->operation_runner()->Cancel(
195 operation_id, base::Bind(&OnCopyCancelled));
200 void FileBrowserPrivateRequestFileSystemFunction::DidFail(
201 base::File::Error error_code) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
204 SetError(base::StringPrintf("File error %d", static_cast<int>(error_code)));
208 bool FileBrowserPrivateRequestFileSystemFunction::
209 SetupFileSystemAccessPermissions(
210 scoped_refptr<fileapi::FileSystemContext> file_system_context,
213 scoped_refptr<const extensions::Extension> extension) {
214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
216 if (!extension.get())
219 // Make sure that only component extension can access the entire
220 // local file system.
221 if (extension_->location() != extensions::Manifest::COMPONENT) {
222 NOTREACHED() << "Private method access by non-component extension "
227 fileapi::ExternalFileSystemBackend* backend =
228 file_system_context->external_backend();
232 // Grant full access to File API from this component extension.
233 backend->GrantFullAccessToExtension(extension_->id());
235 // Grant R/W file permissions to the renderer hosting component
236 // extension for all paths exposed by our local file system backend.
237 std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
238 for (size_t i = 0; i < root_dirs.size(); ++i) {
239 ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
240 child_id, root_dirs[i]);
243 // Grant R/W permissions to profile-specific directories (Drive, Downloads)
244 // from other profiles. Those directories may not be mounted at this moment
245 // yet, so we need to do this separately from the above loop over
246 // GetRootDirectories().
247 const std::vector<Profile*>& profiles =
248 g_browser_process->profile_manager()->GetLoadedProfiles();
249 for (size_t i = 0; i < profiles.size(); ++i) {
250 if (!profiles[i]->IsOffTheRecord()) {
251 file_manager::util::SetupProfileFileAccessPermissions(child_id,
259 bool FileBrowserPrivateRequestFileSystemFunction::RunImpl() {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261 using extensions::api::file_browser_private::RequestFileSystem::Params;
262 const scoped_ptr<Params> params(Params::Create(*args_));
263 EXTENSION_FUNCTION_VALIDATE(params);
265 // TODO(satorux): Handle the file system ID. crbug.com/322305.
266 DCHECK_EQ("compatible", params->volume_id);
268 if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
271 set_log_on_completion(true);
273 scoped_refptr<fileapi::FileSystemContext> file_system_context =
274 file_manager::util::GetFileSystemContextForRenderViewHost(
275 GetProfile(), render_view_host());
277 // Set up file permission access.
278 const int child_id = render_view_host()->GetProcess()->GetID();
279 if (!SetupFileSystemAccessPermissions(file_system_context,
283 DidFail(base::File::FILE_ERROR_SECURITY);
287 fileapi::FileSystemInfo info =
288 fileapi::GetFileSystemInfoForChromeOS(source_url_.GetOrigin());
290 base::DictionaryValue* dict = new base::DictionaryValue();
292 dict->SetString("name", info.name);
293 dict->SetString("root_url", info.root_url.spec());
294 dict->SetInteger("error", drive::FILE_ERROR_OK);
299 void FileWatchFunctionBase::Respond(bool success) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
302 SetResult(base::Value::CreateBooleanValue(success));
303 SendResponse(success);
306 bool FileWatchFunctionBase::RunImpl() {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
309 if (!render_view_host() || !render_view_host()->GetProcess())
312 // First param is url of a file to watch.
314 if (!args_->GetString(0, &url) || url.empty())
317 scoped_refptr<fileapi::FileSystemContext> file_system_context =
318 file_manager::util::GetFileSystemContextForRenderViewHost(
319 GetProfile(), render_view_host());
321 FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
322 base::FilePath local_path = file_watch_url.path();
323 base::FilePath virtual_path = file_watch_url.virtual_path();
324 if (local_path.empty()) {
328 PerformFileWatchOperation(local_path, virtual_path, extension_id());
333 void FileBrowserPrivateAddFileWatchFunction::PerformFileWatchOperation(
334 const base::FilePath& local_path,
335 const base::FilePath& virtual_path,
336 const std::string& extension_id) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339 file_manager::EventRouter* event_router =
340 file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
341 event_router->AddFileWatch(
345 base::Bind(&FileBrowserPrivateAddFileWatchFunction::Respond, this));
348 void FileBrowserPrivateRemoveFileWatchFunction::PerformFileWatchOperation(
349 const base::FilePath& local_path,
350 const base::FilePath& unused,
351 const std::string& extension_id) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 file_manager::EventRouter* event_router =
355 file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
356 event_router->RemoveFileWatch(local_path, extension_id);
360 bool FileBrowserPrivateGetSizeStatsFunction::RunImpl() {
361 using extensions::api::file_browser_private::GetSizeStats::Params;
362 const scoped_ptr<Params> params(Params::Create(*args_));
363 EXTENSION_FUNCTION_VALIDATE(params);
365 using file_manager::VolumeManager;
366 using file_manager::VolumeInfo;
367 VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
371 VolumeInfo volume_info;
372 if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
375 if (volume_info.type == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
376 drive::FileSystemInterface* file_system =
377 drive::util::GetFileSystemByProfile(GetProfile());
379 // |file_system| is NULL if Drive is disabled.
380 // If stats couldn't be gotten for drive, result should be left
381 // undefined. See comments in GetDriveAvailableSpaceCallback().
386 file_system->GetAvailableSpace(
387 base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
388 GetDriveAvailableSpaceCallback,
391 uint64* total_size = new uint64(0);
392 uint64* remaining_size = new uint64(0);
393 BrowserThread::PostBlockingPoolTaskAndReply(
395 base::Bind(&GetSizeStatsOnBlockingPool,
396 volume_info.mount_path.value(),
399 base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
400 GetSizeStatsCallback,
402 base::Owned(total_size),
403 base::Owned(remaining_size)));
408 void FileBrowserPrivateGetSizeStatsFunction::GetDriveAvailableSpaceCallback(
409 drive::FileError error,
412 if (error == drive::FILE_ERROR_OK) {
413 const uint64 bytes_total_unsigned = bytes_total;
414 const uint64 bytes_remaining_unsigned = bytes_total - bytes_used;
415 GetSizeStatsCallback(&bytes_total_unsigned,
416 &bytes_remaining_unsigned);
418 // If stats couldn't be gotten for drive, result should be left undefined.
423 void FileBrowserPrivateGetSizeStatsFunction::GetSizeStatsCallback(
424 const uint64* total_size,
425 const uint64* remaining_size) {
426 base::DictionaryValue* sizes = new base::DictionaryValue();
429 sizes->SetDouble("totalSize", static_cast<double>(*total_size));
430 sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
435 bool FileBrowserPrivateValidatePathNameLengthFunction::RunImpl() {
436 using extensions::api::file_browser_private::ValidatePathNameLength::Params;
437 const scoped_ptr<Params> params(Params::Create(*args_));
438 EXTENSION_FUNCTION_VALIDATE(params);
440 scoped_refptr<fileapi::FileSystemContext> file_system_context =
441 file_manager::util::GetFileSystemContextForRenderViewHost(
442 GetProfile(), render_view_host());
444 fileapi::FileSystemURL filesystem_url(
445 file_system_context->CrackURL(GURL(params->parent_directory_url)));
446 if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
449 // No explicit limit on the length of Drive file names.
450 if (filesystem_url.type() == fileapi::kFileSystemTypeDrive) {
451 SetResult(new base::FundamentalValue(true));
456 base::PostTaskAndReplyWithResult(
457 BrowserThread::GetBlockingPool(),
459 base::Bind(&GetFileNameMaxLengthOnBlockingPool,
460 filesystem_url.path().AsUTF8Unsafe()),
461 base::Bind(&FileBrowserPrivateValidatePathNameLengthFunction::
462 OnFilePathLimitRetrieved,
463 this, params->name.size()));
467 void FileBrowserPrivateValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
468 size_t current_length,
470 SetResult(new base::FundamentalValue(current_length <= max_length));
474 bool FileBrowserPrivateFormatVolumeFunction::RunImpl() {
475 using extensions::api::file_browser_private::FormatVolume::Params;
476 const scoped_ptr<Params> params(Params::Create(*args_));
477 EXTENSION_FUNCTION_VALIDATE(params);
479 using file_manager::VolumeManager;
480 using file_manager::VolumeInfo;
481 VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
485 VolumeInfo volume_info;
486 if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
489 DiskMountManager::GetInstance()->FormatMountedDevice(
490 volume_info.mount_path.AsUTF8Unsafe());
495 bool FileBrowserPrivateStartCopyFunction::RunImpl() {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498 using extensions::api::file_browser_private::StartCopy::Params;
499 const scoped_ptr<Params> params(Params::Create(*args_));
500 EXTENSION_FUNCTION_VALIDATE(params);
502 if (params->source_url.empty() || params->parent.empty() ||
503 params->new_name.empty()) {
504 // Error code in format of DOMError.name.
505 SetError("EncodingError");
509 scoped_refptr<fileapi::FileSystemContext> file_system_context =
510 file_manager::util::GetFileSystemContextForRenderViewHost(
511 GetProfile(), render_view_host());
513 fileapi::FileSystemURL source_url(
514 file_system_context->CrackURL(GURL(params->source_url)));
515 fileapi::FileSystemURL destination_url(file_system_context->CrackURL(
516 GURL(params->parent + "/" + net::EscapePath(params->new_name))));
518 if (!source_url.is_valid() || !destination_url.is_valid()) {
519 // Error code in format of DOMError.name.
520 SetError("EncodingError");
524 return BrowserThread::PostTaskAndReplyWithResult(
527 base::Bind(&StartCopyOnIOThread,
532 base::Bind(&FileBrowserPrivateStartCopyFunction::RunAfterStartCopy,
536 void FileBrowserPrivateStartCopyFunction::RunAfterStartCopy(
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
540 SetResult(base::Value::CreateIntegerValue(operation_id));
544 bool FileBrowserPrivateCancelCopyFunction::RunImpl() {
545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547 using extensions::api::file_browser_private::CancelCopy::Params;
548 const scoped_ptr<Params> params(Params::Create(*args_));
549 EXTENSION_FUNCTION_VALIDATE(params);
551 scoped_refptr<fileapi::FileSystemContext> file_system_context =
552 file_manager::util::GetFileSystemContextForRenderViewHost(
553 GetProfile(), render_view_host());
555 // We don't much take care about the result of cancellation.
556 BrowserThread::PostTask(
559 base::Bind(&CancelCopyOnIOThread, file_system_context, params->copy_id));
564 } // namespace extensions