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/memory/weak_ptr.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/media_galleries/fileapi/mtp_device_map_service.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/pref_names.h"
28 #include "chromeos/disks/disk_mount_manager.h"
29 #include "components/storage_monitor/storage_monitor.h"
30 #include "content/public/browser/browser_context.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "storage/browser/fileapi/external_mount_points.h"
34 namespace file_manager
{
37 const char kFileManagerMTPMountNamePrefix
[] = "fileman-mtp-";
38 const char kMtpVolumeIdPrefix
[] = "mtp:";
40 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
41 // If another folder is already mounted. It revokes and overrides the old one.
42 bool RegisterDownloadsMountPoint(Profile
* profile
, const base::FilePath
& path
) {
43 // Although we show only profile's own "Downloads" folder in Files.app,
44 // in the backend we need to mount all profile's download directory globally.
45 // Otherwise, Files.app cannot support cross-profile file copies, etc.
46 // For this reason, we need to register to the global GetSystemInstance().
47 const std::string mount_point_name
=
48 file_manager::util::GetDownloadsMountPointName(profile
);
49 storage::ExternalMountPoints
* const mount_points
=
50 storage::ExternalMountPoints::GetSystemInstance();
52 // In some tests we want to override existing Downloads mount point, so we
53 // first revoke the existing mount point (if any).
54 mount_points
->RevokeFileSystem(mount_point_name
);
55 return mount_points
->RegisterFileSystem(mount_point_name
,
56 storage::kFileSystemTypeNativeLocal
,
57 storage::FileSystemMountOption(),
61 // Finds the path register as the "Downloads" folder to FileSystem API backend.
62 // Returns false if it is not registered.
63 bool FindDownloadsMountPointPath(Profile
* profile
, base::FilePath
* path
) {
64 const std::string mount_point_name
=
65 util::GetDownloadsMountPointName(profile
);
66 storage::ExternalMountPoints
* const mount_points
=
67 storage::ExternalMountPoints::GetSystemInstance();
69 return mount_points
->GetRegisteredPath(mount_point_name
, path
);
72 VolumeType
MountTypeToVolumeType(chromeos::MountType type
) {
74 case chromeos::MOUNT_TYPE_INVALID
:
75 // We don't expect this value, but list here, so that when any value
76 // is added to the enum definition but this is not edited, the compiler
79 case chromeos::MOUNT_TYPE_DEVICE
:
80 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION
;
81 case chromeos::MOUNT_TYPE_ARCHIVE
:
82 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
;
86 return VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
89 // Returns a string representation of the given volume type.
90 std::string
VolumeTypeToString(VolumeType type
) {
92 case VOLUME_TYPE_GOOGLE_DRIVE
:
94 case VOLUME_TYPE_DOWNLOADS_DIRECTORY
:
96 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION
:
98 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
:
100 case VOLUME_TYPE_CLOUD_DEVICE
:
101 return "cloud_device";
102 case VOLUME_TYPE_PROVIDED
:
104 case VOLUME_TYPE_MTP
:
106 case VOLUME_TYPE_TESTING
:
108 case NUM_VOLUME_TYPE
:
115 // Generates a unique volume ID for the given volume info.
116 std::string
GenerateVolumeId(const Volume
& volume
) {
117 // For the same volume type, base names are unique, as mount points are
118 // flat for the same volume type.
119 return (VolumeTypeToString(volume
.type()) + ":" +
120 volume
.mount_path().BaseName().AsUTF8Unsafe());
123 std::string
GetMountPointNameForMediaStorage(
124 const storage_monitor::StorageInfo
& info
) {
125 std::string
name(kFileManagerMTPMountNamePrefix
);
126 name
+= info
.device_id();
133 : type_(VOLUME_TYPE_GOOGLE_DRIVE
),
134 device_type_(chromeos::DEVICE_TYPE_UNKNOWN
),
135 mount_condition_(chromeos::disks::MOUNT_CONDITION_NONE
),
136 mount_context_(MOUNT_CONTEXT_UNKNOWN
),
138 is_read_only_(false),
146 Volume
* Volume::CreateForDrive(Profile
* profile
) {
147 const base::FilePath
& drive_path
=
148 drive::util::GetDriveMountPointPath(profile
);
149 Volume
* const volume
= new Volume
;
150 volume
->type_
= VOLUME_TYPE_GOOGLE_DRIVE
;
151 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
152 volume
->source_path_
= drive_path
;
153 volume
->mount_path_
= drive_path
;
154 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
155 volume
->is_parent_
= false;
156 volume
->is_read_only_
= false;
157 volume
->has_media_
= false;
158 volume
->volume_id_
= GenerateVolumeId(*volume
);
163 Volume
* Volume::CreateForDownloads(const base::FilePath
& downloads_path
) {
164 Volume
* const volume
= new Volume
;
165 volume
->type_
= VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
166 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
167 // Keep source_path empty.
168 volume
->mount_path_
= downloads_path
;
169 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
170 volume
->is_parent_
= false;
171 volume
->is_read_only_
= false;
172 volume
->has_media_
= false;
173 volume
->volume_id_
= GenerateVolumeId(*volume
);
178 Volume
* Volume::CreateForRemovable(
179 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_point
,
180 const chromeos::disks::DiskMountManager::Disk
* disk
) {
181 Volume
* const volume
= new Volume
;
182 volume
->type_
= MountTypeToVolumeType(mount_point
.mount_type
);
183 volume
->source_path_
= base::FilePath(mount_point
.source_path
);
184 volume
->mount_path_
= base::FilePath(mount_point
.mount_path
);
185 volume
->mount_condition_
= mount_point
.mount_condition
;
186 volume
->volume_label_
= volume
->mount_path().BaseName().AsUTF8Unsafe();
188 volume
->device_type_
= disk
->device_type();
189 volume
->system_path_prefix_
= base::FilePath(disk
->system_path_prefix());
190 volume
->is_parent_
= disk
->is_parent();
191 volume
->is_read_only_
= disk
->is_read_only();
192 volume
->has_media_
= disk
->has_media();
194 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
195 volume
->is_parent_
= false;
196 volume
->is_read_only_
=
197 (mount_point
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
);
198 volume
->has_media_
= false;
200 volume
->volume_id_
= GenerateVolumeId(*volume
);
205 Volume
* Volume::CreateForProvidedFileSystem(
206 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
208 MountContext mount_context
) {
209 Volume
* const volume
= new Volume
;
210 volume
->file_system_id_
= file_system_info
.file_system_id();
211 volume
->extension_id_
= file_system_info
.extension_id();
212 volume
->volume_label_
= file_system_info
.display_name();
213 volume
->type_
= VOLUME_TYPE_PROVIDED
;
214 volume
->mount_path_
= file_system_info
.mount_path();
215 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
216 volume
->mount_context_
= mount_context
;
217 volume
->is_parent_
= true;
218 volume
->is_read_only_
= !file_system_info
.writable();
219 volume
->has_media_
= false;
220 volume
->volume_id_
= GenerateVolumeId(*volume
);
225 Volume
* Volume::CreateForMTP(const base::FilePath
& mount_path
,
226 const std::string
& label
,
228 Volume
* const volume
= new Volume
;
229 volume
->type_
= VOLUME_TYPE_MTP
;
230 volume
->mount_path_
= mount_path
;
231 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
232 volume
->is_parent_
= true;
233 volume
->is_read_only_
= read_only
;
234 volume
->volume_id_
= kMtpVolumeIdPrefix
+ label
;
235 volume
->volume_label_
= label
;
236 volume
->source_path_
= mount_path
;
237 volume
->device_type_
= chromeos::DEVICE_TYPE_MOBILE
;
242 Volume
* Volume::CreateForTesting(const base::FilePath
& path
,
243 VolumeType volume_type
,
244 chromeos::DeviceType device_type
,
246 Volume
* const volume
= new Volume
;
247 volume
->type_
= volume_type
;
248 volume
->device_type_
= device_type
;
249 // Keep source_path empty.
250 volume
->mount_path_
= path
;
251 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
252 volume
->is_parent_
= false;
253 volume
->is_read_only_
= read_only
;
254 volume
->has_media_
= false;
255 volume
->volume_id_
= GenerateVolumeId(*volume
);
260 Volume
* Volume::CreateForTesting(const base::FilePath
& device_path
,
261 const base::FilePath
& mount_path
) {
262 Volume
* const volume
= new Volume
;
263 volume
->system_path_prefix_
= device_path
;
264 volume
->mount_path_
= mount_path
;
268 VolumeManager::VolumeManager(
270 drive::DriveIntegrationService
* drive_integration_service
,
271 chromeos::PowerManagerClient
* power_manager_client
,
272 chromeos::disks::DiskMountManager
* disk_mount_manager
,
273 chromeos::file_system_provider::Service
* file_system_provider_service
)
275 drive_integration_service_(drive_integration_service
),
276 disk_mount_manager_(disk_mount_manager
),
277 file_system_provider_service_(file_system_provider_service
),
278 snapshot_manager_(new SnapshotManager(profile_
)),
279 weak_ptr_factory_(this) {
280 DCHECK(disk_mount_manager
);
283 VolumeManager::~VolumeManager() {
286 VolumeManager
* VolumeManager::Get(content::BrowserContext
* context
) {
287 return VolumeManagerFactory::Get(context
);
290 void VolumeManager::Initialize() {
291 // If in Sign in profile, then skip mounting and listening for mount events.
292 if (chromeos::ProfileHelper::IsSigninProfile(profile_
))
295 // Register 'Downloads' folder for the profile to the file system.
296 const base::FilePath downloads
=
297 file_manager::util::GetDownloadsFolderForProfile(profile_
);
298 const bool success
= RegisterDownloadsMountPoint(profile_
, downloads
);
301 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
302 make_linked_ptr(Volume::CreateForDownloads(downloads
)));
304 // Subscribe to DriveIntegrationService.
305 if (drive_integration_service_
) {
306 drive_integration_service_
->AddObserver(this);
307 if (drive_integration_service_
->IsMounted()) {
308 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
309 make_linked_ptr(Volume::CreateForDrive(profile_
)));
313 // Subscribe to DiskMountManager.
314 disk_mount_manager_
->AddObserver(this);
315 disk_mount_manager_
->EnsureMountInfoRefreshed(
316 base::Bind(&VolumeManager::OnDiskMountManagerRefreshed
,
317 weak_ptr_factory_
.GetWeakPtr()));
319 // Subscribe to FileSystemProviderService and register currently mounted
320 // volumes for the profile.
321 if (file_system_provider_service_
) {
322 using chromeos::file_system_provider::ProvidedFileSystemInfo
;
323 file_system_provider_service_
->AddObserver(this);
325 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
326 file_system_provider_service_
->GetProvidedFileSystemInfoList();
327 for (size_t i
= 0; i
< file_system_info_list
.size(); ++i
) {
328 linked_ptr
<Volume
> volume(Volume::CreateForProvidedFileSystem(
329 file_system_info_list
[i
], MOUNT_CONTEXT_AUTO
));
330 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
334 // Subscribe to Profile Preference change.
335 pref_change_registrar_
.Init(profile_
->GetPrefs());
336 pref_change_registrar_
.Add(
337 prefs::kExternalStorageDisabled
,
338 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged
,
339 weak_ptr_factory_
.GetWeakPtr()));
341 // Subscribe to storage monitor for MTP notifications.
342 if (storage_monitor::StorageMonitor::GetInstance()) {
343 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
344 base::Bind(&VolumeManager::OnStorageMonitorInitialized
,
345 weak_ptr_factory_
.GetWeakPtr()));
349 void VolumeManager::Shutdown() {
350 weak_ptr_factory_
.InvalidateWeakPtrs();
352 snapshot_manager_
.reset();
353 pref_change_registrar_
.RemoveAll();
354 disk_mount_manager_
->RemoveObserver(this);
355 if (storage_monitor::StorageMonitor::GetInstance())
356 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
358 if (drive_integration_service_
)
359 drive_integration_service_
->RemoveObserver(this);
361 if (file_system_provider_service_
)
362 file_system_provider_service_
->RemoveObserver(this);
365 void VolumeManager::AddObserver(VolumeManagerObserver
* observer
) {
366 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
368 observers_
.AddObserver(observer
);
371 void VolumeManager::RemoveObserver(VolumeManagerObserver
* observer
) {
372 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
374 observers_
.RemoveObserver(observer
);
377 std::vector
<base::WeakPtr
<Volume
>> VolumeManager::GetVolumeList() {
378 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
380 std::vector
<base::WeakPtr
<Volume
>> result
;
381 for (const auto& pair
: mounted_volumes_
) {
382 result
.push_back(pair
.second
->AsWeakPtr());
387 base::WeakPtr
<Volume
> VolumeManager::FindVolumeById(
388 const std::string
& volume_id
) {
389 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
391 const auto it
= mounted_volumes_
.find(volume_id
);
392 if (it
!= mounted_volumes_
.end())
393 return it
->second
->AsWeakPtr();
394 return base::WeakPtr
<Volume
>();
397 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
398 const base::FilePath
& path
) {
399 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
401 base::FilePath old_path
;
402 if (FindDownloadsMountPointPath(profile_
, &old_path
)) {
403 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
,
404 make_linked_ptr(Volume::CreateForDownloads(old_path
)));
407 bool success
= RegisterDownloadsMountPoint(profile_
, path
);
409 success
? chromeos::MOUNT_ERROR_NONE
: chromeos::MOUNT_ERROR_INVALID_PATH
,
410 make_linked_ptr(Volume::CreateForDownloads(path
)));
414 void VolumeManager::AddVolumeForTesting(const base::FilePath
& path
,
415 VolumeType volume_type
,
416 chromeos::DeviceType device_type
,
418 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
419 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
420 make_linked_ptr(Volume::CreateForTesting(
421 path
, volume_type
, device_type
, read_only
)));
424 void VolumeManager::OnFileSystemMounted() {
425 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
427 // Raise mount event.
428 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
429 // or network is unreachable. These two errors will be handled later.
430 linked_ptr
<Volume
> volume(Volume::CreateForDrive(profile_
));
431 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
434 void VolumeManager::OnFileSystemBeingUnmounted() {
435 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
437 linked_ptr
<Volume
> volume(Volume::CreateForDrive(profile_
));
438 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
441 void VolumeManager::OnDiskEvent(
442 chromeos::disks::DiskMountManager::DiskEvent event
,
443 const chromeos::disks::DiskMountManager::Disk
* disk
) {
444 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
446 // Disregard hidden devices.
447 if (disk
->is_hidden())
451 case chromeos::disks::DiskMountManager::DISK_ADDED
:
452 case chromeos::disks::DiskMountManager::DISK_CHANGED
: {
453 if (disk
->device_path().empty()) {
454 DVLOG(1) << "Empty system path for " << disk
->device_path();
458 bool mounting
= false;
459 if (disk
->mount_path().empty() && disk
->has_media() &&
460 !profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
461 // If disk is not mounted yet and it has media and there is no policy
462 // forbidding external storage, give it a try.
463 // Initiate disk mount operation. MountPath auto-detects the filesystem
464 // format if the second argument is empty. The third argument (mount
465 // label) is not used in a disk mount operation.
466 disk_mount_manager_
->MountPath(
467 disk
->device_path(), std::string(), std::string(),
468 chromeos::MOUNT_TYPE_DEVICE
);
472 // Notify to observers.
473 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
474 OnDiskAdded(*disk
, mounting
));
478 case chromeos::disks::DiskMountManager::DISK_REMOVED
:
479 // If the disk is already mounted, unmount it.
480 if (!disk
->mount_path().empty()) {
481 disk_mount_manager_
->UnmountPath(
483 chromeos::UNMOUNT_OPTIONS_LAZY
,
484 chromeos::disks::DiskMountManager::UnmountPathCallback());
487 // Notify to observers.
488 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
489 OnDiskRemoved(*disk
));
495 void VolumeManager::OnDeviceEvent(
496 chromeos::disks::DiskMountManager::DeviceEvent event
,
497 const std::string
& device_path
) {
498 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
500 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << device_path
;
502 case chromeos::disks::DiskMountManager::DEVICE_ADDED
:
503 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
504 OnDeviceAdded(device_path
));
506 case chromeos::disks::DiskMountManager::DEVICE_REMOVED
: {
508 VolumeManagerObserver
, observers_
, OnDeviceRemoved(device_path
));
511 case chromeos::disks::DiskMountManager::DEVICE_SCANNED
:
512 DVLOG(1) << "Ignore SCANNED event: " << device_path
;
518 void VolumeManager::OnMountEvent(
519 chromeos::disks::DiskMountManager::MountEvent event
,
520 chromeos::MountError error_code
,
521 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_info
) {
522 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
523 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID
, mount_info
.mount_type
);
525 if (mount_info
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
526 // If the file is not mounted now, tell it to drive file system so that
527 // it can handle file caching correctly.
528 // Note that drive file system knows if the file is managed by drive file
529 // system or not, so here we report all paths.
530 if ((event
== chromeos::disks::DiskMountManager::MOUNTING
&&
531 error_code
!= chromeos::MOUNT_ERROR_NONE
) ||
532 (event
== chromeos::disks::DiskMountManager::UNMOUNTING
&&
533 error_code
== chromeos::MOUNT_ERROR_NONE
)) {
534 drive::FileSystemInterface
* const file_system
=
535 drive::util::GetFileSystemByProfile(profile_
);
537 file_system
->MarkCacheFileAsUnmounted(
538 base::FilePath(mount_info
.source_path
),
539 base::Bind(&drive::util::EmptyFileOperationCallback
));
544 // Notify a mounting/unmounting event to observers.
545 const chromeos::disks::DiskMountManager::Disk
* const disk
=
546 disk_mount_manager_
->FindDiskBySourcePath(mount_info
.source_path
);
547 linked_ptr
<Volume
> volume(Volume::CreateForRemovable(mount_info
, disk
));
549 case chromeos::disks::DiskMountManager::MOUNTING
: {
550 DoMountEvent(error_code
, volume
);
553 case chromeos::disks::DiskMountManager::UNMOUNTING
:
554 DoUnmountEvent(error_code
, volume
);
560 void VolumeManager::OnFormatEvent(
561 chromeos::disks::DiskMountManager::FormatEvent event
,
562 chromeos::FormatError error_code
,
563 const std::string
& device_path
) {
564 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
565 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << error_code
566 << ", " << device_path
;
569 case chromeos::disks::DiskMountManager::FORMAT_STARTED
:
571 VolumeManagerObserver
, observers_
,
572 OnFormatStarted(device_path
,
573 error_code
== chromeos::FORMAT_ERROR_NONE
));
575 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED
:
576 if (error_code
== chromeos::FORMAT_ERROR_NONE
) {
577 // If format is completed successfully, try to mount the device.
578 // MountPath auto-detects filesystem format if second argument is
579 // empty. The third argument (mount label) is not used in a disk mount
581 disk_mount_manager_
->MountPath(
582 device_path
, std::string(), std::string(),
583 chromeos::MOUNT_TYPE_DEVICE
);
587 VolumeManagerObserver
, observers_
,
588 OnFormatCompleted(device_path
,
589 error_code
== chromeos::FORMAT_ERROR_NONE
));
596 void VolumeManager::OnProvidedFileSystemMount(
597 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
599 chromeos::file_system_provider::MountContext context
,
600 base::File::Error error
) {
601 MountContext volume_context
= MOUNT_CONTEXT_UNKNOWN
;
603 case chromeos::file_system_provider::MOUNT_CONTEXT_USER
:
604 volume_context
= MOUNT_CONTEXT_USER
;
606 case chromeos::file_system_provider::MOUNT_CONTEXT_RESTORE
:
607 volume_context
= MOUNT_CONTEXT_AUTO
;
611 linked_ptr
<Volume
> volume(
612 Volume::CreateForProvidedFileSystem(file_system_info
, volume_context
));
614 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
615 // since it is related to cros disks only.
616 chromeos::MountError mount_error
;
618 case base::File::FILE_OK
:
619 mount_error
= chromeos::MOUNT_ERROR_NONE
;
621 case base::File::FILE_ERROR_EXISTS
:
622 mount_error
= chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED
;
625 mount_error
= chromeos::MOUNT_ERROR_UNKNOWN
;
629 DoMountEvent(mount_error
, volume
);
632 void VolumeManager::OnProvidedFileSystemUnmount(
633 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
635 base::File::Error error
) {
636 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
637 // since it is related to cros disks only.
638 const chromeos::MountError mount_error
= error
== base::File::FILE_OK
639 ? chromeos::MOUNT_ERROR_NONE
640 : chromeos::MOUNT_ERROR_UNKNOWN
;
641 linked_ptr
<Volume
> volume(Volume::CreateForProvidedFileSystem(
642 file_system_info
, MOUNT_CONTEXT_UNKNOWN
));
643 DoUnmountEvent(mount_error
, volume
);
646 void VolumeManager::OnExternalStorageDisabledChanged() {
647 // If the policy just got disabled we have to unmount every device currently
648 // mounted. The opposite is fine - we can let the user re-plug her device to
649 // make it available.
650 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
651 // We do not iterate on mount_points directly, because mount_points can
652 // be changed by UnmountPath().
653 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
654 while (!disk_mount_manager_
->mount_points().empty()) {
655 std::string mount_path
=
656 disk_mount_manager_
->mount_points().begin()->second
.mount_path
;
657 disk_mount_manager_
->UnmountPath(
659 chromeos::UNMOUNT_OPTIONS_NONE
,
660 chromeos::disks::DiskMountManager::UnmountPathCallback());
665 void VolumeManager::OnRemovableStorageAttached(
666 const storage_monitor::StorageInfo
& info
) {
667 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
669 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
))
672 const base::FilePath path
= base::FilePath::FromUTF8Unsafe(info
.location());
673 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
674 const std::string base_name
= base::UTF16ToUTF8(info
.model_name());
676 // Assign a fresh volume ID based on the volume name.
677 std::string label
= base_name
;
678 for (int i
= 2; mounted_volumes_
.count(kMtpVolumeIdPrefix
+ label
); ++i
)
679 label
= base_name
+ base::StringPrintf(" (%d)", i
);
682 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
684 storage::kFileSystemTypeDeviceMediaAsFileStorage
,
685 storage::FileSystemMountOption(),
689 // TODO(yawano) A variable to switch MTP write support. This variable should
690 // be false until MTP write operation is implemented and shipped.
691 bool write_supported
= false;
693 content::BrowserThread::PostTask(
694 content::BrowserThread::IO
, FROM_HERE
,
695 base::Bind(&MTPDeviceMapService::RegisterMTPFileSystem
,
696 base::Unretained(MTPDeviceMapService::GetInstance()),
697 info
.location(), fsid
, !write_supported
/* read_only */));
699 linked_ptr
<Volume
> volume(
700 Volume::CreateForMTP(path
, label
, !write_supported
));
701 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
704 void VolumeManager::OnRemovableStorageDetached(
705 const storage_monitor::StorageInfo
& info
) {
706 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
709 for (const auto mounted_volume
: mounted_volumes_
) {
710 if (mounted_volume
.second
->source_path().value() == info
.location()) {
711 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, mounted_volume
.second
);
713 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
714 storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fsid
);
715 content::BrowserThread::PostTask(
716 content::BrowserThread::IO
, FROM_HERE
, base::Bind(
717 &MTPDeviceMapService::RevokeMTPFileSystem
,
718 base::Unretained(MTPDeviceMapService::GetInstance()),
725 void VolumeManager::OnDiskMountManagerRefreshed(bool success
) {
727 LOG(ERROR
) << "Failed to refresh disk mount manager";
731 std::vector
<linked_ptr
<Volume
>> archives
;
733 const chromeos::disks::DiskMountManager::MountPointMap
& mount_points
=
734 disk_mount_manager_
->mount_points();
735 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it
=
736 mount_points
.begin();
737 it
!= mount_points
.end();
739 if (it
->second
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
740 // Archives are mounted after other types of volume. See below.
742 make_linked_ptr(Volume::CreateForRemovable(it
->second
, NULL
)));
745 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
746 make_linked_ptr(Volume::CreateForRemovable(
747 it
->second
, disk_mount_manager_
->FindDiskBySourcePath(
748 it
->second
.source_path
))));
751 // We mount archives only if they are opened from currently mounted volumes.
752 // To check the condition correctly in DoMountEvent, we care about the order.
753 std::vector
<bool> done(archives
.size(), false);
754 for (size_t i
= 0; i
< archives
.size(); ++i
) {
758 std::vector
<linked_ptr
<Volume
>> chain
;
760 chain
.push_back(archives
[i
]);
762 // If archives[i]'s source_path is in another archive, mount it first.
763 for (size_t parent
= i
+ 1; parent
< archives
.size(); ++parent
) {
765 archives
[parent
]->mount_path().IsParent(
766 chain
.back()->source_path())) {
768 chain
.push_back(archives
[parent
]);
769 parent
= i
+ 1; // Search archives[parent]'s parent from the beginning.
773 // Mount from the tail of chain.
774 for (size_t i
= chain
.size(); i
> 0; --i
) {
775 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, chain
[i
- 1]);
780 void VolumeManager::OnStorageMonitorInitialized() {
781 std::vector
<storage_monitor::StorageInfo
> storages
=
782 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
783 for (size_t i
= 0; i
< storages
.size(); ++i
)
784 OnRemovableStorageAttached(storages
[i
]);
785 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
788 void VolumeManager::DoMountEvent(chromeos::MountError error_code
,
789 linked_ptr
<Volume
> volume
) {
790 // Archive files are mounted globally in system. We however don't want to show
791 // archives from profile-specific folders (Drive/Downloads) of other users in
792 // multi-profile session. To this end, we filter out archives not on the
793 // volumes already mounted on this VolumeManager instance.
794 if (volume
->type() == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
) {
795 // Source may be in Drive cache folder under the current profile directory.
796 bool from_current_profile
=
797 profile_
->GetPath().IsParent(volume
->source_path());
798 for (const auto& mounted_volume
: mounted_volumes_
) {
799 if (mounted_volume
.second
->mount_path().IsParent(volume
->source_path())) {
800 from_current_profile
= true;
804 if (!from_current_profile
)
808 // Filter out removable disks if forbidden by policy for this profile.
809 if (volume
->type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION
&&
810 profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
814 if (error_code
== chromeos::MOUNT_ERROR_NONE
|| volume
->mount_condition()) {
815 mounted_volumes_
[volume
->volume_id()] = volume
;
816 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", volume
->type(),
820 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
821 OnVolumeMounted(error_code
, *volume
));
824 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code
,
825 linked_ptr
<Volume
> volume
) {
826 if (mounted_volumes_
.find(volume
->volume_id()) == mounted_volumes_
.end())
828 if (error_code
== chromeos::MOUNT_ERROR_NONE
)
829 mounted_volumes_
.erase(volume
->volume_id());
831 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
832 OnVolumeUnmounted(error_code
, *volume
.get()));
835 } // namespace file_manager