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/file_manager/volume_manager.h" 6 7 #include "base/basictypes.h" 8 #include "base/bind.h" 9 #include "base/files/file_path.h" 10 #include "base/logging.h" 11 #include "base/metrics/histogram.h" 12 #include "base/prefs/pref_service.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "chrome/browser/chromeos/drive/drive_integration_service.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/file_manager/path_util.h" 19 #include "chrome/browser/chromeos/file_manager/snapshot_manager.h" 20 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h" 21 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h" 22 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" 23 #include "chrome/browser/chromeos/profiles/profile_helper.h" 24 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/common/pref_names.h" 27 #include "chromeos/disks/disk_mount_manager.h" 28 #include "components/storage_monitor/storage_monitor.h" 29 #include "content/public/browser/browser_context.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "storage/browser/fileapi/external_mount_points.h" 32 33 namespace file_manager { 34 namespace { 35 36 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-"; 37 const char kMtpVolumeIdPrefix [] = "mtp:"; 38 39 // Registers |path| as the "Downloads" folder to the FileSystem API backend. 40 // If another folder is already mounted. It revokes and overrides the old one. 41 bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) { 42 // Although we show only profile's own "Downloads" folder in Files.app, 43 // in the backend we need to mount all profile's download directory globally. 44 // Otherwise, Files.app cannot support cross-profile file copies, etc. 45 // For this reason, we need to register to the global GetSystemInstance(). 46 const std::string mount_point_name = 47 file_manager::util::GetDownloadsMountPointName(profile); 48 storage::ExternalMountPoints* const mount_points = 49 storage::ExternalMountPoints::GetSystemInstance(); 50 51 // In some tests we want to override existing Downloads mount point, so we 52 // first revoke the existing mount point (if any). 53 mount_points->RevokeFileSystem(mount_point_name); 54 return mount_points->RegisterFileSystem(mount_point_name, 55 storage::kFileSystemTypeNativeLocal, 56 storage::FileSystemMountOption(), 57 path); 58 } 59 60 // Finds the path register as the "Downloads" folder to FileSystem API backend. 61 // Returns false if it is not registered. 62 bool FindDownloadsMountPointPath(Profile* profile, base::FilePath* path) { 63 const std::string mount_point_name = 64 util::GetDownloadsMountPointName(profile); 65 storage::ExternalMountPoints* const mount_points = 66 storage::ExternalMountPoints::GetSystemInstance(); 67 68 return mount_points->GetRegisteredPath(mount_point_name, path); 69 } 70 71 VolumeType MountTypeToVolumeType(chromeos::MountType type) { 72 switch (type) { 73 case chromeos::MOUNT_TYPE_INVALID: 74 // We don't expect this value, but list here, so that when any value 75 // is added to the enum definition but this is not edited, the compiler 76 // warns it. 77 break; 78 case chromeos::MOUNT_TYPE_DEVICE: 79 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION; 80 case chromeos::MOUNT_TYPE_ARCHIVE: 81 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE; 82 } 83 84 NOTREACHED(); 85 return VOLUME_TYPE_DOWNLOADS_DIRECTORY; 86 } 87 88 // Returns a string representation of the given volume type. 89 std::string VolumeTypeToString(VolumeType type) { 90 switch (type) { 91 case VOLUME_TYPE_GOOGLE_DRIVE: 92 return "drive"; 93 case VOLUME_TYPE_DOWNLOADS_DIRECTORY: 94 return "downloads"; 95 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION: 96 return "removable"; 97 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE: 98 return "archive"; 99 case VOLUME_TYPE_CLOUD_DEVICE: 100 return "cloud_device"; 101 case VOLUME_TYPE_PROVIDED: 102 return "provided"; 103 case VOLUME_TYPE_MTP: 104 return "mtp"; 105 case VOLUME_TYPE_TESTING: 106 return "testing"; 107 case NUM_VOLUME_TYPE: 108 break; 109 } 110 NOTREACHED(); 111 return ""; 112 } 113 114 // Generates a unique volume ID for the given volume info. 115 std::string GenerateVolumeId(const VolumeInfo& volume_info) { 116 // For the same volume type, base names are unique, as mount points are 117 // flat for the same volume type. 118 return (VolumeTypeToString(volume_info.type) + ":" + 119 volume_info.mount_path.BaseName().AsUTF8Unsafe()); 120 } 121 122 // Returns the VolumeInfo for Drive file system. 123 VolumeInfo CreateDriveVolumeInfo(Profile* profile) { 124 const base::FilePath& drive_path = 125 drive::util::GetDriveMountPointPath(profile); 126 127 VolumeInfo volume_info; 128 volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE; 129 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN; 130 volume_info.source_path = drive_path; 131 volume_info.mount_path = drive_path; 132 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE; 133 volume_info.is_parent = false; 134 volume_info.is_read_only = false; 135 volume_info.volume_id = GenerateVolumeId(volume_info); 136 return volume_info; 137 } 138 139 VolumeInfo CreateDownloadsVolumeInfo(const base::FilePath& downloads_path) { 140 VolumeInfo volume_info; 141 volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY; 142 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN; 143 // Keep source_path empty. 144 volume_info.mount_path = downloads_path; 145 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE; 146 volume_info.is_parent = false; 147 volume_info.is_read_only = false; 148 volume_info.volume_id = GenerateVolumeId(volume_info); 149 return volume_info; 150 } 151 152 VolumeInfo CreateTestingVolumeInfo(const base::FilePath& path, 153 VolumeType volume_type, 154 chromeos::DeviceType device_type) { 155 VolumeInfo volume_info; 156 volume_info.type = volume_type; 157 volume_info.device_type = device_type; 158 // Keep source_path empty. 159 volume_info.mount_path = path; 160 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE; 161 volume_info.is_parent = false; 162 volume_info.is_read_only = false; 163 volume_info.volume_id = GenerateVolumeId(volume_info); 164 return volume_info; 165 } 166 167 VolumeInfo CreateVolumeInfoFromMountPointInfo( 168 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point, 169 const chromeos::disks::DiskMountManager::Disk* disk) { 170 VolumeInfo volume_info; 171 volume_info.type = MountTypeToVolumeType(mount_point.mount_type); 172 volume_info.source_path = base::FilePath(mount_point.source_path); 173 volume_info.mount_path = base::FilePath(mount_point.mount_path); 174 volume_info.mount_condition = mount_point.mount_condition; 175 volume_info.volume_label = volume_info.mount_path.BaseName().AsUTF8Unsafe(); 176 if (disk) { 177 volume_info.device_type = disk->device_type(); 178 volume_info.system_path_prefix = 179 base::FilePath(disk->system_path_prefix()); 180 volume_info.is_parent = disk->is_parent(); 181 volume_info.is_read_only = disk->is_read_only(); 182 } else { 183 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN; 184 volume_info.is_parent = false; 185 volume_info.is_read_only = 186 (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE); 187 } 188 volume_info.volume_id = GenerateVolumeId(volume_info); 189 190 return volume_info; 191 } 192 193 VolumeInfo CreateProvidedFileSystemVolumeInfo( 194 const chromeos::file_system_provider::ProvidedFileSystemInfo& 195 file_system_info) { 196 VolumeInfo volume_info; 197 volume_info.file_system_id = file_system_info.file_system_id(); 198 volume_info.extension_id = file_system_info.extension_id(); 199 volume_info.volume_label = file_system_info.display_name(); 200 volume_info.type = VOLUME_TYPE_PROVIDED; 201 volume_info.mount_path = file_system_info.mount_path(); 202 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE; 203 volume_info.is_parent = true; 204 volume_info.is_read_only = !file_system_info.writable(); 205 volume_info.volume_id = GenerateVolumeId(volume_info); 206 return volume_info; 207 } 208 209 std::string GetMountPointNameForMediaStorage( 210 const storage_monitor::StorageInfo& info) { 211 std::string name(kFileManagerMTPMountNamePrefix); 212 name += info.device_id(); 213 return name; 214 } 215 216 } // namespace 217 218 VolumeInfo::VolumeInfo() 219 : type(VOLUME_TYPE_GOOGLE_DRIVE), 220 device_type(chromeos::DEVICE_TYPE_UNKNOWN), 221 mount_condition(chromeos::disks::MOUNT_CONDITION_NONE), 222 is_parent(false), 223 is_read_only(false) { 224 } 225 226 VolumeInfo::~VolumeInfo() { 227 } 228 229 VolumeManager::VolumeManager( 230 Profile* profile, 231 drive::DriveIntegrationService* drive_integration_service, 232 chromeos::PowerManagerClient* power_manager_client, 233 chromeos::disks::DiskMountManager* disk_mount_manager, 234 chromeos::file_system_provider::Service* file_system_provider_service) 235 : profile_(profile), 236 drive_integration_service_(drive_integration_service), 237 disk_mount_manager_(disk_mount_manager), 238 file_system_provider_service_(file_system_provider_service), 239 snapshot_manager_(new SnapshotManager(profile_)), 240 weak_ptr_factory_(this) { 241 DCHECK(disk_mount_manager); 242 } 243 244 VolumeManager::~VolumeManager() { 245 } 246 247 VolumeManager* VolumeManager::Get(content::BrowserContext* context) { 248 return VolumeManagerFactory::Get(context); 249 } 250 251 void VolumeManager::Initialize() { 252 // If in Sign in profile, then skip mounting and listening for mount events. 253 if (chromeos::ProfileHelper::IsSigninProfile(profile_)) 254 return; 255 256 // Path to mount user folders have changed several times. We need to migrate 257 // the old preferences on paths to the new format when needed. For the detail, 258 // see the comments in file_manager::util::MigratePathFromOldFormat, 259 // Note: Preferences related to downloads are handled in download_prefs.cc. 260 // TODO(kinaba): Remove this after several rounds of releases. 261 const base::FilePath old_path = 262 profile_->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory); 263 base::FilePath new_path; 264 if (!old_path.empty() && 265 file_manager::util::MigratePathFromOldFormat(profile_, 266 old_path, &new_path)) { 267 profile_->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory, 268 new_path); 269 } 270 271 // Register 'Downloads' folder for the profile to the file system. 272 const base::FilePath downloads = 273 file_manager::util::GetDownloadsFolderForProfile(profile_); 274 const bool success = RegisterDownloadsMountPoint(profile_, downloads); 275 DCHECK(success); 276 277 DoMountEvent(chromeos::MOUNT_ERROR_NONE, 278 CreateDownloadsVolumeInfo(downloads)); 279 280 // Subscribe to DriveIntegrationService. 281 if (drive_integration_service_) { 282 drive_integration_service_->AddObserver(this); 283 if (drive_integration_service_->IsMounted()) { 284 DoMountEvent(chromeos::MOUNT_ERROR_NONE, 285 CreateDriveVolumeInfo(profile_)); 286 } 287 } 288 289 // Subscribe to DiskMountManager. 290 disk_mount_manager_->AddObserver(this); 291 disk_mount_manager_->EnsureMountInfoRefreshed( 292 base::Bind(&VolumeManager::OnDiskMountManagerRefreshed, 293 weak_ptr_factory_.GetWeakPtr())); 294 295 // Subscribe to FileSystemProviderService and register currently mounted 296 // volumes for the profile. 297 if (file_system_provider_service_) { 298 using chromeos::file_system_provider::ProvidedFileSystemInfo; 299 file_system_provider_service_->AddObserver(this); 300 301 std::vector<ProvidedFileSystemInfo> file_system_info_list = 302 file_system_provider_service_->GetProvidedFileSystemInfoList(); 303 for (size_t i = 0; i < file_system_info_list.size(); ++i) { 304 VolumeInfo volume_info = 305 CreateProvidedFileSystemVolumeInfo(file_system_info_list[i]); 306 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info); 307 } 308 } 309 310 // Subscribe to Profile Preference change. 311 pref_change_registrar_.Init(profile_->GetPrefs()); 312 pref_change_registrar_.Add( 313 prefs::kExternalStorageDisabled, 314 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged, 315 weak_ptr_factory_.GetWeakPtr())); 316 317 // Subscribe to storage monitor for MTP notifications. 318 if (storage_monitor::StorageMonitor::GetInstance()) { 319 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized( 320 base::Bind(&VolumeManager::OnStorageMonitorInitialized, 321 weak_ptr_factory_.GetWeakPtr())); 322 } 323 } 324 325 void VolumeManager::Shutdown() { 326 weak_ptr_factory_.InvalidateWeakPtrs(); 327 328 snapshot_manager_.reset(); 329 pref_change_registrar_.RemoveAll(); 330 disk_mount_manager_->RemoveObserver(this); 331 if (storage_monitor::StorageMonitor::GetInstance()) 332 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this); 333 334 if (drive_integration_service_) 335 drive_integration_service_->RemoveObserver(this); 336 337 if (file_system_provider_service_) 338 file_system_provider_service_->RemoveObserver(this); 339 } 340 341 void VolumeManager::AddObserver(VolumeManagerObserver* observer) { 342 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 343 DCHECK(observer); 344 observers_.AddObserver(observer); 345 } 346 347 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) { 348 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 349 DCHECK(observer); 350 observers_.RemoveObserver(observer); 351 } 352 353 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const { 354 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 355 356 std::vector<VolumeInfo> result; 357 for (std::map<std::string, VolumeInfo>::const_iterator iter = 358 mounted_volumes_.begin(); 359 iter != mounted_volumes_.end(); 360 ++iter) { 361 result.push_back(iter->second); 362 } 363 return result; 364 } 365 366 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id, 367 VolumeInfo* result) const { 368 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 369 DCHECK(result); 370 371 std::map<std::string, VolumeInfo>::const_iterator iter = 372 mounted_volumes_.find(volume_id); 373 if (iter == mounted_volumes_.end()) 374 return false; 375 *result = iter->second; 376 return true; 377 } 378 379 bool VolumeManager::RegisterDownloadsDirectoryForTesting( 380 const base::FilePath& path) { 381 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 382 383 base::FilePath old_path; 384 if (FindDownloadsMountPointPath(profile_, &old_path)) { 385 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, 386 CreateDownloadsVolumeInfo(old_path)); 387 } 388 389 bool success = RegisterDownloadsMountPoint(profile_, path); 390 DoMountEvent( 391 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH, 392 CreateDownloadsVolumeInfo(path)); 393 return success; 394 } 395 396 void VolumeManager::AddVolumeInfoForTesting(const base::FilePath& path, 397 VolumeType volume_type, 398 chromeos::DeviceType device_type) { 399 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 400 DoMountEvent(chromeos::MOUNT_ERROR_NONE, 401 CreateTestingVolumeInfo(path, volume_type, device_type)); 402 } 403 404 void VolumeManager::OnFileSystemMounted() { 405 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 406 407 // Raise mount event. 408 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed 409 // or network is unreachable. These two errors will be handled later. 410 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_); 411 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info); 412 } 413 414 void VolumeManager::OnFileSystemBeingUnmounted() { 415 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 416 417 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_); 418 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info); 419 } 420 421 void VolumeManager::OnDiskEvent( 422 chromeos::disks::DiskMountManager::DiskEvent event, 423 const chromeos::disks::DiskMountManager::Disk* disk) { 424 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 425 426 // Disregard hidden devices. 427 if (disk->is_hidden()) 428 return; 429 430 switch (event) { 431 case chromeos::disks::DiskMountManager::DISK_ADDED: 432 case chromeos::disks::DiskMountManager::DISK_CHANGED: { 433 if (disk->device_path().empty()) { 434 DVLOG(1) << "Empty system path for " << disk->device_path(); 435 return; 436 } 437 438 bool mounting = false; 439 if (disk->mount_path().empty() && disk->has_media() && 440 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) { 441 // If disk is not mounted yet and it has media and there is no policy 442 // forbidding external storage, give it a try. 443 // Initiate disk mount operation. MountPath auto-detects the filesystem 444 // format if the second argument is empty. The third argument (mount 445 // label) is not used in a disk mount operation. 446 disk_mount_manager_->MountPath( 447 disk->device_path(), std::string(), std::string(), 448 chromeos::MOUNT_TYPE_DEVICE); 449 mounting = true; 450 } 451 452 // Notify to observers. 453 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_, 454 OnDiskAdded(*disk, mounting)); 455 return; 456 } 457 458 case chromeos::disks::DiskMountManager::DISK_REMOVED: 459 // If the disk is already mounted, unmount it. 460 if (!disk->mount_path().empty()) { 461 disk_mount_manager_->UnmountPath( 462 disk->mount_path(), 463 chromeos::UNMOUNT_OPTIONS_LAZY, 464 chromeos::disks::DiskMountManager::UnmountPathCallback()); 465 } 466 467 // Notify to observers. 468 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_, 469 OnDiskRemoved(*disk)); 470 return; 471 } 472 NOTREACHED(); 473 } 474 475 void VolumeManager::OnDeviceEvent( 476 chromeos::disks::DiskMountManager::DeviceEvent event, 477 const std::string& device_path) { 478 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 479 480 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path; 481 switch (event) { 482 case chromeos::disks::DiskMountManager::DEVICE_ADDED: 483 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_, 484 OnDeviceAdded(device_path)); 485 return; 486 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: { 487 FOR_EACH_OBSERVER( 488 VolumeManagerObserver, observers_, OnDeviceRemoved(device_path)); 489 return; 490 } 491 case chromeos::disks::DiskMountManager::DEVICE_SCANNED: 492 DVLOG(1) << "Ignore SCANNED event: " << device_path; 493 return; 494 } 495 NOTREACHED(); 496 } 497 498 void VolumeManager::OnMountEvent( 499 chromeos::disks::DiskMountManager::MountEvent event, 500 chromeos::MountError error_code, 501 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) { 502 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 503 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type); 504 505 if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) { 506 // If the file is not mounted now, tell it to drive file system so that 507 // it can handle file caching correctly. 508 // Note that drive file system knows if the file is managed by drive file 509 // system or not, so here we report all paths. 510 if ((event == chromeos::disks::DiskMountManager::MOUNTING && 511 error_code != chromeos::MOUNT_ERROR_NONE) || 512 (event == chromeos::disks::DiskMountManager::UNMOUNTING && 513 error_code == chromeos::MOUNT_ERROR_NONE)) { 514 drive::FileSystemInterface* const file_system = 515 drive::util::GetFileSystemByProfile(profile_); 516 if (file_system) { 517 file_system->MarkCacheFileAsUnmounted( 518 base::FilePath(mount_info.source_path), 519 base::Bind(&drive::util::EmptyFileOperationCallback)); 520 } 521 } 522 } 523 524 // Notify a mounting/unmounting event to observers. 525 const chromeos::disks::DiskMountManager::Disk* const disk = 526 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path); 527 const VolumeInfo volume_info = 528 CreateVolumeInfoFromMountPointInfo(mount_info, disk); 529 switch (event) { 530 case chromeos::disks::DiskMountManager::MOUNTING: { 531 DoMountEvent(error_code, volume_info); 532 return; 533 } 534 case chromeos::disks::DiskMountManager::UNMOUNTING: 535 DoUnmountEvent(error_code, volume_info); 536 return; 537 } 538 NOTREACHED(); 539 } 540 541 void VolumeManager::OnFormatEvent( 542 chromeos::disks::DiskMountManager::FormatEvent event, 543 chromeos::FormatError error_code, 544 const std::string& device_path) { 545 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 546 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code 547 << ", " << device_path; 548 549 switch (event) { 550 case chromeos::disks::DiskMountManager::FORMAT_STARTED: 551 FOR_EACH_OBSERVER( 552 VolumeManagerObserver, observers_, 553 OnFormatStarted(device_path, 554 error_code == chromeos::FORMAT_ERROR_NONE)); 555 return; 556 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED: 557 if (error_code == chromeos::FORMAT_ERROR_NONE) { 558 // If format is completed successfully, try to mount the device. 559 // MountPath auto-detects filesystem format if second argument is 560 // empty. The third argument (mount label) is not used in a disk mount 561 // operation. 562 disk_mount_manager_->MountPath( 563 device_path, std::string(), std::string(), 564 chromeos::MOUNT_TYPE_DEVICE); 565 } 566 567 FOR_EACH_OBSERVER( 568 VolumeManagerObserver, observers_, 569 OnFormatCompleted(device_path, 570 error_code == chromeos::FORMAT_ERROR_NONE)); 571 572 return; 573 } 574 NOTREACHED(); 575 } 576 577 void VolumeManager::OnProvidedFileSystemMount( 578 const chromeos::file_system_provider::ProvidedFileSystemInfo& 579 file_system_info, 580 base::File::Error error) { 581 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info); 582 // TODO(mtomasz): Introduce own type, and avoid using MountError internally, 583 // since it is related to cros disks only. 584 const chromeos::MountError mount_error = error == base::File::FILE_OK 585 ? chromeos::MOUNT_ERROR_NONE 586 : chromeos::MOUNT_ERROR_UNKNOWN; 587 DoMountEvent(mount_error, volume_info); 588 } 589 590 void VolumeManager::OnProvidedFileSystemUnmount( 591 const chromeos::file_system_provider::ProvidedFileSystemInfo& 592 file_system_info, 593 base::File::Error error) { 594 // TODO(mtomasz): Introduce own type, and avoid using MountError internally, 595 // since it is related to cros disks only. 596 const chromeos::MountError mount_error = error == base::File::FILE_OK 597 ? chromeos::MOUNT_ERROR_NONE 598 : chromeos::MOUNT_ERROR_UNKNOWN; 599 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info); 600 DoUnmountEvent(mount_error, volume_info); 601 } 602 603 void VolumeManager::OnExternalStorageDisabledChanged() { 604 // If the policy just got disabled we have to unmount every device currently 605 // mounted. The opposite is fine - we can let the user re-plug her device to 606 // make it available. 607 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) { 608 // We do not iterate on mount_points directly, because mount_points can 609 // be changed by UnmountPath(). 610 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here? 611 while (!disk_mount_manager_->mount_points().empty()) { 612 std::string mount_path = 613 disk_mount_manager_->mount_points().begin()->second.mount_path; 614 disk_mount_manager_->UnmountPath( 615 mount_path, 616 chromeos::UNMOUNT_OPTIONS_NONE, 617 chromeos::disks::DiskMountManager::UnmountPathCallback()); 618 } 619 } 620 } 621 622 void VolumeManager::OnRemovableStorageAttached( 623 const storage_monitor::StorageInfo& info) { 624 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id())) 625 return; 626 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) 627 return; 628 629 const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location()); 630 const std::string fsid = GetMountPointNameForMediaStorage(info); 631 const std::string base_name = base::UTF16ToUTF8(info.model_name()); 632 633 // Assign a fresh volume ID based on the volume name. 634 std::string label = base_name; 635 for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i) 636 label = base_name + base::StringPrintf(" (%d)", i); 637 638 bool result = 639 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( 640 fsid, 641 storage::kFileSystemTypeDeviceMediaAsFileStorage, 642 storage::FileSystemMountOption(), 643 path); 644 DCHECK(result); 645 content::BrowserThread::PostTask( 646 content::BrowserThread::IO, FROM_HERE, base::Bind( 647 &MTPDeviceMapService::RegisterMTPFileSystem, 648 base::Unretained(MTPDeviceMapService::GetInstance()), 649 info.location(), fsid)); 650 651 VolumeInfo volume_info; 652 volume_info.type = VOLUME_TYPE_MTP; 653 volume_info.mount_path = path; 654 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE; 655 volume_info.is_parent = true; 656 volume_info.is_read_only = true; 657 volume_info.volume_id = kMtpVolumeIdPrefix + label; 658 volume_info.volume_label = label; 659 volume_info.source_path = path; 660 volume_info.device_type = chromeos::DEVICE_TYPE_MOBILE; 661 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info); 662 } 663 664 void VolumeManager::OnRemovableStorageDetached( 665 const storage_monitor::StorageInfo& info) { 666 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id())) 667 return; 668 669 for (std::map<std::string, VolumeInfo>::iterator it = 670 mounted_volumes_.begin(); it != mounted_volumes_.end(); ++it) { 671 if (it->second.source_path.value() == info.location()) { 672 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, VolumeInfo(it->second)); 673 674 const std::string fsid = GetMountPointNameForMediaStorage(info); 675 storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fsid); 676 content::BrowserThread::PostTask( 677 content::BrowserThread::IO, FROM_HERE, base::Bind( 678 &MTPDeviceMapService::RevokeMTPFileSystem, 679 base::Unretained(MTPDeviceMapService::GetInstance()), 680 fsid)); 681 return; 682 } 683 } 684 } 685 686 void VolumeManager::OnDiskMountManagerRefreshed(bool success) { 687 if (!success) { 688 LOG(ERROR) << "Failed to refresh disk mount manager"; 689 return; 690 } 691 692 std::vector<VolumeInfo> archives; 693 694 const chromeos::disks::DiskMountManager::MountPointMap& mount_points = 695 disk_mount_manager_->mount_points(); 696 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it = 697 mount_points.begin(); 698 it != mount_points.end(); 699 ++it) { 700 if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) { 701 // Archives are mounted after other types of volume. See below. 702 archives.push_back(CreateVolumeInfoFromMountPointInfo(it->second, NULL)); 703 continue; 704 } 705 DoMountEvent( 706 chromeos::MOUNT_ERROR_NONE, 707 CreateVolumeInfoFromMountPointInfo( 708 it->second, 709 disk_mount_manager_->FindDiskBySourcePath(it->second.source_path))); 710 } 711 712 // We mount archives only if they are opened from currently mounted volumes. 713 // To check the condition correctly in DoMountEvent, we care about the order. 714 std::vector<bool> done(archives.size(), false); 715 for (size_t i = 0; i < archives.size(); ++i) { 716 if (done[i]) 717 continue; 718 719 std::vector<VolumeInfo> chain; 720 done[i] = true; 721 chain.push_back(archives[i]); 722 723 // If archives[i]'s source_path is in another archive, mount it first. 724 for (size_t parent = i + 1; parent < archives.size(); ++parent) { 725 if (!done[parent] && 726 archives[parent].mount_path.IsParent(chain.back().source_path)) { 727 done[parent] = true; 728 chain.push_back(archives[parent]); 729 parent = i + 1; // Search archives[parent]'s parent from the beginning. 730 } 731 } 732 733 // Mount from the tail of chain. 734 for (size_t i = chain.size(); i > 0; --i) 735 DoMountEvent(chromeos::MOUNT_ERROR_NONE, chain[i - 1]); 736 } 737 } 738 739 void VolumeManager::OnStorageMonitorInitialized() { 740 std::vector<storage_monitor::StorageInfo> storages = 741 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages(); 742 for (size_t i = 0; i < storages.size(); ++i) 743 OnRemovableStorageAttached(storages[i]); 744 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this); 745 } 746 747 void VolumeManager::DoMountEvent(chromeos::MountError error_code, 748 const VolumeInfo& volume_info) { 749 // Archive files are mounted globally in system. We however don't want to show 750 // archives from profile-specific folders (Drive/Downloads) of other users in 751 // multi-profile session. To this end, we filter out archives not on the 752 // volumes already mounted on this VolumeManager instance. 753 if (volume_info.type == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) { 754 // Source may be in Drive cache folder under the current profile directory. 755 bool from_current_profile = 756 profile_->GetPath().IsParent(volume_info.source_path); 757 for (std::map<std::string, VolumeInfo>::const_iterator iter = 758 mounted_volumes_.begin(); 759 !from_current_profile && iter != mounted_volumes_.end(); 760 ++iter) { 761 if (iter->second.mount_path.IsParent(volume_info.source_path)) 762 from_current_profile = true; 763 } 764 if (!from_current_profile) 765 return; 766 } 767 768 // Filter out removable disks if forbidden by policy for this profile. 769 if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION && 770 profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) { 771 return; 772 } 773 774 if (error_code == chromeos::MOUNT_ERROR_NONE || volume_info.mount_condition) { 775 mounted_volumes_[volume_info.volume_id] = volume_info; 776 777 778 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", 779 volume_info.type, 780 NUM_VOLUME_TYPE); 781 } 782 783 FOR_EACH_OBSERVER(VolumeManagerObserver, 784 observers_, 785 OnVolumeMounted(error_code, volume_info)); 786 } 787 788 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code, 789 const VolumeInfo& volume_info) { 790 if (mounted_volumes_.find(volume_info.volume_id) == mounted_volumes_.end()) 791 return; 792 if (error_code == chromeos::MOUNT_ERROR_NONE) 793 mounted_volumes_.erase(volume_info.volume_id); 794 795 FOR_EACH_OBSERVER(VolumeManagerObserver, 796 observers_, 797 OnVolumeUnmounted(error_code, volume_info)); 798 } 799 800 } // namespace file_manager 801