Upstream version 10.39.225.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/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.
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.
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
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.
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.
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.
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
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
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
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
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
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
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
226 VolumeInfo::~VolumeInfo() {
227 }
228
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
244 VolumeManager::~VolumeManager() {
245 }
246
247 VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
248   return VolumeManagerFactory::Get(context);
249 }
250
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
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
341 void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
342   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
343   DCHECK(observer);
344   observers_.AddObserver(observer);
345 }
346
347 void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
348   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
349   DCHECK(observer);
350   observers_.RemoveObserver(observer);
351 }
352
353 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
366 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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