Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / volume_manager.cc
blob84e01dfeeed87d70262bfe430a1ea1a3e7369eb3
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/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 {
35 namespace {
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(),
58 path);
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) {
73 switch (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
77 // warns it.
78 break;
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;
85 NOTREACHED();
86 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
89 // Returns a string representation of the given volume type.
90 std::string VolumeTypeToString(VolumeType type) {
91 switch (type) {
92 case VOLUME_TYPE_GOOGLE_DRIVE:
93 return "drive";
94 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
95 return "downloads";
96 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
97 return "removable";
98 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
99 return "archive";
100 case VOLUME_TYPE_CLOUD_DEVICE:
101 return "cloud_device";
102 case VOLUME_TYPE_PROVIDED:
103 return "provided";
104 case VOLUME_TYPE_MTP:
105 return "mtp";
106 case VOLUME_TYPE_TESTING:
107 return "testing";
108 case NUM_VOLUME_TYPE:
109 break;
111 NOTREACHED();
112 return "";
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();
127 return name;
130 } // namespace
132 Volume::Volume()
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),
137 is_parent_(false),
138 is_read_only_(false),
139 has_media_(false) {
142 Volume::~Volume() {
145 // static
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);
159 return volume;
162 // static
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);
174 return volume;
177 // static
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();
187 if (disk) {
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();
193 } else {
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);
201 return volume;
204 // static
205 Volume* Volume::CreateForProvidedFileSystem(
206 const chromeos::file_system_provider::ProvidedFileSystemInfo&
207 file_system_info,
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);
221 return volume;
224 // static
225 Volume* Volume::CreateForMTP(const base::FilePath& mount_path,
226 const std::string& label,
227 bool read_only) {
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;
238 return volume;
241 // static
242 Volume* Volume::CreateForTesting(const base::FilePath& path,
243 VolumeType volume_type,
244 chromeos::DeviceType device_type,
245 bool read_only) {
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);
256 return volume;
259 // static
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;
265 return volume;
268 VolumeManager::VolumeManager(
269 Profile* profile,
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)
274 : profile_(profile),
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_))
293 return;
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);
299 DCHECK(success);
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);
367 DCHECK(observer);
368 observers_.AddObserver(observer);
371 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
372 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
373 DCHECK(observer);
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());
384 return result;
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);
408 DoMountEvent(
409 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
410 make_linked_ptr(Volume::CreateForDownloads(path)));
411 return success;
414 void VolumeManager::AddVolumeForTesting(const base::FilePath& path,
415 VolumeType volume_type,
416 chromeos::DeviceType device_type,
417 bool read_only) {
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())
448 return;
450 switch (event) {
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();
455 return;
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);
469 mounting = true;
472 // Notify to observers.
473 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
474 OnDiskAdded(*disk, mounting));
475 return;
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(
482 disk->mount_path(),
483 chromeos::UNMOUNT_OPTIONS_LAZY,
484 chromeos::disks::DiskMountManager::UnmountPathCallback());
487 // Notify to observers.
488 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
489 OnDiskRemoved(*disk));
490 return;
492 NOTREACHED();
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;
501 switch (event) {
502 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
503 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
504 OnDeviceAdded(device_path));
505 return;
506 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
507 FOR_EACH_OBSERVER(
508 VolumeManagerObserver, observers_, OnDeviceRemoved(device_path));
509 return;
511 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
512 DVLOG(1) << "Ignore SCANNED event: " << device_path;
513 return;
515 NOTREACHED();
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_);
536 if (file_system) {
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));
548 switch (event) {
549 case chromeos::disks::DiskMountManager::MOUNTING: {
550 DoMountEvent(error_code, volume);
551 return;
553 case chromeos::disks::DiskMountManager::UNMOUNTING:
554 DoUnmountEvent(error_code, volume);
555 return;
557 NOTREACHED();
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;
568 switch (event) {
569 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
570 FOR_EACH_OBSERVER(
571 VolumeManagerObserver, observers_,
572 OnFormatStarted(device_path,
573 error_code == chromeos::FORMAT_ERROR_NONE));
574 return;
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
580 // operation.
581 disk_mount_manager_->MountPath(
582 device_path, std::string(), std::string(),
583 chromeos::MOUNT_TYPE_DEVICE);
586 FOR_EACH_OBSERVER(
587 VolumeManagerObserver, observers_,
588 OnFormatCompleted(device_path,
589 error_code == chromeos::FORMAT_ERROR_NONE));
591 return;
593 NOTREACHED();
596 void VolumeManager::OnProvidedFileSystemMount(
597 const chromeos::file_system_provider::ProvidedFileSystemInfo&
598 file_system_info,
599 chromeos::file_system_provider::MountContext context,
600 base::File::Error error) {
601 MountContext volume_context = MOUNT_CONTEXT_UNKNOWN;
602 switch (context) {
603 case chromeos::file_system_provider::MOUNT_CONTEXT_USER:
604 volume_context = MOUNT_CONTEXT_USER;
605 break;
606 case chromeos::file_system_provider::MOUNT_CONTEXT_RESTORE:
607 volume_context = MOUNT_CONTEXT_AUTO;
608 break;
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;
617 switch (error) {
618 case base::File::FILE_OK:
619 mount_error = chromeos::MOUNT_ERROR_NONE;
620 break;
621 case base::File::FILE_ERROR_EXISTS:
622 mount_error = chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED;
623 break;
624 default:
625 mount_error = chromeos::MOUNT_ERROR_UNKNOWN;
626 break;
629 DoMountEvent(mount_error, volume);
632 void VolumeManager::OnProvidedFileSystemUnmount(
633 const chromeos::file_system_provider::ProvidedFileSystemInfo&
634 file_system_info,
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(
658 mount_path,
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()))
668 return;
669 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
670 return;
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);
681 bool result =
682 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
683 fsid,
684 storage::kFileSystemTypeDeviceMediaAsFileStorage,
685 storage::FileSystemMountOption(),
686 path);
687 DCHECK(result);
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()))
707 return;
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()),
719 fsid));
720 return;
725 void VolumeManager::OnDiskMountManagerRefreshed(bool success) {
726 if (!success) {
727 LOG(ERROR) << "Failed to refresh disk mount manager";
728 return;
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();
738 ++it) {
739 if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
740 // Archives are mounted after other types of volume. See below.
741 archives.push_back(
742 make_linked_ptr(Volume::CreateForRemovable(it->second, NULL)));
743 continue;
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) {
755 if (done[i])
756 continue;
758 std::vector<linked_ptr<Volume>> chain;
759 done[i] = true;
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) {
764 if (!done[parent] &&
765 archives[parent]->mount_path().IsParent(
766 chain.back()->source_path())) {
767 done[parent] = true;
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;
801 break;
804 if (!from_current_profile)
805 return;
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)) {
811 return;
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(),
817 NUM_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())
827 return;
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