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