1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
7 #include "base/basictypes.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/memory/singleton.h"
14 #include "base/prefs/pref_service.h"
15 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
16 #include "chrome/browser/chromeos/drive/file_errors.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/mounted_disk_monitor.h"
20 #include "chrome/browser/chromeos/file_manager/path_util.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/profiles/profile_helper.h"
24 #include "chrome/browser/local_discovery/storage/privet_filesystem_constants.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_names.h"
28 #include "chromeos/dbus/cros_disks_client.h"
29 #include "chromeos/disks/disk_mount_manager.h"
30 #include "content/public/browser/browser_thread.h"
32 namespace file_manager {
35 // Called on completion of MarkCacheFileAsUnmounted.
36 void OnMarkCacheFileAsUnmounted(drive::FileError error) {
40 VolumeType MountTypeToVolumeType(
41 chromeos::MountType type) {
43 case chromeos::MOUNT_TYPE_INVALID:
44 // We don't expect this value, but list here, so that when any value
45 // is added to the enum definition but this is not edited, the compiler
48 case chromeos::MOUNT_TYPE_DEVICE:
49 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
50 case chromeos::MOUNT_TYPE_ARCHIVE:
51 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
55 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
58 // Returns a string representation of the given volume type.
59 std::string VolumeTypeToString(VolumeType type) {
61 case VOLUME_TYPE_GOOGLE_DRIVE:
63 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
65 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
67 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
69 case VOLUME_TYPE_CLOUD_DEVICE:
70 return "cloud_device";
76 // Generates a unique volume ID for the given volume info.
77 std::string GenerateVolumeId(const VolumeInfo& volume_info) {
78 // For the same volume type, base names are unique, as mount points are
79 // flat for the same volume type.
80 return (VolumeTypeToString(volume_info.type) + ":" +
81 volume_info.mount_path.BaseName().AsUTF8Unsafe());
84 // Returns the VolumeInfo for Drive file system.
85 VolumeInfo CreateDriveVolumeInfo() {
86 const base::FilePath& drive_path = drive::util::GetDriveMountPointPath();
88 VolumeInfo volume_info;
89 volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE;
90 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
91 volume_info.source_path = drive_path;
92 volume_info.mount_path = drive_path;
93 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
94 volume_info.is_parent = false;
95 volume_info.is_read_only = false;
96 volume_info.volume_id = GenerateVolumeId(volume_info);
100 VolumeInfo CreateDownloadsVolumeInfo(
101 const base::FilePath& downloads_path) {
102 VolumeInfo volume_info;
103 volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
104 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
105 // Keep source_path empty.
106 volume_info.mount_path = downloads_path;
107 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
108 volume_info.is_parent = false;
109 volume_info.is_read_only = false;
110 volume_info.volume_id = GenerateVolumeId(volume_info);
114 VolumeInfo CreateVolumeInfoFromMountPointInfo(
115 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
116 const chromeos::disks::DiskMountManager::Disk* disk) {
117 VolumeInfo volume_info;
118 volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
119 volume_info.source_path = base::FilePath(mount_point.source_path);
120 volume_info.mount_path = base::FilePath(mount_point.mount_path);
121 volume_info.mount_condition = mount_point.mount_condition;
123 volume_info.device_type = disk->device_type();
124 volume_info.system_path_prefix =
125 base::FilePath(disk->system_path_prefix());
126 volume_info.drive_label = disk->drive_label();
127 volume_info.is_parent = disk->is_parent();
128 volume_info.is_read_only = disk->is_read_only();
130 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
131 volume_info.is_parent = false;
132 volume_info.is_read_only =
133 (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
135 volume_info.volume_id = GenerateVolumeId(volume_info);
140 VolumeInfo CreatePrivetVolumeInfo() {
141 VolumeInfo volume_info;
142 volume_info.type = VOLUME_TYPE_CLOUD_DEVICE;
143 volume_info.mount_path = base::FilePath(local_discovery::kPrivetFilePath);
144 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
145 volume_info.is_parent = true;
146 volume_info.is_read_only = true;
147 volume_info.volume_id = GenerateVolumeId(volume_info);
153 VolumeInfo::VolumeInfo() {
156 VolumeInfo::~VolumeInfo() {
159 VolumeManager::VolumeManager(
161 drive::DriveIntegrationService* drive_integration_service,
162 chromeos::PowerManagerClient* power_manager_client,
163 chromeos::disks::DiskMountManager* disk_mount_manager)
165 drive_integration_service_(drive_integration_service),
166 disk_mount_manager_(disk_mount_manager),
167 mounted_disk_monitor_(
168 new MountedDiskMonitor(power_manager_client, disk_mount_manager)) {
169 DCHECK(disk_mount_manager);
172 VolumeManager::~VolumeManager() {
175 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
176 return VolumeManagerFactory::Get(context);
179 void VolumeManager::Initialize() {
180 // Path to mount user folders have changed several times. We need to migrate
181 // the old preferences on paths to the new format when needed. For the detail,
182 // see the comments in file_manager::util::MigratePathFromOldFormat,
183 // Note: Preferences related to downloads are handled in download_prefs.cc.
184 // TODO(kinaba): Remove this are several rounds of releases.
185 const base::FilePath old_path =
186 profile_->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory);
187 base::FilePath new_path;
188 if (!old_path.empty() &&
189 file_manager::util::MigratePathFromOldFormat(profile_,
190 old_path, &new_path)) {
191 profile_->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory,
195 // Register 'Downloads' folder for the profile to the file system.
196 if (!chromeos::ProfileHelper::IsSigninProfile(profile_)) {
197 bool success = util::RegisterDownloadsMountPoint(
199 file_manager::util::GetDownloadsFolderForProfile(profile_));
203 // Subscribe to DriveIntegrationService.
204 if (drive_integration_service_)
205 drive_integration_service_->AddObserver(this);
207 // Subscribe to DiskMountManager.
208 disk_mount_manager_->AddObserver(this);
209 disk_mount_manager_->RequestMountInfoRefresh();
211 // Subscribe to Profile Preference change.
212 pref_change_registrar_.Init(profile_->GetPrefs());
213 pref_change_registrar_.Add(
214 prefs::kExternalStorageDisabled,
215 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
216 base::Unretained(this)));
219 void VolumeManager::Shutdown() {
220 pref_change_registrar_.RemoveAll();
221 disk_mount_manager_->RemoveObserver(this);
223 if (drive_integration_service_)
224 drive_integration_service_->RemoveObserver(this);
227 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
228 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
230 observers_.AddObserver(observer);
233 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
234 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
236 observers_.RemoveObserver(observer);
239 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
240 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242 std::vector<VolumeInfo> result;
244 // Adds "Drive" volume.
245 if (drive_integration_service_ && drive_integration_service_->IsMounted())
246 result.push_back(CreateDriveVolumeInfo());
249 // Usually, the path of the directory is where we registered in Initialize(),
250 // but in tests, the mount point may be overridden. To take it into account,
251 // here we explicitly retrieves the path from the file API mount points.
252 base::FilePath downloads;
253 if (util::FindDownloadsMountPointPath(profile_, &downloads))
254 result.push_back(CreateDownloadsVolumeInfo(downloads));
256 // Adds disks (both removable disks and zip archives).
257 const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
258 disk_mount_manager_->mount_points();
259 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
260 mount_points.begin();
261 it != mount_points.end(); ++it) {
262 result.push_back(CreateVolumeInfoFromMountPointInfo(
264 disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)));
267 if (CommandLine::ForCurrentProcess()->HasSwitch(
268 switches::kEnablePrivetStorage)) {
269 result.push_back(CreatePrivetVolumeInfo());
275 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
276 VolumeInfo* result) const {
277 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
280 std::vector<VolumeInfo> info_list = GetVolumeInfoList();
281 for (size_t i = 0; i < info_list.size(); ++i) {
282 if (info_list[i].volume_id == volume_id) {
283 *result = info_list[i];
291 void VolumeManager::OnFileSystemMounted() {
292 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
294 // Raise mount event.
295 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
296 // or network is unreachable. These two errors will be handled later.
297 VolumeInfo volume_info = CreateDriveVolumeInfo();
298 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
299 OnVolumeMounted(chromeos::MOUNT_ERROR_NONE,
301 false)); // Not remounting.
304 void VolumeManager::OnFileSystemBeingUnmounted() {
305 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
307 VolumeInfo volume_info = CreateDriveVolumeInfo();
309 VolumeManagerObserver, observers_,
310 OnVolumeUnmounted(chromeos::MOUNT_ERROR_NONE, volume_info));
313 void VolumeManager::OnDiskEvent(
314 chromeos::disks::DiskMountManager::DiskEvent event,
315 const chromeos::disks::DiskMountManager::Disk* disk) {
316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
318 // Disregard hidden devices.
319 if (disk->is_hidden())
323 case chromeos::disks::DiskMountManager::DISK_ADDED: {
324 if (disk->device_path().empty()) {
325 DVLOG(1) << "Empty system path for " << disk->device_path();
329 bool mounting = false;
330 if (disk->mount_path().empty() && disk->has_media() &&
331 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
332 // If disk is not mounted yet and it has media and there is no policy
333 // forbidding external storage, give it a try.
334 // Initiate disk mount operation. MountPath auto-detects the filesystem
335 // format if the second argument is empty. The third argument (mount
336 // label) is not used in a disk mount operation.
337 disk_mount_manager_->MountPath(
338 disk->device_path(), std::string(), std::string(),
339 chromeos::MOUNT_TYPE_DEVICE);
343 // Notify to observers.
344 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
345 OnDiskAdded(*disk, mounting));
349 case chromeos::disks::DiskMountManager::DISK_REMOVED:
350 // If the disk is already mounted, unmount it.
351 if (!disk->mount_path().empty()) {
352 disk_mount_manager_->UnmountPath(
354 chromeos::UNMOUNT_OPTIONS_LAZY,
355 chromeos::disks::DiskMountManager::UnmountPathCallback());
358 // Notify to observers.
359 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
360 OnDiskRemoved(*disk));
363 case chromeos::disks::DiskMountManager::DISK_CHANGED:
364 DVLOG(1) << "Ignore CHANGED event.";
370 void VolumeManager::OnDeviceEvent(
371 chromeos::disks::DiskMountManager::DeviceEvent event,
372 const std::string& device_path) {
373 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
374 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
377 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
378 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
379 OnDeviceAdded(device_path));
381 case chromeos::disks::DiskMountManager::DEVICE_REMOVED:
382 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
383 OnDeviceRemoved(device_path));
385 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
386 DVLOG(1) << "Ignore SCANNED event: " << device_path;
392 void VolumeManager::OnMountEvent(
393 chromeos::disks::DiskMountManager::MountEvent event,
394 chromeos::MountError error_code,
395 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
396 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
397 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
399 if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
400 // If the file is not mounted now, tell it to drive file system so that
401 // it can handle file caching correctly.
402 // Note that drive file system knows if the file is managed by drive file
403 // system or not, so here we report all paths.
404 if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
405 error_code != chromeos::MOUNT_ERROR_NONE) ||
406 (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
407 error_code == chromeos::MOUNT_ERROR_NONE)) {
408 drive::FileSystemInterface* file_system =
409 drive::util::GetFileSystemByProfile(profile_);
411 file_system->MarkCacheFileAsUnmounted(
412 base::FilePath(mount_info.source_path),
413 base::Bind(&OnMarkCacheFileAsUnmounted));
418 // Notify a mounting/unmounting event to observers.
419 const chromeos::disks::DiskMountManager::Disk* disk =
420 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
421 VolumeInfo volume_info =
422 CreateVolumeInfoFromMountPointInfo(mount_info, disk);
424 case chromeos::disks::DiskMountManager::MOUNTING: {
426 disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
428 VolumeManagerObserver, observers_,
429 OnVolumeMounted(error_code, volume_info, is_remounting));
432 case chromeos::disks::DiskMountManager::UNMOUNTING:
433 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
434 OnVolumeUnmounted(error_code, volume_info));
440 void VolumeManager::OnFormatEvent(
441 chromeos::disks::DiskMountManager::FormatEvent event,
442 chromeos::FormatError error_code,
443 const std::string& device_path) {
444 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
445 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
446 << ", " << device_path;
449 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
451 VolumeManagerObserver, observers_,
452 OnFormatStarted(device_path,
453 error_code == chromeos::FORMAT_ERROR_NONE));
455 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
456 if (error_code == chromeos::FORMAT_ERROR_NONE) {
457 // If format is completed successfully, try to mount the device.
458 // MountPath auto-detects filesystem format if second argument is
459 // empty. The third argument (mount label) is not used in a disk mount
461 disk_mount_manager_->MountPath(
462 device_path, std::string(), std::string(),
463 chromeos::MOUNT_TYPE_DEVICE);
467 VolumeManagerObserver, observers_,
468 OnFormatCompleted(device_path,
469 error_code == chromeos::FORMAT_ERROR_NONE));
476 void VolumeManager::OnExternalStorageDisabledChanged() {
477 // If the policy just got disabled we have to unmount every device currently
478 // mounted. The opposite is fine - we can let the user re-plug her device to
479 // make it available.
480 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
481 // We do not iterate on mount_points directly, because mount_points can
482 // be changed by UnmountPath().
483 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
484 while (!disk_mount_manager_->mount_points().empty()) {
485 std::string mount_path =
486 disk_mount_manager_->mount_points().begin()->second.mount_path;
487 disk_mount_manager_->UnmountPath(
489 chromeos::UNMOUNT_OPTIONS_NONE,
490 chromeos::disks::DiskMountManager::UnmountPathCallback());
495 } // namespace file_manager