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/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
19 #include "chrome/browser/chromeos/drive/file_system_core_util.h"
20 #include "chrome/browser/chromeos/drive/file_system_interface.h"
21 #include "chrome/browser/chromeos/drive/file_system_util.h"
22 #include "chrome/browser/chromeos/file_manager/path_util.h"
23 #include "chrome/browser/chromeos/file_manager/snapshot_manager.h"
24 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
25 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
26 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
27 #include "chrome/browser/chromeos/profiles/profile_helper.h"
28 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/common/pref_names.h"
31 #include "chromeos/chromeos_switches.h"
32 #include "chromeos/disks/disk_mount_manager.h"
33 #include "components/storage_monitor/storage_monitor.h"
34 #include "content/public/browser/browser_context.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "device/media_transfer_protocol/media_transfer_protocol_manager.h"
37 #include "storage/browser/fileapi/external_mount_points.h"
39 namespace file_manager
{
42 const uint32 kAccessCapabilityReadWrite
= 0;
43 const uint32 kFilesystemTypeGenericHierarchical
= 2;
44 const char kFileManagerMTPMountNamePrefix
[] = "fileman-mtp-";
45 const char kMtpVolumeIdPrefix
[] = "mtp:";
46 const char kRootPath
[] = "/";
48 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
49 // If another folder is already mounted. It revokes and overrides the old one.
50 bool RegisterDownloadsMountPoint(Profile
* profile
, const base::FilePath
& path
) {
51 // Although we show only profile's own "Downloads" folder in Files.app,
52 // in the backend we need to mount all profile's download directory globally.
53 // Otherwise, Files.app cannot support cross-profile file copies, etc.
54 // For this reason, we need to register to the global GetSystemInstance().
55 const std::string mount_point_name
=
56 file_manager::util::GetDownloadsMountPointName(profile
);
57 storage::ExternalMountPoints
* const mount_points
=
58 storage::ExternalMountPoints::GetSystemInstance();
60 // In some tests we want to override existing Downloads mount point, so we
61 // first revoke the existing mount point (if any).
62 mount_points
->RevokeFileSystem(mount_point_name
);
63 return mount_points
->RegisterFileSystem(mount_point_name
,
64 storage::kFileSystemTypeNativeLocal
,
65 storage::FileSystemMountOption(),
69 // Finds the path register as the "Downloads" folder to FileSystem API backend.
70 // Returns false if it is not registered.
71 bool FindDownloadsMountPointPath(Profile
* profile
, base::FilePath
* path
) {
72 const std::string mount_point_name
=
73 util::GetDownloadsMountPointName(profile
);
74 storage::ExternalMountPoints
* const mount_points
=
75 storage::ExternalMountPoints::GetSystemInstance();
77 return mount_points
->GetRegisteredPath(mount_point_name
, path
);
80 VolumeType
MountTypeToVolumeType(chromeos::MountType type
) {
82 case chromeos::MOUNT_TYPE_INVALID
:
83 // We don't expect this value, but list here, so that when any value
84 // is added to the enum definition but this is not edited, the compiler
87 case chromeos::MOUNT_TYPE_DEVICE
:
88 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION
;
89 case chromeos::MOUNT_TYPE_ARCHIVE
:
90 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
;
94 return VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
97 // Returns a string representation of the given volume type.
98 std::string
VolumeTypeToString(VolumeType type
) {
100 case VOLUME_TYPE_GOOGLE_DRIVE
:
102 case VOLUME_TYPE_DOWNLOADS_DIRECTORY
:
104 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION
:
106 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
:
108 case VOLUME_TYPE_PROVIDED
:
110 case VOLUME_TYPE_MTP
:
112 case VOLUME_TYPE_TESTING
:
114 case NUM_VOLUME_TYPE
:
121 // Generates a unique volume ID for the given volume info.
122 std::string
GenerateVolumeId(const Volume
& volume
) {
123 // For the same volume type, base names are unique, as mount points are
124 // flat for the same volume type.
125 return (VolumeTypeToString(volume
.type()) + ":" +
126 volume
.mount_path().BaseName().AsUTF8Unsafe());
129 std::string
GetMountPointNameForMediaStorage(
130 const storage_monitor::StorageInfo
& info
) {
131 std::string
name(kFileManagerMTPMountNamePrefix
);
132 name
+= info
.device_id();
139 : source_(SOURCE_FILE
),
140 type_(VOLUME_TYPE_GOOGLE_DRIVE
),
141 device_type_(chromeos::DEVICE_TYPE_UNKNOWN
),
142 mount_condition_(chromeos::disks::MOUNT_CONDITION_NONE
),
143 mount_context_(MOUNT_CONTEXT_UNKNOWN
),
145 is_read_only_(false),
147 configurable_(false),
155 Volume
* Volume::CreateForDrive(Profile
* profile
) {
156 const base::FilePath
& drive_path
=
157 drive::util::GetDriveMountPointPath(profile
);
158 Volume
* const volume
= new Volume
;
159 volume
->type_
= VOLUME_TYPE_GOOGLE_DRIVE
;
160 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
161 volume
->source_path_
= drive_path
;
162 volume
->source_
= SOURCE_NETWORK
;
163 volume
->mount_path_
= drive_path
;
164 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
165 volume
->volume_id_
= GenerateVolumeId(*volume
);
166 volume
->watchable_
= true;
171 Volume
* Volume::CreateForDownloads(const base::FilePath
& downloads_path
) {
172 Volume
* const volume
= new Volume
;
173 volume
->type_
= VOLUME_TYPE_DOWNLOADS_DIRECTORY
;
174 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
175 // Keep source_path empty.
176 volume
->source_
= SOURCE_SYSTEM
;
177 volume
->mount_path_
= downloads_path
;
178 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
179 volume
->volume_id_
= GenerateVolumeId(*volume
);
180 volume
->watchable_
= true;
185 Volume
* Volume::CreateForRemovable(
186 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_point
,
187 const chromeos::disks::DiskMountManager::Disk
* disk
) {
188 Volume
* const volume
= new Volume
;
189 volume
->type_
= MountTypeToVolumeType(mount_point
.mount_type
);
190 volume
->source_path_
= base::FilePath(mount_point
.source_path
);
191 volume
->source_
= mount_point
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
194 volume
->mount_path_
= base::FilePath(mount_point
.mount_path
);
195 volume
->mount_condition_
= mount_point
.mount_condition
;
196 volume
->volume_label_
= volume
->mount_path().BaseName().AsUTF8Unsafe();
198 volume
->device_type_
= disk
->device_type();
199 volume
->system_path_prefix_
= base::FilePath(disk
->system_path_prefix());
200 volume
->is_parent_
= disk
->is_parent();
201 volume
->is_read_only_
= disk
->is_read_only();
202 volume
->has_media_
= disk
->has_media();
204 volume
->device_type_
= chromeos::DEVICE_TYPE_UNKNOWN
;
205 volume
->is_read_only_
=
206 (mount_point
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
);
208 volume
->volume_id_
= GenerateVolumeId(*volume
);
209 volume
->watchable_
= true;
214 Volume
* Volume::CreateForProvidedFileSystem(
215 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
217 MountContext mount_context
) {
218 Volume
* const volume
= new Volume
;
219 volume
->file_system_id_
= file_system_info
.file_system_id();
220 volume
->extension_id_
= file_system_info
.extension_id();
221 switch (file_system_info
.source()) {
222 case extensions::SOURCE_FILE
:
223 volume
->source_
= SOURCE_FILE
;
225 case extensions::SOURCE_DEVICE
:
226 volume
->source_
= SOURCE_DEVICE
;
228 case extensions::SOURCE_NETWORK
:
229 volume
->source_
= SOURCE_NETWORK
;
232 volume
->volume_label_
= file_system_info
.display_name();
233 volume
->type_
= VOLUME_TYPE_PROVIDED
;
234 volume
->mount_path_
= file_system_info
.mount_path();
235 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
236 volume
->mount_context_
= mount_context
;
237 volume
->is_parent_
= true;
238 volume
->is_read_only_
= !file_system_info
.writable();
239 volume
->configurable_
= file_system_info
.configurable();
240 volume
->watchable_
= file_system_info
.watchable();
241 volume
->volume_id_
= GenerateVolumeId(*volume
);
246 Volume
* Volume::CreateForMTP(const base::FilePath
& mount_path
,
247 const std::string
& label
,
249 Volume
* const volume
= new Volume
;
250 volume
->type_
= VOLUME_TYPE_MTP
;
251 volume
->mount_path_
= mount_path
;
252 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
253 volume
->is_parent_
= true;
254 volume
->is_read_only_
= read_only
;
255 volume
->volume_id_
= kMtpVolumeIdPrefix
+ label
;
256 volume
->volume_label_
= label
;
257 volume
->source_path_
= mount_path
;
258 volume
->source_
= SOURCE_DEVICE
;
259 volume
->device_type_
= chromeos::DEVICE_TYPE_MOBILE
;
264 Volume
* Volume::CreateForTesting(const base::FilePath
& path
,
265 VolumeType volume_type
,
266 chromeos::DeviceType device_type
,
268 Volume
* const volume
= new Volume
;
269 volume
->type_
= volume_type
;
270 volume
->device_type_
= device_type
;
271 // Keep source_path empty.
272 volume
->source_
= SOURCE_DEVICE
;
273 volume
->mount_path_
= path
;
274 volume
->mount_condition_
= chromeos::disks::MOUNT_CONDITION_NONE
;
275 volume
->is_read_only_
= read_only
;
276 volume
->volume_id_
= GenerateVolumeId(*volume
);
281 Volume
* Volume::CreateForTesting(const base::FilePath
& device_path
,
282 const base::FilePath
& mount_path
) {
283 Volume
* const volume
= new Volume
;
284 volume
->system_path_prefix_
= device_path
;
285 volume
->mount_path_
= mount_path
;
289 VolumeManager::VolumeManager(
291 drive::DriveIntegrationService
* drive_integration_service
,
292 chromeos::PowerManagerClient
* power_manager_client
,
293 chromeos::disks::DiskMountManager
* disk_mount_manager
,
294 chromeos::file_system_provider::Service
* file_system_provider_service
,
295 GetMtpStorageInfoCallback get_mtp_storage_info_callback
)
297 drive_integration_service_(drive_integration_service
),
298 disk_mount_manager_(disk_mount_manager
),
299 file_system_provider_service_(file_system_provider_service
),
300 get_mtp_storage_info_callback_(get_mtp_storage_info_callback
),
301 snapshot_manager_(new SnapshotManager(profile_
)),
302 weak_ptr_factory_(this) {
303 DCHECK(disk_mount_manager
);
306 VolumeManager::~VolumeManager() {
309 VolumeManager
* VolumeManager::Get(content::BrowserContext
* context
) {
310 return VolumeManagerFactory::Get(context
);
313 void VolumeManager::Initialize() {
314 // If in Sign in profile, then skip mounting and listening for mount events.
315 if (chromeos::ProfileHelper::IsSigninProfile(profile_
))
318 // Register 'Downloads' folder for the profile to the file system.
319 const base::FilePath downloads
=
320 file_manager::util::GetDownloadsFolderForProfile(profile_
);
321 const bool success
= RegisterDownloadsMountPoint(profile_
, downloads
);
324 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
325 make_linked_ptr(Volume::CreateForDownloads(downloads
)));
327 // Subscribe to DriveIntegrationService.
328 if (drive_integration_service_
) {
329 drive_integration_service_
->AddObserver(this);
330 if (drive_integration_service_
->IsMounted()) {
331 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
332 make_linked_ptr(Volume::CreateForDrive(profile_
)));
336 // Subscribe to DiskMountManager.
337 disk_mount_manager_
->AddObserver(this);
338 disk_mount_manager_
->EnsureMountInfoRefreshed(
339 base::Bind(&VolumeManager::OnDiskMountManagerRefreshed
,
340 weak_ptr_factory_
.GetWeakPtr()));
342 // Subscribe to FileSystemProviderService and register currently mounted
343 // volumes for the profile.
344 if (file_system_provider_service_
) {
345 using chromeos::file_system_provider::ProvidedFileSystemInfo
;
346 file_system_provider_service_
->AddObserver(this);
348 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
349 file_system_provider_service_
->GetProvidedFileSystemInfoList();
350 for (size_t i
= 0; i
< file_system_info_list
.size(); ++i
) {
351 linked_ptr
<Volume
> volume(Volume::CreateForProvidedFileSystem(
352 file_system_info_list
[i
], MOUNT_CONTEXT_AUTO
));
353 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
357 // Subscribe to Profile Preference change.
358 pref_change_registrar_
.Init(profile_
->GetPrefs());
359 pref_change_registrar_
.Add(
360 prefs::kExternalStorageDisabled
,
361 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged
,
362 weak_ptr_factory_
.GetWeakPtr()));
364 // Subscribe to storage monitor for MTP notifications.
365 if (storage_monitor::StorageMonitor::GetInstance()) {
366 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
367 base::Bind(&VolumeManager::OnStorageMonitorInitialized
,
368 weak_ptr_factory_
.GetWeakPtr()));
372 void VolumeManager::Shutdown() {
373 weak_ptr_factory_
.InvalidateWeakPtrs();
375 snapshot_manager_
.reset();
376 pref_change_registrar_
.RemoveAll();
377 disk_mount_manager_
->RemoveObserver(this);
378 if (storage_monitor::StorageMonitor::GetInstance())
379 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
381 if (drive_integration_service_
)
382 drive_integration_service_
->RemoveObserver(this);
384 if (file_system_provider_service_
)
385 file_system_provider_service_
->RemoveObserver(this);
388 void VolumeManager::AddObserver(VolumeManagerObserver
* observer
) {
389 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
391 observers_
.AddObserver(observer
);
394 void VolumeManager::RemoveObserver(VolumeManagerObserver
* observer
) {
395 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
397 observers_
.RemoveObserver(observer
);
400 std::vector
<base::WeakPtr
<Volume
>> VolumeManager::GetVolumeList() {
401 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
403 std::vector
<base::WeakPtr
<Volume
>> result
;
404 for (const auto& pair
: mounted_volumes_
) {
405 result
.push_back(pair
.second
->AsWeakPtr());
410 base::WeakPtr
<Volume
> VolumeManager::FindVolumeById(
411 const std::string
& volume_id
) {
412 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
414 const auto it
= mounted_volumes_
.find(volume_id
);
415 if (it
!= mounted_volumes_
.end())
416 return it
->second
->AsWeakPtr();
417 return base::WeakPtr
<Volume
>();
420 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
421 const base::FilePath
& path
) {
422 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
424 base::FilePath old_path
;
425 if (FindDownloadsMountPointPath(profile_
, &old_path
)) {
426 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
,
427 make_linked_ptr(Volume::CreateForDownloads(old_path
)));
430 bool success
= RegisterDownloadsMountPoint(profile_
, path
);
432 success
? chromeos::MOUNT_ERROR_NONE
: chromeos::MOUNT_ERROR_INVALID_PATH
,
433 make_linked_ptr(Volume::CreateForDownloads(path
)));
437 void VolumeManager::AddVolumeForTesting(const base::FilePath
& path
,
438 VolumeType volume_type
,
439 chromeos::DeviceType device_type
,
441 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
442 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
443 make_linked_ptr(Volume::CreateForTesting(
444 path
, volume_type
, device_type
, read_only
)));
447 void VolumeManager::AddVolumeForTesting(const linked_ptr
<Volume
>& volume
) {
448 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
449 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
452 void VolumeManager::OnFileSystemMounted() {
453 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
455 // Raise mount event.
456 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
457 // or network is unreachable. These two errors will be handled later.
458 linked_ptr
<Volume
> volume(Volume::CreateForDrive(profile_
));
459 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
462 void VolumeManager::OnFileSystemBeingUnmounted() {
463 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
465 linked_ptr
<Volume
> volume(Volume::CreateForDrive(profile_
));
466 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
469 void VolumeManager::OnDiskEvent(
470 chromeos::disks::DiskMountManager::DiskEvent event
,
471 const chromeos::disks::DiskMountManager::Disk
* disk
) {
472 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
474 // Disregard hidden devices.
475 if (disk
->is_hidden())
479 case chromeos::disks::DiskMountManager::DISK_ADDED
:
480 case chromeos::disks::DiskMountManager::DISK_CHANGED
: {
481 if (disk
->device_path().empty()) {
482 DVLOG(1) << "Empty system path for " << disk
->device_path();
486 bool mounting
= false;
487 if (disk
->mount_path().empty() && disk
->has_media() &&
488 !profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
489 // If disk is not mounted yet and it has media and there is no policy
490 // forbidding external storage, give it a try.
491 // Initiate disk mount operation. MountPath auto-detects the filesystem
492 // format if the second argument is empty. The third argument (mount
493 // label) is not used in a disk mount operation.
494 disk_mount_manager_
->MountPath(
495 disk
->device_path(), std::string(), std::string(),
496 chromeos::MOUNT_TYPE_DEVICE
);
500 // Notify to observers.
501 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
502 OnDiskAdded(*disk
, mounting
));
506 case chromeos::disks::DiskMountManager::DISK_REMOVED
:
507 // If the disk is already mounted, unmount it.
508 if (!disk
->mount_path().empty()) {
509 disk_mount_manager_
->UnmountPath(
511 chromeos::UNMOUNT_OPTIONS_LAZY
,
512 chromeos::disks::DiskMountManager::UnmountPathCallback());
515 // Notify to observers.
516 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
517 OnDiskRemoved(*disk
));
523 void VolumeManager::OnDeviceEvent(
524 chromeos::disks::DiskMountManager::DeviceEvent event
,
525 const std::string
& device_path
) {
526 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
528 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << device_path
;
530 case chromeos::disks::DiskMountManager::DEVICE_ADDED
:
531 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
532 OnDeviceAdded(device_path
));
534 case chromeos::disks::DiskMountManager::DEVICE_REMOVED
: {
536 VolumeManagerObserver
, observers_
, OnDeviceRemoved(device_path
));
539 case chromeos::disks::DiskMountManager::DEVICE_SCANNED
:
540 DVLOG(1) << "Ignore SCANNED event: " << device_path
;
546 void VolumeManager::OnMountEvent(
547 chromeos::disks::DiskMountManager::MountEvent event
,
548 chromeos::MountError error_code
,
549 const chromeos::disks::DiskMountManager::MountPointInfo
& mount_info
) {
550 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
551 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID
, mount_info
.mount_type
);
553 if (mount_info
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
554 // If the file is not mounted now, tell it to drive file system so that
555 // it can handle file caching correctly.
556 // Note that drive file system knows if the file is managed by drive file
557 // system or not, so here we report all paths.
558 if ((event
== chromeos::disks::DiskMountManager::MOUNTING
&&
559 error_code
!= chromeos::MOUNT_ERROR_NONE
) ||
560 (event
== chromeos::disks::DiskMountManager::UNMOUNTING
&&
561 error_code
== chromeos::MOUNT_ERROR_NONE
)) {
562 drive::FileSystemInterface
* const file_system
=
563 drive::util::GetFileSystemByProfile(profile_
);
565 file_system
->MarkCacheFileAsUnmounted(
566 base::FilePath(mount_info
.source_path
),
567 base::Bind(&drive::util::EmptyFileOperationCallback
));
572 // Notify a mounting/unmounting event to observers.
573 const chromeos::disks::DiskMountManager::Disk
* const disk
=
574 disk_mount_manager_
->FindDiskBySourcePath(mount_info
.source_path
);
575 linked_ptr
<Volume
> volume(Volume::CreateForRemovable(mount_info
, disk
));
577 case chromeos::disks::DiskMountManager::MOUNTING
: {
578 DoMountEvent(error_code
, volume
);
581 case chromeos::disks::DiskMountManager::UNMOUNTING
:
582 DoUnmountEvent(error_code
, volume
);
588 void VolumeManager::OnFormatEvent(
589 chromeos::disks::DiskMountManager::FormatEvent event
,
590 chromeos::FormatError error_code
,
591 const std::string
& device_path
) {
592 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
593 DVLOG(1) << "OnDeviceEvent: " << event
<< ", " << error_code
594 << ", " << device_path
;
597 case chromeos::disks::DiskMountManager::FORMAT_STARTED
:
599 VolumeManagerObserver
, observers_
,
600 OnFormatStarted(device_path
,
601 error_code
== chromeos::FORMAT_ERROR_NONE
));
603 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED
:
604 if (error_code
== chromeos::FORMAT_ERROR_NONE
) {
605 // If format is completed successfully, try to mount the device.
606 // MountPath auto-detects filesystem format if second argument is
607 // empty. The third argument (mount label) is not used in a disk mount
609 disk_mount_manager_
->MountPath(
610 device_path
, std::string(), std::string(),
611 chromeos::MOUNT_TYPE_DEVICE
);
615 VolumeManagerObserver
, observers_
,
616 OnFormatCompleted(device_path
,
617 error_code
== chromeos::FORMAT_ERROR_NONE
));
624 void VolumeManager::OnProvidedFileSystemMount(
625 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
627 chromeos::file_system_provider::MountContext context
,
628 base::File::Error error
) {
629 MountContext volume_context
= MOUNT_CONTEXT_UNKNOWN
;
631 case chromeos::file_system_provider::MOUNT_CONTEXT_USER
:
632 volume_context
= MOUNT_CONTEXT_USER
;
634 case chromeos::file_system_provider::MOUNT_CONTEXT_RESTORE
:
635 volume_context
= MOUNT_CONTEXT_AUTO
;
639 linked_ptr
<Volume
> volume(
640 Volume::CreateForProvidedFileSystem(file_system_info
, volume_context
));
642 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
643 // since it is related to cros disks only.
644 chromeos::MountError mount_error
;
646 case base::File::FILE_OK
:
647 mount_error
= chromeos::MOUNT_ERROR_NONE
;
649 case base::File::FILE_ERROR_EXISTS
:
650 mount_error
= chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED
;
653 mount_error
= chromeos::MOUNT_ERROR_UNKNOWN
;
657 DoMountEvent(mount_error
, volume
);
660 void VolumeManager::OnProvidedFileSystemUnmount(
661 const chromeos::file_system_provider::ProvidedFileSystemInfo
&
663 base::File::Error error
) {
664 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
665 // since it is related to cros disks only.
666 const chromeos::MountError mount_error
= error
== base::File::FILE_OK
667 ? chromeos::MOUNT_ERROR_NONE
668 : chromeos::MOUNT_ERROR_UNKNOWN
;
669 linked_ptr
<Volume
> volume(Volume::CreateForProvidedFileSystem(
670 file_system_info
, MOUNT_CONTEXT_UNKNOWN
));
671 DoUnmountEvent(mount_error
, volume
);
674 void VolumeManager::OnExternalStorageDisabledChanged() {
675 // If the policy just got disabled we have to unmount every device currently
676 // mounted. The opposite is fine - we can let the user re-plug her device to
677 // make it available.
678 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
679 // We do not iterate on mount_points directly, because mount_points can
680 // be changed by UnmountPath().
681 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
682 while (!disk_mount_manager_
->mount_points().empty()) {
683 std::string mount_path
=
684 disk_mount_manager_
->mount_points().begin()->second
.mount_path
;
685 disk_mount_manager_
->UnmountPath(
687 chromeos::UNMOUNT_OPTIONS_NONE
,
688 chromeos::disks::DiskMountManager::UnmountPathCallback());
693 void VolumeManager::OnRemovableStorageAttached(
694 const storage_monitor::StorageInfo
& info
) {
695 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
697 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
))
700 const base::FilePath path
= base::FilePath::FromUTF8Unsafe(info
.location());
701 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
702 const std::string base_name
= base::UTF16ToUTF8(info
.model_name());
704 // Assign a fresh volume ID based on the volume name.
705 std::string label
= base_name
;
706 for (int i
= 2; mounted_volumes_
.count(kMtpVolumeIdPrefix
+ label
); ++i
)
707 label
= base_name
+ base::StringPrintf(" (%d)", i
);
710 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
712 storage::kFileSystemTypeDeviceMediaAsFileStorage
,
713 storage::FileSystemMountOption(),
717 // Resolve mtp storage name and get MtpStorageInfo.
718 std::string storage_name
;
719 base::RemoveChars(info
.location(), kRootPath
, &storage_name
);
720 DCHECK(!storage_name
.empty());
722 const MtpStorageInfo
* mtp_storage_info
;
723 if (get_mtp_storage_info_callback_
.is_null()) {
724 mtp_storage_info
= storage_monitor::StorageMonitor::GetInstance()
725 ->media_transfer_protocol_manager()
726 ->GetStorageInfo(storage_name
);
728 mtp_storage_info
= get_mtp_storage_info_callback_
.Run(storage_name
);
730 DCHECK(mtp_storage_info
);
732 // Mtp write is enabled only when the device is writable and supports generic
733 // hierarchical file system.
734 const bool read_only
=
735 base::CommandLine::ForCurrentProcess()->HasSwitch(
736 chromeos::switches::kDisableMtpWriteSupport
) ||
737 mtp_storage_info
->access_capability() != kAccessCapabilityReadWrite
||
738 mtp_storage_info
->filesystem_type() != kFilesystemTypeGenericHierarchical
;
740 content::BrowserThread::PostTask(
741 content::BrowserThread::IO
, FROM_HERE
,
742 base::Bind(&MTPDeviceMapService::RegisterMTPFileSystem
,
743 base::Unretained(MTPDeviceMapService::GetInstance()),
744 info
.location(), fsid
, read_only
));
746 linked_ptr
<Volume
> volume(Volume::CreateForMTP(path
, label
, read_only
));
747 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, volume
);
750 void VolumeManager::OnRemovableStorageDetached(
751 const storage_monitor::StorageInfo
& info
) {
752 if (!storage_monitor::StorageInfo::IsMTPDevice(info
.device_id()))
755 for (const auto mounted_volume
: mounted_volumes_
) {
756 if (mounted_volume
.second
->source_path().value() == info
.location()) {
757 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE
, mounted_volume
.second
);
759 const std::string fsid
= GetMountPointNameForMediaStorage(info
);
760 storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(fsid
);
761 content::BrowserThread::PostTask(
762 content::BrowserThread::IO
, FROM_HERE
, base::Bind(
763 &MTPDeviceMapService::RevokeMTPFileSystem
,
764 base::Unretained(MTPDeviceMapService::GetInstance()),
771 void VolumeManager::OnDiskMountManagerRefreshed(bool success
) {
773 LOG(ERROR
) << "Failed to refresh disk mount manager";
777 std::vector
<linked_ptr
<Volume
>> archives
;
779 const chromeos::disks::DiskMountManager::MountPointMap
& mount_points
=
780 disk_mount_manager_
->mount_points();
781 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it
=
782 mount_points
.begin();
783 it
!= mount_points
.end();
785 if (it
->second
.mount_type
== chromeos::MOUNT_TYPE_ARCHIVE
) {
786 // Archives are mounted after other types of volume. See below.
788 make_linked_ptr(Volume::CreateForRemovable(it
->second
, NULL
)));
791 DoMountEvent(chromeos::MOUNT_ERROR_NONE
,
792 make_linked_ptr(Volume::CreateForRemovable(
793 it
->second
, disk_mount_manager_
->FindDiskBySourcePath(
794 it
->second
.source_path
))));
797 // We mount archives only if they are opened from currently mounted volumes.
798 // To check the condition correctly in DoMountEvent, we care about the order.
799 std::vector
<bool> done(archives
.size(), false);
800 for (size_t i
= 0; i
< archives
.size(); ++i
) {
804 std::vector
<linked_ptr
<Volume
>> chain
;
806 chain
.push_back(archives
[i
]);
808 // If archives[i]'s source_path is in another archive, mount it first.
809 for (size_t parent
= i
+ 1; parent
< archives
.size(); ++parent
) {
811 archives
[parent
]->mount_path().IsParent(
812 chain
.back()->source_path())) {
814 chain
.push_back(archives
[parent
]);
815 parent
= i
+ 1; // Search archives[parent]'s parent from the beginning.
819 // Mount from the tail of chain.
820 for (size_t i
= chain
.size(); i
> 0; --i
) {
821 DoMountEvent(chromeos::MOUNT_ERROR_NONE
, chain
[i
- 1]);
826 void VolumeManager::OnStorageMonitorInitialized() {
827 std::vector
<storage_monitor::StorageInfo
> storages
=
828 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
829 for (size_t i
= 0; i
< storages
.size(); ++i
)
830 OnRemovableStorageAttached(storages
[i
]);
831 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
834 void VolumeManager::DoMountEvent(chromeos::MountError error_code
,
835 const linked_ptr
<Volume
>& volume
) {
836 // Archive files are mounted globally in system. We however don't want to show
837 // archives from profile-specific folders (Drive/Downloads) of other users in
838 // multi-profile session. To this end, we filter out archives not on the
839 // volumes already mounted on this VolumeManager instance.
840 if (volume
->type() == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
) {
841 // Source may be in Drive cache folder under the current profile directory.
842 bool from_current_profile
=
843 profile_
->GetPath().IsParent(volume
->source_path());
844 for (const auto& mounted_volume
: mounted_volumes_
) {
845 if (mounted_volume
.second
->mount_path().IsParent(volume
->source_path())) {
846 from_current_profile
= true;
850 if (!from_current_profile
)
854 // Filter out removable disks if forbidden by policy for this profile.
855 if (volume
->type() == VOLUME_TYPE_REMOVABLE_DISK_PARTITION
&&
856 profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
860 if (error_code
== chromeos::MOUNT_ERROR_NONE
|| volume
->mount_condition()) {
861 mounted_volumes_
[volume
->volume_id()] = volume
;
862 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType", volume
->type(),
866 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
867 OnVolumeMounted(error_code
, *volume
));
870 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code
,
871 const linked_ptr
<Volume
>& volume
) {
872 if (mounted_volumes_
.find(volume
->volume_id()) == mounted_volumes_
.end())
874 if (error_code
== chromeos::MOUNT_ERROR_NONE
)
875 mounted_volumes_
.erase(volume
->volume_id());
877 FOR_EACH_OBSERVER(VolumeManagerObserver
, observers_
,
878 OnVolumeUnmounted(error_code
, *volume
.get()));
881 } // namespace file_manager