volume_manager.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/file_manager/volume_manager.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/callback.h"
10#include "base/command_line.h"
11#include "base/files/file_path.h"
12#include "base/logging.h"
13#include "base/memory/singleton.h"
14#include "base/prefs/pref_service.h"
15#include "chrome/browser/chromeos/drive/drive_integration_service.h"
16#include "chrome/browser/chromeos/drive/file_errors.h"
17#include "chrome/browser/chromeos/drive/file_system_interface.h"
18#include "chrome/browser/chromeos/drive/file_system_util.h"
19#include "chrome/browser/chromeos/file_manager/mounted_disk_monitor.h"
20#include "chrome/browser/chromeos/file_manager/path_util.h"
21#include "chrome/browser/chromeos/file_manager/volume_manager_factory.h"
22#include "chrome/browser/chromeos/file_manager/volume_manager_observer.h"
23#include "chrome/browser/chromeos/profiles/profile_helper.h"
24#include "chrome/browser/local_discovery/storage/privet_filesystem_constants.h"
25#include "chrome/browser/profiles/profile.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/common/pref_names.h"
28#include "chromeos/dbus/cros_disks_client.h"
29#include "chromeos/disks/disk_mount_manager.h"
30#include "content/public/browser/browser_context.h"
31#include "content/public/browser/browser_thread.h"
32#include "webkit/browser/fileapi/external_mount_points.h"
33
34namespace file_manager {
35namespace {
36
37// Registers |path| as the "Downloads" folder to the FileSystem API backend.
38// If another folder is already mounted. It revokes and overrides the old one.
39bool RegisterDownloadsMountPoint(Profile* profile, const base::FilePath& path) {
40  // Although we show only profile's own "Downloads" folder in Files.app,
41  // in the backend we need to mount all profile's download directory globally.
42  // Otherwise, Files.app cannot support cross-profile file copies, etc.
43  // For this reason, we need to register to the global GetSystemInstance().
44  const std::string mount_point_name =
45      file_manager::util::GetDownloadsMountPointName(profile);
46  fileapi::ExternalMountPoints* const mount_points =
47      fileapi::ExternalMountPoints::GetSystemInstance();
48
49  // In some tests we want to override existing Downloads mount point, so we
50  // first revoke the existing mount point (if any).
51  mount_points->RevokeFileSystem(mount_point_name);
52  return mount_points->RegisterFileSystem(
53      mount_point_name, fileapi::kFileSystemTypeNativeLocal,
54      fileapi::FileSystemMountOption(), path);
55}
56
57// Finds the path register as the "Downloads" folder to FileSystem API backend.
58// Returns false if it is not registered.
59bool FindDownloadsMountPointPath(Profile* profile, base::FilePath* path) {
60  const std::string mount_point_name =
61      util::GetDownloadsMountPointName(profile);
62  fileapi::ExternalMountPoints* const mount_points =
63      fileapi::ExternalMountPoints::GetSystemInstance();
64
65  return mount_points->GetRegisteredPath(mount_point_name, path);
66}
67
68// Called on completion of MarkCacheFileAsUnmounted.
69void OnMarkCacheFileAsUnmounted(drive::FileError error) {
70  // Do nothing.
71}
72
73VolumeType MountTypeToVolumeType(
74    chromeos::MountType type) {
75  switch (type) {
76    case chromeos::MOUNT_TYPE_INVALID:
77      // We don't expect this value, but list here, so that when any value
78      // is added to the enum definition but this is not edited, the compiler
79      // warns it.
80      break;
81    case chromeos::MOUNT_TYPE_DEVICE:
82      return VOLUME_TYPE_REMOVABLE_DISK_PARTITION;
83    case chromeos::MOUNT_TYPE_ARCHIVE:
84      return VOLUME_TYPE_MOUNTED_ARCHIVE_FILE;
85  }
86
87  NOTREACHED();
88  return VOLUME_TYPE_DOWNLOADS_DIRECTORY;
89}
90
91// Returns a string representation of the given volume type.
92std::string VolumeTypeToString(VolumeType type) {
93  switch (type) {
94    case VOLUME_TYPE_GOOGLE_DRIVE:
95      return "drive";
96    case VOLUME_TYPE_DOWNLOADS_DIRECTORY:
97      return "downloads";
98    case VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
99      return "removable";
100    case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE:
101      return "archive";
102    case VOLUME_TYPE_CLOUD_DEVICE:
103      return "cloud_device";
104  }
105  NOTREACHED();
106  return "";
107}
108
109// Generates a unique volume ID for the given volume info.
110std::string GenerateVolumeId(const VolumeInfo& volume_info) {
111  // For the same volume type, base names are unique, as mount points are
112  // flat for the same volume type.
113  return (VolumeTypeToString(volume_info.type) + ":" +
114          volume_info.mount_path.BaseName().AsUTF8Unsafe());
115}
116
117// Returns the VolumeInfo for Drive file system.
118VolumeInfo CreateDriveVolumeInfo(Profile* profile) {
119  const base::FilePath& drive_path =
120      drive::util::GetDriveMountPointPath(profile);
121
122  VolumeInfo volume_info;
123  volume_info.type = VOLUME_TYPE_GOOGLE_DRIVE;
124  volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
125  volume_info.source_path = drive_path;
126  volume_info.mount_path = drive_path;
127  volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
128  volume_info.is_parent = false;
129  volume_info.is_read_only = false;
130  volume_info.volume_id = GenerateVolumeId(volume_info);
131  return volume_info;
132}
133
134VolumeInfo CreateDownloadsVolumeInfo(
135    const base::FilePath& downloads_path) {
136  VolumeInfo volume_info;
137  volume_info.type = VOLUME_TYPE_DOWNLOADS_DIRECTORY;
138  volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
139  // Keep source_path empty.
140  volume_info.mount_path = downloads_path;
141  volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
142  volume_info.is_parent = false;
143  volume_info.is_read_only = false;
144  volume_info.volume_id = GenerateVolumeId(volume_info);
145  return volume_info;
146}
147
148VolumeInfo CreateVolumeInfoFromMountPointInfo(
149    const chromeos::disks::DiskMountManager::MountPointInfo& mount_point,
150    const chromeos::disks::DiskMountManager::Disk* disk) {
151  VolumeInfo volume_info;
152  volume_info.type = MountTypeToVolumeType(mount_point.mount_type);
153  volume_info.source_path = base::FilePath(mount_point.source_path);
154  volume_info.mount_path = base::FilePath(mount_point.mount_path);
155  volume_info.mount_condition = mount_point.mount_condition;
156  if (disk) {
157    volume_info.device_type = disk->device_type();
158    volume_info.system_path_prefix =
159        base::FilePath(disk->system_path_prefix());
160    volume_info.drive_label = disk->drive_label();
161    volume_info.is_parent = disk->is_parent();
162    volume_info.is_read_only = disk->is_read_only();
163  } else {
164    volume_info.device_type = chromeos::DEVICE_TYPE_UNKNOWN;
165    volume_info.is_parent = false;
166    volume_info.is_read_only =
167        (mount_point.mount_type == chromeos::MOUNT_TYPE_ARCHIVE);
168  }
169  volume_info.volume_id = GenerateVolumeId(volume_info);
170
171  return volume_info;
172}
173
174VolumeInfo CreatePrivetVolumeInfo(
175    const local_discovery::PrivetVolumeLister::VolumeInfo& privet_volume_info) {
176  VolumeInfo volume_info;
177  volume_info.type = VOLUME_TYPE_CLOUD_DEVICE;
178  volume_info.mount_path = privet_volume_info.volume_path;
179  volume_info.mount_condition = chromeos::disks::MOUNT_CONDITION_NONE;
180  volume_info.is_parent = true;
181  volume_info.is_read_only = true;
182  volume_info.volume_id = GenerateVolumeId(volume_info);
183  return volume_info;
184}
185
186}  // namespace
187
188VolumeInfo::VolumeInfo() {
189}
190
191VolumeInfo::~VolumeInfo() {
192}
193
194VolumeManager::VolumeManager(
195    Profile* profile,
196    drive::DriveIntegrationService* drive_integration_service,
197    chromeos::PowerManagerClient* power_manager_client,
198    chromeos::disks::DiskMountManager* disk_mount_manager)
199    : profile_(profile),
200      drive_integration_service_(drive_integration_service),
201      disk_mount_manager_(disk_mount_manager),
202      mounted_disk_monitor_(
203          new MountedDiskMonitor(power_manager_client, disk_mount_manager)) {
204  DCHECK(disk_mount_manager);
205}
206
207VolumeManager::~VolumeManager() {
208}
209
210VolumeManager* VolumeManager::Get(content::BrowserContext* context) {
211  return VolumeManagerFactory::Get(context);
212}
213
214void VolumeManager::Initialize() {
215  const bool kNotRemounting = false;
216
217  // Path to mount user folders have changed several times. We need to migrate
218  // the old preferences on paths to the new format when needed. For the detail,
219  // see the comments in file_manager::util::MigratePathFromOldFormat,
220  // Note: Preferences related to downloads are handled in download_prefs.cc.
221  // TODO(kinaba): Remove this after several rounds of releases.
222  const base::FilePath old_path =
223      profile_->GetPrefs()->GetFilePath(prefs::kSelectFileLastDirectory);
224  base::FilePath new_path;
225  if (!old_path.empty() &&
226      file_manager::util::MigratePathFromOldFormat(profile_,
227                                                   old_path, &new_path)) {
228    profile_->GetPrefs()->SetFilePath(prefs::kSelectFileLastDirectory,
229                                      new_path);
230  }
231
232  // Register 'Downloads' folder for the profile to the file system.
233  if (!chromeos::ProfileHelper::IsSigninProfile(profile_)) {
234    const base::FilePath downloads =
235        file_manager::util::GetDownloadsFolderForProfile(profile_);
236    const bool success = RegisterDownloadsMountPoint(profile_, downloads);
237    DCHECK(success);
238
239    DoMountEvent(chromeos::MOUNT_ERROR_NONE,
240                 CreateDownloadsVolumeInfo(downloads),
241                 kNotRemounting);
242  }
243
244  // Subscribe to DriveIntegrationService.
245  if (drive_integration_service_) {
246    drive_integration_service_->AddObserver(this);
247    if (drive_integration_service_->IsMounted()) {
248      DoMountEvent(chromeos::MOUNT_ERROR_NONE,
249                   CreateDriveVolumeInfo(profile_),
250                   kNotRemounting);
251    }
252  }
253
254  // Subscribe to DiskMountManager.
255  disk_mount_manager_->AddObserver(this);
256
257  std::vector<VolumeInfo> archives;
258
259  const chromeos::disks::DiskMountManager::MountPointMap& mount_points =
260      disk_mount_manager_->mount_points();
261  for (chromeos::disks::DiskMountManager::MountPointMap::const_iterator it =
262           mount_points.begin();
263       it != mount_points.end();
264       ++it) {
265    if (it->second.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
266      // Archives are mounted after other type of volumes. See below.
267      archives.push_back(CreateVolumeInfoFromMountPointInfo(it->second, NULL));
268      continue;
269    }
270    DoMountEvent(
271        chromeos::MOUNT_ERROR_NONE,
272        CreateVolumeInfoFromMountPointInfo(
273            it->second,
274            disk_mount_manager_->FindDiskBySourcePath(it->second.source_path)),
275            kNotRemounting);
276  }
277
278  // We mount archives only if they are opened from currently mounted volumes.
279  // To check the condition correctly in DoMountEvent, we care the order.
280  std::vector<bool> done(archives.size(), false);
281  for (size_t i = 0; i < archives.size(); ++i) {
282    if (!done[i]) {
283      std::vector<VolumeInfo> chain;
284      done[i] = true;
285      chain.push_back(archives[i]);
286
287      // If archives[i]'s source_path is in another archive, mount it first.
288      for (size_t parent = 0; parent < archives.size(); ++parent) {
289        if (!done[parent] &&
290            archives[parent].mount_path.IsParent(chain.back().source_path)) {
291          done[parent] = true;
292          chain.push_back(archives[parent]);
293          parent = 0;  // Search archives[parent]'s parent from the beginning.
294        }
295      }
296
297      // Mount from the tail of chain.
298      for (size_t i = chain.size(); i > 0; --i)
299        DoMountEvent(chromeos::MOUNT_ERROR_NONE, chain[i - 1], kNotRemounting);
300    }
301  }
302
303  disk_mount_manager_->RequestMountInfoRefresh();
304
305  // Subscribe to Profile Preference change.
306  pref_change_registrar_.Init(profile_->GetPrefs());
307  pref_change_registrar_.Add(
308      prefs::kExternalStorageDisabled,
309      base::Bind(&VolumeManager::OnExternalStorageDisabledChanged,
310                 base::Unretained(this)));
311
312  if (CommandLine::ForCurrentProcess()->HasSwitch(
313          switches::kEnablePrivetStorage)) {
314    privet_volume_lister_.reset(new local_discovery::PrivetVolumeLister(
315        base::Bind(&VolumeManager::OnPrivetVolumesAvailable,
316                   base::Unretained(this))));
317    privet_volume_lister_->Start();
318  }
319}
320
321void VolumeManager::Shutdown() {
322  pref_change_registrar_.RemoveAll();
323  disk_mount_manager_->RemoveObserver(this);
324
325  if (drive_integration_service_)
326    drive_integration_service_->RemoveObserver(this);
327}
328
329void VolumeManager::AddObserver(VolumeManagerObserver* observer) {
330  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
331  DCHECK(observer);
332  observers_.AddObserver(observer);
333}
334
335void VolumeManager::RemoveObserver(VolumeManagerObserver* observer) {
336  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
337  DCHECK(observer);
338  observers_.RemoveObserver(observer);
339}
340
341std::vector<VolumeInfo> VolumeManager::GetVolumeInfoList() const {
342  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
343
344  std::vector<VolumeInfo> result;
345  for (std::map<std::string, VolumeInfo>::const_iterator iter =
346           mounted_volumes_.begin();
347       iter != mounted_volumes_.end();
348       ++iter) {
349    result.push_back(iter->second);
350  }
351  return result;
352}
353
354bool VolumeManager::FindVolumeInfoById(const std::string& volume_id,
355                                       VolumeInfo* result) const {
356  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
357  DCHECK(result);
358
359  std::map<std::string, VolumeInfo>::const_iterator iter =
360      mounted_volumes_.find(volume_id);
361  if (iter == mounted_volumes_.end())
362    return false;
363  *result = iter->second;
364  return true;
365}
366
367bool VolumeManager::RegisterDownloadsDirectoryForTesting(
368    const base::FilePath& path) {
369  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
370
371  base::FilePath old_path;
372  if (FindDownloadsMountPointPath(profile_, &old_path)) {
373    DoUnmountEvent(chromeos::MOUNT_ERROR_NONE,
374                   CreateDownloadsVolumeInfo(old_path));
375  }
376
377  bool success = RegisterDownloadsMountPoint(profile_, path);
378  DoMountEvent(
379      success ? chromeos::MOUNT_ERROR_NONE : chromeos::MOUNT_ERROR_INVALID_PATH,
380      CreateDownloadsVolumeInfo(path),
381      false /* is_remounting */);
382  return success;
383}
384
385void VolumeManager::OnFileSystemMounted() {
386  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
387
388  // Raise mount event.
389  // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
390  // or network is unreachable. These two errors will be handled later.
391  VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
392  DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info,
393               false /* is_remounting */);
394}
395
396void VolumeManager::OnFileSystemBeingUnmounted() {
397  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
398
399  VolumeInfo volume_info = CreateDriveVolumeInfo(profile_);
400  DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
401}
402
403void VolumeManager::OnDiskEvent(
404    chromeos::disks::DiskMountManager::DiskEvent event,
405    const chromeos::disks::DiskMountManager::Disk* disk) {
406  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
407
408  // Disregard hidden devices.
409  if (disk->is_hidden())
410    return;
411
412  switch (event) {
413    case chromeos::disks::DiskMountManager::DISK_ADDED: {
414      if (disk->device_path().empty()) {
415        DVLOG(1) << "Empty system path for " << disk->device_path();
416        return;
417      }
418
419      bool mounting = false;
420      if (disk->mount_path().empty() && disk->has_media() &&
421          !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
422        // If disk is not mounted yet and it has media and there is no policy
423        // forbidding external storage, give it a try.
424        // Initiate disk mount operation. MountPath auto-detects the filesystem
425        // format if the second argument is empty. The third argument (mount
426        // label) is not used in a disk mount operation.
427        disk_mount_manager_->MountPath(
428            disk->device_path(), std::string(), std::string(),
429            chromeos::MOUNT_TYPE_DEVICE);
430        mounting = true;
431      }
432
433      // Notify to observers.
434      FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
435                        OnDiskAdded(*disk, mounting));
436      return;
437    }
438
439    case chromeos::disks::DiskMountManager::DISK_REMOVED:
440      // If the disk is already mounted, unmount it.
441      if (!disk->mount_path().empty()) {
442        disk_mount_manager_->UnmountPath(
443            disk->mount_path(),
444            chromeos::UNMOUNT_OPTIONS_LAZY,
445            chromeos::disks::DiskMountManager::UnmountPathCallback());
446      }
447
448      // Notify to observers.
449      FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
450                        OnDiskRemoved(*disk));
451      return;
452
453    case chromeos::disks::DiskMountManager::DISK_CHANGED:
454      DVLOG(1) << "Ignore CHANGED event.";
455      return;
456  }
457  NOTREACHED();
458}
459
460void VolumeManager::OnDeviceEvent(
461    chromeos::disks::DiskMountManager::DeviceEvent event,
462    const std::string& device_path) {
463  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
464  DVLOG(1) << "OnDeviceEvent: " << event << ", " << device_path;
465
466  switch (event) {
467    case chromeos::disks::DiskMountManager::DEVICE_ADDED:
468      FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
469                        OnDeviceAdded(device_path));
470      return;
471    case chromeos::disks::DiskMountManager::DEVICE_REMOVED:
472      FOR_EACH_OBSERVER(VolumeManagerObserver, observers_,
473                        OnDeviceRemoved(device_path));
474      return;
475    case chromeos::disks::DiskMountManager::DEVICE_SCANNED:
476      DVLOG(1) << "Ignore SCANNED event: " << device_path;
477      return;
478  }
479  NOTREACHED();
480}
481
482void VolumeManager::OnMountEvent(
483    chromeos::disks::DiskMountManager::MountEvent event,
484    chromeos::MountError error_code,
485    const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
486  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
487  DCHECK_NE(chromeos::MOUNT_TYPE_INVALID, mount_info.mount_type);
488
489  if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
490    // If the file is not mounted now, tell it to drive file system so that
491    // it can handle file caching correctly.
492    // Note that drive file system knows if the file is managed by drive file
493    // system or not, so here we report all paths.
494    if ((event == chromeos::disks::DiskMountManager::MOUNTING &&
495         error_code != chromeos::MOUNT_ERROR_NONE) ||
496        (event == chromeos::disks::DiskMountManager::UNMOUNTING &&
497         error_code == chromeos::MOUNT_ERROR_NONE)) {
498      drive::FileSystemInterface* file_system =
499          drive::util::GetFileSystemByProfile(profile_);
500      if (file_system) {
501        file_system->MarkCacheFileAsUnmounted(
502            base::FilePath(mount_info.source_path),
503            base::Bind(&OnMarkCacheFileAsUnmounted));
504      }
505    }
506  }
507
508  // Notify a mounting/unmounting event to observers.
509  const chromeos::disks::DiskMountManager::Disk* disk =
510      disk_mount_manager_->FindDiskBySourcePath(mount_info.source_path);
511  VolumeInfo volume_info =
512      CreateVolumeInfoFromMountPointInfo(mount_info, disk);
513  switch (event) {
514    case chromeos::disks::DiskMountManager::MOUNTING: {
515      bool is_remounting =
516          disk && mounted_disk_monitor_->DiskIsRemounting(*disk);
517      DoMountEvent(error_code, volume_info, is_remounting);
518      return;
519    }
520    case chromeos::disks::DiskMountManager::UNMOUNTING:
521      DoUnmountEvent(error_code, volume_info);
522      return;
523  }
524  NOTREACHED();
525}
526
527void VolumeManager::OnFormatEvent(
528    chromeos::disks::DiskMountManager::FormatEvent event,
529    chromeos::FormatError error_code,
530    const std::string& device_path) {
531  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
532  DVLOG(1) << "OnDeviceEvent: " << event << ", " << error_code
533           << ", " << device_path;
534
535  switch (event) {
536    case chromeos::disks::DiskMountManager::FORMAT_STARTED:
537      FOR_EACH_OBSERVER(
538          VolumeManagerObserver, observers_,
539          OnFormatStarted(device_path,
540                          error_code == chromeos::FORMAT_ERROR_NONE));
541      return;
542    case chromeos::disks::DiskMountManager::FORMAT_COMPLETED:
543      if (error_code == chromeos::FORMAT_ERROR_NONE) {
544        // If format is completed successfully, try to mount the device.
545        // MountPath auto-detects filesystem format if second argument is
546        // empty. The third argument (mount label) is not used in a disk mount
547        // operation.
548        disk_mount_manager_->MountPath(
549            device_path, std::string(), std::string(),
550            chromeos::MOUNT_TYPE_DEVICE);
551      }
552
553      FOR_EACH_OBSERVER(
554          VolumeManagerObserver, observers_,
555          OnFormatCompleted(device_path,
556                            error_code == chromeos::FORMAT_ERROR_NONE));
557
558      return;
559  }
560  NOTREACHED();
561}
562
563void VolumeManager::OnExternalStorageDisabledChanged() {
564  // If the policy just got disabled we have to unmount every device currently
565  // mounted. The opposite is fine - we can let the user re-plug her device to
566  // make it available.
567  if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
568    // We do not iterate on mount_points directly, because mount_points can
569    // be changed by UnmountPath().
570    // TODO(hidehiko): Is it necessary to unmount mounted archives, too, here?
571    while (!disk_mount_manager_->mount_points().empty()) {
572      std::string mount_path =
573          disk_mount_manager_->mount_points().begin()->second.mount_path;
574      disk_mount_manager_->UnmountPath(
575          mount_path,
576          chromeos::UNMOUNT_OPTIONS_NONE,
577          chromeos::disks::DiskMountManager::UnmountPathCallback());
578    }
579  }
580}
581
582void VolumeManager::OnPrivetVolumesAvailable(
583    const local_discovery::PrivetVolumeLister::VolumeList& volumes) {
584  for (local_discovery::PrivetVolumeLister::VolumeList::const_iterator i =
585           volumes.begin(); i != volumes.end(); i++) {
586    VolumeInfo volume_info = CreatePrivetVolumeInfo(*i);
587    DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false);
588  }
589}
590
591void VolumeManager::DoMountEvent(chromeos::MountError error_code,
592                                 const VolumeInfo& volume_info,
593                                 bool is_remounting) {
594  // Archive files are mounted globally in system. We however don't want to show
595  // archives from profile-specific folders (Drive/Downloads) of other users in
596  // multi-profile session. To this end, we filter out archives not on the
597  // volumes already mounted on this VolumeManager instance.
598  if (volume_info.type == VOLUME_TYPE_MOUNTED_ARCHIVE_FILE) {
599    // Source may be in Drive cache folder under the current profile directory.
600    bool from_current_profile =
601        profile_->GetPath().IsParent(volume_info.source_path);
602    for (std::map<std::string, VolumeInfo>::const_iterator iter =
603             mounted_volumes_.begin();
604         !from_current_profile && iter != mounted_volumes_.end();
605         ++iter) {
606      if (iter->second.mount_path.IsParent(volume_info.source_path))
607        from_current_profile = true;
608    }
609    if (!from_current_profile)
610      return;
611  }
612
613  if (error_code == chromeos::MOUNT_ERROR_NONE || volume_info.mount_condition)
614    mounted_volumes_[volume_info.volume_id] = volume_info;
615
616  FOR_EACH_OBSERVER(VolumeManagerObserver,
617                    observers_,
618                    OnVolumeMounted(error_code, volume_info, is_remounting));
619}
620
621void VolumeManager::DoUnmountEvent(chromeos::MountError error_code,
622                                   const VolumeInfo& volume_info) {
623  if (mounted_volumes_.find(volume_info.volume_id) == mounted_volumes_.end())
624    return;
625  if (error_code == chromeos::MOUNT_ERROR_NONE)
626    mounted_volumes_.erase(volume_info.volume_id);
627
628  FOR_EACH_OBSERVER(VolumeManagerObserver,
629                    observers_,
630                    OnVolumeUnmounted(error_code, volume_info));
631}
632
633}  // namespace file_manager
634