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 <map>
8#include <set>
9
10#include "base/bind.h"
11#include "base/memory/weak_ptr.h"
12#include "base/observer_list.h"
13#include "base/stl_util.h"
14#include "base/strings/string_util.h"
15#include "chromeos/dbus/dbus_thread_manager.h"
16
17namespace chromeos {
18namespace disks {
19
20namespace {
21
22const char kDeviceNotFound[] = "Device could not be found";
23
24DiskMountManager* g_disk_mount_manager = NULL;
25
26// The DiskMountManager implementation.
27class DiskMountManagerImpl : public DiskMountManager {
28 public:
29  DiskMountManagerImpl() :
30    already_refreshed_(false),
31    weak_ptr_factory_(this) {
32    DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
33    DCHECK(dbus_thread_manager);
34    cros_disks_client_ = dbus_thread_manager->GetCrosDisksClient();
35    DCHECK(cros_disks_client_);
36    cros_disks_client_->SetMountEventHandler(
37        base::Bind(&DiskMountManagerImpl::OnMountEvent,
38                   weak_ptr_factory_.GetWeakPtr()));
39    cros_disks_client_->SetMountCompletedHandler(
40        base::Bind(&DiskMountManagerImpl::OnMountCompleted,
41                   weak_ptr_factory_.GetWeakPtr()));
42    cros_disks_client_->SetFormatCompletedHandler(
43        base::Bind(&DiskMountManagerImpl::OnFormatCompleted,
44                   weak_ptr_factory_.GetWeakPtr()));
45  }
46
47  virtual ~DiskMountManagerImpl() {
48    STLDeleteContainerPairSecondPointers(disks_.begin(), disks_.end());
49  }
50
51  // DiskMountManager override.
52  virtual void AddObserver(Observer* observer) OVERRIDE {
53    observers_.AddObserver(observer);
54  }
55
56  // DiskMountManager override.
57  virtual void RemoveObserver(Observer* observer) OVERRIDE {
58    observers_.RemoveObserver(observer);
59  }
60
61  // DiskMountManager override.
62  virtual void MountPath(const std::string& source_path,
63                         const std::string& source_format,
64                         const std::string& mount_label,
65                         MountType type) OVERRIDE {
66    // Hidden and non-existent devices should not be mounted.
67    if (type == MOUNT_TYPE_DEVICE) {
68      DiskMap::const_iterator it = disks_.find(source_path);
69      if (it == disks_.end() || it->second->is_hidden()) {
70        OnMountCompleted(MountEntry(MOUNT_ERROR_INTERNAL, source_path, type,
71                                    ""));
72        return;
73      }
74    }
75    cros_disks_client_->Mount(
76        source_path,
77        source_format,
78        mount_label,
79        // When succeeds, OnMountCompleted will be called by
80        // "MountCompleted" signal instead.
81        base::Bind(&base::DoNothing),
82        base::Bind(&DiskMountManagerImpl::OnMountCompleted,
83                   weak_ptr_factory_.GetWeakPtr(),
84                   MountEntry(MOUNT_ERROR_INTERNAL, source_path, type, "")));
85  }
86
87  // DiskMountManager override.
88  virtual void UnmountPath(const std::string& mount_path,
89                           UnmountOptions options,
90                           const UnmountPathCallback& callback) OVERRIDE {
91    UnmountChildMounts(mount_path);
92    cros_disks_client_->Unmount(mount_path, options,
93                                base::Bind(&DiskMountManagerImpl::OnUnmountPath,
94                                           weak_ptr_factory_.GetWeakPtr(),
95                                           callback,
96                                           true,
97                                           mount_path),
98                                base::Bind(&DiskMountManagerImpl::OnUnmountPath,
99                                           weak_ptr_factory_.GetWeakPtr(),
100                                           callback,
101                                           false,
102                                           mount_path));
103  }
104
105  // DiskMountManager override.
106  virtual void FormatMountedDevice(const std::string& mount_path) OVERRIDE {
107    MountPointMap::const_iterator mount_point = mount_points_.find(mount_path);
108    if (mount_point == mount_points_.end()) {
109      LOG(ERROR) << "Mount point with path \"" << mount_path << "\" not found.";
110      OnFormatCompleted(FORMAT_ERROR_UNKNOWN, mount_path);
111      return;
112    }
113
114    std::string device_path = mount_point->second.source_path;
115    DiskMap::const_iterator disk = disks_.find(device_path);
116    if (disk == disks_.end()) {
117      LOG(ERROR) << "Device with path \"" << device_path << "\" not found.";
118      OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
119      return;
120    }
121
122    UnmountPath(disk->second->mount_path(),
123                UNMOUNT_OPTIONS_NONE,
124                base::Bind(&DiskMountManagerImpl::OnUnmountPathForFormat,
125                           weak_ptr_factory_.GetWeakPtr(),
126                           device_path));
127  }
128
129  // DiskMountManager override.
130  virtual void UnmountDeviceRecursively(
131      const std::string& device_path,
132      const UnmountDeviceRecursivelyCallbackType& callback) OVERRIDE {
133    std::vector<std::string> devices_to_unmount;
134
135    // Get list of all devices to unmount.
136    int device_path_len = device_path.length();
137    for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) {
138      if (!it->second->mount_path().empty() &&
139          strncmp(device_path.c_str(), it->second->device_path().c_str(),
140                  device_path_len) == 0) {
141        devices_to_unmount.push_back(it->second->mount_path());
142      }
143    }
144
145    // We should detect at least original device.
146    if (devices_to_unmount.empty()) {
147      if (disks_.find(device_path) == disks_.end()) {
148        LOG(WARNING) << "Unmount recursive request failed for device "
149                     << device_path << ", with error: " << kDeviceNotFound;
150        callback.Run(false);
151        return;
152      }
153
154      // Nothing to unmount.
155      callback.Run(true);
156      return;
157    }
158
159    // We will send the same callback data object to all Unmount calls and use
160    // it to synchronize callbacks.
161    // Note: this implementation has a potential memory leak issue. For
162    // example if this instance is destructed before all the callbacks for
163    // Unmount are invoked, the memory pointed by |cb_data| will be leaked.
164    // It is because the UnmountDeviceRecursivelyCallbackData keeps how
165    // many times OnUnmountDeviceRecursively callback is called and when
166    // all the callbacks are called, |cb_data| will be deleted in the method.
167    // However destructing the instance before all callback invocations will
168    // cancel all pending callbacks, so that the |cb_data| would never be
169    // deleted.
170    // Fortunately, in the real scenario, the instance will be destructed
171    // only for ShutDown. So, probably the memory would rarely be leaked.
172    // TODO(hidehiko): Fix the issue.
173    UnmountDeviceRecursivelyCallbackData* cb_data =
174        new UnmountDeviceRecursivelyCallbackData(
175            callback, devices_to_unmount.size());
176    for (size_t i = 0; i < devices_to_unmount.size(); ++i) {
177      cros_disks_client_->Unmount(
178          devices_to_unmount[i],
179          UNMOUNT_OPTIONS_NONE,
180          base::Bind(&DiskMountManagerImpl::OnUnmountDeviceRecursively,
181                     weak_ptr_factory_.GetWeakPtr(),
182                     cb_data,
183                     true,
184                     devices_to_unmount[i]),
185          base::Bind(&DiskMountManagerImpl::OnUnmountDeviceRecursively,
186                     weak_ptr_factory_.GetWeakPtr(),
187                     cb_data,
188                     false,
189                     devices_to_unmount[i]));
190    }
191  }
192
193  // DiskMountManager override.
194  virtual void EnsureMountInfoRefreshed(
195      const EnsureMountInfoRefreshedCallback& callback) OVERRIDE {
196    if (already_refreshed_) {
197      callback.Run(true);
198      return;
199    }
200
201    refresh_callbacks_.push_back(callback);
202    if (refresh_callbacks_.size() == 1) {
203      // If there's no in-flight refreshing task, start it.
204      cros_disks_client_->EnumerateAutoMountableDevices(
205          base::Bind(&DiskMountManagerImpl::RefreshAfterEnumerateDevices,
206                     weak_ptr_factory_.GetWeakPtr()),
207          base::Bind(&DiskMountManagerImpl::RefreshCompleted,
208                     weak_ptr_factory_.GetWeakPtr(), false));
209    }
210  }
211
212  // DiskMountManager override.
213  virtual const DiskMap& disks() const OVERRIDE { return disks_; }
214
215  // DiskMountManager override.
216  virtual const Disk* FindDiskBySourcePath(const std::string& source_path)
217      const OVERRIDE {
218    DiskMap::const_iterator disk_it = disks_.find(source_path);
219    return disk_it == disks_.end() ? NULL : disk_it->second;
220  }
221
222  // DiskMountManager override.
223  virtual const MountPointMap& mount_points() const OVERRIDE {
224    return mount_points_;
225  }
226
227  // DiskMountManager override.
228  virtual bool AddDiskForTest(Disk* disk) OVERRIDE {
229    if (disks_.find(disk->device_path()) != disks_.end()) {
230      LOG(ERROR) << "Attempt to add a duplicate disk";
231      return false;
232    }
233
234    disks_.insert(std::make_pair(disk->device_path(), disk));
235    return true;
236  }
237
238  // DiskMountManager override.
239  // Corresponding disk should be added to the manager before this is called.
240  virtual bool AddMountPointForTest(
241      const MountPointInfo& mount_point) OVERRIDE {
242    if (mount_points_.find(mount_point.mount_path) != mount_points_.end()) {
243      LOG(ERROR) << "Attempt to add a duplicate mount point";
244      return false;
245    }
246    if (mount_point.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
247        disks_.find(mount_point.source_path) == disks_.end()) {
248      LOG(ERROR) << "Device mount points must have a disk entry.";
249      return false;
250    }
251
252    mount_points_.insert(std::make_pair(mount_point.mount_path, mount_point));
253    return true;
254  }
255
256 private:
257  struct UnmountDeviceRecursivelyCallbackData {
258    UnmountDeviceRecursivelyCallbackData(
259        const UnmountDeviceRecursivelyCallbackType& in_callback,
260        int in_num_pending_callbacks)
261        : callback(in_callback),
262          num_pending_callbacks(in_num_pending_callbacks) {
263    }
264
265    const UnmountDeviceRecursivelyCallbackType callback;
266    size_t num_pending_callbacks;
267  };
268
269  // Unmounts all mount points whose source path is transitively parented by
270  // |mount_path|.
271  void UnmountChildMounts(const std::string& mount_path_in) {
272    std::string mount_path = mount_path_in;
273    // Let's make sure mount path has trailing slash.
274    if (mount_path[mount_path.length() - 1] != '/')
275      mount_path += '/';
276
277    for (MountPointMap::iterator it = mount_points_.begin();
278         it != mount_points_.end();
279         ++it) {
280      if (StartsWithASCII(it->second.source_path, mount_path,
281                          true /*case sensitive*/)) {
282        // TODO(tbarzic): Handle the case where this fails.
283        UnmountPath(it->second.mount_path,
284                    UNMOUNT_OPTIONS_NONE,
285                    UnmountPathCallback());
286      }
287    }
288  }
289
290  // Callback for UnmountDeviceRecursively.
291  void OnUnmountDeviceRecursively(
292      UnmountDeviceRecursivelyCallbackData* cb_data,
293      bool success,
294      const std::string& mount_path) {
295    if (success) {
296      // Do standard processing for Unmount event.
297      OnUnmountPath(UnmountPathCallback(), true, mount_path);
298      VLOG(1) << mount_path <<  " unmounted.";
299    }
300    // This is safe as long as all callbacks are called on the same thread as
301    // UnmountDeviceRecursively.
302    cb_data->num_pending_callbacks--;
303
304    if (cb_data->num_pending_callbacks == 0) {
305      // This code has a problem that the |success| status used here is for the
306      // last "unmount" callback, but not whether all unmounting is succeeded.
307      // TODO(hidehiko): Fix the issue.
308      cb_data->callback.Run(success);
309      delete cb_data;
310    }
311  }
312
313  // Callback to handle MountCompleted signal and Mount method call failure.
314  void OnMountCompleted(const MountEntry& entry) {
315    MountCondition mount_condition = MOUNT_CONDITION_NONE;
316    if (entry.mount_type() == MOUNT_TYPE_DEVICE) {
317      if (entry.error_code() == MOUNT_ERROR_UNKNOWN_FILESYSTEM) {
318        mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM;
319      }
320      if (entry.error_code() == MOUNT_ERROR_UNSUPPORTED_FILESYSTEM) {
321        mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM;
322      }
323    }
324    const MountPointInfo mount_info(entry.source_path(),
325                                    entry.mount_path(),
326                                    entry.mount_type(),
327                                    mount_condition);
328
329    NotifyMountStatusUpdate(MOUNTING, entry.error_code(), mount_info);
330
331    // If the device is corrupted but it's still possible to format it, it will
332    // be fake mounted.
333    if ((entry.error_code() == MOUNT_ERROR_NONE ||
334         mount_info.mount_condition) &&
335        mount_points_.find(mount_info.mount_path) == mount_points_.end()) {
336      mount_points_.insert(MountPointMap::value_type(mount_info.mount_path,
337                                                     mount_info));
338    }
339    if ((entry.error_code() == MOUNT_ERROR_NONE ||
340         mount_info.mount_condition) &&
341        mount_info.mount_type == MOUNT_TYPE_DEVICE &&
342        !mount_info.source_path.empty() &&
343        !mount_info.mount_path.empty()) {
344      DiskMap::iterator iter = disks_.find(mount_info.source_path);
345      if (iter == disks_.end()) {
346        // disk might have been removed by now?
347        return;
348      }
349      Disk* disk = iter->second;
350      DCHECK(disk);
351      disk->set_mount_path(mount_info.mount_path);
352    }
353  }
354
355  // Callback for UnmountPath.
356  void OnUnmountPath(const UnmountPathCallback& callback,
357                     bool success,
358                     const std::string& mount_path) {
359    MountPointMap::iterator mount_points_it = mount_points_.find(mount_path);
360    if (mount_points_it == mount_points_.end()) {
361      // The path was unmounted, but not as a result of this unmount request,
362      // so return error.
363      if (!callback.is_null())
364        callback.Run(MOUNT_ERROR_INTERNAL);
365      return;
366    }
367
368    NotifyMountStatusUpdate(
369        UNMOUNTING,
370        success ? MOUNT_ERROR_NONE : MOUNT_ERROR_INTERNAL,
371        MountPointInfo(mount_points_it->second.source_path,
372                       mount_points_it->second.mount_path,
373                       mount_points_it->second.mount_type,
374                       mount_points_it->second.mount_condition));
375
376    std::string path(mount_points_it->second.source_path);
377    if (success)
378      mount_points_.erase(mount_points_it);
379
380    DiskMap::iterator disk_iter = disks_.find(path);
381    if (disk_iter != disks_.end()) {
382      DCHECK(disk_iter->second);
383      if (success)
384        disk_iter->second->clear_mount_path();
385    }
386
387    if (!callback.is_null())
388      callback.Run(success ? MOUNT_ERROR_NONE : MOUNT_ERROR_INTERNAL);
389  }
390
391  void OnUnmountPathForFormat(const std::string& device_path,
392                              MountError error_code) {
393    if (error_code == MOUNT_ERROR_NONE &&
394        disks_.find(device_path) != disks_.end()) {
395      FormatUnmountedDevice(device_path);
396    } else {
397      OnFormatCompleted(FORMAT_ERROR_UNKNOWN, device_path);
398    }
399  }
400
401  // Starts device formatting.
402  void FormatUnmountedDevice(const std::string& device_path) {
403    DiskMap::const_iterator disk = disks_.find(device_path);
404    DCHECK(disk != disks_.end() && disk->second->mount_path().empty());
405
406    const char kFormatVFAT[] = "vfat";
407    cros_disks_client_->Format(
408        device_path,
409        kFormatVFAT,
410        base::Bind(&DiskMountManagerImpl::OnFormatStarted,
411                   weak_ptr_factory_.GetWeakPtr(),
412                   device_path),
413        base::Bind(&DiskMountManagerImpl::OnFormatCompleted,
414                   weak_ptr_factory_.GetWeakPtr(),
415                   FORMAT_ERROR_UNKNOWN,
416                   device_path));
417  }
418
419  // Callback for Format.
420  void OnFormatStarted(const std::string& device_path) {
421    NotifyFormatStatusUpdate(FORMAT_STARTED, FORMAT_ERROR_NONE, device_path);
422  }
423
424  // Callback to handle FormatCompleted signal and Format method call failure.
425  void OnFormatCompleted(FormatError error_code,
426                         const std::string& device_path) {
427    NotifyFormatStatusUpdate(FORMAT_COMPLETED, error_code, device_path);
428  }
429
430  // Callback for GetDeviceProperties.
431  void OnGetDeviceProperties(const DiskInfo& disk_info) {
432    // TODO(zelidrag): Find a better way to filter these out before we
433    // fetch the properties:
434    // Ignore disks coming from the device we booted the system from.
435    if (disk_info.on_boot_device())
436      return;
437
438    LOG(WARNING) << "Found disk " << disk_info.device_path();
439    // Delete previous disk info for this path:
440    bool is_new = true;
441    DiskMap::iterator iter = disks_.find(disk_info.device_path());
442    if (iter != disks_.end()) {
443      delete iter->second;
444      disks_.erase(iter);
445      is_new = false;
446    }
447    Disk* disk = new Disk(disk_info.device_path(),
448                          disk_info.mount_path(),
449                          disk_info.system_path(),
450                          disk_info.file_path(),
451                          disk_info.label(),
452                          disk_info.drive_label(),
453                          disk_info.vendor_id(),
454                          disk_info.vendor_name(),
455                          disk_info.product_id(),
456                          disk_info.product_name(),
457                          disk_info.uuid(),
458                          FindSystemPathPrefix(disk_info.system_path()),
459                          disk_info.device_type(),
460                          disk_info.total_size_in_bytes(),
461                          disk_info.is_drive(),
462                          disk_info.is_read_only(),
463                          disk_info.has_media(),
464                          disk_info.on_boot_device(),
465                          disk_info.on_removable_device(),
466                          disk_info.is_hidden());
467    disks_.insert(std::make_pair(disk_info.device_path(), disk));
468    NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, disk);
469  }
470
471  // Part of EnsureMountInfoRefreshed(). Called after the list of devices are
472  // enumerated.
473  void RefreshAfterEnumerateDevices(const std::vector<std::string>& devices) {
474    std::set<std::string> current_device_set(devices.begin(), devices.end());
475    for (DiskMap::iterator iter = disks_.begin(); iter != disks_.end(); ) {
476      if (current_device_set.find(iter->first) == current_device_set.end()) {
477        delete iter->second;
478        disks_.erase(iter++);
479      } else {
480        ++iter;
481      }
482    }
483    RefreshDeviceAtIndex(devices, 0);
484  }
485
486  // Part of EnsureMountInfoRefreshed(). Called for each device to refresh info.
487  void RefreshDeviceAtIndex(const std::vector<std::string>& devices,
488                            size_t index) {
489    if (index == devices.size()) {
490      // All devices info retrieved. Proceed to enumerate mount point info.
491      cros_disks_client_->EnumerateMountEntries(
492          base::Bind(&DiskMountManagerImpl::RefreshAfterEnumerateMountEntries,
493                     weak_ptr_factory_.GetWeakPtr()),
494          base::Bind(&DiskMountManagerImpl::RefreshCompleted,
495                     weak_ptr_factory_.GetWeakPtr(), false));
496      return;
497    }
498
499    cros_disks_client_->GetDeviceProperties(
500        devices[index],
501        base::Bind(&DiskMountManagerImpl::RefreshAfterGetDeviceProperties,
502                   weak_ptr_factory_.GetWeakPtr(), devices, index + 1),
503        base::Bind(&DiskMountManagerImpl::RefreshCompleted,
504                   weak_ptr_factory_.GetWeakPtr(), false));
505  }
506
507  // Part of EnsureMountInfoRefreshed().
508  void RefreshAfterGetDeviceProperties(const std::vector<std::string>& devices,
509                                       size_t next_index,
510                                       const DiskInfo& disk_info) {
511    OnGetDeviceProperties(disk_info);
512    RefreshDeviceAtIndex(devices, next_index);
513  }
514
515  // Part of EnsureMountInfoRefreshed(). Called after mount entries are listed.
516  void RefreshAfterEnumerateMountEntries(
517      const std::vector<MountEntry>& entries) {
518    for (size_t i = 0; i < entries.size(); ++i)
519      OnMountCompleted(entries[i]);
520    RefreshCompleted(true);
521  }
522
523  // Part of EnsureMountInfoRefreshed(). Called when the refreshing is done.
524  void RefreshCompleted(bool success) {
525    already_refreshed_ = true;
526    for (size_t i = 0; i < refresh_callbacks_.size(); ++i)
527      refresh_callbacks_[i].Run(success);
528    refresh_callbacks_.clear();
529  }
530
531  // Callback to handle mount event signals.
532  void OnMountEvent(MountEventType event, const std::string& device_path_arg) {
533    // Take a copy of the argument so we can modify it below.
534    std::string device_path = device_path_arg;
535    switch (event) {
536      case CROS_DISKS_DISK_ADDED: {
537        cros_disks_client_->GetDeviceProperties(
538            device_path,
539            base::Bind(&DiskMountManagerImpl::OnGetDeviceProperties,
540                       weak_ptr_factory_.GetWeakPtr()),
541            base::Bind(&base::DoNothing));
542        break;
543      }
544      case CROS_DISKS_DISK_REMOVED: {
545        // Search and remove disks that are no longer present.
546        DiskMountManager::DiskMap::iterator iter = disks_.find(device_path);
547        if (iter != disks_.end()) {
548          Disk* disk = iter->second;
549          NotifyDiskStatusUpdate(DISK_REMOVED, disk);
550          delete iter->second;
551          disks_.erase(iter);
552        }
553        break;
554      }
555      case CROS_DISKS_DEVICE_ADDED: {
556        system_path_prefixes_.insert(device_path);
557        NotifyDeviceStatusUpdate(DEVICE_ADDED, device_path);
558        break;
559      }
560      case CROS_DISKS_DEVICE_REMOVED: {
561        system_path_prefixes_.erase(device_path);
562        NotifyDeviceStatusUpdate(DEVICE_REMOVED, device_path);
563        break;
564      }
565      case CROS_DISKS_DEVICE_SCANNED: {
566        NotifyDeviceStatusUpdate(DEVICE_SCANNED, device_path);
567        break;
568      }
569      default: {
570        LOG(ERROR) << "Unknown event: " << event;
571      }
572    }
573  }
574
575  // Notifies all observers about disk status update.
576  void NotifyDiskStatusUpdate(DiskEvent event,
577                              const Disk* disk) {
578    FOR_EACH_OBSERVER(Observer, observers_, OnDiskEvent(event, disk));
579  }
580
581  // Notifies all observers about device status update.
582  void NotifyDeviceStatusUpdate(DeviceEvent event,
583                                const std::string& device_path) {
584    FOR_EACH_OBSERVER(Observer, observers_, OnDeviceEvent(event, device_path));
585  }
586
587  // Notifies all observers about mount completion.
588  void NotifyMountStatusUpdate(MountEvent event,
589                               MountError error_code,
590                               const MountPointInfo& mount_info) {
591    FOR_EACH_OBSERVER(Observer, observers_,
592                      OnMountEvent(event, error_code, mount_info));
593  }
594
595  void NotifyFormatStatusUpdate(FormatEvent event,
596                                FormatError error_code,
597                                const std::string& device_path) {
598    FOR_EACH_OBSERVER(Observer, observers_,
599                      OnFormatEvent(event, error_code, device_path));
600  }
601
602  // Finds system path prefix from |system_path|.
603  const std::string& FindSystemPathPrefix(const std::string& system_path) {
604    if (system_path.empty())
605      return base::EmptyString();
606    for (SystemPathPrefixSet::const_iterator it = system_path_prefixes_.begin();
607         it != system_path_prefixes_.end();
608         ++it) {
609      const std::string& prefix = *it;
610      if (StartsWithASCII(system_path, prefix, true))
611        return prefix;
612    }
613    return base::EmptyString();
614  }
615
616  // Mount event change observers.
617  ObserverList<Observer> observers_;
618
619  CrosDisksClient* cros_disks_client_;
620
621  // The list of disks found.
622  DiskMountManager::DiskMap disks_;
623
624  DiskMountManager::MountPointMap mount_points_;
625
626  typedef std::set<std::string> SystemPathPrefixSet;
627  SystemPathPrefixSet system_path_prefixes_;
628
629  bool already_refreshed_;
630  std::vector<EnsureMountInfoRefreshedCallback> refresh_callbacks_;
631
632  base::WeakPtrFactory<DiskMountManagerImpl> weak_ptr_factory_;
633
634  DISALLOW_COPY_AND_ASSIGN(DiskMountManagerImpl);
635};
636
637}  // namespace
638
639DiskMountManager::Disk::Disk(const std::string& device_path,
640                             const std::string& mount_path,
641                             const std::string& system_path,
642                             const std::string& file_path,
643                             const std::string& device_label,
644                             const std::string& drive_label,
645                             const std::string& vendor_id,
646                             const std::string& vendor_name,
647                             const std::string& product_id,
648                             const std::string& product_name,
649                             const std::string& fs_uuid,
650                             const std::string& system_path_prefix,
651                             DeviceType device_type,
652                             uint64 total_size_in_bytes,
653                             bool is_parent,
654                             bool is_read_only,
655                             bool has_media,
656                             bool on_boot_device,
657                             bool on_removable_device,
658                             bool is_hidden)
659    : device_path_(device_path),
660      mount_path_(mount_path),
661      system_path_(system_path),
662      file_path_(file_path),
663      device_label_(device_label),
664      drive_label_(drive_label),
665      vendor_id_(vendor_id),
666      vendor_name_(vendor_name),
667      product_id_(product_id),
668      product_name_(product_name),
669      fs_uuid_(fs_uuid),
670      system_path_prefix_(system_path_prefix),
671      device_type_(device_type),
672      total_size_in_bytes_(total_size_in_bytes),
673      is_parent_(is_parent),
674      is_read_only_(is_read_only),
675      has_media_(has_media),
676      on_boot_device_(on_boot_device),
677      on_removable_device_(on_removable_device),
678      is_hidden_(is_hidden) {
679}
680
681DiskMountManager::Disk::~Disk() {}
682
683bool DiskMountManager::AddDiskForTest(Disk* disk) {
684  return false;
685}
686
687bool DiskMountManager::AddMountPointForTest(const MountPointInfo& mount_point) {
688  return false;
689}
690
691// static
692std::string DiskMountManager::MountConditionToString(MountCondition condition) {
693  switch (condition) {
694    case MOUNT_CONDITION_NONE:
695      return "";
696    case MOUNT_CONDITION_UNKNOWN_FILESYSTEM:
697      return "unknown_filesystem";
698    case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM:
699      return "unsupported_filesystem";
700    default:
701      NOTREACHED();
702  }
703  return "";
704}
705
706// static
707std::string DiskMountManager::DeviceTypeToString(DeviceType type) {
708  switch (type) {
709    case DEVICE_TYPE_USB:
710      return "usb";
711    case DEVICE_TYPE_SD:
712      return "sd";
713    case DEVICE_TYPE_OPTICAL_DISC:
714      return "optical";
715    case DEVICE_TYPE_MOBILE:
716      return "mobile";
717    default:
718      return "unknown";
719  }
720}
721
722// static
723void DiskMountManager::Initialize() {
724  if (g_disk_mount_manager) {
725    LOG(WARNING) << "DiskMountManager was already initialized";
726    return;
727  }
728  g_disk_mount_manager = new DiskMountManagerImpl();
729  VLOG(1) << "DiskMountManager initialized";
730}
731
732// static
733void DiskMountManager::InitializeForTesting(
734    DiskMountManager* disk_mount_manager) {
735  if (g_disk_mount_manager) {
736    LOG(WARNING) << "DiskMountManager was already initialized";
737    return;
738  }
739  g_disk_mount_manager = disk_mount_manager;
740  VLOG(1) << "DiskMountManager initialized";
741}
742
743// static
744void DiskMountManager::Shutdown() {
745  if (!g_disk_mount_manager) {
746    LOG(WARNING) << "DiskMountManager::Shutdown() called with NULL manager";
747    return;
748  }
749  delete g_disk_mount_manager;
750  g_disk_mount_manager = NULL;
751  VLOG(1) << "DiskMountManager Shutdown completed";
752}
753
754// static
755DiskMountManager* DiskMountManager::GetInstance() {
756  return g_disk_mount_manager;
757}
758
759}  // namespace disks
760}  // namespace chromeos
761