普通文本  |  471行  |  16.08 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/cros/mount_library.h"

#include <set>

#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "content/browser/browser_thread.h"

const char* kLibraryNotLoaded = "Cros Library not loaded";

namespace chromeos {

MountLibrary::Disk::Disk(const std::string& device_path,
     const std::string& mount_path,
     const std::string& system_path,
     const std::string& file_path,
     const std::string& device_label,
     const std::string& drive_label,
     const std::string& parent_path,
     DeviceType device_type,
     uint64 total_size,
     bool is_parent,
     bool is_read_only,
     bool has_media,
     bool on_boot_device)
    : device_path_(device_path),
      mount_path_(mount_path),
      system_path_(system_path),
      file_path_(file_path),
      device_label_(device_label),
      drive_label_(drive_label),
      parent_path_(parent_path),
      device_type_(device_type),
      total_size_(total_size),
      is_parent_(is_parent),
      is_read_only_(is_read_only),
      has_media_(has_media),
      on_boot_device_(on_boot_device) {
  // Add trailing slash to mount path.
  if (mount_path_.length() && mount_path_.at(mount_path_.length() -1) != '/')
    mount_path_ = mount_path_.append("/");
}

class MountLibraryImpl : public MountLibrary {
 public:
  MountLibraryImpl() : mount_status_connection_(NULL) {
    if (CrosLibrary::Get()->EnsureLoaded())
      Init();
    else
      LOG(ERROR) << kLibraryNotLoaded;
  }

  virtual ~MountLibraryImpl() {
    if (mount_status_connection_)
      DisconnectMountEventMonitor(mount_status_connection_);
  }

  // MountLibrary overrides.
  virtual void AddObserver(Observer* observer) OVERRIDE {
    observers_.AddObserver(observer);
  }

  virtual void RemoveObserver(Observer* observer) OVERRIDE {
    observers_.RemoveObserver(observer);
  }

  virtual void MountPath(const char* device_path) OVERRIDE {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!CrosLibrary::Get()->EnsureLoaded()) {
      OnMountRemovableDevice(device_path,
                             NULL,
                             MOUNT_METHOD_ERROR_LOCAL,
                             kLibraryNotLoaded);
      return;
    }
    MountRemovableDevice(device_path,
                         &MountLibraryImpl::MountRemovableDeviceCallback,
                         this);
  }

  virtual void UnmountPath(const char* device_path) OVERRIDE {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!CrosLibrary::Get()->EnsureLoaded()) {
      OnUnmountRemovableDevice(device_path,
                               MOUNT_METHOD_ERROR_LOCAL,
                               kLibraryNotLoaded);
      return;
    }
    UnmountRemovableDevice(device_path,
                           &MountLibraryImpl::UnmountRemovableDeviceCallback,
                           this);
  }

  virtual void RequestMountInfoRefresh() OVERRIDE {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    if (!CrosLibrary::Get()->EnsureLoaded()) {
      OnRequestMountInfo(NULL,
                         0,
                         MOUNT_METHOD_ERROR_LOCAL,
                         kLibraryNotLoaded);
      return;
    }
    RequestMountInfo(&MountLibraryImpl::RequestMountInfoCallback,
                     this);
  }

  virtual void RefreshDiskProperties(const Disk* disk) OVERRIDE {
    CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    DCHECK(disk);
    if (!CrosLibrary::Get()->EnsureLoaded()) {
      OnGetDiskProperties(disk->device_path().c_str(),
                          NULL,
                          MOUNT_METHOD_ERROR_LOCAL,
                          kLibraryNotLoaded);
      return;
    }
    GetDiskProperties(disk->device_path().c_str(),
                      &MountLibraryImpl::GetDiskPropertiesCallback,
                      this);
  }

  const DiskMap& disks() const OVERRIDE { return disks_; }

 private:

  // Callback for MountRemovableDevice method.
  static void MountRemovableDeviceCallback(void* object,
                                           const char* device_path,
                                           const char* mount_path,
                                           MountMethodErrorType error,
                                           const char* error_message) {
    DCHECK(object);
    MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    self->OnMountRemovableDevice(device_path,
                                 mount_path,
                                 error,
                                 error_message);
  }

  // Callback for UnmountRemovableDevice method.
  static void UnmountRemovableDeviceCallback(void* object,
                                             const char* device_path,
                                             const char* mount_path,
                                             MountMethodErrorType error,
                                             const char* error_message) {
    DCHECK(object);
    MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    self->OnUnmountRemovableDevice(device_path,
                                   error,
                                   error_message);
  }

  // Callback for disk information retrieval calls.
  static void GetDiskPropertiesCallback(void* object,
                                        const char* device_path,
                                        const DiskInfo* disk,
                                        MountMethodErrorType error,
                                        const char* error_message) {
    DCHECK(object);
    MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    self->OnGetDiskProperties(device_path,
                              disk,
                              error,
                              error_message);
  }

  // Callback for RequestMountInfo call.
  static void RequestMountInfoCallback(void* object,
                                       const char** devices,
                                       size_t device_len,
                                       MountMethodErrorType error,
                                       const char* error_message) {
    DCHECK(object);
    MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    self->OnRequestMountInfo(devices,
                             device_len,
                             error,
                             error_message);
  }

  // This method will receive events that are caused by drive status changes.
  static void MonitorMountEventsHandler(void* object,
                                        MountEventType evt,
                                        const char* device_path) {
    DCHECK(object);
    MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object);
    self->OnMountEvent(evt, device_path);
  }


  void OnMountRemovableDevice(const char* device_path,
                              const char* mount_path,
                              MountMethodErrorType error,
                              const char* error_message) {
    DCHECK(device_path);

    if (error == MOUNT_METHOD_ERROR_NONE && device_path && mount_path) {
      std::string path(device_path);
      DiskMap::iterator iter = disks_.find(path);
      if (iter == disks_.end()) {
        // disk might have been removed by now?
        return;
      }
      Disk* disk = iter->second;
      DCHECK(disk);
      disk->set_mount_path(mount_path);
      FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk);
    } else {
      LOG(WARNING) << "Mount request failed for device "
                   << device_path << ", with error: "
                   << (error_message ? error_message : "Unknown");
    }
  }

  void OnUnmountRemovableDevice(const char* device_path,
                                MountMethodErrorType error,
                                const char* error_message) {
    DCHECK(device_path);
    if (error == MOUNT_METHOD_ERROR_NONE && device_path) {
      std::string path(device_path);
      DiskMap::iterator iter = disks_.find(path);
      if (iter == disks_.end()) {
        // disk might have been removed by now?
        return;
      }
      Disk* disk = iter->second;
      DCHECK(disk);
      disk->clear_mount_path();
      FireDiskStatusUpdate(MOUNT_DISK_UNMOUNTED, disk);
    } else {
      LOG(WARNING) << "Unmount request failed for device "
                   << device_path << ", with error: "
                   << (error_message ? error_message : "Unknown");
    }
  }

  void OnGetDiskProperties(const char* device_path,
                           const DiskInfo* disk1,
                           MountMethodErrorType error,
                           const char* error_message) {
    DCHECK(device_path);
    if (error == MOUNT_METHOD_ERROR_NONE && device_path) {
      // TODO(zelidrag): Find a better way to filter these out before we
      // fetch the properties:
      // Ignore disks coming from the device we booted the system from.

      // This cast is temporal solution, until we merge DiskInfo and
      // DiskInfoAdvanced into single interface.
      const DiskInfoAdvanced* disk =
          reinterpret_cast<const DiskInfoAdvanced*>(disk1);
      if (disk->on_boot_device())
        return;

      LOG(WARNING) << "Found disk " << device_path;
      // Delete previous disk info for this path:
      bool is_new = true;
      std::string device_path_string(device_path);
      DiskMap::iterator iter = disks_.find(device_path_string);
      if (iter != disks_.end()) {
        delete iter->second;
        disks_.erase(iter);
        is_new = false;
      }
      std::string path;
      std::string mountpath;
      std::string systempath;
      std::string filepath;
      std::string devicelabel;
      std::string drivelabel;
      std::string parentpath;

      if (disk->path() != NULL)
        path = disk->path();

      if (disk->mount_path() != NULL)
        mountpath = disk->mount_path();

      if (disk->system_path() != NULL)
        systempath = disk->system_path();

      if (disk->file_path() != NULL)
        filepath = disk->file_path();

      if (disk->label() != NULL)
        devicelabel = disk->label();

      if (disk->drive_label() != NULL)
        drivelabel = disk->drive_label();

      if (disk->partition_slave() != NULL)
        parentpath = disk->partition_slave();

      Disk* new_disk = new Disk(path,
                                mountpath,
                                systempath,
                                filepath,
                                devicelabel,
                                drivelabel,
                                parentpath,
                                disk->device_type(),
                                disk->size(),
                                disk->is_drive(),
                                disk->is_read_only(),
                                disk->has_media(),
                                disk->on_boot_device());
      disks_.insert(
          std::pair<std::string, Disk*>(device_path_string, new_disk));
      FireDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED,
                           new_disk);
    } else {
      LOG(WARNING) << "Property retrieval request failed for device "
                   << device_path << ", with error: "
                   << (error_message ? error_message : "Unknown");
    }
  }

  void OnRequestMountInfo(const char** devices,
                          size_t devices_len,
                          MountMethodErrorType error,
                          const char* error_message) {
    std::set<std::string> current_device_set;
    if (error == MOUNT_METHOD_ERROR_NONE && devices && devices_len) {
      // Initiate properties fetch for all removable disks,
      bool found_disk = false;
      for (size_t i = 0; i < devices_len; i++) {
        if (!devices[i]) {
          NOTREACHED();
          continue;
        }
        current_device_set.insert(std::string(devices[i]));
        found_disk = true;
        // Initiate disk property retrieval for each relevant device path.
        GetDiskProperties(devices[i],
                          &MountLibraryImpl::GetDiskPropertiesCallback,
                          this);
      }
    } else if (error != MOUNT_METHOD_ERROR_NONE) {
      LOG(WARNING) << "Request mount info retrieval request failed with error: "
                   << (error_message ? error_message : "Unknown");
    }
    // Search and remove disks that are no longer present.
    for (MountLibrary::DiskMap::iterator iter = disks_.begin();
         iter != disks_.end(); ) {
      if (current_device_set.find(iter->first) == current_device_set.end()) {
        Disk* disk = iter->second;
        FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk);
        delete iter->second;
        disks_.erase(iter++);
      } else {
        ++iter;
      }
    }
  }

  void OnMountEvent(MountEventType evt,
                    const char* device_path) {
    if (!device_path)
      return;
    MountLibraryEventType type = MOUNT_DEVICE_ADDED;
    switch (evt) {
      case DISK_ADDED:
      case DISK_CHANGED: {
        GetDiskProperties(device_path,
                          &MountLibraryImpl::GetDiskPropertiesCallback,
                          this);
        return;
      }
      case DISK_REMOVED: {
        // Search and remove disks that are no longer present.
        MountLibrary::DiskMap::iterator iter =
            disks_.find(std::string(device_path));
        if (iter != disks_.end()) {
            Disk* disk = iter->second;
            FireDiskStatusUpdate(MOUNT_DISK_REMOVED, disk);
            delete iter->second;
            disks_.erase(iter);
        }
        return;
      }
      case DEVICE_ADDED: {
        type = MOUNT_DEVICE_ADDED;
        break;
      }
      case DEVICE_REMOVED: {
        type = MOUNT_DEVICE_REMOVED;
        break;
      }
      case DEVICE_SCANNED: {
        type = MOUNT_DEVICE_SCANNED;
        break;
      }
    }
    FireDeviceStatusUpdate(type, std::string(device_path));
  }

  void Init() {
    // Getting the monitor status so that the daemon starts up.
    mount_status_connection_ = MonitorMountEvents(
        &MonitorMountEventsHandler, this);
  }

  void FireDiskStatusUpdate(MountLibraryEventType evt,
                            const Disk* disk) {
    // Make sure we run on UI thread.
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    FOR_EACH_OBSERVER(
        Observer, observers_, DiskChanged(evt, disk));
  }

  void FireDeviceStatusUpdate(MountLibraryEventType evt,
                              const std::string& device_path) {
    // Make sure we run on UI thread.
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    FOR_EACH_OBSERVER(
        Observer, observers_, DeviceChanged(evt, device_path));
  }

  // Mount event change observers.
  ObserverList<Observer> observers_;

  // A reference to the  mount api, to allow callbacks when the mount
  // status changes.
  MountEventConnection mount_status_connection_;

  // The list of disks found.
  MountLibrary::DiskMap disks_;

  DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl);
};

class MountLibraryStubImpl : public MountLibrary {
 public:
  MountLibraryStubImpl() {}
  virtual ~MountLibraryStubImpl() {}

  // MountLibrary overrides.
  virtual void AddObserver(Observer* observer) OVERRIDE {}
  virtual void RemoveObserver(Observer* observer) OVERRIDE {}
  virtual const DiskMap& disks() const OVERRIDE { return disks_; }
  virtual void RequestMountInfoRefresh() OVERRIDE {}
  virtual void MountPath(const char* device_path) OVERRIDE {}
  virtual void UnmountPath(const char* device_path) OVERRIDE {}
  virtual bool IsBootPath(const char* device_path) OVERRIDE { return true; }

 private:
  // The list of disks found.
  DiskMap disks_;

  DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl);
};

// static
MountLibrary* MountLibrary::GetImpl(bool stub) {
  if (stub)
    return new MountLibraryStubImpl();
  else
    return new MountLibraryImpl();
}

}  // namespace chromeos

// Allows InvokeLater without adding refcounting. This class is a Singleton and
// won't be deleted until it's last InvokeLater is run.
DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::MountLibraryImpl);