Home | History | Annotate | Download | only in cros
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/cros/mount_library.h"
      6 
      7 #include <set>
      8 
      9 #include "base/message_loop.h"
     10 #include "base/string_util.h"
     11 #include "chrome/browser/chromeos/cros/cros_library.h"
     12 #include "content/browser/browser_thread.h"
     13 
     14 const char* kLibraryNotLoaded = "Cros Library not loaded";
     15 
     16 namespace chromeos {
     17 
     18 MountLibrary::Disk::Disk(const std::string& device_path,
     19      const std::string& mount_path,
     20      const std::string& system_path,
     21      const std::string& file_path,
     22      const std::string& device_label,
     23      const std::string& drive_label,
     24      const std::string& parent_path,
     25      DeviceType device_type,
     26      uint64 total_size,
     27      bool is_parent,
     28      bool is_read_only,
     29      bool has_media,
     30      bool on_boot_device)
     31     : device_path_(device_path),
     32       mount_path_(mount_path),
     33       system_path_(system_path),
     34       file_path_(file_path),
     35       device_label_(device_label),
     36       drive_label_(drive_label),
     37       parent_path_(parent_path),
     38       device_type_(device_type),
     39       total_size_(total_size),
     40       is_parent_(is_parent),
     41       is_read_only_(is_read_only),
     42       has_media_(has_media),
     43       on_boot_device_(on_boot_device) {
     44   // Add trailing slash to mount path.
     45   if (mount_path_.length() && mount_path_.at(mount_path_.length() -1) != '/')
     46     mount_path_ = mount_path_.append("/");
     47 }
     48 
     49 class MountLibraryImpl : public MountLibrary {
     50  public:
     51   MountLibraryImpl() : mount_status_connection_(NULL) {
     52     if (CrosLibrary::Get()->EnsureLoaded())
     53       Init();
     54     else
     55       LOG(ERROR) << kLibraryNotLoaded;
     56   }
     57 
     58   virtual ~MountLibraryImpl() {
     59     if (mount_status_connection_)
     60       DisconnectMountEventMonitor(mount_status_connection_);
     61   }
     62 
     63   // MountLibrary overrides.
     64   virtual void AddObserver(Observer* observer) OVERRIDE {
     65     observers_.AddObserver(observer);
     66   }
     67 
     68   virtual void RemoveObserver(Observer* observer) OVERRIDE {
     69     observers_.RemoveObserver(observer);
     70   }
     71 
     72   virtual void MountPath(const char* device_path) OVERRIDE {
     73     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     74     if (!CrosLibrary::Get()->EnsureLoaded()) {
     75       OnMountRemovableDevice(device_path,
     76                              NULL,
     77                              MOUNT_METHOD_ERROR_LOCAL,
     78                              kLibraryNotLoaded);
     79       return;
     80     }
     81     MountRemovableDevice(device_path,
     82                          &MountLibraryImpl::MountRemovableDeviceCallback,
     83                          this);
     84   }
     85 
     86   virtual void UnmountPath(const char* device_path) OVERRIDE {
     87     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     88     if (!CrosLibrary::Get()->EnsureLoaded()) {
     89       OnUnmountRemovableDevice(device_path,
     90                                MOUNT_METHOD_ERROR_LOCAL,
     91                                kLibraryNotLoaded);
     92       return;
     93     }
     94     UnmountRemovableDevice(device_path,
     95                            &MountLibraryImpl::UnmountRemovableDeviceCallback,
     96                            this);
     97   }
     98 
     99   virtual void RequestMountInfoRefresh() OVERRIDE {
    100     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    101     if (!CrosLibrary::Get()->EnsureLoaded()) {
    102       OnRequestMountInfo(NULL,
    103                          0,
    104                          MOUNT_METHOD_ERROR_LOCAL,
    105                          kLibraryNotLoaded);
    106       return;
    107     }
    108     RequestMountInfo(&MountLibraryImpl::RequestMountInfoCallback,
    109                      this);
    110   }
    111 
    112   virtual void RefreshDiskProperties(const Disk* disk) OVERRIDE {
    113     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    114     DCHECK(disk);
    115     if (!CrosLibrary::Get()->EnsureLoaded()) {
    116       OnGetDiskProperties(disk->device_path().c_str(),
    117                           NULL,
    118                           MOUNT_METHOD_ERROR_LOCAL,
    119                           kLibraryNotLoaded);
    120       return;
    121     }
    122     GetDiskProperties(disk->device_path().c_str(),
    123                       &MountLibraryImpl::GetDiskPropertiesCallback,
    124                       this);
    125   }
    126 
    127   const DiskMap& disks() const OVERRIDE { return disks_; }
    128 
    129  private:
    130 
    131   // Callback for MountRemovableDevice method.
    132   static void MountRemovableDeviceCallback(void* object,
    133                                            const char* device_path,
    134                                            const char* mount_path,
    135                                            MountMethodErrorType error,
    136                                            const char* error_message) {
    137     DCHECK(object);
    138     MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    139     self->OnMountRemovableDevice(device_path,
    140                                  mount_path,
    141                                  error,
    142                                  error_message);
    143   }
    144 
    145   // Callback for UnmountRemovableDevice method.
    146   static void UnmountRemovableDeviceCallback(void* object,
    147                                              const char* device_path,
    148                                              const char* mount_path,
    149                                              MountMethodErrorType error,
    150                                              const char* error_message) {
    151     DCHECK(object);
    152     MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    153     self->OnUnmountRemovableDevice(device_path,
    154                                    error,
    155                                    error_message);
    156   }
    157 
    158   // Callback for disk information retrieval calls.
    159   static void GetDiskPropertiesCallback(void* object,
    160                                         const char* device_path,
    161                                         const DiskInfo* disk,
    162                                         MountMethodErrorType error,
    163                                         const char* error_message) {
    164     DCHECK(object);
    165     MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    166     self->OnGetDiskProperties(device_path,
    167                               disk,
    168                               error,
    169                               error_message);
    170   }
    171 
    172   // Callback for RequestMountInfo call.
    173   static void RequestMountInfoCallback(void* object,
    174                                        const char** devices,
    175                                        size_t device_len,
    176                                        MountMethodErrorType error,
    177                                        const char* error_message) {
    178     DCHECK(object);
    179     MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    180     self->OnRequestMountInfo(devices,
    181                              device_len,
    182                              error,
    183                              error_message);
    184   }
    185 
    186   // This method will receive events that are caused by drive status changes.
    187   static void MonitorMountEventsHandler(void* object,
    188                                         MountEventType evt,
    189                                         const char* device_path) {
    190     DCHECK(object);
    191     MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    192     self->OnMountEvent(evt, device_path);
    193   }
    194 
    195 
    196   void OnMountRemovableDevice(const char* device_path,
    197                               const char* mount_path,
    198                               MountMethodErrorType error,
    199                               const char* error_message) {
    200     DCHECK(device_path);
    201 
    202     if (error == MOUNT_METHOD_ERROR_NONE && device_path && mount_path) {
    203       std::string path(device_path);
    204       DiskMap::iterator iter = disks_.find(path);
    205       if (iter == disks_.end()) {
    206         // disk might have been removed by now?
    207         return;
    208       }
    209       Disk* disk = iter->second;
    210       DCHECK(disk);
    211       disk->set_mount_path(mount_path);
    212       FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk);
    213     } else {
    214       LOG(WARNING) << "Mount request failed for device "
    215                    << device_path << ", with error: "
    216                    << (error_message ? error_message : "Unknown");
    217     }
    218   }
    219 
    220   void OnUnmountRemovableDevice(const char* device_path,
    221                                 MountMethodErrorType error,
    222                                 const char* error_message) {
    223     DCHECK(device_path);
    224     if (error == MOUNT_METHOD_ERROR_NONE && device_path) {
    225       std::string path(device_path);
    226       DiskMap::iterator iter = disks_.find(path);
    227       if (iter == disks_.end()) {
    228         // disk might have been removed by now?
    229         return;
    230       }
    231       Disk* disk = iter->second;
    232       DCHECK(disk);
    233       disk->clear_mount_path();
    234       FireDiskStatusUpdate(MOUNT_DISK_UNMOUNTED, disk);
    235     } else {
    236       LOG(WARNING) << "Unmount request failed for device "
    237                    << device_path << ", with error: "
    238                    << (error_message ? error_message : "Unknown");
    239     }
    240   }
    241 
    242   void OnGetDiskProperties(const char* device_path,
    243                            const DiskInfo* disk1,
    244                            MountMethodErrorType error,
    245                            const char* error_message) {
    246     DCHECK(device_path);
    247     if (error == MOUNT_METHOD_ERROR_NONE && device_path) {
    248       // TODO(zelidrag): Find a better way to filter these out before we
    249       // fetch the properties:
    250       // Ignore disks coming from the device we booted the system from.
    251 
    252       // This cast is temporal solution, until we merge DiskInfo and
    253       // DiskInfoAdvanced into single interface.
    254       const DiskInfoAdvanced* disk =
    255           reinterpret_cast<const DiskInfoAdvanced*>(disk1);
    256       if (disk->on_boot_device())
    257         return;
    258 
    259       LOG(WARNING) << "Found disk " << device_path;
    260       // Delete previous disk info for this path:
    261       bool is_new = true;
    262       std::string device_path_string(device_path);
    263       DiskMap::iterator iter = disks_.find(device_path_string);
    264       if (iter != disks_.end()) {
    265         delete iter->second;
    266         disks_.erase(iter);
    267         is_new = false;
    268       }
    269       std::string path;
    270       std::string mountpath;
    271       std::string systempath;
    272       std::string filepath;
    273       std::string devicelabel;
    274       std::string drivelabel;
    275       std::string parentpath;
    276 
    277       if (disk->path() != NULL)
    278         path = disk->path();
    279 
    280       if (disk->mount_path() != NULL)
    281         mountpath = disk->mount_path();
    282 
    283       if (disk->system_path() != NULL)
    284         systempath = disk->system_path();
    285 
    286       if (disk->file_path() != NULL)
    287         filepath = disk->file_path();
    288 
    289       if (disk->label() != NULL)
    290         devicelabel = disk->label();
    291 
    292       if (disk->drive_label() != NULL)
    293         drivelabel = disk->drive_label();
    294 
    295       if (disk->partition_slave() != NULL)
    296         parentpath = disk->partition_slave();
    297 
    298       Disk* new_disk = new Disk(path,
    299                                 mountpath,
    300                                 systempath,
    301                                 filepath,
    302                                 devicelabel,
    303                                 drivelabel,
    304                                 parentpath,
    305                                 disk->device_type(),
    306                                 disk->size(),
    307                                 disk->is_drive(),
    308                                 disk->is_read_only(),
    309                                 disk->has_media(),
    310                                 disk->on_boot_device());
    311       disks_.insert(
    312           std::pair<std::string, Disk*>(device_path_string, new_disk));
    313       FireDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED,
    314                            new_disk);
    315     } else {
    316       LOG(WARNING) << "Property retrieval request failed for device "
    317                    << device_path << ", with error: "
    318                    << (error_message ? error_message : "Unknown");
    319     }
    320   }
    321 
    322   void OnRequestMountInfo(const char** devices,
    323                           size_t devices_len,
    324                           MountMethodErrorType error,
    325                           const char* error_message) {
    326     std::set<std::string> current_device_set;
    327     if (error == MOUNT_METHOD_ERROR_NONE && devices && devices_len) {
    328       // Initiate properties fetch for all removable disks,
    329       bool found_disk = false;
    330       for (size_t i = 0; i < devices_len; i++) {
    331         if (!devices[i]) {
    332           NOTREACHED();
    333           continue;
    334         }
    335         current_device_set.insert(std::string(devices[i]));
    336         found_disk = true;
    337         // Initiate disk property retrieval for each relevant device path.
    338         GetDiskProperties(devices[i],
    339                           &MountLibraryImpl::GetDiskPropertiesCallback,
    340                           this);
    341       }
    342     } else if (error != MOUNT_METHOD_ERROR_NONE) {
    343       LOG(WARNING) << "Request mount info retrieval request failed with error: "
    344                    << (error_message ? error_message : "Unknown");
    345     }
    346     // Search and remove disks that are no longer present.
    347     for (MountLibrary::DiskMap::iterator iter = disks_.begin();
    348          iter != disks_.end(); ) {
    349       if (current_device_set.find(iter->first) == current_device_set.end()) {
    350         Disk* disk = iter->second;
    351         FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk);
    352         delete iter->second;
    353         disks_.erase(iter++);
    354       } else {
    355         ++iter;
    356       }
    357     }
    358   }
    359 
    360   void OnMountEvent(MountEventType evt,
    361                     const char* device_path) {
    362     if (!device_path)
    363       return;
    364     MountLibraryEventType type = MOUNT_DEVICE_ADDED;
    365     switch (evt) {
    366       case DISK_ADDED:
    367       case DISK_CHANGED: {
    368         GetDiskProperties(device_path,
    369                           &MountLibraryImpl::GetDiskPropertiesCallback,
    370                           this);
    371         return;
    372       }
    373       case DISK_REMOVED: {
    374         // Search and remove disks that are no longer present.
    375         MountLibrary::DiskMap::iterator iter =
    376             disks_.find(std::string(device_path));
    377         if (iter != disks_.end()) {
    378             Disk* disk = iter->second;
    379             FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk);
    380             delete iter->second;
    381             disks_.erase(iter);
    382         }
    383         return;
    384       }
    385       case DEVICE_ADDED: {
    386         type = MOUNT_DEVICE_ADDED;
    387         break;
    388       }
    389       case DEVICE_REMOVED: {
    390         type = MOUNT_DEVICE_REMOVED;
    391         break;
    392       }
    393       case DEVICE_SCANNED: {
    394         type = MOUNT_DEVICE_SCANNED;
    395         break;
    396       }
    397     }
    398     FireDeviceStatusUpdate(type, std::string(device_path));
    399   }
    400 
    401   void Init() {
    402     // Getting the monitor status so that the daemon starts up.
    403     mount_status_connection_ = MonitorMountEvents(
    404         &MonitorMountEventsHandler, this);
    405   }
    406 
    407   void FireDiskStatusUpdate(MountLibraryEventType evt,
    408                             const Disk* disk) {
    409     // Make sure we run on UI thread.
    410     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    411     FOR_EACH_OBSERVER(
    412         Observer, observers_, DiskChanged(evt, disk));
    413   }
    414 
    415   void FireDeviceStatusUpdate(MountLibraryEventType evt,
    416                               const std::string& device_path) {
    417     // Make sure we run on UI thread.
    418     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    419     FOR_EACH_OBSERVER(
    420         Observer, observers_, DeviceChanged(evt, device_path));
    421   }
    422 
    423   // Mount event change observers.
    424   ObserverList<Observer> observers_;
    425 
    426   // A reference to the  mount api, to allow callbacks when the mount
    427   // status changes.
    428   MountEventConnection mount_status_connection_;
    429 
    430   // The list of disks found.
    431   MountLibrary::DiskMap disks_;
    432 
    433   DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl);
    434 };
    435 
    436 class MountLibraryStubImpl : public MountLibrary {
    437  public:
    438   MountLibraryStubImpl() {}
    439   virtual ~MountLibraryStubImpl() {}
    440 
    441   // MountLibrary overrides.
    442   virtual void AddObserver(Observer* observer) OVERRIDE {}
    443   virtual void RemoveObserver(Observer* observer) OVERRIDE {}
    444   virtual const DiskMap& disks() const OVERRIDE { return disks_; }
    445   virtual void RequestMountInfoRefresh() OVERRIDE {}
    446   virtual void MountPath(const char* device_path) OVERRIDE {}
    447   virtual void UnmountPath(const char* device_path) OVERRIDE {}
    448   virtual bool IsBootPath(const char* device_path) OVERRIDE { return true; }
    449 
    450  private:
    451   // The list of disks found.
    452   DiskMap disks_;
    453 
    454   DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl);
    455 };
    456 
    457 // static
    458 MountLibrary* MountLibrary::GetImpl(bool stub) {
    459   if (stub)
    460     return new MountLibraryStubImpl();
    461   else
    462     return new MountLibraryImpl();
    463 }
    464 
    465 }  // namespace chromeos
    466 
    467 // Allows InvokeLater without adding refcounting. This class is a Singleton and
    468 // won't be deleted until it's last InvokeLater is run.
    469 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::MountLibraryImpl);
    470 
    471