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.
5 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
7 #include "base/basictypes.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"
33 namespace file_manager {
36 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
37 const char kMtpVolumeIdPrefix [] = "mtp:";
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();
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(),
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();
68 return mount_points->GetRegisteredPath(mount_point_name, path);
71 VolumeType MountTypeToVolumeType(chromeos::MountType 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
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;
85 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
88 // Returns a string representation of the given volume type.
89 std::string VolumeTypeToString(VolumeType type) {
91 case VOLUME_TYPE_GOOGLE_DRIVE:
93 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
95 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
97 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
99 case VOLUME_TYPE_CLOUD_DEVICE:
100 return "cloud_device";
101 case VOLUME_TYPE_PROVIDED:
103 case VOLUME_TYPE_MTP:
105 case VOLUME_TYPE_TESTING:
107 case NUM_VOLUME_TYPE:
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());
122 // Returns the VolumeInfo for Drive file system.
123 VolumeInfo CreateDriveVolumeInfo(Profile* profile) {
124 const base::FilePath& drive_path =
125 drive::util::GetDriveMountPointPath(profile);
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.has_media = false;
136 volume_info.volume_id = GenerateVolumeId(volume_info);
140 VolumeInfo CreateDownloadsVolumeInfo(const base::FilePath& downloads_path) {
141 VolumeInfo volume_info;
142 volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
143 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
144 // Keep source_path empty.
145 volume_info.mount_path = downloads_path;
146 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
147 volume_info.is_parent = false;
148 volume_info.is_read_only = false;
149 volume_info.has_media = false;
150 volume_info.volume_id = GenerateVolumeId(volume_info);
154 VolumeInfo CreateTestingVolumeInfo(const base::FilePath& path,
155 VolumeType volume_type,
156 chromeos::DeviceType device_type) {
157 VolumeInfo volume_info;
158 volume_info.type = volume_type;
159 volume_info.device_type = device_type;
160 // Keep source_path empty.
161 volume_info.mount_path = path;
162 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
163 volume_info.is_parent = false;
164 volume_info.is_read_only = false;
165 volume_info.has_media = false;
166 volume_info.volume_id = GenerateVolumeId(volume_info);
170 VolumeInfo CreateVolumeInfoFromMountPointInfo(
171 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
172 const chromeos::disks::DiskMountManager::Disk* disk) {
173 VolumeInfo volume_info;
174 volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
175 volume_info.source_path = base::FilePath(mount_point.source_path);
176 volume_info.mount_path = base::FilePath(mount_point.mount_path);
177 volume_info.mount_condition = mount_point.mount_condition;
178 volume_info.volume_label = volume_info.mount_path.BaseName().AsUTF8Unsafe();
180 volume_info.device_type = disk->device_type();
181 volume_info.system_path_prefix =
182 base::FilePath(disk->system_path_prefix());
183 volume_info.is_parent = disk->is_parent();
184 volume_info.is_read_only = disk->is_read_only();
185 volume_info.has_media = disk->has_media();
187 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
188 volume_info.is_parent = false;
189 volume_info.is_read_only =
190 (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
191 volume_info.has_media = false;
193 volume_info.volume_id = GenerateVolumeId(volume_info);
198 VolumeInfo CreateProvidedFileSystemVolumeInfo(
199 const chromeos::file_system_provider::ProvidedFileSystemInfo&
201 VolumeInfo volume_info;
202 volume_info.file_system_id = file_system_info.file_system_id();
203 volume_info.extension_id = file_system_info.extension_id();
204 volume_info.volume_label = file_system_info.display_name();
205 volume_info.type = VOLUME_TYPE_PROVIDED;
206 volume_info.mount_path = file_system_info.mount_path();
207 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
208 volume_info.is_parent = true;
209 volume_info.is_read_only = !file_system_info.writable();
210 volume_info.has_media = false;
211 volume_info.volume_id = GenerateVolumeId(volume_info);
215 std::string GetMountPointNameForMediaStorage(
216 const storage_monitor::StorageInfo& info) {
217 std::string name(kFileManagerMTPMountNamePrefix);
218 name += info.device_id();
224 VolumeInfo::VolumeInfo()
225 : type(VOLUME_TYPE_GOOGLE_DRIVE),
226 device_type(chromeos::DEVICE_TYPE_UNKNOWN),
227 mount_condition(chromeos::disks::MOUNT_CONDITION_NONE),
233 VolumeInfo::~VolumeInfo() {
236 VolumeManager::VolumeManager(
238 drive::DriveIntegrationService* drive_integration_service,
239 chromeos::PowerManagerClient* power_manager_client,
240 chromeos::disks::DiskMountManager* disk_mount_manager,
241 chromeos::file_system_provider::Service* file_system_provider_service)
243 drive_integration_service_(drive_integration_service),
244 disk_mount_manager_(disk_mount_manager),
245 file_system_provider_service_(file_system_provider_service),
246 snapshot_manager_(new SnapshotManager(profile_)),
247 weak_ptr_factory_(this) {
248 DCHECK(disk_mount_manager);
251 VolumeManager::~VolumeManager() {
254 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
255 return VolumeManagerFactory::Get(context);
258 void VolumeManager::Initialize() {
259 // If in Sign in profile, then skip mounting and listening for mount events.
260 if (chromeos::ProfileHelper::IsSigninProfile(profile_))
263 // Register 'Downloads' folder for the profile to the file system.
264 const base::FilePath downloads =
265 file_manager::util::GetDownloadsFolderForProfile(profile_);
266 const bool success = RegisterDownloadsMountPoint(profile_, downloads);
269 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
270 CreateDownloadsVolumeInfo(downloads));
272 // Subscribe to DriveIntegrationService.
273 if (drive_integration_service_) {
274 drive_integration_service_->AddObserver(this);
275 if (drive_integration_service_->IsMounted()) {
276 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
277 CreateDriveVolumeInfo(profile_));
281 // Subscribe to DiskMountManager.
282 disk_mount_manager_->AddObserver(this);
283 disk_mount_manager_->EnsureMountInfoRefreshed(
284 base::Bind(&VolumeManager::OnDiskMountManagerRefreshed,
285 weak_ptr_factory_.GetWeakPtr()));
287 // Subscribe to FileSystemProviderService and register currently mounted
288 // volumes for the profile.
289 if (file_system_provider_service_) {
290 using chromeos::file_system_provider::ProvidedFileSystemInfo;
291 file_system_provider_service_->AddObserver(this);
293 std::vector<ProvidedFileSystemInfo> file_system_info_list =
294 file_system_provider_service_->GetProvidedFileSystemInfoList();
295 for (size_t i = 0; i < file_system_info_list.size(); ++i) {
296 VolumeInfo volume_info =
297 CreateProvidedFileSystemVolumeInfo(file_system_info_list[i]);
298 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
302 // Subscribe to Profile Preference change.
303 pref_change_registrar_.Init(profile_->GetPrefs());
304 pref_change_registrar_.Add(
305 prefs::kExternalStorageDisabled,
306 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
307 weak_ptr_factory_.GetWeakPtr()));
309 // Subscribe to storage monitor for MTP notifications.
310 if (storage_monitor::StorageMonitor::GetInstance()) {
311 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
312 base::Bind(&VolumeManager::OnStorageMonitorInitialized,
313 weak_ptr_factory_.GetWeakPtr()));
317 void VolumeManager::Shutdown() {
318 weak_ptr_factory_.InvalidateWeakPtrs();
320 snapshot_manager_.reset();
321 pref_change_registrar_.RemoveAll();
322 disk_mount_manager_->RemoveObserver(this);
323 if (storage_monitor::StorageMonitor::GetInstance())
324 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
326 if (drive_integration_service_)
327 drive_integration_service_->RemoveObserver(this);
329 if (file_system_provider_service_)
330 file_system_provider_service_->RemoveObserver(this);
333 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
334 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
336 observers_.AddObserver(observer);
339 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
340 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
342 observers_.RemoveObserver(observer);
345 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
346 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
348 std::vector<VolumeInfo> result;
349 for (std::map<std::string, VolumeInfo>::const_iterator iter =
350 mounted_volumes_.begin();
351 iter != mounted_volumes_.end();
353 result.push_back(iter->second);
358 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
359 VolumeInfo* result) const {
360 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
363 std::map<std::string, VolumeInfo>::const_iterator iter =
364 mounted_volumes_.find(volume_id);
365 if (iter == mounted_volumes_.end())
367 *result = iter->second;
371 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
372 const base::FilePath& path) {
373 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
375 base::FilePath old_path;
376 if (FindDownloadsMountPointPath(profile_, &old_path)) {
377 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
378 CreateDownloadsVolumeInfo(old_path));
381 bool success = RegisterDownloadsMountPoint(profile_, path);
383 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
384 CreateDownloadsVolumeInfo(path));
388 void VolumeManager::AddVolumeInfoForTesting(const base::FilePath& path,
389 VolumeType volume_type,
390 chromeos::DeviceType device_type) {
391 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
392 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
393 CreateTestingVolumeInfo(path, volume_type, device_type));
396 void VolumeManager::OnFileSystemMounted() {
397 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
399 // Raise mount event.
400 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
401 // or network is unreachable. These two errors will be handled later.
402 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
403 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
406 void VolumeManager::OnFileSystemBeingUnmounted() {
407 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
409 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
410 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
413 void VolumeManager::OnDiskEvent(
414 chromeos::disks::DiskMountManager::DiskEvent event,
415 const chromeos::disks::DiskMountManager::Disk* disk) {
416 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
418 // Disregard hidden devices.
419 if (disk->is_hidden())
423 case chromeos::disks::DiskMountManager::DISK_ADDED:
424 case chromeos::disks::DiskMountManager::DISK_CHANGED: {
425 if (disk->device_path().empty()) {
426 DVLOG(1) << "Empty system path for " << disk->device_path();
430 bool mounting = false;
431 if (disk->mount_path().empty() && disk->has_media() &&
432 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
433 // If disk is not mounted yet and it has media and there is no policy
434 // forbidding external storage, give it a try.
435 // Initiate disk mount operation. MountPath auto-detects the filesystem
436 // format if the second argument is empty. The third argument (mount
437 // label) is not used in a disk mount operation.
438 disk_mount_manager_->MountPath(
439 disk->device_path(), std::string(), std::string(),
440 chromeos::MOUNT_TYPE_DEVICE);
444 // Notify to observers.
445 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
446 OnDiskAdded(*disk, mounting));
450 case chromeos::disks::DiskMountManager::DISK_REMOVED:
451 // If the disk is already mounted, unmount it.
452 if (!disk->mount_path().empty()) {
453 disk_mount_manager_->UnmountPath(
455 chromeos::UNMOUNT_OPTIONS_LAZY,
456 chromeos::disks::DiskMountManager::UnmountPathCallback());
459 // Notify to observers.
460 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
461 OnDiskRemoved(*disk));
467 void VolumeManager::OnDeviceEvent(
468 chromeos::disks::DiskMountManager::DeviceEvent event,
469 const std::string& device_path) {
470 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
472 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
474 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
475 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
476 OnDeviceAdded(device_path));
478 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
480 VolumeManagerObserver, observers_, OnDeviceRemoved(device_path));
483 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
484 DVLOG(1) << "Ignore SCANNED event: " << device_path;
490 void VolumeManager::OnMountEvent(
491 chromeos::disks::DiskMountManager::MountEvent event,
492 chromeos::MountError error_code,
493 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
494 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
495 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
497 if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
498 // If the file is not mounted now, tell it to drive file system so that
499 // it can handle file caching correctly.
500 // Note that drive file system knows if the file is managed by drive file
501 // system or not, so here we report all paths.
502 if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
503 error_code != chromeos::MOUNT_ERROR_NONE) ||
504 (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
505 error_code == chromeos::MOUNT_ERROR_NONE)) {
506 drive::FileSystemInterface* const file_system =
507 drive::util::GetFileSystemByProfile(profile_);
509 file_system->MarkCacheFileAsUnmounted(
510 base::FilePath(mount_info.source_path),
511 base::Bind(&drive::util::EmptyFileOperationCallback));
516 // Notify a mounting/unmounting event to observers.
517 const chromeos::disks::DiskMountManager::Disk* const disk =
518 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
519 const VolumeInfo volume_info =
520 CreateVolumeInfoFromMountPointInfo(mount_info, disk);
522 case chromeos::disks::DiskMountManager::MOUNTING: {
523 DoMountEvent(error_code, volume_info);
526 case chromeos::disks::DiskMountManager::UNMOUNTING:
527 DoUnmountEvent(error_code, volume_info);
533 void VolumeManager::OnFormatEvent(
534 chromeos::disks::DiskMountManager::FormatEvent event,
535 chromeos::FormatError error_code,
536 const std::string& device_path) {
537 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
538 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
539 << ", " << device_path;
542 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
544 VolumeManagerObserver, observers_,
545 OnFormatStarted(device_path,
546 error_code == chromeos::FORMAT_ERROR_NONE));
548 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
549 if (error_code == chromeos::FORMAT_ERROR_NONE) {
550 // If format is completed successfully, try to mount the device.
551 // MountPath auto-detects filesystem format if second argument is
552 // empty. The third argument (mount label) is not used in a disk mount
554 disk_mount_manager_->MountPath(
555 device_path, std::string(), std::string(),
556 chromeos::MOUNT_TYPE_DEVICE);
560 VolumeManagerObserver, observers_,
561 OnFormatCompleted(device_path,
562 error_code == chromeos::FORMAT_ERROR_NONE));
569 void VolumeManager::OnProvidedFileSystemMount(
570 const chromeos::file_system_provider::ProvidedFileSystemInfo&
572 base::File::Error error) {
573 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
574 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
575 // since it is related to cros disks only.
576 const chromeos::MountError mount_error = error == base::File::FILE_OK
577 ? chromeos::MOUNT_ERROR_NONE
578 : chromeos::MOUNT_ERROR_UNKNOWN;
579 DoMountEvent(mount_error, volume_info);
582 void VolumeManager::OnProvidedFileSystemUnmount(
583 const chromeos::file_system_provider::ProvidedFileSystemInfo&
585 base::File::Error error) {
586 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
587 // since it is related to cros disks only.
588 const chromeos::MountError mount_error = error == base::File::FILE_OK
589 ? chromeos::MOUNT_ERROR_NONE
590 : chromeos::MOUNT_ERROR_UNKNOWN;
591 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
592 DoUnmountEvent(mount_error, volume_info);
595 void VolumeManager::OnExternalStorageDisabledChanged() {
596 // If the policy just got disabled we have to unmount every device currently
597 // mounted. The opposite is fine - we can let the user re-plug her device to
598 // make it available.
599 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
600 // We do not iterate on mount_points directly, because mount_points can
601 // be changed by UnmountPath().
602 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
603 while (!disk_mount_manager_->mount_points().empty()) {
604 std::string mount_path =
605 disk_mount_manager_->mount_points().begin()->second.mount_path;
606 disk_mount_manager_->UnmountPath(
608 chromeos::UNMOUNT_OPTIONS_NONE,
609 chromeos::disks::DiskMountManager::UnmountPathCallback());
614 void VolumeManager::OnRemovableStorageAttached(
615 const storage_monitor::StorageInfo& info) {
616 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
618 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
621 const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location());
622 const std::string fsid = GetMountPointNameForMediaStorage(info);
623 const std::string base_name = base::UTF16ToUTF8(info.model_name());
625 // Assign a fresh volume ID based on the volume name.
626 std::string label = base_name;
627 for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i)
628 label = base_name + base::StringPrintf(" (%d)", i);
631 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
633 storage::kFileSystemTypeDeviceMediaAsFileStorage,
634 storage::FileSystemMountOption(),
637 content::BrowserThread::PostTask(
638 content::BrowserThread::IO, FROM_HERE, base::Bind(
639 &MTPDeviceMapService::RegisterMTPFileSystem,
640 base::Unretained(MTPDeviceMapService::GetInstance()),
641 info.location(), fsid));
643 VolumeInfo volume_info;
644 volume_info.type = VOLUME_TYPE_MTP;
645 volume_info.mount_path = path;
646 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
647 volume_info.is_parent = true;
648 volume_info.is_read_only = true;
649 volume_info.volume_id = kMtpVolumeIdPrefix + label;
650 volume_info.volume_label = label;
651 volume_info.source_path = path;
652 volume_info.device_type = chromeos::DEVICE_TYPE_MOBILE;
653 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
656 void VolumeManager::OnRemovableStorageDetached(
657 const storage_monitor::StorageInfo& info) {
658 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
661 for (std::map<std::string, VolumeInfo>::iterator it =
662 mounted_volumes_.begin(); it != mounted_volumes_.end(); ++it) {
663 if (it->second.source_path.value() == info.location()) {
664 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, VolumeInfo(it->second));
666 const std::string fsid = GetMountPointNameForMediaStorage(info);
667 storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fsid);
668 content::BrowserThread::PostTask(
669 content::BrowserThread::IO, FROM_HERE, base::Bind(
670 &MTPDeviceMapService::RevokeMTPFileSystem,
671 base::Unretained(MTPDeviceMapService::GetInstance()),
678 void VolumeManager::OnDiskMountManagerRefreshed(bool success) {
680 LOG(ERROR) << "Failed to refresh disk mount manager";
684 std::vector<VolumeInfo> archives;
686 const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
687 disk_mount_manager_->mount_points();
688 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
689 mount_points.begin();
690 it != mount_points.end();
692 if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
693 // Archives are mounted after other types of volume. See below.
694 archives.push_back(CreateVolumeInfoFromMountPointInfo(it->second, NULL));
698 chromeos::MOUNT_ERROR_NONE,
699 CreateVolumeInfoFromMountPointInfo(
701 disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)));
704 // We mount archives only if they are opened from currently mounted volumes.
705 // To check the condition correctly in DoMountEvent, we care about the order.
706 std::vector<bool> done(archives.size(), false);
707 for (size_t i = 0; i < archives.size(); ++i) {
711 std::vector<VolumeInfo> chain;
713 chain.push_back(archives[i]);
715 // If archives[i]'s source_path is in another archive, mount it first.
716 for (size_t parent = i + 1; parent < archives.size(); ++parent) {
718 archives[parent].mount_path.IsParent(chain.back().source_path)) {
720 chain.push_back(archives[parent]);
721 parent = i + 1; // Search archives[parent]'s parent from the beginning.
725 // Mount from the tail of chain.
726 for (size_t i = chain.size(); i > 0; --i)
727 DoMountEvent(chromeos::MOUNT_ERROR_NONE, chain[i - 1]);
731 void VolumeManager::OnStorageMonitorInitialized() {
732 std::vector<storage_monitor::StorageInfo> storages =
733 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
734 for (size_t i = 0; i < storages.size(); ++i)
735 OnRemovableStorageAttached(storages[i]);
736 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
739 void VolumeManager::DoMountEvent(chromeos::MountError error_code,
740 const VolumeInfo& volume_info) {
741 // Archive files are mounted globally in system. We however don't want to show
742 // archives from profile-specific folders (Drive/Downloads) of other users in
743 // multi-profile session. To this end, we filter out archives not on the
744 // volumes already mounted on this VolumeManager instance.
745 if (volume_info.type == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
746 // Source may be in Drive cache folder under the current profile directory.
747 bool from_current_profile =
748 profile_->GetPath().IsParent(volume_info.source_path);
749 for (std::map<std::string, VolumeInfo>::const_iterator iter =
750 mounted_volumes_.begin();
751 !from_current_profile && iter != mounted_volumes_.end();
753 if (iter->second.mount_path.IsParent(volume_info.source_path))
754 from_current_profile = true;
756 if (!from_current_profile)
760 // Filter out removable disks if forbidden by policy for this profile.
761 if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
762 profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
766 if (error_code == chromeos::MOUNT_ERROR_NONE || volume_info.mount_condition) {
767 mounted_volumes_[volume_info.volume_id] = volume_info;
770 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType",
775 FOR_EACH_OBSERVER(VolumeManagerObserver,
777 OnVolumeMounted(error_code, volume_info));
780 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code,
781 const VolumeInfo& volume_info) {
782 if (mounted_volumes_.find(volume_info.volume_id) == mounted_volumes_.end())
784 if (error_code == chromeos::MOUNT_ERROR_NONE)
785 mounted_volumes_.erase(volume_info.volume_id);
787 FOR_EACH_OBSERVER(VolumeManagerObserver,
789 OnVolumeUnmounted(error_code, volume_info));
792 } // namespace file_manager