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