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/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 
     38 namespace file_manager {
     39 namespace {
     40 
     41 // A named constant to be passed to the |is_remounting| parameter.
     42 const bool kNotRemounting = false;
     43 
     44 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
     45 const 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.
     49 bool 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.
     69 bool 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 
     78 VolumeType 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.
     96 std::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.
    122 std::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.
    130 VolumeInfo 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 
    146 VolumeInfo 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 
    159 VolumeInfo 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 
    174 VolumeInfo 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 
    200 VolumeInfo 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 
    212 VolumeInfo 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 
    228 std::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 
    237 VolumeInfo::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 
    245 VolumeInfo::~VolumeInfo() {
    246 }
    247 
    248 VolumeManager::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 
    265 VolumeManager::~VolumeManager() {
    266 }
    267 
    268 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
    269   return VolumeManagerFactory::Get(context);
    270 }
    271 
    272 void 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) != "true";
    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 
    405 void 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 
    421 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
    422   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    423   DCHECK(observer);
    424   observers_.AddObserver(observer);
    425 }
    426 
    427 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
    428   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    429   DCHECK(observer);
    430   observers_.RemoveObserver(observer);
    431 }
    432 
    433 std::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 
    446 bool 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 
    459 bool 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 
    477 void 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 
    486 void 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 
    496 void 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 
    503 void 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 
    557 void 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 
    584 void 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 
    629 void 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 
    665 void 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 
    678 void 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 
    691 void 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 
    710 void 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 
    719 void 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 
    759 void 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 
    782 void 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 
    790 void 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 
    833 void 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