Home | History | Annotate | Download | only in file_manager
      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