Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / volume_manager.cc
blob73bc7542b2035b0e1ec0b309318c215250fb44a4
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/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 {
40 namespace {
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(),
66 path);
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) {
81 switch (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
85 // warns it.
86 break;
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;
93 NOTREACHED();
94 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
97 // Returns a string representation of the given volume type.
98 std::string VolumeTypeToString(VolumeType type) {
99 switch (type) {
100 case VOLUME_TYPE_GOOGLE_DRIVE:
101 return "drive";
102 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
103 return "downloads";
104 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
105 return "removable";
106 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
107 return "archive";
108 case VOLUME_TYPE_PROVIDED:
109 return "provided";
110 case VOLUME_TYPE_MTP:
111 return "mtp";
112 case VOLUME_TYPE_TESTING:
113 return "testing";
114 case NUM_VOLUME_TYPE:
115 break;
117 NOTREACHED();
118 return "";
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();
133 return name;
136 } // namespace
138 Volume::Volume()
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),
144 is_parent_(false),
145 is_read_only_(false),
146 has_media_(false),
147 configurable_(false),
148 watchable_(false) {
151 Volume::~Volume() {
154 // static
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;
167 return volume;
170 // static
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;
181 return volume;
184 // static
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
192 ? SOURCE_FILE
193 : SOURCE_DEVICE;
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();
197 if (disk) {
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();
203 } else {
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;
210 return volume;
213 // static
214 Volume* Volume::CreateForProvidedFileSystem(
215 const chromeos::file_system_provider::ProvidedFileSystemInfo&
216 file_system_info,
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;
224 break;
225 case extensions::SOURCE_DEVICE:
226 volume->source_ = SOURCE_DEVICE;
227 break;
228 case extensions::SOURCE_NETWORK:
229 volume->source_ = SOURCE_NETWORK;
230 break;
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);
242 return volume;
245 // static
246 Volume* Volume::CreateForMTP(const base::FilePath& mount_path,
247 const std::string& label,
248 bool read_only) {
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;
260 return volume;
263 // static
264 Volume* Volume::CreateForTesting(const base::FilePath& path,
265 VolumeType volume_type,
266 chromeos::DeviceType device_type,
267 bool read_only) {
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);
277 return volume;
280 // static
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;
286 return volume;
289 VolumeManager::VolumeManager(
290 Profile* profile,
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)
296 : profile_(profile),
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_))
316 return;
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);
322 DCHECK(success);
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);
390 DCHECK(observer);
391 observers_.AddObserver(observer);
394 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
395 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
396 DCHECK(observer);
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());
407 return result;
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);
431 DoMountEvent(
432 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
433 make_linked_ptr(Volume::CreateForDownloads(path)));
434 return success;
437 void VolumeManager::AddVolumeForTesting(const base::FilePath& path,
438 VolumeType volume_type,
439 chromeos::DeviceType device_type,
440 bool read_only) {
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())
476 return;
478 switch (event) {
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();
483 return;
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);
497 mounting = true;
500 // Notify to observers.
501 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
502 OnDiskAdded(*disk, mounting));
503 return;
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(
510 disk->mount_path(),
511 chromeos::UNMOUNT_OPTIONS_LAZY,
512 chromeos::disks::DiskMountManager::UnmountPathCallback());
515 // Notify to observers.
516 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
517 OnDiskRemoved(*disk));
518 return;
520 NOTREACHED();
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;
529 switch (event) {
530 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
531 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
532 OnDeviceAdded(device_path));
533 return;
534 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
535 FOR_EACH_OBSERVER(
536 VolumeManagerObserver, observers_, OnDeviceRemoved(device_path));
537 return;
539 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
540 DVLOG(1) << "Ignore SCANNED event: " << device_path;
541 return;
543 NOTREACHED();
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_);
564 if (file_system) {
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));
576 switch (event) {
577 case chromeos::disks::DiskMountManager::MOUNTING: {
578 DoMountEvent(error_code, volume);
579 return;
581 case chromeos::disks::DiskMountManager::UNMOUNTING:
582 DoUnmountEvent(error_code, volume);
583 return;
585 NOTREACHED();
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;
596 switch (event) {
597 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
598 FOR_EACH_OBSERVER(
599 VolumeManagerObserver, observers_,
600 OnFormatStarted(device_path,
601 error_code == chromeos::FORMAT_ERROR_NONE));
602 return;
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
608 // operation.
609 disk_mount_manager_->MountPath(
610 device_path, std::string(), std::string(),
611 chromeos::MOUNT_TYPE_DEVICE);
614 FOR_EACH_OBSERVER(
615 VolumeManagerObserver, observers_,
616 OnFormatCompleted(device_path,
617 error_code == chromeos::FORMAT_ERROR_NONE));
619 return;
621 NOTREACHED();
624 void VolumeManager::OnProvidedFileSystemMount(
625 const chromeos::file_system_provider::ProvidedFileSystemInfo&
626 file_system_info,
627 chromeos::file_system_provider::MountContext context,
628 base::File::Error error) {
629 MountContext volume_context = MOUNT_CONTEXT_UNKNOWN;
630 switch (context) {
631 case chromeos::file_system_provider::MOUNT_CONTEXT_USER:
632 volume_context = MOUNT_CONTEXT_USER;
633 break;
634 case chromeos::file_system_provider::MOUNT_CONTEXT_RESTORE:
635 volume_context = MOUNT_CONTEXT_AUTO;
636 break;
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;
645 switch (error) {
646 case base::File::FILE_OK:
647 mount_error = chromeos::MOUNT_ERROR_NONE;
648 break;
649 case base::File::FILE_ERROR_EXISTS:
650 mount_error = chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED;
651 break;
652 default:
653 mount_error = chromeos::MOUNT_ERROR_UNKNOWN;
654 break;
657 DoMountEvent(mount_error, volume);
660 void VolumeManager::OnProvidedFileSystemUnmount(
661 const chromeos::file_system_provider::ProvidedFileSystemInfo&
662 file_system_info,
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(
686 mount_path,
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()))
696 return;
697 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
698 return;
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);
709 bool result =
710 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
711 fsid,
712 storage::kFileSystemTypeDeviceMediaAsFileStorage,
713 storage::FileSystemMountOption(),
714 path);
715 DCHECK(result);
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);
727 } else {
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()))
753 return;
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()),
765 fsid));
766 return;
771 void VolumeManager::OnDiskMountManagerRefreshed(bool success) {
772 if (!success) {
773 LOG(ERROR) << "Failed to refresh disk mount manager";
774 return;
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();
784 ++it) {
785 if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
786 // Archives are mounted after other types of volume. See below.
787 archives.push_back(
788 make_linked_ptr(Volume::CreateForRemovable(it->second, NULL)));
789 continue;
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) {
801 if (done[i])
802 continue;
804 std::vector<linked_ptr<Volume>> chain;
805 done[i] = true;
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) {
810 if (!done[parent] &&
811 archives[parent]->mount_path().IsParent(
812 chain.back()->source_path())) {
813 done[parent] = true;
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;
847 break;
850 if (!from_current_profile)
851 return;
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)) {
857 return;
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(),
863 NUM_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())
873 return;
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