Home | History | Annotate | Download | only in disks
      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 
     17 namespace chromeos {
     18 namespace disks {
     19 
     20 namespace {
     21 
     22 const char kDeviceNotFound[] = "Device could not be found";
     23 
     24 DiskMountManager* g_disk_mount_manager = NULL;
     25 
     26 // The DiskMountManager implementation.
     27 class 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 
    639 DiskMountManager::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 
    681 DiskMountManager::Disk::~Disk() {}
    682 
    683 bool DiskMountManager::AddDiskForTest(Disk* disk) {
    684   return false;
    685 }
    686 
    687 bool DiskMountManager::AddMountPointForTest(const MountPointInfo& mount_point) {
    688   return false;
    689 }
    690 
    691 // static
    692 std::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
    707 std::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
    723 void 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
    733 void 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
    744 void 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
    755 DiskMountManager* DiskMountManager::GetInstance() {
    756   return g_disk_mount_manager;
    757 }
    758 
    759 }  // namespace disks
    760 }  // namespace chromeos
    761