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/callback.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/memory/singleton.h"
14 #include "base/prefs/pref_service.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_errors.h"
18 #include "chrome/browser/chromeos/drive/file_system_interface.h"
19 #include "chrome/browser/chromeos/drive/file_system_util.h"
20 #include "chrome/browser/chromeos/file_manager/mounted_disk_monitor.h"
21 #include "chrome/browser/chromeos/file_manager/path_util.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/dbus/cros_disks_client.h"
33 #include "chromeos/disks/disk_mount_manager.h"
34 #include "components/storage_monitor/storage_monitor.h"
35 #include "content/public/browser/browser_context.h"
36 #include "content/public/browser/browser_thread.h"
37 #include "webkit/browser/fileapi/external_mount_points.h"
39 namespace file_manager
{
42 // A named constant to be passed to the |is_remounting| parameter.
43 const bool kNotRemounting
= false;
45 const char kFileManagerMTPMountNamePrefix
[] = "fileman-mtp-";
47 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
48 // If another folder is already mounted. It revokes and overrides the old one.
49 bool RegisterDownloadsMountPoint(Profile
* profile
, const base::FilePath
& path
) {
50 // Although we show only profile's own "Downloads" folder in Files.app,
51 // in the backend we need to mount all profile's download directory globally.
52 // Otherwise, Files.app cannot support cross-profile file copies, etc.
53 // For this reason, we need to register to the global GetSystemInstance().
54 const std::string mount_point_name
=
55 file_manager::util::GetDownloadsMountPointName(profile
);
56 fileapi::ExternalMountPoints
* const mount_points
=
57 fileapi::ExternalMountPoints::GetSystemInstance();
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
);
67 // Finds the path register as the "Downloads" folder to FileSystem API backend.
68 // Returns false if it is not registered.
69 bool FindDownloadsMountPointPath(Profile
* profile
, base::FilePath
* path
) {
70 const std::string mount_point_name
=
71 util::GetDownloadsMountPointName(profile
);
72 fileapi::ExternalMountPoints
* const mount_points
=
73 fileapi::ExternalMountPoints::GetSystemInstance();
75 return mount_points
->GetRegisteredPath(mount_point_name
, path
);
78 VolumeType
MountTypeToVolumeType(chromeos::MountType 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
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
;
92 return VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
95 // Returns a string representation of the given volume type.
96 std::string
VolumeTypeToString(VolumeType type
) {
98 case VOLUME_TYPE_GOOGLE_DRIVE
:
100 case VOLUME_TYPE_DOWNLOADS_DIRECTORY
:
102 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION
:
104 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
:
106 case VOLUME_TYPE_CLOUD_DEVICE
:
107 return "cloud_device";
108 case VOLUME_TYPE_PROVIDED
:
110 case VOLUME_TYPE_MTP
:
112 case VOLUME_TYPE_TESTING
:
119 // Generates a unique volume ID for the given volume info.
120 std::string
GenerateVolumeId(const VolumeInfo
& volume_info
) {
121 // For the same volume type, base names are unique, as mount points are
122 // flat for the same volume type.
123 return (VolumeTypeToString(volume_info
.type
) + ":" +
124 volume_info
.mount_path
.BaseName().AsUTF8Unsafe());
127 // Returns the VolumeInfo for Drive file system.
128 VolumeInfo
CreateDriveVolumeInfo(Profile
* profile
) {
129 const base::FilePath
& drive_path
=
130 drive::util::GetDriveMountPointPath(profile
);
132 VolumeInfo volume_info
;
133 volume_info
.type
= VOLUME_TYPE_GOOGLE_DRIVE
;
134 volume_info
.device_type
= chromeos::DEVICE_TYPE_UNKNOWN
;
135 volume_info
.source_path
= drive_path
;
136 volume_info
.mount_path
= drive_path
;
137 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
138 volume_info
.is_parent
= false;
139 volume_info
.is_read_only
= false;
140 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
144 VolumeInfo
CreateDownloadsVolumeInfo(const base::FilePath
& downloads_path
) {
145 VolumeInfo volume_info
;
146 volume_info
.type
= VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
147 volume_info
.device_type
= chromeos::DEVICE_TYPE_UNKNOWN
;
148 // Keep source_path empty.
149 volume_info
.mount_path
= downloads_path
;
150 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
151 volume_info
.is_parent
= false;
152 volume_info
.is_read_only
= false;
153 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
157 VolumeInfo
CreateTestingVolumeInfo(const base::FilePath
& path
,
158 VolumeType volume_type
,
159 chromeos::DeviceType device_type
) {
160 VolumeInfo volume_info
;
161 volume_info
.type
= volume_type
;
162 volume_info
.device_type
= device_type
;
163 // Keep source_path empty.
164 volume_info
.mount_path
= path
;
165 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
166 volume_info
.is_parent
= false;
167 volume_info
.is_read_only
= false;
168 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
172 VolumeInfo
CreateVolumeInfoFromMountPointInfo(
173 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_point
,
174 const chromeos::disks::DiskMountManager::Disk
* disk
) {
175 VolumeInfo volume_info
;
176 volume_info
.type
= MountTypeToVolumeType(mount_point
.mount_type
);
177 volume_info
.source_path
= base::FilePath(mount_point
.source_path
);
178 volume_info
.mount_path
= base::FilePath(mount_point
.mount_path
);
179 volume_info
.mount_condition
= mount_point
.mount_condition
;
181 volume_info
.device_type
= disk
->device_type();
182 volume_info
.system_path_prefix
=
183 base::FilePath(disk
->system_path_prefix());
184 volume_info
.drive_label
= disk
->drive_label();
185 volume_info
.is_parent
= disk
->is_parent();
186 volume_info
.is_read_only
= disk
->is_read_only();
188 volume_info
.device_type
= chromeos::DEVICE_TYPE_UNKNOWN
;
189 volume_info
.is_parent
= false;
190 volume_info
.is_read_only
=
191 (mount_point
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
);
193 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
198 VolumeInfo
CreatePrivetVolumeInfo(
199 const local_discovery::PrivetVolumeLister::VolumeInfo
& privet_volume_info
) {
200 VolumeInfo volume_info
;
201 volume_info
.type
= VOLUME_TYPE_CLOUD_DEVICE
;
202 volume_info
.mount_path
= privet_volume_info
.volume_path
;
203 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
204 volume_info
.is_parent
= true;
205 volume_info
.is_read_only
= true;
206 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
210 VolumeInfo
CreateProvidedFileSystemVolumeInfo(
211 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
213 VolumeInfo volume_info
;
214 volume_info
.type
= VOLUME_TYPE_PROVIDED
;
215 volume_info
.mount_path
= file_system_info
.mount_path();
216 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
217 volume_info
.is_parent
= true;
218 volume_info
.is_read_only
= true;
219 volume_info
.volume_id
= GenerateVolumeId(volume_info
);
220 volume_info
.file_system_id
= file_system_info
.file_system_id();
224 std::string
GetMountPointNameForMediaStorage(
225 const storage_monitor::StorageInfo
& info
) {
226 std::string
name(kFileManagerMTPMountNamePrefix
);
227 name
+= info
.device_id();
233 VolumeInfo::VolumeInfo()
235 type(VOLUME_TYPE_GOOGLE_DRIVE
),
236 mount_condition(chromeos::disks::MOUNT_CONDITION_NONE
),
238 is_read_only(false) {}
240 VolumeInfo::~VolumeInfo() {
243 VolumeManager::VolumeManager(
245 drive::DriveIntegrationService
* drive_integration_service
,
246 chromeos::PowerManagerClient
* power_manager_client
,
247 chromeos::disks::DiskMountManager
* disk_mount_manager
,
248 chromeos::file_system_provider::Service
* file_system_provider_service
)
250 drive_integration_service_(drive_integration_service
),
251 disk_mount_manager_(disk_mount_manager
),
252 mounted_disk_monitor_(
253 new MountedDiskMonitor(power_manager_client
, disk_mount_manager
)),
254 file_system_provider_service_(file_system_provider_service
),
255 weak_ptr_factory_(this) {
256 DCHECK(disk_mount_manager
);
259 VolumeManager::~VolumeManager() {
262 VolumeManager
* VolumeManager::Get(content::BrowserContext
* context
) {
263 return VolumeManagerFactory::Get(context
);
266 void VolumeManager::Initialize() {
267 // If in Sign in profile, then skip mounting and listening for mount events.
268 if (chromeos::ProfileHelper::IsSigninProfile(profile_
))
271 // Path to mount user folders have changed several times. We need to migrate
272 // the old preferences on paths to the new format when needed. For the detail,
273 // see the comments in file_manager::util::MigratePathFromOldFormat,
274 // Note: Preferences related to downloads are handled in download_prefs.cc.
275 // TODO(kinaba): Remove this after several rounds of releases.
276 const base::FilePath old_path
=
277 profile_
->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory
);
278 base::FilePath new_path
;
279 if (!old_path
.empty() &&
280 file_manager::util::MigratePathFromOldFormat(profile_
,
281 old_path
, &new_path
)) {
282 profile_
->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory
,
286 // Register 'Downloads' folder for the profile to the file system.
287 const base::FilePath downloads
=
288 file_manager::util::GetDownloadsFolderForProfile(profile_
);
289 const bool success
= RegisterDownloadsMountPoint(profile_
, downloads
);
292 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
293 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_
),
306 // Subscribe to DiskMountManager.
307 disk_mount_manager_
->AddObserver(this);
309 // Subscribe to FileSystemProviderService and register currently mounted
310 // volumes for the profile.
311 if (file_system_provider_service_
) {
312 using chromeos::file_system_provider::ProvidedFileSystemInfo
;
313 file_system_provider_service_
->AddObserver(this);
315 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
316 file_system_provider_service_
->GetProvidedFileSystemInfoList();
317 for (size_t i
= 0; i
< file_system_info_list
.size(); ++i
) {
318 VolumeInfo volume_info
=
319 CreateProvidedFileSystemVolumeInfo(file_system_info_list
[i
]);
320 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume_info
, kNotRemounting
);
324 std::vector
<VolumeInfo
> archives
;
326 const chromeos::disks::DiskMountManager::MountPointMap
& mount_points
=
327 disk_mount_manager_
->mount_points();
328 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it
=
329 mount_points
.begin();
330 it
!= mount_points
.end();
332 if (it
->second
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
333 // Archives are mounted after other type of volumes. See below.
334 archives
.push_back(CreateVolumeInfoFromMountPointInfo(it
->second
, NULL
));
338 chromeos::MOUNT_ERROR_NONE
,
339 CreateVolumeInfoFromMountPointInfo(
341 disk_mount_manager_
->FindDiskBySourcePath(it
->second
.source_path
)),
345 // We mount archives only if they are opened from currently mounted volumes.
346 // To check the condition correctly in DoMountEvent, we care the order.
347 std::vector
<bool> done(archives
.size(), false);
348 for (size_t i
= 0; i
< archives
.size(); ++i
) {
350 std::vector
<VolumeInfo
> chain
;
352 chain
.push_back(archives
[i
]);
354 // If archives[i]'s source_path is in another archive, mount it first.
355 for (size_t parent
= 0; parent
< archives
.size(); ++parent
) {
357 archives
[parent
].mount_path
.IsParent(chain
.back().source_path
)) {
359 chain
.push_back(archives
[parent
]);
360 parent
= 0; // Search archives[parent]'s parent from the beginning.
364 // Mount from the tail of chain.
365 for (size_t i
= chain
.size(); i
> 0; --i
)
366 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, chain
[i
- 1], kNotRemounting
);
370 disk_mount_manager_
->RequestMountInfoRefresh();
372 // Subscribe to Profile Preference change.
373 pref_change_registrar_
.Init(profile_
->GetPrefs());
374 pref_change_registrar_
.Add(
375 prefs::kExternalStorageDisabled
,
376 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged
,
377 weak_ptr_factory_
.GetWeakPtr()));
379 // Subscribe to Privet volume lister.
380 if (CommandLine::ForCurrentProcess()->HasSwitch(
381 switches::kEnablePrivetStorage
)) {
382 privet_volume_lister_
.reset(new local_discovery::PrivetVolumeLister(
383 base::Bind(&VolumeManager::OnPrivetVolumesAvailable
,
384 weak_ptr_factory_
.GetWeakPtr())));
385 privet_volume_lister_
->Start();
388 // Subscribe to storage monitor for MTP notifications.
389 if (CommandLine::ForCurrentProcess()->HasSwitch(
390 chromeos::switches::kEnableFileManagerMTP
) &&
391 storage_monitor::StorageMonitor::GetInstance()) {
392 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
393 base::Bind(&VolumeManager::OnStorageMonitorInitialized
,
394 weak_ptr_factory_
.GetWeakPtr()));
398 void VolumeManager::Shutdown() {
399 weak_ptr_factory_
.InvalidateWeakPtrs();
401 pref_change_registrar_
.RemoveAll();
402 disk_mount_manager_
->RemoveObserver(this);
403 if (storage_monitor::StorageMonitor::GetInstance())
404 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
406 if (drive_integration_service_
)
407 drive_integration_service_
->RemoveObserver(this);
409 if (file_system_provider_service_
)
410 file_system_provider_service_
->RemoveObserver(this);
413 void VolumeManager::AddObserver(VolumeManagerObserver
* observer
) {
414 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
416 observers_
.AddObserver(observer
);
419 void VolumeManager::RemoveObserver(VolumeManagerObserver
* observer
) {
420 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
422 observers_
.RemoveObserver(observer
);
425 std::vector
<VolumeInfo
> VolumeManager::GetVolumeInfoList() const {
426 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
428 std::vector
<VolumeInfo
> result
;
429 for (std::map
<std::string
, VolumeInfo
>::const_iterator iter
=
430 mounted_volumes_
.begin();
431 iter
!= mounted_volumes_
.end();
433 result
.push_back(iter
->second
);
438 bool VolumeManager::FindVolumeInfoById(const std::string
& volume_id
,
439 VolumeInfo
* result
) const {
440 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
443 std::map
<std::string
, VolumeInfo
>::const_iterator iter
=
444 mounted_volumes_
.find(volume_id
);
445 if (iter
== mounted_volumes_
.end())
447 *result
= iter
->second
;
451 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
452 const base::FilePath
& path
) {
453 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
455 base::FilePath old_path
;
456 if (FindDownloadsMountPointPath(profile_
, &old_path
)) {
457 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
,
458 CreateDownloadsVolumeInfo(old_path
));
461 bool success
= RegisterDownloadsMountPoint(profile_
, path
);
463 success
? chromeos::MOUNT_ERROR_NONE
: chromeos::MOUNT_ERROR_INVALID_PATH
,
464 CreateDownloadsVolumeInfo(path
),
469 void VolumeManager::AddVolumeInfoForTesting(const base::FilePath
& path
,
470 VolumeType volume_type
,
471 chromeos::DeviceType device_type
) {
472 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
473 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
474 CreateTestingVolumeInfo(path
, volume_type
, device_type
),
478 void VolumeManager::OnFileSystemMounted() {
479 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
481 // Raise mount event.
482 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
483 // or network is unreachable. These two errors will be handled later.
484 VolumeInfo volume_info
= CreateDriveVolumeInfo(profile_
);
485 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume_info
, kNotRemounting
);
488 void VolumeManager::OnFileSystemBeingUnmounted() {
489 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
491 VolumeInfo volume_info
= CreateDriveVolumeInfo(profile_
);
492 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, volume_info
);
495 void VolumeManager::OnDiskEvent(
496 chromeos::disks::DiskMountManager::DiskEvent event
,
497 const chromeos::disks::DiskMountManager::Disk
* disk
) {
498 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
500 // Disregard hidden devices.
501 if (disk
->is_hidden())
505 case chromeos::disks::DiskMountManager::DISK_ADDED
:
506 case chromeos::disks::DiskMountManager::DISK_CHANGED
: {
507 if (disk
->device_path().empty()) {
508 DVLOG(1) << "Empty system path for " << disk
->device_path();
512 bool mounting
= false;
513 if (disk
->mount_path().empty() && disk
->has_media() &&
514 !profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
515 // If disk is not mounted yet and it has media and there is no policy
516 // forbidding external storage, give it a try.
517 // Initiate disk mount operation. MountPath auto-detects the filesystem
518 // format if the second argument is empty. The third argument (mount
519 // label) is not used in a disk mount operation.
520 disk_mount_manager_
->MountPath(
521 disk
->device_path(), std::string(), std::string(),
522 chromeos::MOUNT_TYPE_DEVICE
);
526 // Notify to observers.
527 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
528 OnDiskAdded(*disk
, mounting
));
532 case chromeos::disks::DiskMountManager::DISK_REMOVED
:
533 // If the disk is already mounted, unmount it.
534 if (!disk
->mount_path().empty()) {
535 disk_mount_manager_
->UnmountPath(
537 chromeos::UNMOUNT_OPTIONS_LAZY
,
538 chromeos::disks::DiskMountManager::UnmountPathCallback());
541 // Notify to observers.
542 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
543 OnDiskRemoved(*disk
));
549 void VolumeManager::OnDeviceEvent(
550 chromeos::disks::DiskMountManager::DeviceEvent event
,
551 const std::string
& device_path
) {
552 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
553 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << device_path
;
556 case chromeos::disks::DiskMountManager::DEVICE_ADDED
:
557 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
558 OnDeviceAdded(device_path
));
560 case chromeos::disks::DiskMountManager::DEVICE_REMOVED
: {
561 const bool hard_unplugged
=
562 mounted_disk_monitor_
->DeviceIsHardUnplugged(device_path
);
563 FOR_EACH_OBSERVER(VolumeManagerObserver
,
565 OnDeviceRemoved(device_path
, hard_unplugged
));
566 mounted_disk_monitor_
->ClearHardUnpluggedFlag(device_path
);
569 case chromeos::disks::DiskMountManager::DEVICE_SCANNED
:
570 DVLOG(1) << "Ignore SCANNED event: " << device_path
;
576 void VolumeManager::OnMountEvent(
577 chromeos::disks::DiskMountManager::MountEvent event
,
578 chromeos::MountError error_code
,
579 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_info
) {
580 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
581 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID
, mount_info
.mount_type
);
583 if (mount_info
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
584 // If the file is not mounted now, tell it to drive file system so that
585 // it can handle file caching correctly.
586 // Note that drive file system knows if the file is managed by drive file
587 // system or not, so here we report all paths.
588 if ((event
== chromeos::disks::DiskMountManager::MOUNTING
&&
589 error_code
!= chromeos::MOUNT_ERROR_NONE
) ||
590 (event
== chromeos::disks::DiskMountManager::UNMOUNTING
&&
591 error_code
== chromeos::MOUNT_ERROR_NONE
)) {
592 drive::FileSystemInterface
* file_system
=
593 drive::util::GetFileSystemByProfile(profile_
);
595 file_system
->MarkCacheFileAsUnmounted(
596 base::FilePath(mount_info
.source_path
),
597 base::Bind(&drive::util::EmptyFileOperationCallback
));
602 // Notify a mounting/unmounting event to observers.
603 const chromeos::disks::DiskMountManager::Disk
* disk
=
604 disk_mount_manager_
->FindDiskBySourcePath(mount_info
.source_path
);
605 VolumeInfo volume_info
=
606 CreateVolumeInfoFromMountPointInfo(mount_info
, disk
);
608 case chromeos::disks::DiskMountManager::MOUNTING
: {
610 disk
&& mounted_disk_monitor_
->DiskIsRemounting(*disk
);
611 DoMountEvent(error_code
, volume_info
, is_remounting
);
614 case chromeos::disks::DiskMountManager::UNMOUNTING
:
615 DoUnmountEvent(error_code
, volume_info
);
621 void VolumeManager::OnFormatEvent(
622 chromeos::disks::DiskMountManager::FormatEvent event
,
623 chromeos::FormatError error_code
,
624 const std::string
& device_path
) {
625 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
626 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << error_code
627 << ", " << device_path
;
630 case chromeos::disks::DiskMountManager::FORMAT_STARTED
:
632 VolumeManagerObserver
, observers_
,
633 OnFormatStarted(device_path
,
634 error_code
== chromeos::FORMAT_ERROR_NONE
));
636 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED
:
637 if (error_code
== chromeos::FORMAT_ERROR_NONE
) {
638 // If format is completed successfully, try to mount the device.
639 // MountPath auto-detects filesystem format if second argument is
640 // empty. The third argument (mount label) is not used in a disk mount
642 disk_mount_manager_
->MountPath(
643 device_path
, std::string(), std::string(),
644 chromeos::MOUNT_TYPE_DEVICE
);
648 VolumeManagerObserver
, observers_
,
649 OnFormatCompleted(device_path
,
650 error_code
== chromeos::FORMAT_ERROR_NONE
));
657 void VolumeManager::OnProvidedFileSystemMount(
658 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
660 base::File::Error error
) {
661 VolumeInfo volume_info
= CreateProvidedFileSystemVolumeInfo(file_system_info
);
662 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
663 // since it is related to cros disks only.
664 const chromeos::MountError mount_error
= error
== base::File::FILE_OK
665 ? chromeos::MOUNT_ERROR_NONE
666 : chromeos::MOUNT_ERROR_UNKNOWN
;
667 DoMountEvent(mount_error
, volume_info
, kNotRemounting
);
670 void VolumeManager::OnProvidedFileSystemUnmount(
671 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
673 base::File::Error error
) {
674 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
675 // since it is related to cros disks only.
676 const chromeos::MountError mount_error
= error
== base::File::FILE_OK
677 ? chromeos::MOUNT_ERROR_NONE
678 : chromeos::MOUNT_ERROR_UNKNOWN
;
679 VolumeInfo volume_info
= CreateProvidedFileSystemVolumeInfo(file_system_info
);
680 DoUnmountEvent(mount_error
, volume_info
);
683 void VolumeManager::OnExternalStorageDisabledChanged() {
684 // If the policy just got disabled we have to unmount every device currently
685 // mounted. The opposite is fine - we can let the user re-plug her device to
686 // make it available.
687 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
688 // We do not iterate on mount_points directly, because mount_points can
689 // be changed by UnmountPath().
690 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
691 while (!disk_mount_manager_
->mount_points().empty()) {
692 std::string mount_path
=
693 disk_mount_manager_
->mount_points().begin()->second
.mount_path
;
694 disk_mount_manager_
->UnmountPath(
696 chromeos::UNMOUNT_OPTIONS_NONE
,
697 chromeos::disks::DiskMountManager::UnmountPathCallback());
702 void VolumeManager::OnPrivetVolumesAvailable(
703 const local_discovery::PrivetVolumeLister::VolumeList
& volumes
) {
704 for (local_discovery::PrivetVolumeLister::VolumeList::const_iterator i
=
705 volumes
.begin(); i
!= volumes
.end(); i
++) {
706 VolumeInfo volume_info
= CreatePrivetVolumeInfo(*i
);
707 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume_info
, false);
711 void VolumeManager::OnRemovableStorageAttached(
712 const storage_monitor::StorageInfo
& info
) {
713 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
715 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
))
718 const base::FilePath path
= base::FilePath::FromUTF8Unsafe(info
.location());
719 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
720 const std::string name
= base::UTF16ToUTF8(info
.GetDisplayName(false));
723 fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
724 fsid
, fileapi::kFileSystemTypeDeviceMediaAsFileStorage
,
725 fileapi::FileSystemMountOption(), path
);
727 content::BrowserThread::PostTask(
728 content::BrowserThread::IO
, FROM_HERE
, base::Bind(
729 &MTPDeviceMapService::RegisterMTPFileSystem
,
730 base::Unretained(MTPDeviceMapService::GetInstance()),
731 info
.location(), fsid
));
733 VolumeInfo volume_info
;
734 volume_info
.type
= VOLUME_TYPE_MTP
;
735 volume_info
.mount_path
= path
;
736 volume_info
.mount_condition
= chromeos::disks::MOUNT_CONDITION_NONE
;
737 volume_info
.is_parent
= true;
738 volume_info
.is_read_only
= true;
739 volume_info
.volume_id
= "mtp:" + name
;
740 volume_info
.source_path
= path
;
741 volume_info
.device_type
= chromeos::DEVICE_TYPE_MOBILE
;
742 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume_info
, false);
745 void VolumeManager::OnRemovableStorageDetached(
746 const storage_monitor::StorageInfo
& info
) {
747 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
750 for (std::map
<std::string
, VolumeInfo
>::iterator it
=
751 mounted_volumes_
.begin(); it
!= mounted_volumes_
.end(); ++it
) {
752 if (it
->second
.source_path
.value() == info
.location()) {
753 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, VolumeInfo(it
->second
));
755 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
756 fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
758 content::BrowserThread::PostTask(
759 content::BrowserThread::IO
, FROM_HERE
, base::Bind(
760 &MTPDeviceMapService::RevokeMTPFileSystem
,
761 base::Unretained(MTPDeviceMapService::GetInstance()),
768 void VolumeManager::OnStorageMonitorInitialized() {
769 std::vector
<storage_monitor::StorageInfo
> storages
=
770 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
771 for (size_t i
= 0; i
< storages
.size(); ++i
)
772 OnRemovableStorageAttached(storages
[i
]);
773 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
776 void VolumeManager::DoMountEvent(chromeos::MountError error_code
,
777 const VolumeInfo
& volume_info
,
778 bool is_remounting
) {
779 // Archive files are mounted globally in system. We however don't want to show
780 // archives from profile-specific folders (Drive/Downloads) of other users in
781 // multi-profile session. To this end, we filter out archives not on the
782 // volumes already mounted on this VolumeManager instance.
783 if (volume_info
.type
== VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
) {
784 // Source may be in Drive cache folder under the current profile directory.
785 bool from_current_profile
=
786 profile_
->GetPath().IsParent(volume_info
.source_path
);
787 for (std::map
<std::string
, VolumeInfo
>::const_iterator iter
=
788 mounted_volumes_
.begin();
789 !from_current_profile
&& iter
!= mounted_volumes_
.end();
791 if (iter
->second
.mount_path
.IsParent(volume_info
.source_path
))
792 from_current_profile
= true;
794 if (!from_current_profile
)
798 // Filter out removable disks if forbidden by policy for this profile.
799 if (volume_info
.type
== VOLUME_TYPE_REMOVABLE_DISK_PARTITION
&&
800 profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
804 if (error_code
== chromeos::MOUNT_ERROR_NONE
|| volume_info
.mount_condition
)
805 mounted_volumes_
[volume_info
.volume_id
] = volume_info
;
807 FOR_EACH_OBSERVER(VolumeManagerObserver
,
809 OnVolumeMounted(error_code
, volume_info
, is_remounting
));
812 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code
,
813 const VolumeInfo
& volume_info
) {
814 if (mounted_volumes_
.find(volume_info
.volume_id
) == mounted_volumes_
.end())
816 if (error_code
== chromeos::MOUNT_ERROR_NONE
)
817 mounted_volumes_
.erase(volume_info
.volume_id
);
819 FOR_EACH_OBSERVER(VolumeManagerObserver
,
821 OnVolumeUnmounted(error_code
, volume_info
));
824 } // namespace file_manager