private_api_file_system.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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. 4 5#include "chrome/browser/chromeos/extensions/file_manager/private_api_file_system.h" 6 7#include <sys/stat.h> 8#include <sys/statvfs.h> 9#include <sys/types.h> 10#include <utime.h> 11 12#include "base/posix/eintr_wrapper.h" 13#include "base/strings/stringprintf.h" 14#include "base/task_runner_util.h" 15#include "base/threading/sequenced_worker_pool.h" 16#include "chrome/browser/chromeos/drive/drive.pb.h" 17#include "chrome/browser/chromeos/drive/drive_integration_service.h" 18#include "chrome/browser/chromeos/drive/file_system_interface.h" 19#include "chrome/browser/chromeos/drive/file_system_util.h" 20#include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h" 21#include "chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.h" 22#include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h" 23#include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h" 24#include "chrome/browser/chromeos/fileapi/file_system_backend.h" 25#include "chrome/browser/profiles/profile.h" 26#include "chromeos/disks/disk_mount_manager.h" 27#include "content/public/browser/browser_context.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 "content/public/browser/storage_partition.h" 32#include "webkit/browser/fileapi/file_system_context.h" 33#include "webkit/browser/fileapi/file_system_file_util.h" 34#include "webkit/browser/fileapi/file_system_operation_context.h" 35#include "webkit/browser/fileapi/file_system_url.h" 36#include "webkit/common/fileapi/file_system_types.h" 37#include "webkit/common/fileapi/file_system_util.h" 38 39using chromeos::disks::DiskMountManager; 40using content::BrowserContext; 41using content::BrowserThread; 42using content::ChildProcessSecurityPolicy; 43using content::WebContents; 44using fileapi::FileSystemURL; 45 46namespace file_manager { 47namespace { 48 49// Error messages. 50const char kFileError[] = "File error %d"; 51 52const DiskMountManager::Disk* GetVolumeAsDisk(const std::string& mount_path) { 53 DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance(); 54 55 DiskMountManager::MountPointMap::const_iterator mount_point_it = 56 disk_mount_manager->mount_points().find(mount_path); 57 if (mount_point_it == disk_mount_manager->mount_points().end()) 58 return NULL; 59 60 const DiskMountManager::Disk* disk = disk_mount_manager->FindDiskBySourcePath( 61 mount_point_it->second.source_path); 62 63 return (disk && disk->is_hidden()) ? NULL : disk; 64} 65 66base::DictionaryValue* CreateValueFromDisk( 67 Profile* profile, 68 const std::string& extension_id, 69 const DiskMountManager::Disk* volume) { 70 base::DictionaryValue* volume_info = new base::DictionaryValue(); 71 72 std::string mount_path; 73 if (!volume->mount_path().empty()) { 74 base::FilePath relative_mount_path; 75 util::ConvertFileToRelativeFileSystemPath( 76 profile, extension_id, base::FilePath(volume->mount_path()), 77 &relative_mount_path); 78 mount_path = relative_mount_path.value(); 79 } 80 81 volume_info->SetString("devicePath", volume->device_path()); 82 volume_info->SetString("mountPath", mount_path); 83 volume_info->SetString("systemPath", volume->system_path()); 84 volume_info->SetString("filePath", volume->file_path()); 85 volume_info->SetString("deviceLabel", volume->device_label()); 86 volume_info->SetString("driveLabel", volume->drive_label()); 87 volume_info->SetString( 88 "deviceType", 89 DiskMountManager::DeviceTypeToString(volume->device_type())); 90 volume_info->SetDouble("totalSize", 91 static_cast<double>(volume->total_size_in_bytes())); 92 volume_info->SetBoolean("isParent", volume->is_parent()); 93 volume_info->SetBoolean("isReadOnly", volume->is_read_only()); 94 volume_info->SetBoolean("hasMedia", volume->has_media()); 95 volume_info->SetBoolean("isOnBootDevice", volume->on_boot_device()); 96 97 return volume_info; 98} 99 100// Sets permissions for the Drive mount point so Files.app can access files 101// in the mount point directory. It's safe to call this function even if 102// Drive is disabled by the setting (i.e. prefs::kDisableDrive is true). 103void SetDriveMountPointPermissions( 104 Profile* profile, 105 const std::string& extension_id, 106 content::RenderViewHost* render_view_host) { 107 if (!render_view_host || 108 !render_view_host->GetSiteInstance() || !render_view_host->GetProcess()) { 109 return; 110 } 111 112 content::SiteInstance* site_instance = render_view_host->GetSiteInstance(); 113 fileapi::ExternalFileSystemBackend* backend = 114 BrowserContext::GetStoragePartition(profile, site_instance)-> 115 GetFileSystemContext()->external_backend(); 116 if (!backend) 117 return; 118 119 const base::FilePath mount_point = drive::util::GetDriveMountPointPath(); 120 // Grant R/W permissions to drive 'folder'. File API layer still 121 // expects this to be satisfied. 122 ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile( 123 render_view_host->GetProcess()->GetID(), mount_point); 124 125 base::FilePath mount_point_virtual; 126 if (backend->GetVirtualPath(mount_point, &mount_point_virtual)) 127 backend->GrantFileAccessToExtension(extension_id, mount_point_virtual); 128} 129 130// Retrieves total and remaining available size on |mount_path|. 131void GetSizeStatsOnBlockingPool(const std::string& mount_path, 132 uint64* total_size, 133 uint64* remaining_size) { 134 struct statvfs stat = {}; // Zero-clear 135 if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) { 136 *total_size = 137 static_cast<uint64>(stat.f_blocks) * stat.f_frsize; 138 *remaining_size = 139 static_cast<uint64>(stat.f_bfree) * stat.f_frsize; 140 } 141} 142 143// Retrieves the maximum file name length of the file system of |path|. 144// Returns 0 if it could not be queried. 145size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) { 146 struct statvfs stat = {}; 147 if (statvfs(path.c_str(), &stat) != 0) { 148 // The filesystem seems not supporting statvfs(). Assume it to be a commonly 149 // used bound 255, and log the failure. 150 LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path; 151 return 255; 152 } 153 return stat.f_namemax; 154} 155 156// Sets last modified date. 157bool SetLastModifiedOnBlockingPool(const base::FilePath& local_path, 158 time_t timestamp) { 159 if (local_path.empty()) 160 return false; 161 162 struct stat stat_buffer; 163 if (stat(local_path.value().c_str(), &stat_buffer) != 0) 164 return false; 165 166 struct utimbuf times; 167 times.actime = stat_buffer.st_atime; 168 times.modtime = timestamp; 169 return utime(local_path.value().c_str(), ×) == 0; 170} 171 172} // namespace 173 174RequestFileSystemFunction::RequestFileSystemFunction() { 175} 176 177RequestFileSystemFunction::~RequestFileSystemFunction() { 178} 179 180void RequestFileSystemFunction::DidOpenFileSystem( 181 scoped_refptr<fileapi::FileSystemContext> file_system_context, 182 base::PlatformFileError result, 183 const std::string& name, 184 const GURL& root_path) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 187 if (result != base::PLATFORM_FILE_OK) { 188 DidFail(result); 189 return; 190 } 191 192 // RenderViewHost may have gone while the task is posted asynchronously. 193 if (!render_view_host()) { 194 DidFail(base::PLATFORM_FILE_ERROR_FAILED); 195 return; 196 } 197 198 // Set up file permission access. 199 const int child_id = render_view_host()->GetProcess()->GetID(); 200 if (!SetupFileSystemAccessPermissions(file_system_context, 201 child_id, 202 GetExtension())) { 203 DidFail(base::PLATFORM_FILE_ERROR_SECURITY); 204 return; 205 } 206 207 // Set permissions for the Drive mount point immediately when we kick of 208 // first instance of file manager. The actual mount event will be sent to 209 // UI only when we perform proper authentication. 210 // 211 // Note that we call this function even when Drive is disabled by the 212 // setting. Otherwise, we need to call this when the setting is changed at 213 // a later time, which complicates the code. 214 SetDriveMountPointPermissions(profile_, extension_id(), render_view_host()); 215 216 DictionaryValue* dict = new DictionaryValue(); 217 SetResult(dict); 218 dict->SetString("name", name); 219 dict->SetString("path", root_path.spec()); 220 dict->SetInteger("error", drive::FILE_ERROR_OK); 221 SendResponse(true); 222} 223 224void RequestFileSystemFunction::DidFail( 225 base::PlatformFileError error_code) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 228 error_ = base::StringPrintf(kFileError, static_cast<int>(error_code)); 229 SendResponse(false); 230} 231 232bool RequestFileSystemFunction::SetupFileSystemAccessPermissions( 233 scoped_refptr<fileapi::FileSystemContext> file_system_context, 234 int child_id, 235 scoped_refptr<const extensions::Extension> extension) { 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 237 238 if (!extension.get()) 239 return false; 240 241 // Make sure that only component extension can access the entire 242 // local file system. 243 if (extension_->location() != extensions::Manifest::COMPONENT) { 244 NOTREACHED() << "Private method access by non-component extension " 245 << extension->id(); 246 return false; 247 } 248 249 fileapi::ExternalFileSystemBackend* backend = 250 file_system_context->external_backend(); 251 if (!backend) 252 return false; 253 254 // Grant full access to File API from this component extension. 255 backend->GrantFullAccessToExtension(extension_->id()); 256 257 // Grant R/W file permissions to the renderer hosting component 258 // extension for all paths exposed by our local file system backend. 259 std::vector<base::FilePath> root_dirs = backend->GetRootDirectories(); 260 for (size_t i = 0; i < root_dirs.size(); ++i) { 261 ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile( 262 child_id, root_dirs[i]); 263 } 264 return true; 265} 266 267bool RequestFileSystemFunction::RunImpl() { 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 269 270 if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess()) 271 return false; 272 273 set_log_on_completion(true); 274 275 content::SiteInstance* site_instance = render_view_host()->GetSiteInstance(); 276 scoped_refptr<fileapi::FileSystemContext> file_system_context = 277 BrowserContext::GetStoragePartition(profile_, site_instance)-> 278 GetFileSystemContext(); 279 280 const GURL origin_url = source_url_.GetOrigin(); 281 file_system_context->OpenFileSystem( 282 origin_url, 283 fileapi::kFileSystemTypeExternal, 284 fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, 285 base::Bind(&RequestFileSystemFunction::DidOpenFileSystem, 286 this, 287 file_system_context)); 288 return true; 289} 290 291FileWatchFunctionBase::FileWatchFunctionBase() { 292} 293 294FileWatchFunctionBase::~FileWatchFunctionBase() { 295} 296 297void FileWatchFunctionBase::Respond(bool success) { 298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 299 300 SetResult(Value::CreateBooleanValue(success)); 301 SendResponse(success); 302} 303 304bool FileWatchFunctionBase::RunImpl() { 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 306 307 if (!render_view_host() || !render_view_host()->GetProcess()) 308 return false; 309 310 // First param is url of a file to watch. 311 std::string url; 312 if (!args_->GetString(0, &url) || url.empty()) 313 return false; 314 315 content::SiteInstance* site_instance = render_view_host()->GetSiteInstance(); 316 scoped_refptr<fileapi::FileSystemContext> file_system_context = 317 BrowserContext::GetStoragePartition(profile(), site_instance)-> 318 GetFileSystemContext(); 319 320 FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url)); 321 base::FilePath local_path = file_watch_url.path(); 322 base::FilePath virtual_path = file_watch_url.virtual_path(); 323 if (local_path.empty()) { 324 Respond(false); 325 return true; 326 } 327 PerformFileWatchOperation(local_path, virtual_path, extension_id()); 328 329 return true; 330} 331 332AddFileWatchFunction::AddFileWatchFunction() { 333} 334 335AddFileWatchFunction::~AddFileWatchFunction() { 336} 337 338void AddFileWatchFunction::PerformFileWatchOperation( 339 const base::FilePath& local_path, 340 const base::FilePath& virtual_path, 341 const std::string& extension_id) { 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 344 FileManagerEventRouter* event_router = 345 FileBrowserPrivateAPI::Get(profile_)->event_router(); 346 event_router->AddFileWatch( 347 local_path, 348 virtual_path, 349 extension_id, 350 base::Bind(&AddFileWatchFunction::Respond, this)); 351} 352 353RemoveFileWatchFunction::RemoveFileWatchFunction() { 354} 355 356RemoveFileWatchFunction::~RemoveFileWatchFunction() { 357} 358 359void RemoveFileWatchFunction::PerformFileWatchOperation( 360 const base::FilePath& local_path, 361 const base::FilePath& unused, 362 const std::string& extension_id) { 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 364 365 FileManagerEventRouter* event_router = 366 FileBrowserPrivateAPI::Get(profile_)->event_router(); 367 event_router->RemoveFileWatch(local_path, extension_id); 368 Respond(true); 369} 370 371SetLastModifiedFunction::SetLastModifiedFunction() { 372} 373 374SetLastModifiedFunction::~SetLastModifiedFunction() { 375} 376 377bool SetLastModifiedFunction::RunImpl() { 378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 379 380 if (args_->GetSize() != 2) { 381 return false; 382 } 383 384 std::string file_url; 385 if (!args_->GetString(0, &file_url)) 386 return false; 387 388 std::string timestamp; 389 if (!args_->GetString(1, ×tamp)) 390 return false; 391 392 base::FilePath local_path = util::GetLocalPathFromURL( 393 render_view_host(), profile(), GURL(file_url)); 394 395 base::PostTaskAndReplyWithResult( 396 BrowserThread::GetBlockingPool(), 397 FROM_HERE, 398 base::Bind(&SetLastModifiedOnBlockingPool, 399 local_path, 400 strtoul(timestamp.c_str(), NULL, 0)), 401 base::Bind(&SetLastModifiedFunction::SendResponse, 402 this)); 403 return true; 404} 405 406GetSizeStatsFunction::GetSizeStatsFunction() { 407} 408 409GetSizeStatsFunction::~GetSizeStatsFunction() { 410} 411 412bool GetSizeStatsFunction::RunImpl() { 413 if (args_->GetSize() != 1) { 414 return false; 415 } 416 417 std::string mount_url; 418 if (!args_->GetString(0, &mount_url)) 419 return false; 420 421 base::FilePath file_path = util::GetLocalPathFromURL( 422 render_view_host(), profile(), GURL(mount_url)); 423 if (file_path.empty()) 424 return false; 425 426 if (file_path == drive::util::GetDriveMountPointPath()) { 427 drive::DriveIntegrationService* integration_service = 428 drive::DriveIntegrationServiceFactory::GetForProfile(profile_); 429 // |integration_service| is NULL if Drive is disabled. 430 if (!integration_service) { 431 // If stats couldn't be gotten for drive, result should be left 432 // undefined. See comments in GetDriveAvailableSpaceCallback(). 433 SendResponse(true); 434 return true; 435 } 436 437 drive::FileSystemInterface* file_system = 438 integration_service->file_system(); 439 440 file_system->GetAvailableSpace( 441 base::Bind(&GetSizeStatsFunction::GetDriveAvailableSpaceCallback, 442 this)); 443 444 } else { 445 uint64* total_size = new uint64(0); 446 uint64* remaining_size = new uint64(0); 447 BrowserThread::PostBlockingPoolTaskAndReply( 448 FROM_HERE, 449 base::Bind(&GetSizeStatsOnBlockingPool, 450 file_path.value(), 451 total_size, 452 remaining_size), 453 base::Bind(&GetSizeStatsFunction::GetSizeStatsCallback, 454 this, 455 base::Owned(total_size), 456 base::Owned(remaining_size))); 457 } 458 return true; 459} 460 461void GetSizeStatsFunction::GetDriveAvailableSpaceCallback( 462 drive::FileError error, 463 int64 bytes_total, 464 int64 bytes_used) { 465 if (error == drive::FILE_ERROR_OK) { 466 const uint64 bytes_total_unsigned = bytes_total; 467 const uint64 bytes_remaining_unsigned = bytes_total - bytes_used; 468 GetSizeStatsCallback(&bytes_total_unsigned, 469 &bytes_remaining_unsigned); 470 } else { 471 // If stats couldn't be gotten for drive, result should be left undefined. 472 SendResponse(true); 473 } 474} 475 476void GetSizeStatsFunction::GetSizeStatsCallback( 477 const uint64* total_size, 478 const uint64* remaining_size) { 479 base::DictionaryValue* sizes = new base::DictionaryValue(); 480 SetResult(sizes); 481 482 sizes->SetDouble("totalSize", static_cast<double>(*total_size)); 483 sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size)); 484 485 SendResponse(true); 486} 487 488GetVolumeMetadataFunction::GetVolumeMetadataFunction() { 489} 490 491GetVolumeMetadataFunction::~GetVolumeMetadataFunction() { 492} 493 494bool GetVolumeMetadataFunction::RunImpl() { 495 if (args_->GetSize() != 1) { 496 error_ = "Invalid argument count"; 497 return false; 498 } 499 500 std::string volume_mount_url; 501 if (!args_->GetString(0, &volume_mount_url)) { 502 NOTREACHED(); 503 return false; 504 } 505 506 base::FilePath file_path = util::GetLocalPathFromURL( 507 render_view_host(), profile(), GURL(volume_mount_url)); 508 if (file_path.empty()) { 509 error_ = "Invalid mount path."; 510 return false; 511 } 512 513 results_.reset(); 514 515 const DiskMountManager::Disk* volume = GetVolumeAsDisk(file_path.value()); 516 if (volume) { 517 DictionaryValue* volume_info = 518 CreateValueFromDisk(profile_, extension_->id(), volume); 519 SetResult(volume_info); 520 } 521 522 SendResponse(true); 523 return true; 524} 525 526ValidatePathNameLengthFunction::ValidatePathNameLengthFunction() { 527} 528 529ValidatePathNameLengthFunction::~ValidatePathNameLengthFunction() { 530} 531 532bool ValidatePathNameLengthFunction::RunImpl() { 533 std::string parent_url; 534 if (!args_->GetString(0, &parent_url)) 535 return false; 536 537 std::string name; 538 if (!args_->GetString(1, &name)) 539 return false; 540 541 content::SiteInstance* site_instance = render_view_host()->GetSiteInstance(); 542 scoped_refptr<fileapi::FileSystemContext> file_system_context = 543 BrowserContext::GetStoragePartition(profile(), site_instance)-> 544 GetFileSystemContext(); 545 fileapi::FileSystemURL filesystem_url( 546 file_system_context->CrackURL(GURL(parent_url))); 547 if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url)) 548 return false; 549 550 // No explicit limit on the length of Drive file names. 551 if (filesystem_url.type() == fileapi::kFileSystemTypeDrive) { 552 SetResult(new base::FundamentalValue(true)); 553 SendResponse(true); 554 return true; 555 } 556 557 base::PostTaskAndReplyWithResult( 558 BrowserThread::GetBlockingPool(), 559 FROM_HERE, 560 base::Bind(&GetFileNameMaxLengthOnBlockingPool, 561 filesystem_url.path().AsUTF8Unsafe()), 562 base::Bind(&ValidatePathNameLengthFunction::OnFilePathLimitRetrieved, 563 this, name.size())); 564 return true; 565} 566 567void ValidatePathNameLengthFunction::OnFilePathLimitRetrieved( 568 size_t current_length, 569 size_t max_length) { 570 SetResult(new base::FundamentalValue(current_length <= max_length)); 571 SendResponse(true); 572} 573 574FormatDeviceFunction::FormatDeviceFunction() { 575} 576 577FormatDeviceFunction::~FormatDeviceFunction() { 578} 579 580bool FormatDeviceFunction::RunImpl() { 581 if (args_->GetSize() != 1) { 582 return false; 583 } 584 585 std::string volume_file_url; 586 if (!args_->GetString(0, &volume_file_url)) { 587 NOTREACHED(); 588 return false; 589 } 590 591 base::FilePath file_path = util::GetLocalPathFromURL( 592 render_view_host(), profile(), GURL(volume_file_url)); 593 if (file_path.empty()) 594 return false; 595 596 DiskMountManager::GetInstance()->FormatMountedDevice(file_path.value()); 597 SendResponse(true); 598 return true; 599} 600 601} // namespace file_manager 602