• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
RegisterDownloadsMountPoint(Profile * profile,const base::FilePath & path)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.
FindDownloadsMountPointPath(Profile * profile,base::FilePath * path)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 
MountTypeToVolumeType(chromeos::MountType type)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.
VolumeTypeToString(VolumeType 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.
GenerateVolumeId(const VolumeInfo & 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.
CreateDriveVolumeInfo(Profile * profile)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 
CreateDownloadsVolumeInfo(const base::FilePath & downloads_path)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 
CreateTestingVolumeInfo(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type)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 
CreateVolumeInfoFromMountPointInfo(const chromeos::disks::DiskMountManager::MountPointInfo & mount_point,const chromeos::disks::DiskMountManager::Disk * disk)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 
CreatePrivetVolumeInfo(const local_discovery::PrivetVolumeLister::VolumeInfo & privet_volume_info)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 
CreateProvidedFileSystemVolumeInfo(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info)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 
GetMountPointNameForMediaStorage(const storage_monitor::StorageInfo & info)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 
VolumeInfo()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 
~VolumeInfo()245 VolumeInfo::~VolumeInfo() {
246 }
247 
VolumeManager(Profile * profile,drive::DriveIntegrationService * drive_integration_service,chromeos::PowerManagerClient * power_manager_client,chromeos::disks::DiskMountManager * disk_mount_manager,chromeos::file_system_provider::Service * file_system_provider_service)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 
~VolumeManager()265 VolumeManager::~VolumeManager() {
266 }
267 
Get(content::BrowserContext * context)268 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
269   return VolumeManagerFactory::Get(context);
270 }
271 
Initialize()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 
Shutdown()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 
AddObserver(VolumeManagerObserver * observer)421 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
422   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
423   DCHECK(observer);
424   observers_.AddObserver(observer);
425 }
426 
RemoveObserver(VolumeManagerObserver * observer)427 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
428   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
429   DCHECK(observer);
430   observers_.RemoveObserver(observer);
431 }
432 
GetVolumeInfoList() const433 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 
FindVolumeInfoById(const std::string & volume_id,VolumeInfo * result) const446 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 
RegisterDownloadsDirectoryForTesting(const base::FilePath & path)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 
AddVolumeInfoForTesting(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type)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 
OnFileSystemMounted()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 
OnFileSystemBeingUnmounted()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 
OnDiskEvent(chromeos::disks::DiskMountManager::DiskEvent event,const chromeos::disks::DiskMountManager::Disk * disk)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 
OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,const std::string & device_path)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 
OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,chromeos::MountError error_code,const chromeos::disks::DiskMountManager::MountPointInfo & mount_info)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 
OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,chromeos::FormatError error_code,const std::string & device_path)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 
OnProvidedFileSystemMount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,base::File::Error error)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 
OnProvidedFileSystemUnmount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,base::File::Error error)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 
OnExternalStorageDisabledChanged()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 
OnPrivetVolumesAvailable(const local_discovery::PrivetVolumeLister::VolumeList & volumes)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 
OnRemovableStorageAttached(const storage_monitor::StorageInfo & info)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 
OnRemovableStorageDetached(const storage_monitor::StorageInfo & info)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 
OnStorageMonitorInitialized()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 
DoMountEvent(chromeos::MountError error_code,const VolumeInfo & volume_info,bool is_remounting)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 
DoUnmountEvent(chromeos::MountError error_code,const VolumeInfo & volume_info)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