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/command_line.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.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/mounted_disk_monitor.h"
20 #include "chrome/browser/chromeos/file_manager/path_util.h"
21 #include "chrome/browser/chromeos/file_manager/snapshot_manager.h"
22 #include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
23 #include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
24 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
25 #include "chrome/browser/chromeos/profiles/profile_helper.h"
26 #include "chrome/browser/local_discovery/storage/privet_filesystem_constants.h"
27 #include "chrome/browser/media_galleries/fileapi/mtp_device_map_service.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/common/chrome_switches.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 "webkit/browser/fileapi/external_mount_points.h"
37
38 namespace file_manager {
39 namespace {
40
41 // A named constant to be passed to the |is_remounting| parameter.
42 const bool kNotRemounting = false;
43
44 const char kFileManagerMTPMountNamePrefix[] = "fileman-mtp-";
45 const char kMtpVolumeIdPrefix [] = "mtp:";
46
47 // Registers |path| as the "Downloads" folder to the FileSystem API backend.
48 // If another folder is already mounted. It revokes and overrides the old one.
RegisterDownloadsMountPoint(Profile * profile,const base::FilePath & path)49 bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
50 // Although we show only profile's own "Downloads" folder in Files.app,
51 // in the backend we need to mount all profile's download directory globally.
52 // Otherwise, Files.app cannot support cross-profile file copies, etc.
53 // For this reason, we need to register to the global GetSystemInstance().
54 const std::string mount_point_name =
55 file_manager::util::GetDownloadsMountPointName(profile);
56 fileapi::ExternalMountPoints* const mount_points =
57 fileapi::ExternalMountPoints::GetSystemInstance();
58
59 // In some tests we want to override existing Downloads mount point, so we
60 // first revoke the existing mount point (if any).
61 mount_points->RevokeFileSystem(mount_point_name);
62 return mount_points->RegisterFileSystem(
63 mount_point_name, fileapi::kFileSystemTypeNativeLocal,
64 fileapi::FileSystemMountOption(), path);
65 }
66
67 // Finds the path register as the "Downloads" folder to FileSystem API backend.
68 // Returns false if it is not registered.
FindDownloadsMountPointPath(Profile * profile,base::FilePath * path)69 bool FindDownloadsMountPointPath(Profile* profile, base::FilePath* path) {
70 const std::string mount_point_name =
71 util::GetDownloadsMountPointName(profile);
72 fileapi::ExternalMountPoints* const mount_points =
73 fileapi::ExternalMountPoints::GetSystemInstance();
74
75 return mount_points->GetRegisteredPath(mount_point_name, path);
76 }
77
MountTypeToVolumeType(chromeos::MountType type)78 VolumeType MountTypeToVolumeType(chromeos::MountType type) {
79 switch (type) {
80 case chromeos::MOUNT_TYPE_INVALID:
81 // We don't expect this value, but list here, so that when any value
82 // is added to the enum definition but this is not edited, the compiler
83 // warns it.
84 break;
85 case chromeos::MOUNT_TYPE_DEVICE:
86 return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
87 case chromeos::MOUNT_TYPE_ARCHIVE:
88 return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
89 }
90
91 NOTREACHED();
92 return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
93 }
94
95 // Returns a string representation of the given volume type.
VolumeTypeToString(VolumeType type)96 std::string VolumeTypeToString(VolumeType type) {
97 switch (type) {
98 case VOLUME_TYPE_GOOGLE_DRIVE:
99 return "drive";
100 case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
101 return "downloads";
102 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
103 return "removable";
104 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
105 return "archive";
106 case VOLUME_TYPE_CLOUD_DEVICE:
107 return "cloud_device";
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;
116 }
117 NOTREACHED();
118 return "";
119 }
120
121 // Generates a unique volume ID for the given volume info.
GenerateVolumeId(const VolumeInfo & volume_info)122 std::string GenerateVolumeId(const VolumeInfo& volume_info) {
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_info.type) + ":" +
126 volume_info.mount_path.BaseName().AsUTF8Unsafe());
127 }
128
129 // Returns the VolumeInfo for Drive file system.
CreateDriveVolumeInfo(Profile * profile)130 VolumeInfo CreateDriveVolumeInfo(Profile* profile) {
131 const base::FilePath& drive_path =
132 drive::util::GetDriveMountPointPath(profile);
133
134 VolumeInfo volume_info;
135 volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE;
136 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
137 volume_info.source_path = drive_path;
138 volume_info.mount_path = drive_path;
139 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
140 volume_info.is_parent = false;
141 volume_info.is_read_only = false;
142 volume_info.volume_id = GenerateVolumeId(volume_info);
143 return volume_info;
144 }
145
CreateDownloadsVolumeInfo(const base::FilePath & downloads_path)146 VolumeInfo CreateDownloadsVolumeInfo(const base::FilePath& downloads_path) {
147 VolumeInfo volume_info;
148 volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
149 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
150 // Keep source_path empty.
151 volume_info.mount_path = downloads_path;
152 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
153 volume_info.is_parent = false;
154 volume_info.is_read_only = false;
155 volume_info.volume_id = GenerateVolumeId(volume_info);
156 return volume_info;
157 }
158
CreateTestingVolumeInfo(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type)159 VolumeInfo CreateTestingVolumeInfo(const base::FilePath& path,
160 VolumeType volume_type,
161 chromeos::DeviceType device_type) {
162 VolumeInfo volume_info;
163 volume_info.type = volume_type;
164 volume_info.device_type = device_type;
165 // Keep source_path empty.
166 volume_info.mount_path = path;
167 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
168 volume_info.is_parent = false;
169 volume_info.is_read_only = false;
170 volume_info.volume_id = GenerateVolumeId(volume_info);
171 return volume_info;
172 }
173
CreateVolumeInfoFromMountPointInfo(const chromeos::disks::DiskMountManager::MountPointInfo & mount_point,const chromeos::disks::DiskMountManager::Disk * disk)174 VolumeInfo CreateVolumeInfoFromMountPointInfo(
175 const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
176 const chromeos::disks::DiskMountManager::Disk* disk) {
177 VolumeInfo volume_info;
178 volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
179 volume_info.source_path = base::FilePath(mount_point.source_path);
180 volume_info.mount_path = base::FilePath(mount_point.mount_path);
181 volume_info.mount_condition = mount_point.mount_condition;
182 volume_info.volume_label = volume_info.mount_path.BaseName().AsUTF8Unsafe();
183 if (disk) {
184 volume_info.device_type = disk->device_type();
185 volume_info.system_path_prefix =
186 base::FilePath(disk->system_path_prefix());
187 volume_info.is_parent = disk->is_parent();
188 volume_info.is_read_only = disk->is_read_only();
189 } else {
190 volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
191 volume_info.is_parent = false;
192 volume_info.is_read_only =
193 (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
194 }
195 volume_info.volume_id = GenerateVolumeId(volume_info);
196
197 return volume_info;
198 }
199
CreatePrivetVolumeInfo(const local_discovery::PrivetVolumeLister::VolumeInfo & privet_volume_info)200 VolumeInfo CreatePrivetVolumeInfo(
201 const local_discovery::PrivetVolumeLister::VolumeInfo& privet_volume_info) {
202 VolumeInfo volume_info;
203 volume_info.type = VOLUME_TYPE_CLOUD_DEVICE;
204 volume_info.mount_path = privet_volume_info.volume_path;
205 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
206 volume_info.is_parent = true;
207 volume_info.is_read_only = true;
208 volume_info.volume_id = GenerateVolumeId(volume_info);
209 return volume_info;
210 }
211
CreateProvidedFileSystemVolumeInfo(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info)212 VolumeInfo CreateProvidedFileSystemVolumeInfo(
213 const chromeos::file_system_provider::ProvidedFileSystemInfo&
214 file_system_info) {
215 VolumeInfo volume_info;
216 volume_info.file_system_id = file_system_info.file_system_id();
217 volume_info.extension_id = file_system_info.extension_id();
218 volume_info.volume_label = file_system_info.file_system_name();
219 volume_info.type = VOLUME_TYPE_PROVIDED;
220 volume_info.mount_path = file_system_info.mount_path();
221 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
222 volume_info.is_parent = true;
223 volume_info.is_read_only = true;
224 volume_info.volume_id = GenerateVolumeId(volume_info);
225 return volume_info;
226 }
227
GetMountPointNameForMediaStorage(const storage_monitor::StorageInfo & info)228 std::string GetMountPointNameForMediaStorage(
229 const storage_monitor::StorageInfo& info) {
230 std::string name(kFileManagerMTPMountNamePrefix);
231 name += info.device_id();
232 return name;
233 }
234
235 } // namespace
236
VolumeInfo()237 VolumeInfo::VolumeInfo()
238 : type(VOLUME_TYPE_GOOGLE_DRIVE),
239 device_type(chromeos::DEVICE_TYPE_UNKNOWN),
240 mount_condition(chromeos::disks::MOUNT_CONDITION_NONE),
241 is_parent(false),
242 is_read_only(false) {
243 }
244
~VolumeInfo()245 VolumeInfo::~VolumeInfo() {
246 }
247
VolumeManager(Profile * profile,drive::DriveIntegrationService * drive_integration_service,chromeos::PowerManagerClient * power_manager_client,chromeos::disks::DiskMountManager * disk_mount_manager,chromeos::file_system_provider::Service * file_system_provider_service)248 VolumeManager::VolumeManager(
249 Profile* profile,
250 drive::DriveIntegrationService* drive_integration_service,
251 chromeos::PowerManagerClient* power_manager_client,
252 chromeos::disks::DiskMountManager* disk_mount_manager,
253 chromeos::file_system_provider::Service* file_system_provider_service)
254 : profile_(profile),
255 drive_integration_service_(drive_integration_service),
256 disk_mount_manager_(disk_mount_manager),
257 mounted_disk_monitor_(
258 new MountedDiskMonitor(power_manager_client, disk_mount_manager)),
259 file_system_provider_service_(file_system_provider_service),
260 snapshot_manager_(new SnapshotManager(profile_)),
261 weak_ptr_factory_(this) {
262 DCHECK(disk_mount_manager);
263 }
264
~VolumeManager()265 VolumeManager::~VolumeManager() {
266 }
267
Get(content::BrowserContext * context)268 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
269 return VolumeManagerFactory::Get(context);
270 }
271
Initialize()272 void VolumeManager::Initialize() {
273 // If in Sign in profile, then skip mounting and listening for mount events.
274 if (chromeos::ProfileHelper::IsSigninProfile(profile_))
275 return;
276
277 // Path to mount user folders have changed several times. We need to migrate
278 // the old preferences on paths to the new format when needed. For the detail,
279 // see the comments in file_manager::util::MigratePathFromOldFormat,
280 // Note: Preferences related to downloads are handled in download_prefs.cc.
281 // TODO(kinaba): Remove this after several rounds of releases.
282 const base::FilePath old_path =
283 profile_->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory);
284 base::FilePath new_path;
285 if (!old_path.empty() &&
286 file_manager::util::MigratePathFromOldFormat(profile_,
287 old_path, &new_path)) {
288 profile_->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory,
289 new_path);
290 }
291
292 // Register 'Downloads' folder for the profile to the file system.
293 const base::FilePath downloads =
294 file_manager::util::GetDownloadsFolderForProfile(profile_);
295 const bool success = RegisterDownloadsMountPoint(profile_, downloads);
296 DCHECK(success);
297
298 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
299 CreateDownloadsVolumeInfo(downloads),
300 kNotRemounting);
301
302 // Subscribe to DriveIntegrationService.
303 if (drive_integration_service_) {
304 drive_integration_service_->AddObserver(this);
305 if (drive_integration_service_->IsMounted()) {
306 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
307 CreateDriveVolumeInfo(profile_),
308 kNotRemounting);
309 }
310 }
311
312 // Subscribe to DiskMountManager.
313 disk_mount_manager_->AddObserver(this);
314
315 // Subscribe to FileSystemProviderService and register currently mounted
316 // volumes for the profile.
317 if (file_system_provider_service_) {
318 using chromeos::file_system_provider::ProvidedFileSystemInfo;
319 file_system_provider_service_->AddObserver(this);
320
321 std::vector<ProvidedFileSystemInfo> file_system_info_list =
322 file_system_provider_service_->GetProvidedFileSystemInfoList();
323 for (size_t i = 0; i < file_system_info_list.size(); ++i) {
324 VolumeInfo volume_info =
325 CreateProvidedFileSystemVolumeInfo(file_system_info_list[i]);
326 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, kNotRemounting);
327 }
328 }
329
330 std::vector<VolumeInfo> archives;
331
332 const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
333 disk_mount_manager_->mount_points();
334 for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
335 mount_points.begin();
336 it != mount_points.end();
337 ++it) {
338 if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
339 // Archives are mounted after other type of volumes. See below.
340 archives.push_back(CreateVolumeInfoFromMountPointInfo(it->second, NULL));
341 continue;
342 }
343 DoMountEvent(
344 chromeos::MOUNT_ERROR_NONE,
345 CreateVolumeInfoFromMountPointInfo(
346 it->second,
347 disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)),
348 kNotRemounting);
349 }
350
351 // We mount archives only if they are opened from currently mounted volumes.
352 // To check the condition correctly in DoMountEvent, we care the order.
353 std::vector<bool> done(archives.size(), false);
354 for (size_t i = 0; i < archives.size(); ++i) {
355 if (!done[i]) {
356 std::vector<VolumeInfo> chain;
357 done[i] = true;
358 chain.push_back(archives[i]);
359
360 // If archives[i]'s source_path is in another archive, mount it first.
361 for (size_t parent = 0; parent < archives.size(); ++parent) {
362 if (!done[parent] &&
363 archives[parent].mount_path.IsParent(chain.back().source_path)) {
364 done[parent] = true;
365 chain.push_back(archives[parent]);
366 parent = 0; // Search archives[parent]'s parent from the beginning.
367 }
368 }
369
370 // Mount from the tail of chain.
371 for (size_t i = chain.size(); i > 0; --i)
372 DoMountEvent(chromeos::MOUNT_ERROR_NONE, chain[i - 1], kNotRemounting);
373 }
374 }
375
376 disk_mount_manager_->RequestMountInfoRefresh();
377
378 // Subscribe to Profile Preference change.
379 pref_change_registrar_.Init(profile_->GetPrefs());
380 pref_change_registrar_.Add(
381 prefs::kExternalStorageDisabled,
382 base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
383 weak_ptr_factory_.GetWeakPtr()));
384
385 // Subscribe to Privet volume lister.
386 if (CommandLine::ForCurrentProcess()->HasSwitch(
387 switches::kEnablePrivetStorage)) {
388 privet_volume_lister_.reset(new local_discovery::PrivetVolumeLister(
389 base::Bind(&VolumeManager::OnPrivetVolumesAvailable,
390 weak_ptr_factory_.GetWeakPtr())));
391 privet_volume_lister_->Start();
392 }
393
394 // Subscribe to storage monitor for MTP notifications.
395 const bool disable_mtp =
396 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
397 chromeos::switches::kEnableFileManagerMTP) != "true";
398 if (!disable_mtp && storage_monitor::StorageMonitor::GetInstance()) {
399 storage_monitor::StorageMonitor::GetInstance()->EnsureInitialized(
400 base::Bind(&VolumeManager::OnStorageMonitorInitialized,
401 weak_ptr_factory_.GetWeakPtr()));
402 }
403 }
404
Shutdown()405 void VolumeManager::Shutdown() {
406 weak_ptr_factory_.InvalidateWeakPtrs();
407
408 snapshot_manager_.reset();
409 pref_change_registrar_.RemoveAll();
410 disk_mount_manager_->RemoveObserver(this);
411 if (storage_monitor::StorageMonitor::GetInstance())
412 storage_monitor::StorageMonitor::GetInstance()->RemoveObserver(this);
413
414 if (drive_integration_service_)
415 drive_integration_service_->RemoveObserver(this);
416
417 if (file_system_provider_service_)
418 file_system_provider_service_->RemoveObserver(this);
419 }
420
AddObserver(VolumeManagerObserver * observer)421 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
422 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
423 DCHECK(observer);
424 observers_.AddObserver(observer);
425 }
426
RemoveObserver(VolumeManagerObserver * observer)427 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
428 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
429 DCHECK(observer);
430 observers_.RemoveObserver(observer);
431 }
432
GetVolumeInfoList() const433 std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
434 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
435
436 std::vector<VolumeInfo> result;
437 for (std::map<std::string, VolumeInfo>::const_iterator iter =
438 mounted_volumes_.begin();
439 iter != mounted_volumes_.end();
440 ++iter) {
441 result.push_back(iter->second);
442 }
443 return result;
444 }
445
FindVolumeInfoById(const std::string & volume_id,VolumeInfo * result) const446 bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
447 VolumeInfo* result) const {
448 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
449 DCHECK(result);
450
451 std::map<std::string, VolumeInfo>::const_iterator iter =
452 mounted_volumes_.find(volume_id);
453 if (iter == mounted_volumes_.end())
454 return false;
455 *result = iter->second;
456 return true;
457 }
458
RegisterDownloadsDirectoryForTesting(const base::FilePath & path)459 bool VolumeManager::RegisterDownloadsDirectoryForTesting(
460 const base::FilePath& path) {
461 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
462
463 base::FilePath old_path;
464 if (FindDownloadsMountPointPath(profile_, &old_path)) {
465 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
466 CreateDownloadsVolumeInfo(old_path));
467 }
468
469 bool success = RegisterDownloadsMountPoint(profile_, path);
470 DoMountEvent(
471 success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
472 CreateDownloadsVolumeInfo(path),
473 kNotRemounting);
474 return success;
475 }
476
AddVolumeInfoForTesting(const base::FilePath & path,VolumeType volume_type,chromeos::DeviceType device_type)477 void VolumeManager::AddVolumeInfoForTesting(const base::FilePath& path,
478 VolumeType volume_type,
479 chromeos::DeviceType device_type) {
480 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
481 DoMountEvent(chromeos::MOUNT_ERROR_NONE,
482 CreateTestingVolumeInfo(path, volume_type, device_type),
483 kNotRemounting);
484 }
485
OnFileSystemMounted()486 void VolumeManager::OnFileSystemMounted() {
487 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
488
489 // Raise mount event.
490 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
491 // or network is unreachable. These two errors will be handled later.
492 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
493 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, kNotRemounting);
494 }
495
OnFileSystemBeingUnmounted()496 void VolumeManager::OnFileSystemBeingUnmounted() {
497 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
498
499 VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
500 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
501 }
502
OnDiskEvent(chromeos::disks::DiskMountManager::DiskEvent event,const chromeos::disks::DiskMountManager::Disk * disk)503 void VolumeManager::OnDiskEvent(
504 chromeos::disks::DiskMountManager::DiskEvent event,
505 const chromeos::disks::DiskMountManager::Disk* disk) {
506 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
507
508 // Disregard hidden devices.
509 if (disk->is_hidden())
510 return;
511
512 switch (event) {
513 case chromeos::disks::DiskMountManager::DISK_ADDED:
514 case chromeos::disks::DiskMountManager::DISK_CHANGED: {
515 if (disk->device_path().empty()) {
516 DVLOG(1) << "Empty system path for " << disk->device_path();
517 return;
518 }
519
520 bool mounting = false;
521 if (disk->mount_path().empty() && disk->has_media() &&
522 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
523 // If disk is not mounted yet and it has media and there is no policy
524 // forbidding external storage, give it a try.
525 // Initiate disk mount operation. MountPath auto-detects the filesystem
526 // format if the second argument is empty. The third argument (mount
527 // label) is not used in a disk mount operation.
528 disk_mount_manager_->MountPath(
529 disk->device_path(), std::string(), std::string(),
530 chromeos::MOUNT_TYPE_DEVICE);
531 mounting = true;
532 }
533
534 // Notify to observers.
535 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
536 OnDiskAdded(*disk, mounting));
537 return;
538 }
539
540 case chromeos::disks::DiskMountManager::DISK_REMOVED:
541 // If the disk is already mounted, unmount it.
542 if (!disk->mount_path().empty()) {
543 disk_mount_manager_->UnmountPath(
544 disk->mount_path(),
545 chromeos::UNMOUNT_OPTIONS_LAZY,
546 chromeos::disks::DiskMountManager::UnmountPathCallback());
547 }
548
549 // Notify to observers.
550 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
551 OnDiskRemoved(*disk));
552 return;
553 }
554 NOTREACHED();
555 }
556
OnDeviceEvent(chromeos::disks::DiskMountManager::DeviceEvent event,const std::string & device_path)557 void VolumeManager::OnDeviceEvent(
558 chromeos::disks::DiskMountManager::DeviceEvent event,
559 const std::string& device_path) {
560 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
561 DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
562
563 switch (event) {
564 case chromeos::disks::DiskMountManager::DEVICE_ADDED:
565 FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
566 OnDeviceAdded(device_path));
567 return;
568 case chromeos::disks::DiskMountManager::DEVICE_REMOVED: {
569 const bool hard_unplugged =
570 mounted_disk_monitor_->DeviceIsHardUnplugged(device_path);
571 FOR_EACH_OBSERVER(VolumeManagerObserver,
572 observers_,
573 OnDeviceRemoved(device_path, hard_unplugged));
574 mounted_disk_monitor_->ClearHardUnpluggedFlag(device_path);
575 return;
576 }
577 case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
578 DVLOG(1) << "Ignore SCANNED event: " << device_path;
579 return;
580 }
581 NOTREACHED();
582 }
583
OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,chromeos::MountError error_code,const chromeos::disks::DiskMountManager::MountPointInfo & mount_info)584 void VolumeManager::OnMountEvent(
585 chromeos::disks::DiskMountManager::MountEvent event,
586 chromeos::MountError error_code,
587 const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
588 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
589 DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
590
591 if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
592 // If the file is not mounted now, tell it to drive file system so that
593 // it can handle file caching correctly.
594 // Note that drive file system knows if the file is managed by drive file
595 // system or not, so here we report all paths.
596 if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
597 error_code != chromeos::MOUNT_ERROR_NONE) ||
598 (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
599 error_code == chromeos::MOUNT_ERROR_NONE)) {
600 drive::FileSystemInterface* file_system =
601 drive::util::GetFileSystemByProfile(profile_);
602 if (file_system) {
603 file_system->MarkCacheFileAsUnmounted(
604 base::FilePath(mount_info.source_path),
605 base::Bind(&drive::util::EmptyFileOperationCallback));
606 }
607 }
608 }
609
610 // Notify a mounting/unmounting event to observers.
611 const chromeos::disks::DiskMountManager::Disk* disk =
612 disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
613 VolumeInfo volume_info =
614 CreateVolumeInfoFromMountPointInfo(mount_info, disk);
615 switch (event) {
616 case chromeos::disks::DiskMountManager::MOUNTING: {
617 bool is_remounting =
618 disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
619 DoMountEvent(error_code, volume_info, is_remounting);
620 return;
621 }
622 case chromeos::disks::DiskMountManager::UNMOUNTING:
623 DoUnmountEvent(error_code, volume_info);
624 return;
625 }
626 NOTREACHED();
627 }
628
OnFormatEvent(chromeos::disks::DiskMountManager::FormatEvent event,chromeos::FormatError error_code,const std::string & device_path)629 void VolumeManager::OnFormatEvent(
630 chromeos::disks::DiskMountManager::FormatEvent event,
631 chromeos::FormatError error_code,
632 const std::string& device_path) {
633 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
634 DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
635 << ", " << device_path;
636
637 switch (event) {
638 case chromeos::disks::DiskMountManager::FORMAT_STARTED:
639 FOR_EACH_OBSERVER(
640 VolumeManagerObserver, observers_,
641 OnFormatStarted(device_path,
642 error_code == chromeos::FORMAT_ERROR_NONE));
643 return;
644 case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
645 if (error_code == chromeos::FORMAT_ERROR_NONE) {
646 // If format is completed successfully, try to mount the device.
647 // MountPath auto-detects filesystem format if second argument is
648 // empty. The third argument (mount label) is not used in a disk mount
649 // operation.
650 disk_mount_manager_->MountPath(
651 device_path, std::string(), std::string(),
652 chromeos::MOUNT_TYPE_DEVICE);
653 }
654
655 FOR_EACH_OBSERVER(
656 VolumeManagerObserver, observers_,
657 OnFormatCompleted(device_path,
658 error_code == chromeos::FORMAT_ERROR_NONE));
659
660 return;
661 }
662 NOTREACHED();
663 }
664
OnProvidedFileSystemMount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,base::File::Error error)665 void VolumeManager::OnProvidedFileSystemMount(
666 const chromeos::file_system_provider::ProvidedFileSystemInfo&
667 file_system_info,
668 base::File::Error error) {
669 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
670 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
671 // since it is related to cros disks only.
672 const chromeos::MountError mount_error = error == base::File::FILE_OK
673 ? chromeos::MOUNT_ERROR_NONE
674 : chromeos::MOUNT_ERROR_UNKNOWN;
675 DoMountEvent(mount_error, volume_info, kNotRemounting);
676 }
677
OnProvidedFileSystemUnmount(const chromeos::file_system_provider::ProvidedFileSystemInfo & file_system_info,base::File::Error error)678 void VolumeManager::OnProvidedFileSystemUnmount(
679 const chromeos::file_system_provider::ProvidedFileSystemInfo&
680 file_system_info,
681 base::File::Error error) {
682 // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
683 // since it is related to cros disks only.
684 const chromeos::MountError mount_error = error == base::File::FILE_OK
685 ? chromeos::MOUNT_ERROR_NONE
686 : chromeos::MOUNT_ERROR_UNKNOWN;
687 VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system_info);
688 DoUnmountEvent(mount_error, volume_info);
689 }
690
OnExternalStorageDisabledChanged()691 void VolumeManager::OnExternalStorageDisabledChanged() {
692 // If the policy just got disabled we have to unmount every device currently
693 // mounted. The opposite is fine - we can let the user re-plug her device to
694 // make it available.
695 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
696 // We do not iterate on mount_points directly, because mount_points can
697 // be changed by UnmountPath().
698 // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
699 while (!disk_mount_manager_->mount_points().empty()) {
700 std::string mount_path =
701 disk_mount_manager_->mount_points().begin()->second.mount_path;
702 disk_mount_manager_->UnmountPath(
703 mount_path,
704 chromeos::UNMOUNT_OPTIONS_NONE,
705 chromeos::disks::DiskMountManager::UnmountPathCallback());
706 }
707 }
708 }
709
OnPrivetVolumesAvailable(const local_discovery::PrivetVolumeLister::VolumeList & volumes)710 void VolumeManager::OnPrivetVolumesAvailable(
711 const local_discovery::PrivetVolumeLister::VolumeList& volumes) {
712 for (local_discovery::PrivetVolumeLister::VolumeList::const_iterator i =
713 volumes.begin(); i != volumes.end(); i++) {
714 VolumeInfo volume_info = CreatePrivetVolumeInfo(*i);
715 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
716 }
717 }
718
OnRemovableStorageAttached(const storage_monitor::StorageInfo & info)719 void VolumeManager::OnRemovableStorageAttached(
720 const storage_monitor::StorageInfo& info) {
721 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
722 return;
723 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled))
724 return;
725
726 const base::FilePath path = base::FilePath::FromUTF8Unsafe(info.location());
727 const std::string fsid = GetMountPointNameForMediaStorage(info);
728 const std::string base_name = base::UTF16ToUTF8(info.model_name());
729
730 // Assign a fresh volume ID based on the volume name.
731 std::string label = base_name;
732 for (int i = 2; mounted_volumes_.count(kMtpVolumeIdPrefix + label); ++i)
733 label = base_name + base::StringPrintf(" (%d)", i);
734
735 bool result =
736 fileapi::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
737 fsid, fileapi::kFileSystemTypeDeviceMediaAsFileStorage,
738 fileapi::FileSystemMountOption(), path);
739 DCHECK(result);
740 content::BrowserThread::PostTask(
741 content::BrowserThread::IO, FROM_HERE, base::Bind(
742 &MTPDeviceMapService::RegisterMTPFileSystem,
743 base::Unretained(MTPDeviceMapService::GetInstance()),
744 info.location(), fsid));
745
746 VolumeInfo volume_info;
747 volume_info.type = VOLUME_TYPE_MTP;
748 volume_info.mount_path = path;
749 volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
750 volume_info.is_parent = true;
751 volume_info.is_read_only = true;
752 volume_info.volume_id = kMtpVolumeIdPrefix + label;
753 volume_info.volume_label = label;
754 volume_info.source_path = path;
755 volume_info.device_type = chromeos::DEVICE_TYPE_MOBILE;
756 DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
757 }
758
OnRemovableStorageDetached(const storage_monitor::StorageInfo & info)759 void VolumeManager::OnRemovableStorageDetached(
760 const storage_monitor::StorageInfo& info) {
761 if (!storage_monitor::StorageInfo::IsMTPDevice(info.device_id()))
762 return;
763
764 for (std::map<std::string, VolumeInfo>::iterator it =
765 mounted_volumes_.begin(); it != mounted_volumes_.end(); ++it) {
766 if (it->second.source_path.value() == info.location()) {
767 DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, VolumeInfo(it->second));
768
769 const std::string fsid = GetMountPointNameForMediaStorage(info);
770 fileapi::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
771 fsid);
772 content::BrowserThread::PostTask(
773 content::BrowserThread::IO, FROM_HERE, base::Bind(
774 &MTPDeviceMapService::RevokeMTPFileSystem,
775 base::Unretained(MTPDeviceMapService::GetInstance()),
776 fsid));
777 return;
778 }
779 }
780 }
781
OnStorageMonitorInitialized()782 void VolumeManager::OnStorageMonitorInitialized() {
783 std::vector<storage_monitor::StorageInfo> storages =
784 storage_monitor::StorageMonitor::GetInstance()->GetAllAvailableStorages();
785 for (size_t i = 0; i < storages.size(); ++i)
786 OnRemovableStorageAttached(storages[i]);
787 storage_monitor::StorageMonitor::GetInstance()->AddObserver(this);
788 }
789
DoMountEvent(chromeos::MountError error_code,const VolumeInfo & volume_info,bool is_remounting)790 void VolumeManager::DoMountEvent(chromeos::MountError error_code,
791 const VolumeInfo& volume_info,
792 bool is_remounting) {
793 // Archive files are mounted globally in system. We however don't want to show
794 // archives from profile-specific folders (Drive/Downloads) of other users in
795 // multi-profile session. To this end, we filter out archives not on the
796 // volumes already mounted on this VolumeManager instance.
797 if (volume_info.type == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
798 // Source may be in Drive cache folder under the current profile directory.
799 bool from_current_profile =
800 profile_->GetPath().IsParent(volume_info.source_path);
801 for (std::map<std::string, VolumeInfo>::const_iterator iter =
802 mounted_volumes_.begin();
803 !from_current_profile && iter != mounted_volumes_.end();
804 ++iter) {
805 if (iter->second.mount_path.IsParent(volume_info.source_path))
806 from_current_profile = true;
807 }
808 if (!from_current_profile)
809 return;
810 }
811
812 // Filter out removable disks if forbidden by policy for this profile.
813 if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
814 profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
815 return;
816 }
817
818 if (error_code == chromeos::MOUNT_ERROR_NONE || volume_info.mount_condition) {
819 mounted_volumes_[volume_info.volume_id] = volume_info;
820
821 if (!is_remounting) {
822 UMA_HISTOGRAM_ENUMERATION("FileBrowser.VolumeType",
823 volume_info.type,
824 NUM_VOLUME_TYPE);
825 }
826 }
827
828 FOR_EACH_OBSERVER(VolumeManagerObserver,
829 observers_,
830 OnVolumeMounted(error_code, volume_info, is_remounting));
831 }
832
DoUnmountEvent(chromeos::MountError error_code,const VolumeInfo & volume_info)833 void VolumeManager::DoUnmountEvent(chromeos::MountError error_code,
834 const VolumeInfo& volume_info) {
835 if (mounted_volumes_.find(volume_info.volume_id) == mounted_volumes_.end())
836 return;
837 if (error_code == chromeos::MOUNT_ERROR_NONE)
838 mounted_volumes_.erase(volume_info.volume_id);
839
840 FOR_EACH_OBSERVER(VolumeManagerObserver,
841 observers_,
842 OnVolumeUnmounted(error_code, volume_info));
843 }
844
845 } // namespace file_manager
846