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