Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / file_manager / volume_manager.cc
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.
4
5 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.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"
31
32 namespace file_manager {
33 namespace {
34
35 // Called on completion of MarkCacheFileAsUnmounted.
36 void OnMarkCacheFileAsUnmounted(drive::FileError error) {
37   // Do nothing.
38 }
39
40 VolumeType MountTypeToVolumeType(
41     chromeos::MountType type) {
42   switch (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
46       // warns it.
47       break;
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;
52   }
53
54   NOTREACHED();
55   return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
56 }
57
58 // Returns a string representation of the given volume type.
59 std::string VolumeTypeToString(VolumeType type) {
60   switch (type) {
61     case VOLUME_TYPE_GOOGLE_DRIVE:
62       return "drive";
63     case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
64       return "downloads";
65     case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
66       return "removable";
67     case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
68       return "archive";
69     case VOLUME_TYPE_CLOUD_DEVICE:
70       return "cloud_device";
71   }
72   NOTREACHED();
73   return "";
74 }
75
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());
82 }
83
84 // Returns the VolumeInfo for Drive file system.
85 VolumeInfo CreateDriveVolumeInfo() {
86   const base::FilePath& drive_path = drive::util::GetDriveMountPointPath();
87
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);
97   return volume_info;
98 }
99
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);
111   return volume_info;
112 }
113
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;
122   if (disk) {
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();
129   } else {
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);
134   }
135   volume_info.volume_id = GenerateVolumeId(volume_info);
136
137   return volume_info;
138 }
139
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);
148   return volume_info;
149 }
150
151 }  // namespace
152
153 VolumeInfo::VolumeInfo() {
154 }
155
156 VolumeInfo::~VolumeInfo() {
157 }
158
159 VolumeManager::VolumeManager(
160     Profile* profile,
161     drive::DriveIntegrationService* drive_integration_service,
162     chromeos::PowerManagerClient* power_manager_client,
163     chromeos::disks::DiskMountManager* disk_mount_manager)
164     : profile_(profile),
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);
170 }
171
172 VolumeManager::~VolumeManager() {
173 }
174
175 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
176   return VolumeManagerFactory::Get(context);
177 }
178
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,
192                                       new_path);
193   }
194
195   // Register 'Downloads' folder for the profile to the file system.
196   if (!chromeos::ProfileHelper::IsSigninProfile(profile_)) {
197     bool success = util::RegisterDownloadsMountPoint(
198         profile_,
199         file_manager::util::GetDownloadsFolderForProfile(profile_));
200     DCHECK(success);
201   }
202
203   // Subscribe to DriveIntegrationService.
204   if (drive_integration_service_)
205     drive_integration_service_->AddObserver(this);
206
207   // Subscribe to DiskMountManager.
208   disk_mount_manager_->AddObserver(this);
209   disk_mount_manager_->RequestMountInfoRefresh();
210
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)));
217 }
218
219 void VolumeManager::Shutdown() {
220   pref_change_registrar_.RemoveAll();
221   disk_mount_manager_->RemoveObserver(this);
222
223   if (drive_integration_service_)
224     drive_integration_service_->RemoveObserver(this);
225 }
226
227 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
228   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
229   DCHECK(observer);
230   observers_.AddObserver(observer);
231 }
232
233 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
234   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
235   DCHECK(observer);
236   observers_.RemoveObserver(observer);
237 }
238
239 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
240   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
241
242   std::vector<VolumeInfo> result;
243
244   // Adds "Drive" volume.
245   if (drive_integration_service_ && drive_integration_service_->IsMounted())
246     result.push_back(CreateDriveVolumeInfo());
247
248   // Adds "Downloads".
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));
255
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(
263         it->second,
264         disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)));
265   }
266
267   if (CommandLine::ForCurrentProcess()->HasSwitch(
268           switches::kEnablePrivetStorage)) {
269     result.push_back(CreatePrivetVolumeInfo());
270   }
271
272   return result;
273 }
274
275 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
276                                        VolumeInfo* result) const {
277   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
278   DCHECK(result);
279
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];
284       return true;
285     }
286   }
287
288   return false;
289 }
290
291 void VolumeManager::OnFileSystemMounted() {
292   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
293
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,
300                                     volume_info,
301                                     false));  // Not remounting.
302 }
303
304 void VolumeManager::OnFileSystemBeingUnmounted() {
305   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
306
307   VolumeInfo volume_info = CreateDriveVolumeInfo();
308   FOR_EACH_OBSERVER(
309       VolumeManagerObserver, observers_,
310       OnVolumeUnmounted(chromeos::MOUNT_ERROR_NONE, volume_info));
311 }
312
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));
317
318   // Disregard hidden devices.
319   if (disk->is_hidden())
320     return;
321
322   switch (event) {
323     case chromeos::disks::DiskMountManager::DISK_ADDED: {
324       if (disk->device_path().empty()) {
325         DVLOG(1) << "Empty system path for " << disk->device_path();
326         return;
327       }
328
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);
340         mounting = true;
341       }
342
343       // Notify to observers.
344       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
345                         OnDiskAdded(*disk, mounting));
346       return;
347     }
348
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(
353             disk->mount_path(),
354             chromeos::UNMOUNT_OPTIONS_LAZY,
355             chromeos::disks::DiskMountManager::UnmountPathCallback());
356       }
357
358       // Notify to observers.
359       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
360                         OnDiskRemoved(*disk));
361       return;
362
363     case chromeos::disks::DiskMountManager::DISK_CHANGED:
364       DVLOG(1) << "Ignore CHANGED event.";
365       return;
366   }
367   NOTREACHED();
368 }
369
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;
375
376   switch (event) {
377     case chromeos::disks::DiskMountManager::DEVICE_ADDED:
378       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
379                         OnDeviceAdded(device_path));
380       return;
381     case chromeos::disks::DiskMountManager::DEVICE_REMOVED:
382       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
383                         OnDeviceRemoved(device_path));
384       return;
385     case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
386       DVLOG(1) << "Ignore SCANNED event: " << device_path;
387       return;
388   }
389   NOTREACHED();
390 }
391
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);
398
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_);
410       if (file_system) {
411         file_system->MarkCacheFileAsUnmounted(
412             base::FilePath(mount_info.source_path),
413             base::Bind(&OnMarkCacheFileAsUnmounted));
414       }
415     }
416   }
417
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);
423   switch (event) {
424     case chromeos::disks::DiskMountManager::MOUNTING: {
425       bool is_remounting =
426           disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
427       FOR_EACH_OBSERVER(
428           VolumeManagerObserver, observers_,
429           OnVolumeMounted(error_code, volume_info, is_remounting));
430       return;
431     }
432     case chromeos::disks::DiskMountManager::UNMOUNTING:
433       FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
434                         OnVolumeUnmounted(error_code, volume_info));
435       return;
436   }
437   NOTREACHED();
438 }
439
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;
447
448   switch (event) {
449     case chromeos::disks::DiskMountManager::FORMAT_STARTED:
450       FOR_EACH_OBSERVER(
451           VolumeManagerObserver, observers_,
452           OnFormatStarted(device_path,
453                           error_code == chromeos::FORMAT_ERROR_NONE));
454       return;
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
460         // operation.
461         disk_mount_manager_->MountPath(
462             device_path, std::string(), std::string(),
463             chromeos::MOUNT_TYPE_DEVICE);
464       }
465
466       FOR_EACH_OBSERVER(
467           VolumeManagerObserver, observers_,
468           OnFormatCompleted(device_path,
469                             error_code == chromeos::FORMAT_ERROR_NONE));
470
471       return;
472   }
473   NOTREACHED();
474 }
475
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(
488           mount_path,
489           chromeos::UNMOUNT_OPTIONS_NONE,
490           chromeos::disks::DiskMountManager::UnmountPathCallback());
491     }
492   }
493 }
494
495 }  // namespace file_manager