1 // Copyright (c) 2012 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 "chromeos/disks/disk_mount_manager.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <map>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/barrier_closure.h"
17 #include "base/bind.h"
18 #include "base/callback_helpers.h"
19 #include "base/logging.h"
20 #include "base/macros.h"
21 #include "base/memory/ptr_util.h"
22 #include "base/memory/weak_ptr.h"
23 #include "base/metrics/histogram_functions.h"
24 #include "base/observer_list.h"
25 #include "base/strings/string_util.h"
26 #include "chromeos/constants/chromeos_features.h"
27 #include "chromeos/dbus/cros_disks_client.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/disks/disk.h"
30 #include "chromeos/disks/suspend_unmount_manager.h"
31 
32 namespace chromeos {
33 namespace disks {
34 
35 namespace {
36 
37 constexpr char kDeviceNotFound[] = "Device could not be found";
38 DiskMountManager* g_disk_mount_manager = NULL;
39 
40 struct UnmountDeviceRecursivelyCallbackData {
UnmountDeviceRecursivelyCallbackDatachromeos::disks::__anon9c9ac18c0111::UnmountDeviceRecursivelyCallbackData41   UnmountDeviceRecursivelyCallbackData(
42       DiskMountManager::UnmountDeviceRecursivelyCallbackType in_callback)
43       : callback(std::move(in_callback)) {}
44 
45   DiskMountManager::UnmountDeviceRecursivelyCallbackType callback;
46   MountError error_code = MOUNT_ERROR_NONE;
47 };
48 
OnAllUnmountDeviceRecursively(std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data)49 void OnAllUnmountDeviceRecursively(
50     std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data) {
51   std::move(cb_data->callback).Run(cb_data->error_code);
52 }
53 
FormatFileSystemTypeToString(FormatFileSystemType filesystem)54 std::string FormatFileSystemTypeToString(FormatFileSystemType filesystem) {
55   switch (filesystem) {
56     case FormatFileSystemType::kUnknown:
57       return "";
58     case FormatFileSystemType::kVfat:
59       return "vfat";
60     case FormatFileSystemType::kExfat:
61       return "exfat";
62     case FormatFileSystemType::kNtfs:
63       return "ntfs";
64   }
65   NOTREACHED() << "Unknown filesystem type " << static_cast<int>(filesystem);
66   return "";
67 }
68 
69 // The DiskMountManager implementation.
70 class DiskMountManagerImpl : public DiskMountManager,
71                              public CrosDisksClient::Observer {
72  public:
DiskMountManagerImpl()73   DiskMountManagerImpl() : already_refreshed_(false) {
74     DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
75     cros_disks_client_ = dbus_thread_manager->GetCrosDisksClient();
76     suspend_unmount_manager_.reset(new SuspendUnmountManager(this));
77 
78     cros_disks_client_->AddObserver(this);
79   }
80 
~DiskMountManagerImpl()81   ~DiskMountManagerImpl() override { cros_disks_client_->RemoveObserver(this); }
82 
83   // DiskMountManager override.
AddObserver(DiskMountManager::Observer * observer)84   void AddObserver(DiskMountManager::Observer* observer) override {
85     observers_.AddObserver(observer);
86   }
87 
88   // DiskMountManager override.
RemoveObserver(DiskMountManager::Observer * observer)89   void RemoveObserver(DiskMountManager::Observer* observer) override {
90     observers_.RemoveObserver(observer);
91   }
92 
93   // DiskMountManager override.
MountPath(const std::string & source_path,const std::string & source_format,const std::string & mount_label,const std::vector<std::string> & mount_options,MountType type,MountAccessMode access_mode)94   void MountPath(const std::string& source_path,
95                  const std::string& source_format,
96                  const std::string& mount_label,
97                  const std::vector<std::string>& mount_options,
98                  MountType type,
99                  MountAccessMode access_mode) override {
100     // Hidden and non-existent devices should not be mounted.
101     if (type == MOUNT_TYPE_DEVICE) {
102       DiskMap::const_iterator it = disks_.find(source_path);
103       if (it == disks_.end() || it->second->is_hidden()) {
104         OnMountCompleted(MountEntry(MOUNT_ERROR_INTERNAL, source_path, type,
105                                     ""));
106         return;
107       }
108     }
109     std::vector<std::string> options = mount_options;
110     if (base::FeatureList::IsEnabled(chromeos::features::kFsNosymfollow))
111       options.push_back("nosymfollow");
112     cros_disks_client_->Mount(
113         source_path, source_format, mount_label, options, access_mode,
114         REMOUNT_OPTION_MOUNT_NEW_DEVICE,
115         base::BindOnce(&DiskMountManagerImpl::OnMount,
116                        weak_ptr_factory_.GetWeakPtr(), source_path, type));
117 
118     // Record the access mode option passed to CrosDisks.
119     // This is needed because CrosDisks service methods doesn't return the info
120     // via DBus.
121     access_modes_.insert(std::make_pair(source_path, access_mode));
122   }
123 
124   // DiskMountManager override.
UnmountPath(const std::string & mount_path,UnmountPathCallback callback)125   void UnmountPath(const std::string& mount_path,
126                    UnmountPathCallback callback) override {
127     UnmountChildMounts(mount_path);
128     cros_disks_client_->Unmount(
129         mount_path, base::BindOnce(&DiskMountManagerImpl::OnUnmountPath,
130                                    weak_ptr_factory_.GetWeakPtr(),
131                                    std::move(callback), mount_path));
132   }
133 
RemountAllRemovableDrives(MountAccessMode mode)134   void RemountAllRemovableDrives(MountAccessMode mode) override {
135     // TODO(yamaguchi): Retry for tentative remount failures. crbug.com/661455
136     for (const auto& device_path_and_disk : disks_) {
137       const Disk& disk = *device_path_and_disk.second;
138       if (disk.is_read_only_hardware()) {
139         // Read-only devices can be mounted in RO mode only. No need to remount.
140         continue;
141       }
142       if (!disk.is_mounted()) {
143         continue;
144       }
145       RemountRemovableDrive(disk, mode);
146     }
147   }
148 
149   // DiskMountManager override.
FormatMountedDevice(const std::string & mount_path,FormatFileSystemType filesystem,const std::string & label)150   void FormatMountedDevice(const std::string& mount_path,
151                            FormatFileSystemType filesystem,
152                            const std::string& label) override {
153     MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
154     if (mount_point == mount_points_.end()) {
155       LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
156       // We can't call OnFormatCompleted until |pending_format_changes_| has
157       // been populated.
158       NotifyFormatStatusUpdate(FORMAT_COMPLETED, FORMAT_ERROR_UNKNOWN,
159                                mount_path, label);
160       return;
161     }
162 
163     std::string device_path = mount_point->second.source_path;
164     const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
165     pending_format_changes_[device_path] = {filesystem_str, label};
166 
167     DiskMap::const_iterator disk = disks_.find(device_path);
168     if (disk == disks_.end()) {
169       LOG(ERROR) << "Device with path \"" << device_path << "\" not found.";
170       OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
171       return;
172     }
173     if (disk->second->is_read_only()) {
174       LOG(ERROR) << "Device with path \"" << device_path << "\" is read-only.";
175       OnFormatCompleted(FORMAT_ERROR_DEVICE_NOT_ALLOWED, device_path);
176       return;
177     }
178 
179     if (filesystem == FormatFileSystemType::kUnknown) {
180       LOG(ERROR) << "Unknown filesystem passed to FormatMountedDevice";
181       OnFormatCompleted(FORMAT_ERROR_UNSUPPORTED_FILESYSTEM, device_path);
182       return;
183     }
184 
185     UnmountPath(disk->second->mount_path(),
186                 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
187                                weak_ptr_factory_.GetWeakPtr(), device_path,
188                                filesystem, label));
189   }
190 
191   // DiskMountManager override.
SinglePartitionFormatDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)192   void SinglePartitionFormatDevice(const std::string& device_path,
193                                    FormatFileSystemType filesystem,
194                                    const std::string& label) override {
195     DiskMap::const_iterator disk_iter = disks_.find(device_path);
196     if (disk_iter == disks_.end()) {
197       LOG(ERROR) << "Device with path \"" << device_path << "\" not found.";
198       OnPartitionCompleted(device_path, filesystem, label,
199                            PARTITION_ERROR_INVALID_DEVICE_PATH);
200       return;
201     }
202 
203     UnmountDeviceRecursively(
204         device_path,
205         base::BindOnce(
206             &DiskMountManagerImpl::OnUnmountDeviceForSinglePartitionFormat,
207             weak_ptr_factory_.GetWeakPtr(), device_path, filesystem, label));
208   }
209 
RenameMountedDevice(const std::string & mount_path,const std::string & volume_name)210   void RenameMountedDevice(const std::string& mount_path,
211                            const std::string& volume_name) override {
212     MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
213     if (mount_point == mount_points_.end()) {
214       LOG(ERROR) << "Mount point with path '" << mount_path << "' not found.";
215       // We can't call OnRenameCompleted until |pending_rename_changes_| has
216       // been populated.
217       NotifyRenameStatusUpdate(RENAME_COMPLETED, RENAME_ERROR_UNKNOWN,
218                                mount_path, volume_name);
219       return;
220     }
221 
222     std::string device_path = mount_point->second.source_path;
223     pending_rename_changes_[device_path] = volume_name;
224 
225     DiskMap::const_iterator iter = disks_.find(device_path);
226     if (iter == disks_.end()) {
227       LOG(ERROR) << "Device with path '" << device_path << "' not found.";
228       OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
229       return;
230     }
231     if (iter->second->is_read_only()) {
232       LOG(ERROR) << "Device with path '" << device_path << "' is read-only.";
233       OnRenameCompleted(RENAME_ERROR_DEVICE_NOT_ALLOWED, device_path);
234       return;
235     }
236 
237     UnmountPath(iter->second->mount_path(),
238                 base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForRename,
239                                weak_ptr_factory_.GetWeakPtr(), device_path,
240                                volume_name));
241   }
242 
243   // DiskMountManager override.
UnmountDeviceRecursively(const std::string & device_path,UnmountDeviceRecursivelyCallbackType callback)244   void UnmountDeviceRecursively(
245       const std::string& device_path,
246       UnmountDeviceRecursivelyCallbackType callback) override {
247     std::vector<std::string> devices_to_unmount;
248 
249     // Get list of all devices to unmount.
250     int device_path_len = device_path.length();
251     for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) {
252       if (!it->second->mount_path().empty() &&
253           strncmp(device_path.c_str(), it->second->device_path().c_str(),
254                   device_path_len) == 0) {
255         devices_to_unmount.push_back(it->second->mount_path());
256       }
257     }
258 
259     // We should detect at least original device.
260     if (devices_to_unmount.empty()) {
261       if (disks_.find(device_path) == disks_.end()) {
262         LOG(WARNING) << "Unmount recursive request failed for device "
263                      << device_path << ", with error: " << kDeviceNotFound;
264         std::move(callback).Run(MOUNT_ERROR_INVALID_DEVICE_PATH);
265         return;
266       }
267 
268       // Nothing to unmount.
269       std::move(callback).Run(MOUNT_ERROR_NONE);
270       return;
271     }
272 
273     std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data =
274         std::make_unique<UnmountDeviceRecursivelyCallbackData>(
275             std::move(callback));
276     UnmountDeviceRecursivelyCallbackData* raw_cb_data = cb_data.get();
277     base::RepeatingClosure done_callback = base::BarrierClosure(
278         devices_to_unmount.size(),
279         base::BindOnce(&OnAllUnmountDeviceRecursively, std::move(cb_data)));
280 
281     for (size_t i = 0; i < devices_to_unmount.size(); ++i) {
282       cros_disks_client_->Unmount(
283           devices_to_unmount[i],
284           base::BindOnce(&DiskMountManagerImpl::OnUnmountDeviceRecursively,
285                          weak_ptr_factory_.GetWeakPtr(), raw_cb_data,
286                          devices_to_unmount[i], done_callback));
287     }
288   }
289 
290   // DiskMountManager override.
EnsureMountInfoRefreshed(EnsureMountInfoRefreshedCallback callback,bool force)291   void EnsureMountInfoRefreshed(EnsureMountInfoRefreshedCallback callback,
292                                 bool force) override {
293     if (!force && already_refreshed_) {
294       std::move(callback).Run(true);
295       return;
296     }
297 
298     refresh_callbacks_.push_back(std::move(callback));
299     if (refresh_callbacks_.size() == 1) {
300       // If there's no in-flight refreshing task, start it.
301       cros_disks_client_->EnumerateDevices(
302           base::BindOnce(&DiskMountManagerImpl::RefreshAfterEnumerateDevices,
303                          weak_ptr_factory_.GetWeakPtr()),
304           base::BindOnce(&DiskMountManagerImpl::RefreshCompleted,
305                          weak_ptr_factory_.GetWeakPtr(), false));
306     }
307   }
308 
309   // DiskMountManager override.
disks() const310   const DiskMap& disks() const override { return disks_; }
311 
312   // DiskMountManager override.
FindDiskBySourcePath(const std::string & source_path) const313   const Disk* FindDiskBySourcePath(
314       const std::string& source_path) const override {
315     DiskMap::const_iterator disk_it = disks_.find(source_path);
316     return disk_it == disks_.end() ? NULL : disk_it->second.get();
317   }
318 
319   // DiskMountManager override.
mount_points() const320   const MountPointMap& mount_points() const override { return mount_points_; }
321 
322   // DiskMountManager override.
AddDiskForTest(std::unique_ptr<Disk> disk)323   bool AddDiskForTest(std::unique_ptr<Disk> disk) override {
324     if (disks_.find(disk->device_path()) != disks_.end()) {
325       LOG(ERROR) << "Attempt to add a duplicate disk";
326       return false;
327     }
328 
329     disks_.insert(std::make_pair(disk->device_path(), std::move(disk)));
330     return true;
331   }
332 
333   // DiskMountManager override.
334   // Corresponding disk should be added to the manager before this is called.
AddMountPointForTest(const MountPointInfo & mount_point)335   bool AddMountPointForTest(const MountPointInfo& mount_point) override {
336     if (mount_points_.find(mount_point.mount_path) != mount_points_.end()) {
337       LOG(ERROR) << "Attempt to add a duplicate mount point";
338       return false;
339     }
340     if (mount_point.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
341         disks_.find(mount_point.source_path) == disks_.end()) {
342       LOG(ERROR) << "Device mount points must have a disk entry.";
343       return false;
344     }
345 
346     mount_points_.insert(std::make_pair(mount_point.mount_path, mount_point));
347     return true;
348   }
349 
350  private:
351   // A struct to represent information about a format changes.
352   struct FormatChange {
353     // new file system type
354     std::string file_system_type;
355     // New volume name
356     std::string volume_name;
357   };
358 
359   // Stores new volume name and file system type for a device on which
360   // formatting is invoked on, so that OnFormatCompleted can set it back to
361   // |disks_|. The key is a device_path and the value is a FormatChange.
362   std::map<std::string, FormatChange> pending_format_changes_;
363 
364   // Stores device path are being partitioning.
365   // It allows preventing auto-mount of the disks in this set.
366   std::set<std::string> pending_partitioning_disks_;
367 
368   // Stores new volume name for a device on which renaming is invoked on, so
369   // that OnRenameCompleted can set it back to |disks_|. The key is a
370   // device_path and the value is new volume_name.
371   std::map<std::string, std::string> pending_rename_changes_;
372 
373   // Called on D-Bus CrosDisksClient::Mount() is done.
OnMount(const std::string & source_path,MountType type,bool result)374   void OnMount(const std::string& source_path, MountType type, bool result) {
375     // When succeeds, OnMountCompleted will be called by "MountCompleted",
376     // signal instead. Do nothing now.
377     if (result)
378       return;
379 
380     OnMountCompleted(
381         MountEntry(MOUNT_ERROR_INTERNAL, source_path, type, std::string()));
382   }
383 
RemountRemovableDrive(const Disk & disk,MountAccessMode access_mode)384   void RemountRemovableDrive(const Disk& disk,
385                              MountAccessMode access_mode) {
386     const std::string& mount_path = disk.mount_path();
387     MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
388     if (mount_point == mount_points_.end()) {
389       // Not in mount_points_. This happens when the mount_points and disks_ are
390       // inconsistent.
391       LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
392       OnMountCompleted(
393           MountEntry(MOUNT_ERROR_PATH_NOT_MOUNTED, disk.device_path(),
394                      MOUNT_TYPE_DEVICE, mount_path));
395       return;
396     }
397     const std::string& source_path = mount_point->second.source_path;
398 
399     // Update the access mode option passed to CrosDisks.
400     // This is needed because CrosDisks service methods doesn't return the info
401     // via DBus, and must be updated before issuing Mount command as it'll be
402     // read by the handler of MountCompleted signal.
403     access_modes_[source_path] = access_mode;
404 
405     cros_disks_client_->Mount(
406         mount_point->second.source_path, std::string(), std::string(), {},
407         access_mode, REMOUNT_OPTION_REMOUNT_EXISTING_DEVICE,
408         base::BindOnce(&DiskMountManagerImpl::OnMount,
409                        weak_ptr_factory_.GetWeakPtr(), source_path,
410                        mount_point->second.mount_type));
411   }
412 
413   // Unmounts all mount points whose source path is transitively parented by
414   // |mount_path|.
UnmountChildMounts(const std::string & mount_path_in)415   void UnmountChildMounts(const std::string& mount_path_in) {
416     std::string mount_path = mount_path_in;
417     // Let's make sure mount path has trailing slash.
418     if (mount_path.back() != '/')
419       mount_path += '/';
420 
421     for (MountPointMap::iterator it = mount_points_.begin();
422          it != mount_points_.end();
423          ++it) {
424       if (base::StartsWith(it->second.source_path, mount_path,
425                            base::CompareCase::SENSITIVE)) {
426         // TODO(tbarzic): Handle the case where this fails.
427         UnmountPath(it->second.mount_path, UnmountPathCallback());
428       }
429     }
430   }
431 
432   // Callback for UnmountDeviceRecursively.
OnUnmountDeviceRecursively(UnmountDeviceRecursivelyCallbackData * cb_data,const std::string & mount_path,base::OnceClosure done_callback,MountError error_code)433   void OnUnmountDeviceRecursively(UnmountDeviceRecursivelyCallbackData* cb_data,
434                                   const std::string& mount_path,
435                                   base::OnceClosure done_callback,
436                                   MountError error_code) {
437     if (error_code == MOUNT_ERROR_PATH_NOT_MOUNTED ||
438         error_code == MOUNT_ERROR_INVALID_PATH) {
439       // The path was already unmounted by something else.
440       error_code = MOUNT_ERROR_NONE;
441     }
442 
443     if (error_code == MOUNT_ERROR_NONE) {
444       // Do standard processing for Unmount event.
445       OnUnmountPath(UnmountPathCallback(), mount_path, MOUNT_ERROR_NONE);
446       VLOG(1) << mount_path <<  " unmounted.";
447     } else {
448       // This causes the last non-success error to be reported.
449       // TODO(amistry): We could ignore certain errors such as
450       // MOUNT_ERROR_PATH_NOT_MOUNTED, or prioritise more important ones.
451       cb_data->error_code = error_code;
452     }
453 
454     std::move(done_callback).Run();
455   }
456 
457   // CrosDisksClient::Observer override.
OnMountCompleted(const MountEntry & entry)458   void OnMountCompleted(const MountEntry& entry) override {
459     MountCondition mount_condition = MOUNT_CONDITION_NONE;
460     if (entry.mount_type() == MOUNT_TYPE_DEVICE) {
461       if (entry.error_code() == MOUNT_ERROR_UNKNOWN_FILESYSTEM) {
462         mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM;
463       }
464       if (entry.error_code() == MOUNT_ERROR_UNSUPPORTED_FILESYSTEM) {
465         mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
466       }
467     }
468     const MountPointInfo mount_info(entry.source_path(),
469                                     entry.mount_path(),
470                                     entry.mount_type(),
471                                     mount_condition);
472 
473     // If the device is corrupted but it's still possible to format it, it will
474     // be fake mounted.
475     if ((entry.error_code() == MOUNT_ERROR_NONE ||
476          mount_info.mount_condition) &&
477         mount_points_.find(mount_info.mount_path) == mount_points_.end()) {
478       mount_points_.insert(MountPointMap::value_type(mount_info.mount_path,
479                                                      mount_info));
480     }
481 
482     Disk* disk = nullptr;
483     if ((entry.error_code() == MOUNT_ERROR_NONE ||
484          mount_info.mount_condition) &&
485         mount_info.mount_type == MOUNT_TYPE_DEVICE &&
486         !mount_info.source_path.empty() &&
487         !mount_info.mount_path.empty()) {
488       DiskMap::iterator iter = disks_.find(mount_info.source_path);
489       if (iter != disks_.end()) {  // disk might have been removed by now?
490         disk = iter->second.get();
491         DCHECK(disk);
492         // Currently the MountCompleted signal doesn't tell whether the device
493         // is mounted in read-only mode or not. Instead use the mount option
494         // recorded by DiskMountManagerImpl::MountPath().
495         // |source_path| should be same as |disk->device_path| because
496         // |VolumeManager::OnDiskEvent()| passes the latter to cros-disks as a
497         // source path when mounting a device.
498         AccessModeMap::iterator it = access_modes_.find(entry.source_path());
499 
500         // Store whether the disk was mounted in read-only mode due to a policy.
501         disk->set_write_disabled_by_policy(
502             it != access_modes_.end() && !disk->is_read_only_hardware()
503                 && it->second == MOUNT_ACCESS_MODE_READ_ONLY);
504         disk->SetMountPath(mount_info.mount_path);
505         // Only set the mount path if the disk is actually mounted. Right now, a
506         // number of code paths (format, rename, unmount) rely on the mount path
507         // being set even if the disk isn't mounted. cros-disks also does some
508         // tracking of non-mounted mount paths. Making this change is
509         // non-trivial.
510         // TODO(amistry): Change these code paths to use device path instead of
511         // mount path.
512         disk->set_mounted(entry.error_code() == MOUNT_ERROR_NONE);
513       }
514     }
515     // Observers may read the values of disks_. So notify them after tweaking
516     // values of disks_.
517     NotifyMountStatusUpdate(MOUNTING, entry.error_code(), mount_info);
518     if (disk) {
519       disk->set_is_first_mount(false);
520     }
521   }
522 
523   // Callback for UnmountPath.
OnUnmountPath(UnmountPathCallback callback,const std::string & mount_path,MountError error_code)524   void OnUnmountPath(UnmountPathCallback callback,
525                      const std::string& mount_path,
526                      MountError error_code) {
527     MountPointMap::iterator mount_points_it = mount_points_.find(mount_path);
528     if (mount_points_it == mount_points_.end()) {
529       // The path was unmounted, but not as a result of this unmount request,
530       // so return error.
531       if (!callback.is_null())
532         std::move(callback).Run(MOUNT_ERROR_INTERNAL);
533       return;
534     }
535 
536     if (error_code == MOUNT_ERROR_PATH_NOT_MOUNTED ||
537         error_code == MOUNT_ERROR_INVALID_PATH) {
538       // The path was already unmounted by something else.
539       error_code = MOUNT_ERROR_NONE;
540     }
541 
542     NotifyMountStatusUpdate(
543         UNMOUNTING, error_code,
544         MountPointInfo(mount_points_it->second.source_path,
545                        mount_points_it->second.mount_path,
546                        mount_points_it->second.mount_type,
547                        mount_points_it->second.mount_condition));
548 
549     std::string path(mount_points_it->second.source_path);
550 
551     if (error_code == MOUNT_ERROR_NONE)
552       mount_points_.erase(mount_points_it);
553 
554     DiskMap::iterator disk_iter = disks_.find(path);
555     if (disk_iter != disks_.end()) {
556       DCHECK(disk_iter->second);
557       if (error_code == MOUNT_ERROR_NONE) {
558         disk_iter->second->clear_mount_path();
559         disk_iter->second->set_mounted(false);
560       }
561     }
562 
563     if (!callback.is_null())
564       std::move(callback).Run(error_code);
565   }
566 
OnUnmountPathForFormat(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,MountError error_code)567   void OnUnmountPathForFormat(const std::string& device_path,
568                               FormatFileSystemType filesystem,
569                               const std::string& label,
570                               MountError error_code) {
571     if (error_code == MOUNT_ERROR_NONE &&
572         disks_.find(device_path) != disks_.end()) {
573       FormatUnmountedDevice(device_path, filesystem, label);
574     } else {
575       OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
576     }
577   }
578 
OnUnmountDeviceForSinglePartitionFormat(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,MountError error_code)579   void OnUnmountDeviceForSinglePartitionFormat(const std::string& device_path,
580                                                FormatFileSystemType filesystem,
581                                                const std::string& label,
582                                                MountError error_code) {
583     if (error_code != MOUNT_ERROR_NONE ||
584         disks_.find(device_path) == disks_.end()) {
585       OnPartitionCompleted(device_path, filesystem, label,
586                            PARTITION_ERROR_UNKNOWN);
587       return;
588     }
589 
590     SinglePartitionFormatUnmountedDevice(device_path, filesystem, label);
591   }
592 
593   // Starts device formatting.
FormatUnmountedDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)594   void FormatUnmountedDevice(const std::string& device_path,
595                              FormatFileSystemType filesystem,
596                              const std::string& label) {
597     DiskMap::const_iterator disk = disks_.find(device_path);
598     DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
599 
600     base::UmaHistogramEnumeration("FileBrowser.FormatFileSystemType",
601                                   filesystem);
602 
603     cros_disks_client_->Format(
604         device_path, FormatFileSystemTypeToString(filesystem), label,
605         base::BindOnce(&DiskMountManagerImpl::OnFormatStarted,
606                        weak_ptr_factory_.GetWeakPtr(), device_path, label));
607   }
608 
609   // Callback for Format.
OnFormatStarted(const std::string & device_path,const std::string & device_label,bool success)610   void OnFormatStarted(const std::string& device_path,
611                        const std::string& device_label,
612                        bool success) {
613     if (!success) {
614       OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
615       return;
616     }
617 
618     NotifyFormatStatusUpdate(FORMAT_STARTED, FORMAT_ERROR_NONE, device_path,
619                              device_label);
620   }
621 
622   // CrosDisksClient::Observer override.
OnFormatCompleted(FormatError error_code,const std::string & device_path)623   void OnFormatCompleted(FormatError error_code,
624                          const std::string& device_path) override {
625     std::string device_label;
626     auto pending_change = pending_format_changes_.find(device_path);
627     if (pending_change != pending_format_changes_.end()) {
628       device_label = pending_change->second.volume_name;
629     }
630 
631     auto iter = disks_.find(device_path);
632 
633     // disk might have been removed by now?
634     if (iter != disks_.end()) {
635       Disk* disk = iter->second.get();
636       DCHECK(disk);
637 
638       if (pending_change != pending_format_changes_.end() &&
639           error_code == FORMAT_ERROR_NONE) {
640         disk->set_device_label(pending_change->second.volume_name);
641         disk->set_file_system_type(pending_change->second.file_system_type);
642       }
643     }
644 
645     pending_format_changes_.erase(device_path);
646 
647     EnsureMountInfoRefreshed(base::DoNothing(), true /* force */);
648 
649     NotifyFormatStatusUpdate(FORMAT_COMPLETED, error_code, device_path,
650                              device_label);
651   }
652 
SinglePartitionFormatUnmountedDevice(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label)653   void SinglePartitionFormatUnmountedDevice(const std::string& device_path,
654                                             FormatFileSystemType filesystem,
655                                             const std::string& label) {
656     DiskMap::const_iterator disk = disks_.find(device_path);
657     DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
658 
659     pending_partitioning_disks_.insert(disk->second->device_path());
660 
661     NotifyPartitionStatusUpdate(PARTITION_STARTED, PARTITION_ERROR_NONE,
662                                 device_path, label);
663 
664     cros_disks_client_->SinglePartitionFormat(
665         disk->second->file_path(),
666         base::BindOnce(&DiskMountManagerImpl::OnPartitionCompleted,
667                        weak_ptr_factory_.GetWeakPtr(), device_path, filesystem,
668                        label));
669   }
670 
OnPartitionCompleted(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,PartitionError error_code)671   void OnPartitionCompleted(const std::string& device_path,
672                             FormatFileSystemType filesystem,
673                             const std::string& label,
674                             PartitionError error_code) {
675     auto iter = disks_.find(device_path);
676 
677     // disk might have been removed by now?
678     if (iter != disks_.end()) {
679       Disk* disk = iter->second.get();
680       DCHECK(disk);
681 
682       if (error_code == PARTITION_ERROR_NONE) {
683         EnsureMountInfoRefreshed(
684             base::BindOnce(&DiskMountManagerImpl::OnRefreshAfterPartition,
685                            weak_ptr_factory_.GetWeakPtr(), device_path,
686                            filesystem, label),
687             true /* force */);
688       }
689 
690     } else {
691       // Remove disk from pending partitioning list if disk removed.
692       pending_partitioning_disks_.erase(iter->second->device_path());
693     }
694 
695     NotifyPartitionStatusUpdate(PARTITION_COMPLETED, error_code, device_path,
696                                 label);
697   }
698 
OnRefreshAfterPartition(const std::string & device_path,FormatFileSystemType filesystem,const std::string & label,bool success)699   void OnRefreshAfterPartition(const std::string& device_path,
700                                FormatFileSystemType filesystem,
701                                const std::string& label,
702                                bool success) {
703     DiskMap::const_iterator device_disk = disks_.find(device_path);
704     if (device_disk == disks_.end()) {
705       LOG(ERROR) << "Device not found, maybe ejected";
706       pending_partitioning_disks_.erase(device_path);
707       NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
708                                   PARTITION_ERROR_INVALID_DEVICE_PATH,
709                                   device_path, label);
710       return;
711     }
712 
713     std::string new_partition_device_path;
714     // Find new partition using common storage path with parent device.
715     for (DiskMountManager::DiskMap::const_iterator it = disks_.begin();
716          it != disks_.end(); ++it) {
717       if (it->second->storage_device_path() ==
718               device_disk->second->storage_device_path() &&
719           !it->second->is_parent()) {
720         new_partition_device_path = it->second->device_path();
721         break;
722       }
723     }
724 
725     if (new_partition_device_path.empty()) {
726       LOG(ERROR) << "New partition couldn't be found";
727       pending_partitioning_disks_.erase(device_path);
728       NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
729                                   PARTITION_ERROR_INVALID_DEVICE_PATH,
730                                   device_path, label);
731       return;
732     }
733 
734     const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
735     pending_format_changes_[new_partition_device_path] = {filesystem_str,
736                                                           label};
737 
738     // It's expected the disks (parent device and new partition) are not
739     // mounted, but try unmounting before starting format if it got
740     // mounted through another flow.
741     UnmountDeviceRecursively(
742         device_path,
743         base::BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
744                        weak_ptr_factory_.GetWeakPtr(),
745                        new_partition_device_path, filesystem, label));
746 
747     // It's ok to remove it from pending partitioning as format flow started.
748     pending_partitioning_disks_.erase(device_path);
749   }
750 
OnUnmountPathForRename(const std::string & device_path,const std::string & volume_name,MountError error_code)751   void OnUnmountPathForRename(const std::string& device_path,
752                               const std::string& volume_name,
753                               MountError error_code) {
754     if (error_code != MOUNT_ERROR_NONE ||
755         disks_.find(device_path) == disks_.end()) {
756       OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
757       return;
758     }
759 
760     RenameUnmountedDevice(device_path, volume_name);
761   }
762 
763   // Start device renaming
RenameUnmountedDevice(const std::string & device_path,const std::string & volume_name)764   void RenameUnmountedDevice(const std::string& device_path,
765                              const std::string& volume_name) {
766     DiskMap::const_iterator disk = disks_.find(device_path);
767     DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
768 
769     cros_disks_client_->Rename(
770         device_path, volume_name,
771         base::BindOnce(&DiskMountManagerImpl::OnRenameStarted,
772                        weak_ptr_factory_.GetWeakPtr(), device_path,
773                        volume_name));
774   }
775 
776   // Callback for Rename.
OnRenameStarted(const std::string & device_path,const std::string & volume_name,bool success)777   void OnRenameStarted(const std::string& device_path,
778                        const std::string& volume_name,
779                        bool success) {
780     if (!success) {
781       OnRenameCompleted(RENAME_ERROR_UNKNOWN, device_path);
782       return;
783     }
784 
785     NotifyRenameStatusUpdate(RENAME_STARTED, RENAME_ERROR_NONE, device_path,
786                              volume_name);
787   }
788 
789   // CrosDisksClient::Observer override.
OnRenameCompleted(RenameError error_code,const std::string & device_path)790   void OnRenameCompleted(RenameError error_code,
791                          const std::string& device_path) override {
792     std::string device_label;
793     auto pending_change = pending_rename_changes_.find(device_path);
794     if (pending_change != pending_rename_changes_.end()) {
795       device_label = pending_change->second;
796     }
797 
798     auto iter = disks_.find(device_path);
799 
800     // disk might have been removed by now?
801     if (iter != disks_.end()) {
802       Disk* disk = iter->second.get();
803       DCHECK(disk);
804 
805       if (pending_change != pending_rename_changes_.end() &&
806           error_code == RENAME_ERROR_NONE)
807         disk->set_device_label(pending_change->second);
808     }
809 
810     pending_rename_changes_.erase(device_path);
811 
812     NotifyRenameStatusUpdate(RENAME_COMPLETED, error_code, device_path,
813                              device_label);
814   }
815 
816   // Callback for GetDeviceProperties.
OnGetDeviceProperties(const DiskInfo & disk_info)817   void OnGetDeviceProperties(const DiskInfo& disk_info) {
818     if (disk_info.is_virtual())
819       return;
820 
821     DVLOG(1) << "Found disk " << disk_info.device_path();
822     // Delete previous disk info for this path:
823     bool is_new = true;
824     bool is_first_mount = false;
825     std::string base_mount_path = std::string();
826     DiskMap::iterator iter = disks_.find(disk_info.device_path());
827     if (iter != disks_.end()) {
828       is_first_mount = iter->second->is_first_mount();
829       base_mount_path = iter->second->base_mount_path();
830       disks_.erase(iter);
831       is_new = false;
832     }
833 
834     // If the device was mounted by the instance, apply recorded parameter.
835     // Otherwise, default to false.
836     // Lookup by |device_path| which we pass to cros-disks when mounting a
837     // device in |VolumeManager::OnDiskEvent()|.
838     auto access_mode = access_modes_.find(disk_info.device_path());
839     bool write_disabled_by_policy = access_mode != access_modes_.end()
840         && access_mode->second == chromeos::MOUNT_ACCESS_MODE_READ_ONLY;
841     Disk* disk = new Disk(disk_info, write_disabled_by_policy,
842                           base_mount_path);
843     if (!is_new) {
844       disk->set_is_first_mount(is_first_mount);
845     }
846     disks_.insert(
847         std::make_pair(disk_info.device_path(), base::WrapUnique(disk)));
848     NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, *disk);
849   }
850 
851   // Part of EnsureMountInfoRefreshed(). Called after the list of devices are
852   // enumerated.
RefreshAfterEnumerateDevices(const std::vector<std::string> & devices)853   void RefreshAfterEnumerateDevices(const std::vector<std::string>& devices) {
854     std::set<std::string> current_device_set(devices.begin(), devices.end());
855     for (DiskMap::iterator iter = disks_.begin(); iter != disks_.end(); ) {
856       if (current_device_set.find(iter->first) == current_device_set.end()) {
857         disks_.erase(iter++);
858       } else {
859         ++iter;
860       }
861     }
862     RefreshDeviceAtIndex(devices, 0);
863   }
864 
865   // Part of EnsureMountInfoRefreshed(). Called for each device to refresh info.
RefreshDeviceAtIndex(const std::vector<std::string> & devices,size_t index)866   void RefreshDeviceAtIndex(const std::vector<std::string>& devices,
867                             size_t index) {
868     if (index == devices.size()) {
869       // All devices info retrieved. Proceed to enumerate mount point info.
870       cros_disks_client_->EnumerateMountEntries(
871           base::BindOnce(
872               &DiskMountManagerImpl::RefreshAfterEnumerateMountEntries,
873               weak_ptr_factory_.GetWeakPtr()),
874           base::BindOnce(&DiskMountManagerImpl::RefreshCompleted,
875                          weak_ptr_factory_.GetWeakPtr(), false));
876       return;
877     }
878 
879     cros_disks_client_->GetDeviceProperties(
880         devices[index],
881         base::BindOnce(&DiskMountManagerImpl::RefreshAfterGetDeviceProperties,
882                        weak_ptr_factory_.GetWeakPtr(), devices, index + 1),
883         base::BindOnce(&DiskMountManagerImpl::RefreshDeviceAtIndex,
884                        weak_ptr_factory_.GetWeakPtr(), devices, index + 1));
885   }
886 
887   // Part of EnsureMountInfoRefreshed().
RefreshAfterGetDeviceProperties(const std::vector<std::string> & devices,size_t next_index,const DiskInfo & disk_info)888   void RefreshAfterGetDeviceProperties(const std::vector<std::string>& devices,
889                                        size_t next_index,
890                                        const DiskInfo& disk_info) {
891     OnGetDeviceProperties(disk_info);
892     RefreshDeviceAtIndex(devices, next_index);
893   }
894 
895   // Part of EnsureMountInfoRefreshed(). Called after mount entries are listed.
RefreshAfterEnumerateMountEntries(const std::vector<MountEntry> & entries)896   void RefreshAfterEnumerateMountEntries(
897       const std::vector<MountEntry>& entries) {
898     for (size_t i = 0; i < entries.size(); ++i)
899       OnMountCompleted(entries[i]);
900     RefreshCompleted(true);
901   }
902 
903   // Part of EnsureMountInfoRefreshed(). Called when the refreshing is done.
RefreshCompleted(bool success)904   void RefreshCompleted(bool success) {
905     already_refreshed_ = true;
906     for (size_t i = 0; i < refresh_callbacks_.size(); ++i)
907       std::move(refresh_callbacks_[i]).Run(success);
908     refresh_callbacks_.clear();
909   }
910 
911   // CrosDisksClient::Observer override.
OnMountEvent(MountEventType event,const std::string & device_path_arg)912   void OnMountEvent(MountEventType event,
913                     const std::string& device_path_arg) override {
914     // Take a copy of the argument so we can modify it below.
915     std::string device_path = device_path_arg;
916     switch (event) {
917       case CROS_DISKS_DISK_ADDED: {
918         cros_disks_client_->GetDeviceProperties(
919             device_path,
920             base::BindOnce(&DiskMountManagerImpl::OnGetDeviceProperties,
921                            weak_ptr_factory_.GetWeakPtr()),
922             base::DoNothing());
923         break;
924       }
925       case CROS_DISKS_DISK_REMOVED: {
926         // Search and remove disks that are no longer present.
927         DiskMountManager::DiskMap::iterator iter = disks_.find(device_path);
928         if (iter != disks_.end()) {
929           Disk* disk = iter->second.get();
930           NotifyDiskStatusUpdate(DISK_REMOVED, *disk);
931           disks_.erase(iter);
932         }
933         break;
934       }
935       case CROS_DISKS_DEVICE_ADDED: {
936         NotifyDeviceStatusUpdate(DEVICE_ADDED, device_path);
937         break;
938       }
939       case CROS_DISKS_DEVICE_REMOVED: {
940         NotifyDeviceStatusUpdate(DEVICE_REMOVED, device_path);
941         break;
942       }
943       case CROS_DISKS_DEVICE_SCANNED: {
944         NotifyDeviceStatusUpdate(DEVICE_SCANNED, device_path);
945         break;
946       }
947       default: {
948         LOG(ERROR) << "Unknown event: " << event;
949       }
950     }
951   }
952 
953   // Notifies all observers about disk status update.
NotifyDiskStatusUpdate(DiskEvent event,const Disk & disk)954   void NotifyDiskStatusUpdate(DiskEvent event, const Disk& disk) {
955     for (auto& observer : observers_) {
956       // Skip mounting of new partitioned disks while waiting for the format.
957       if (IsPendingPartitioningDisk(disk.device_path())) {
958         continue;
959       }
960       disk.is_auto_mountable() ? observer.OnAutoMountableDiskEvent(event, disk)
961                                : observer.OnBootDeviceDiskEvent(event, disk);
962     }
963   }
964 
965   // Notifies all observers about device status update.
NotifyDeviceStatusUpdate(DeviceEvent event,const std::string & device_path)966   void NotifyDeviceStatusUpdate(DeviceEvent event,
967                                 const std::string& device_path) {
968     for (auto& observer : observers_)
969       observer.OnDeviceEvent(event, device_path);
970   }
971 
972   // Notifies all observers about mount completion.
NotifyMountStatusUpdate(MountEvent event,MountError error_code,const MountPointInfo & mount_info)973   void NotifyMountStatusUpdate(MountEvent event,
974                                MountError error_code,
975                                const MountPointInfo& mount_info) {
976     for (auto& observer : observers_)
977       observer.OnMountEvent(event, error_code, mount_info);
978   }
979 
NotifyFormatStatusUpdate(FormatEvent event,FormatError error_code,const std::string & device_path,const std::string & device_label)980   void NotifyFormatStatusUpdate(FormatEvent event,
981                                 FormatError error_code,
982                                 const std::string& device_path,
983                                 const std::string& device_label) {
984     for (auto& observer : observers_)
985       observer.OnFormatEvent(event, error_code, device_path, device_label);
986   }
987 
NotifyPartitionStatusUpdate(PartitionEvent event,PartitionError error_code,const std::string & device_path,const std::string & device_label)988   void NotifyPartitionStatusUpdate(PartitionEvent event,
989                                    PartitionError error_code,
990                                    const std::string& device_path,
991                                    const std::string& device_label) {
992     for (auto& observer : observers_)
993       observer.OnPartitionEvent(event, error_code, device_path, device_label);
994   }
995 
NotifyRenameStatusUpdate(RenameEvent event,RenameError error_code,const std::string & device_path,const std::string & device_label)996   void NotifyRenameStatusUpdate(RenameEvent event,
997                                 RenameError error_code,
998                                 const std::string& device_path,
999                                 const std::string& device_label) {
1000     for (auto& observer : observers_)
1001       observer.OnRenameEvent(event, error_code, device_path, device_label);
1002   }
1003 
IsPendingPartitioningDisk(const std::string & device_path)1004   bool IsPendingPartitioningDisk(const std::string& device_path) {
1005     if (pending_partitioning_disks_.find(device_path) !=
1006         pending_partitioning_disks_.end()) {
1007       return true;
1008     }
1009 
1010     // If device path doesn't match check whether if it's a child path.
1011     for (auto it = pending_partitioning_disks_.begin();
1012          it != pending_partitioning_disks_.end(); ++it) {
1013       if (base::StartsWith(device_path, *it, base::CompareCase::SENSITIVE)) {
1014         return true;
1015       }
1016     }
1017     return false;
1018   }
1019 
1020   // Mount event change observers.
1021   base::ObserverList<DiskMountManager::Observer> observers_;
1022 
1023   CrosDisksClient* cros_disks_client_;
1024 
1025   // The list of disks found.
1026   DiskMountManager::DiskMap disks_;
1027 
1028   DiskMountManager::MountPointMap mount_points_;
1029 
1030   bool already_refreshed_;
1031   std::vector<EnsureMountInfoRefreshedCallback> refresh_callbacks_;
1032 
1033   std::unique_ptr<SuspendUnmountManager> suspend_unmount_manager_;
1034 
1035   // Whether the instance attempted to mount a device in read-only mode for
1036   // each source path.
1037   typedef std::map<std::string, chromeos::MountAccessMode> AccessModeMap;
1038   AccessModeMap access_modes_;
1039 
1040   base::WeakPtrFactory<DiskMountManagerImpl> weak_ptr_factory_{this};
1041 
1042   DISALLOW_COPY_AND_ASSIGN(DiskMountManagerImpl);
1043 };
1044 
1045 }  // namespace
1046 
~Observer()1047 DiskMountManager::Observer::~Observer() {
1048   DCHECK(!IsInObserverList());
1049 }
1050 
AddDiskForTest(std::unique_ptr<Disk> disk)1051 bool DiskMountManager::AddDiskForTest(std::unique_ptr<Disk> disk) {
1052   return false;
1053 }
1054 
AddMountPointForTest(const MountPointInfo & mount_point)1055 bool DiskMountManager::AddMountPointForTest(const MountPointInfo& mount_point) {
1056   return false;
1057 }
1058 
1059 // static
MountConditionToString(MountCondition condition)1060 std::string DiskMountManager::MountConditionToString(MountCondition condition) {
1061   switch (condition) {
1062     case MOUNT_CONDITION_NONE:
1063       return "";
1064     case MOUNT_CONDITION_UNKNOWN_FILESYSTEM:
1065       return "unknown_filesystem";
1066     case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM:
1067       return "unsupported_filesystem";
1068     default:
1069       NOTREACHED();
1070   }
1071   return "";
1072 }
1073 
1074 // static
DeviceTypeToString(DeviceType type)1075 std::string DiskMountManager::DeviceTypeToString(DeviceType type) {
1076   switch (type) {
1077     case DEVICE_TYPE_USB:
1078       return "usb";
1079     case DEVICE_TYPE_SD:
1080       return "sd";
1081     case DEVICE_TYPE_OPTICAL_DISC:
1082       return "optical";
1083     case DEVICE_TYPE_MOBILE:
1084       return "mobile";
1085     default:
1086       return "unknown";
1087   }
1088 }
1089 
1090 // static
Initialize()1091 void DiskMountManager::Initialize() {
1092   if (g_disk_mount_manager) {
1093     LOG(WARNING) << "DiskMountManager was already initialized";
1094     return;
1095   }
1096   g_disk_mount_manager = new DiskMountManagerImpl();
1097   VLOG(1) << "DiskMountManager initialized";
1098 }
1099 
1100 // static
InitializeForTesting(DiskMountManager * disk_mount_manager)1101 void DiskMountManager::InitializeForTesting(
1102     DiskMountManager* disk_mount_manager) {
1103   if (g_disk_mount_manager) {
1104     LOG(WARNING) << "DiskMountManager was already initialized";
1105     return;
1106   }
1107   g_disk_mount_manager = disk_mount_manager;
1108   VLOG(1) << "DiskMountManager initialized";
1109 }
1110 
1111 // static
Shutdown()1112 void DiskMountManager::Shutdown() {
1113   if (!g_disk_mount_manager) {
1114     LOG(WARNING) << "DiskMountManager::Shutdown() called with NULL manager";
1115     return;
1116   }
1117   delete g_disk_mount_manager;
1118   g_disk_mount_manager = NULL;
1119   VLOG(1) << "DiskMountManager Shutdown completed";
1120 }
1121 
1122 // static
GetInstance()1123 DiskMountManager* DiskMountManager::GetInstance() {
1124   return g_disk_mount_manager;
1125 }
1126 
1127 }  // namespace disks
1128 }  // namespace chromeos
1129