file_manager: Generate volume IDs in VolumeManager
[chromium-blink-merge.git] / chrome / browser / chromeos / file_manager / volume_manager.cc
blob3b190ac7648c0c62c80841c574936b557e101f19
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/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/memory/singleton.h"
13 #include "base/prefs/pref_service.h"
14 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
15 #include "chrome/browser/chromeos/drive/file_errors.h"
16 #include "chrome/browser/chromeos/drive/file_system_interface.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/file_manager/mounted_disk_monitor.h"
19 #include "chrome/browser/chromeos/file_manager/path_util.h"
20 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
21 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/pref_names.h"
24 #include "chromeos/dbus/cros_disks_client.h"
25 #include "chromeos/disks/disk_mount_manager.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "webkit/browser/fileapi/external_mount_points.h"
29 namespace file_manager {
30 namespace {
32 // Called on completion of MarkCacheFileAsUnmounted.
33 void OnMarkCacheFileAsUnmounted(drive::FileError error) {
34 // Do nothing.
37 VolumeType MountTypeToVolumeType(
38 chromeos::MountType type) {
39 switch (type) {
40 case chromeos::MOUNT_TYPE_INVALID:
41 // We don't expect this value, but list here, so that when any value
42 // is added to the enum definition but this is not edited, the compiler
43 // warns it.
44 break;
45 case chromeos::MOUNT_TYPE_DEVICE:
46 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
47 case chromeos::MOUNT_TYPE_ARCHIVE:
48 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
51 NOTREACHED();
52 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
55 // Returns a string representation of the given volume type.
56 std::string VolumeTypeToString(VolumeType type) {
57 switch (type) {
58 case VOLUME_TYPE_GOOGLE_DRIVE:
59 return "drive";
60 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
61 return "downloads";
62 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
63 return "removable";
64 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
65 return "archive";
67 NOTREACHED();
68 return "";
71 // Generates a unique volume ID for the given volume info.
72 std::string GenerateVolumeId(const VolumeInfo& volume_info) {
73 // For the same volume type, base names are unique, as mount points are
74 // flat for the same volume type.
75 return (VolumeTypeToString(volume_info.type) + ":" +
76 volume_info.mount_path.BaseName().AsUTF8Unsafe());
79 // Returns the VolumeInfo for Drive file system.
80 VolumeInfo CreateDriveVolumeInfo() {
81 const base::FilePath& drive_path = drive::util::GetDriveMountPointPath();
83 VolumeInfo volume_info;
84 volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE;
85 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
86 volume_info.source_path = drive_path;
87 volume_info.mount_path = drive_path;
88 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
89 volume_info.is_parent = false;
90 volume_info.is_read_only = false;
91 volume_info.volume_id = GenerateVolumeId(volume_info);
92 return volume_info;
95 VolumeInfo CreateDownloadsVolumeInfo(
96 const base::FilePath& downloads_path) {
97 VolumeInfo volume_info;
98 volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
99 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
100 // Keep source_path empty.
101 volume_info.mount_path = downloads_path;
102 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
103 volume_info.is_parent = false;
104 volume_info.is_read_only = false;
105 volume_info.volume_id = GenerateVolumeId(volume_info);
106 return volume_info;
109 VolumeInfo CreateVolumeInfoFromMountPointInfo(
110 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
111 const chromeos::disks::DiskMountManager::Disk* disk) {
112 VolumeInfo volume_info;
113 volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
114 volume_info.source_path = base::FilePath(mount_point.source_path);
115 volume_info.mount_path = base::FilePath(mount_point.mount_path);
116 volume_info.mount_condition = mount_point.mount_condition;
117 if (disk) {
118 volume_info.device_type = disk->device_type();
119 volume_info.system_path_prefix =
120 base::FilePath(disk->system_path_prefix());
121 volume_info.drive_label = disk->drive_label();
122 volume_info.is_parent = disk->is_parent();
123 volume_info.is_read_only = disk->is_read_only();
124 } else {
125 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
126 volume_info.is_parent = false;
127 volume_info.is_read_only = false;
129 volume_info.volume_id = GenerateVolumeId(volume_info);
131 return volume_info;
134 } // namespace
136 VolumeInfo::VolumeInfo() {
139 VolumeInfo::~VolumeInfo() {
142 VolumeManager::VolumeManager(
143 Profile* profile,
144 drive::DriveIntegrationService* drive_integration_service,
145 chromeos::PowerManagerClient* power_manager_client,
146 chromeos::disks::DiskMountManager* disk_mount_manager)
147 : profile_(profile),
148 drive_integration_service_(drive_integration_service),
149 disk_mount_manager_(disk_mount_manager),
150 mounted_disk_monitor_(
151 new MountedDiskMonitor(power_manager_client, disk_mount_manager)) {
152 DCHECK(disk_mount_manager);
155 VolumeManager::~VolumeManager() {
158 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
159 return VolumeManagerFactory::Get(context);
162 void VolumeManager::Initialize() {
163 // Path to mount user folders have changed several times. We need to migrate
164 // the old preferences on paths to the new format when needed. For the detail,
165 // see the comments in file_manager::util::MigratePathFromOldFormat,
166 // TODO(kinaba): Remove this are several rounds of releases.
167 const char* kPathPreference[] = {
168 prefs::kDownloadDefaultDirectory,
169 prefs::kSelectFileLastDirectory,
170 prefs::kSaveFileDefaultDirectory,
172 for (size_t i = 0; i < arraysize(kPathPreference); ++i) {
173 const base::FilePath old_path =
174 profile_->GetPrefs()->GetFilePath(kPathPreference[i]);
175 base::FilePath new_path;
176 if (!old_path.empty() &&
177 file_manager::util::MigratePathFromOldFormat(profile_,
178 old_path, &new_path)) {
179 profile_->GetPrefs()->SetFilePath(kPathPreference[i], new_path);
183 // Register 'Downloads' folder for the profile to the file system.
184 fileapi::ExternalMountPoints* mount_points =
185 content::BrowserContext::GetMountPoints(profile_);
186 DCHECK(mount_points);
188 const base::FilePath downloads_folder =
189 file_manager::util::GetDownloadsFolderForProfile(profile_);
190 bool success = mount_points->RegisterFileSystem(
191 downloads_folder.BaseName().AsUTF8Unsafe(),
192 fileapi::kFileSystemTypeNativeLocal,
193 downloads_folder);
194 DCHECK(success);
196 // Subscribe to DriveIntegrationService.
197 if (drive_integration_service_)
198 drive_integration_service_->AddObserver(this);
200 // Subscribe to DiskMountManager.
201 disk_mount_manager_->AddObserver(this);
202 disk_mount_manager_->RequestMountInfoRefresh();
204 // Subscribe to Profile Preference change.
205 pref_change_registrar_.Init(profile_->GetPrefs());
206 pref_change_registrar_.Add(
207 prefs::kExternalStorageDisabled,
208 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
209 base::Unretained(this)));
212 void VolumeManager::Shutdown() {
213 pref_change_registrar_.RemoveAll();
214 disk_mount_manager_->RemoveObserver(this);
216 if (drive_integration_service_)
217 drive_integration_service_->RemoveObserver(this);
220 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
221 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
222 DCHECK(observer);
223 observers_.AddObserver(observer);
226 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
227 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
228 DCHECK(observer);
229 observers_.RemoveObserver(observer);
232 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
233 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
235 std::vector<VolumeInfo> result;
237 // Adds "Drive" volume.
238 if (drive_integration_service_ && drive_integration_service_->IsMounted())
239 result.push_back(CreateDriveVolumeInfo());
241 // Adds "Downloads".
242 // Usually, the path of the directory is where we registered in Initialize(),
243 // but in tests, the mount point may be overridden. To take it into account,
244 // here we explicitly retrieves the path from the file API mount points.
245 fileapi::ExternalMountPoints* fileapi_mount_points =
246 content::BrowserContext::GetMountPoints(profile_);
247 DCHECK(fileapi_mount_points);
248 base::FilePath downloads;
249 if (fileapi_mount_points->GetRegisteredPath("Downloads", &downloads))
250 result.push_back(CreateDownloadsVolumeInfo(downloads));
252 // Adds disks (both removable disks and zip archives).
253 const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
254 disk_mount_manager_->mount_points();
255 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
256 mount_points.begin();
257 it != mount_points.end(); ++it) {
258 result.push_back(CreateVolumeInfoFromMountPointInfo(
259 it->second,
260 disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)));
263 return result;
266 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
267 VolumeInfo* result) const {
268 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
269 DCHECK(result);
271 std::vector<VolumeInfo> info_list = GetVolumeInfoList();
272 for (size_t i = 0; i < info_list.size(); ++i) {
273 if (info_list[i].volume_id == volume_id) {
274 *result = info_list[i];
275 return true;
279 return false;
282 void VolumeManager::OnFileSystemMounted() {
283 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
285 // Raise mount event.
286 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
287 // or network is unreachable. These two errors will be handled later.
288 VolumeInfo volume_info = CreateDriveVolumeInfo();
289 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
290 OnVolumeMounted(chromeos::MOUNT_ERROR_NONE,
291 volume_info,
292 false)); // Not remounting.
295 void VolumeManager::OnFileSystemBeingUnmounted() {
296 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
298 VolumeInfo volume_info = CreateDriveVolumeInfo();
299 FOR_EACH_OBSERVER(
300 VolumeManagerObserver, observers_,
301 OnVolumeUnmounted(chromeos::MOUNT_ERROR_NONE, volume_info));
304 void VolumeManager::OnDiskEvent(
305 chromeos::disks::DiskMountManager::DiskEvent event,
306 const chromeos::disks::DiskMountManager::Disk* disk) {
307 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
309 // Disregard hidden devices.
310 if (disk->is_hidden())
311 return;
313 switch (event) {
314 case chromeos::disks::DiskMountManager::DISK_ADDED: {
315 if (disk->device_path().empty()) {
316 DVLOG(1) << "Empty system path for " << disk->device_path();
317 return;
320 bool mounting = false;
321 if (disk->mount_path().empty() && disk->has_media() &&
322 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
323 // If disk is not mounted yet and it has media and there is no policy
324 // forbidding external storage, give it a try.
325 // Initiate disk mount operation. MountPath auto-detects the filesystem
326 // format if the second argument is empty. The third argument (mount
327 // label) is not used in a disk mount operation.
328 disk_mount_manager_->MountPath(
329 disk->device_path(), std::string(), std::string(),
330 chromeos::MOUNT_TYPE_DEVICE);
331 mounting = true;
334 // Notify to observers.
335 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
336 OnDiskAdded(*disk, mounting));
337 return;
340 case chromeos::disks::DiskMountManager::DISK_REMOVED:
341 // If the disk is already mounted, unmount it.
342 if (!disk->mount_path().empty()) {
343 disk_mount_manager_->UnmountPath(
344 disk->mount_path(),
345 chromeos::UNMOUNT_OPTIONS_LAZY,
346 chromeos::disks::DiskMountManager::UnmountPathCallback());
349 // Notify to observers.
350 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
351 OnDiskRemoved(*disk));
352 return;
354 case chromeos::disks::DiskMountManager::DISK_CHANGED:
355 DVLOG(1) << "Ignore CHANGED event.";
356 return;
358 NOTREACHED();
361 void VolumeManager::OnDeviceEvent(
362 chromeos::disks::DiskMountManager::DeviceEvent event,
363 const std::string& device_path) {
364 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
365 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
367 switch (event) {
368 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
369 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
370 OnDeviceAdded(device_path));
371 return;
372 case chromeos::disks::DiskMountManager::DEVICE_REMOVED:
373 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
374 OnDeviceRemoved(device_path));
375 return;
376 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
377 DVLOG(1) << "Ignore SCANNED event: " << device_path;
378 return;
380 NOTREACHED();
383 void VolumeManager::OnMountEvent(
384 chromeos::disks::DiskMountManager::MountEvent event,
385 chromeos::MountError error_code,
386 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
387 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
388 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
390 if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
391 // If the file is not mounted now, tell it to drive file system so that
392 // it can handle file caching correctly.
393 // Note that drive file system knows if the file is managed by drive file
394 // system or not, so here we report all paths.
395 if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
396 error_code != chromeos::MOUNT_ERROR_NONE) ||
397 (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
398 error_code == chromeos::MOUNT_ERROR_NONE)) {
399 drive::FileSystemInterface* file_system =
400 drive::util::GetFileSystemByProfile(profile_);
401 if (file_system) {
402 file_system->MarkCacheFileAsUnmounted(
403 base::FilePath(mount_info.source_path),
404 base::Bind(&OnMarkCacheFileAsUnmounted));
409 // Notify a mounting/unmounting event to observers.
410 const chromeos::disks::DiskMountManager::Disk* disk =
411 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
412 VolumeInfo volume_info =
413 CreateVolumeInfoFromMountPointInfo(mount_info, disk);
414 switch (event) {
415 case chromeos::disks::DiskMountManager::MOUNTING: {
416 bool is_remounting =
417 disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
418 FOR_EACH_OBSERVER(
419 VolumeManagerObserver, observers_,
420 OnVolumeMounted(error_code, volume_info, is_remounting));
421 return;
423 case chromeos::disks::DiskMountManager::UNMOUNTING:
424 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
425 OnVolumeUnmounted(error_code, volume_info));
426 return;
428 NOTREACHED();
431 void VolumeManager::OnFormatEvent(
432 chromeos::disks::DiskMountManager::FormatEvent event,
433 chromeos::FormatError error_code,
434 const std::string& device_path) {
435 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
436 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
437 << ", " << device_path;
439 switch (event) {
440 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
441 FOR_EACH_OBSERVER(
442 VolumeManagerObserver, observers_,
443 OnFormatStarted(device_path,
444 error_code == chromeos::FORMAT_ERROR_NONE));
445 return;
446 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
447 if (error_code == chromeos::FORMAT_ERROR_NONE) {
448 // If format is completed successfully, try to mount the device.
449 // MountPath auto-detects filesystem format if second argument is
450 // empty. The third argument (mount label) is not used in a disk mount
451 // operation.
452 disk_mount_manager_->MountPath(
453 device_path, std::string(), std::string(),
454 chromeos::MOUNT_TYPE_DEVICE);
457 FOR_EACH_OBSERVER(
458 VolumeManagerObserver, observers_,
459 OnFormatCompleted(device_path,
460 error_code == chromeos::FORMAT_ERROR_NONE));
462 return;
464 NOTREACHED();
467 void VolumeManager::OnExternalStorageDisabledChanged() {
468 // If the policy just got disabled we have to unmount every device currently
469 // mounted. The opposite is fine - we can let the user re-plug her device to
470 // make it available.
471 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
472 // We do not iterate on mount_points directly, because mount_points can
473 // be changed by UnmountPath().
474 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
475 while (!disk_mount_manager_->mount_points().empty()) {
476 std::string mount_path =
477 disk_mount_manager_->mount_points().begin()->second.mount_path;
478 LOG(INFO) << "Unmounting " << mount_path << " because of preference.";
479 disk_mount_manager_->UnmountPath(
480 mount_path,
481 chromeos::UNMOUNT_OPTIONS_NONE,
482 chromeos::disks::DiskMountManager::UnmountPathCallback());
487 } // namespace file_manager