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