From 4cbead4315188994d0554984777e0c21115f22c0 Mon Sep 17 00:00:00 2001 From: "hashimoto@chromium.org" Date: Sun, 26 Aug 2012 12:02:39 +0000 Subject: chromeos: Move src/chrome/browser/chromeos/disks to src/chromeos GetSizeStatsOnFileThread is moved from disk_mount_manager.cc to file_browser_private_api.cc BUG=None TEST=build TBR=estade@chromium.org, kaznacheev@chromium.org Review URL: https://chromiumcodereview.appspot.com/10874067 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153402 0039d316-1c4b-4281-b951-d872f2087c98 --- chromeos/disks/disk_mount_manager.cc | 674 ++++++++++++++++++++++++++++++ chromeos/disks/disk_mount_manager.h | 267 ++++++++++++ chromeos/disks/mock_disk_mount_manager.cc | 227 ++++++++++ chromeos/disks/mock_disk_mount_manager.h | 101 +++++ 4 files changed, 1269 insertions(+) create mode 100644 chromeos/disks/disk_mount_manager.cc create mode 100644 chromeos/disks/disk_mount_manager.h create mode 100644 chromeos/disks/mock_disk_mount_manager.cc create mode 100644 chromeos/disks/mock_disk_mount_manager.h (limited to 'chromeos/disks') diff --git a/chromeos/disks/disk_mount_manager.cc b/chromeos/disks/disk_mount_manager.cc new file mode 100644 index 0000000..828cebb --- /dev/null +++ b/chromeos/disks/disk_mount_manager.cc @@ -0,0 +1,674 @@ +// Copyright (c) 2012 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 "chromeos/disks/disk_mount_manager.h" + +#include +#include + +#include "base/bind.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/string_util.h" +#include "chromeos/dbus/dbus_thread_manager.h" + +namespace chromeos { +namespace disks { + +namespace { + +const char kDeviceNotFound[] = "Device could not be found"; + +DiskMountManager* g_disk_mount_manager = NULL; + +// The DiskMountManager implementation. +class DiskMountManagerImpl : public DiskMountManager { + public: + DiskMountManagerImpl() : weak_ptr_factory_(this) { + DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get(); + DCHECK(dbus_thread_manager); + cros_disks_client_ = dbus_thread_manager->GetCrosDisksClient(); + DCHECK(cros_disks_client_); + + cros_disks_client_->SetUpConnections( + base::Bind(&DiskMountManagerImpl::OnMountEvent, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&DiskMountManagerImpl::OnMountCompleted, + weak_ptr_factory_.GetWeakPtr())); + } + + virtual ~DiskMountManagerImpl() { + } + + // DiskMountManager override. + virtual void AddObserver(Observer* observer) OVERRIDE { + observers_.AddObserver(observer); + } + + // DiskMountManager override. + virtual void RemoveObserver(Observer* observer) OVERRIDE { + observers_.RemoveObserver(observer); + } + + // DiskMountManager override. + virtual void MountPath(const std::string& source_path, + const std::string& source_format, + const std::string& mount_label, + MountType type) OVERRIDE { + // Hidden and non-existent devices should not be mounted. + if (type == MOUNT_TYPE_DEVICE) { + DiskMap::const_iterator it = disks_.find(source_path); + if (it == disks_.end() || it->second->is_hidden()) { + OnMountCompleted(MOUNT_ERROR_INTERNAL, source_path, type, ""); + return; + } + } + cros_disks_client_->Mount( + source_path, + source_format, + mount_label, + type, + // When succeeds, OnMountCompleted will be called by + // "MountCompleted" signal instead. + base::Bind(&base::DoNothing), + base::Bind(&DiskMountManagerImpl::OnMountCompleted, + weak_ptr_factory_.GetWeakPtr(), + MOUNT_ERROR_INTERNAL, + source_path, + type, + "")); + } + + // DiskMountManager override. + virtual void UnmountPath(const std::string& mount_path) OVERRIDE { + cros_disks_client_->Unmount(mount_path, + base::Bind(&DiskMountManagerImpl::OnUnmountPath, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + } + + // DiskMountManager override. + virtual void FormatUnmountedDevice(const std::string& file_path) OVERRIDE { + for (DiskMountManager::DiskMap::iterator it = disks_.begin(); + it != disks_.end(); ++it) { + if (it->second->file_path() == file_path && + !it->second->mount_path().empty()) { + LOG(ERROR) << "Device is still mounted: " << file_path; + OnFormatDevice(file_path, false); + return; + } + } + const char kFormatVFAT[] = "vfat"; + cros_disks_client_->FormatDevice( + file_path, + kFormatVFAT, + base::Bind(&DiskMountManagerImpl::OnFormatDevice, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&DiskMountManagerImpl::OnFormatDevice, + weak_ptr_factory_.GetWeakPtr(), + file_path, + false)); + } + + // DiskMountManager override. + virtual void FormatMountedDevice(const std::string& mount_path) OVERRIDE { + Disk* disk = NULL; + for (DiskMountManager::DiskMap::iterator it = disks_.begin(); + it != disks_.end(); ++it) { + if (it->second->mount_path() == mount_path) { + disk = it->second; + break; + } + } + if (!disk) { + LOG(ERROR) << "Device with this mount path not found: " << mount_path; + OnFormatDevice(mount_path, false); + return; + } + if (formatting_pending_.find(disk->device_path()) != + formatting_pending_.end()) { + LOG(ERROR) << "Formatting is already pending: " << mount_path; + OnFormatDevice(mount_path, false); + return; + } + // Formatting process continues, after unmounting. + formatting_pending_[disk->device_path()] = disk->file_path(); + UnmountPath(disk->mount_path()); + } + + // DiskMountManager override. + virtual void UnmountDeviceRecursive( + const std::string& device_path, + UnmountDeviceRecursiveCallbackType callback, + void* user_data) OVERRIDE { + bool success = true; + std::string error_message; + std::vector devices_to_unmount; + + // Get list of all devices to unmount. + int device_path_len = device_path.length(); + for (DiskMap::iterator it = disks_.begin(); it != disks_.end(); ++it) { + if (!it->second->mount_path().empty() && + strncmp(device_path.c_str(), it->second->device_path().c_str(), + device_path_len) == 0) { + devices_to_unmount.push_back(it->second->mount_path()); + } + } + // We should detect at least original device. + if (devices_to_unmount.empty()) { + if (disks_.find(device_path) == disks_.end()) { + success = false; + error_message = kDeviceNotFound; + } else { + // Nothing to unmount. + callback(user_data, true); + return; + } + } + if (success) { + // We will send the same callback data object to all Unmount calls and use + // it to syncronize callbacks. + UnmountDeviceRecursiveCallbackData* cb_data = + new UnmountDeviceRecursiveCallbackData(user_data, callback, + devices_to_unmount.size()); + for (size_t i = 0; i < devices_to_unmount.size(); ++i) { + cros_disks_client_->Unmount( + devices_to_unmount[i], + base::Bind(&DiskMountManagerImpl::OnUnmountDeviceRecursive, + weak_ptr_factory_.GetWeakPtr(), cb_data, true), + base::Bind(&DiskMountManagerImpl::OnUnmountDeviceRecursive, + weak_ptr_factory_.GetWeakPtr(), + cb_data, + false, + devices_to_unmount[i])); + } + } else { + LOG(WARNING) << "Unmount recursive request failed for device " + << device_path << ", with error: " << error_message; + callback(user_data, false); + } + } + + // DiskMountManager override. + virtual void RequestMountInfoRefresh() OVERRIDE { + cros_disks_client_->EnumerateAutoMountableDevices( + base::Bind(&DiskMountManagerImpl::OnRequestMountInfo, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + } + + // DiskMountManager override. + const DiskMap& disks() const OVERRIDE { return disks_; } + + // DiskMountManager override. + virtual const Disk* FindDiskBySourcePath(const std::string& source_path) + const OVERRIDE { + DiskMap::const_iterator disk_it = disks_.find(source_path); + return disk_it == disks_.end() ? NULL : disk_it->second; + } + + // DiskMountManager override. + const MountPointMap& mount_points() const OVERRIDE { return mount_points_; } + + private: + struct UnmountDeviceRecursiveCallbackData { + void* user_data; + UnmountDeviceRecursiveCallbackType callback; + size_t pending_callbacks_count; + + UnmountDeviceRecursiveCallbackData(void* ud, + UnmountDeviceRecursiveCallbackType cb, + int count) + : user_data(ud), + callback(cb), + pending_callbacks_count(count) { + } + }; + + // Callback for UnmountDeviceRecursive. + void OnUnmountDeviceRecursive(UnmountDeviceRecursiveCallbackData* cb_data, + bool success, + const std::string& mount_path) { + if (success) { + // Do standard processing for Unmount event. + OnUnmountPath(mount_path); + LOG(INFO) << mount_path << " unmounted."; + } + // This is safe as long as all callbacks are called on the same thread as + // UnmountDeviceRecursive. + cb_data->pending_callbacks_count--; + + if (cb_data->pending_callbacks_count == 0) { + cb_data->callback(cb_data->user_data, success); + delete cb_data; + } + } + + // Callback to handle MountCompleted signal and Mount method call failure. + void OnMountCompleted(MountError error_code, + const std::string& source_path, + MountType mount_type, + const std::string& mount_path) { + MountCondition mount_condition = MOUNT_CONDITION_NONE; + if (mount_type == MOUNT_TYPE_DEVICE) { + if (error_code == MOUNT_ERROR_UNKNOWN_FILESYSTEM) { + mount_condition = MOUNT_CONDITION_UNKNOWN_FILESYSTEM; + } + if (error_code == MOUNT_ERROR_UNSUPPORTED_FILESYSTEM) { + mount_condition = MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM; + } + } + const MountPointInfo mount_info(source_path, mount_path, mount_type, + mount_condition); + + NotifyMountCompleted(MOUNTING, error_code, mount_info); + + // If the device is corrupted but it's still possible to format it, it will + // be fake mounted. + if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && + mount_points_.find(mount_info.mount_path) == mount_points_.end()) { + mount_points_.insert(MountPointMap::value_type(mount_info.mount_path, + mount_info)); + } + if ((error_code == MOUNT_ERROR_NONE || mount_info.mount_condition) && + mount_info.mount_type == MOUNT_TYPE_DEVICE && + !mount_info.source_path.empty() && + !mount_info.mount_path.empty()) { + DiskMap::iterator iter = disks_.find(mount_info.source_path); + if (iter == disks_.end()) { + // disk might have been removed by now? + return; + } + Disk* disk = iter->second; + DCHECK(disk); + disk->set_mount_path(mount_info.mount_path); + NotifyDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk); + } + } + + // Callback for UnmountPath. + void OnUnmountPath(const std::string& mount_path) { + MountPointMap::iterator mount_points_it = mount_points_.find(mount_path); + if (mount_points_it == mount_points_.end()) + return; + // TODO(tbarzic): Add separate, PathUnmounted event to Observer. + NotifyMountCompleted( + UNMOUNTING, MOUNT_ERROR_NONE, + MountPointInfo(mount_points_it->second.source_path, + mount_points_it->second.mount_path, + mount_points_it->second.mount_type, + mount_points_it->second.mount_condition)); + std::string path(mount_points_it->second.source_path); + mount_points_.erase(mount_points_it); + 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(); + // Check if there is a formatting scheduled. + PathMap::iterator it = formatting_pending_.find(disk->device_path()); + if (it != formatting_pending_.end()) { + // Copy the string before it gets erased. + const std::string file_path = it->second; + formatting_pending_.erase(it); + FormatUnmountedDevice(file_path); + } + } + + // Callback for FormatDevice. + void OnFormatDevice(const std::string& device_path, bool success) { + if (success) { + NotifyDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, device_path); + } else { + NotifyDeviceStatusUpdate(MOUNT_FORMATTING_STARTED, + std::string("!") + device_path); + LOG(WARNING) << "Format request failed for device " << device_path; + } + } + + // Callbcak for GetDeviceProperties. + void OnGetDeviceProperties(const DiskInfo& disk_info) { + // 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. + if (disk_info.on_boot_device()) + return; + + LOG(WARNING) << "Found disk " << disk_info.device_path(); + // Delete previous disk info for this path: + bool is_new = true; + DiskMap::iterator iter = disks_.find(disk_info.device_path()); + if (iter != disks_.end()) { + delete iter->second; + disks_.erase(iter); + is_new = false; + } + Disk* disk = new Disk(disk_info.device_path(), + disk_info.mount_path(), + disk_info.system_path(), + disk_info.file_path(), + disk_info.label(), + disk_info.drive_label(), + disk_info.uuid(), + FindSystemPathPrefix(disk_info.system_path()), + disk_info.device_type(), + disk_info.total_size_in_bytes(), + disk_info.is_drive(), + disk_info.is_read_only(), + disk_info.has_media(), + disk_info.on_boot_device(), + disk_info.is_hidden()); + disks_.insert(std::make_pair(disk_info.device_path(), disk)); + NotifyDiskStatusUpdate(is_new ? MOUNT_DISK_ADDED : MOUNT_DISK_CHANGED, + disk); + } + + // Callbcak for RequestMountInfo. + void OnRequestMountInfo(const std::vector& devices) { + std::set current_device_set; + if (!devices.empty()) { + // Initiate properties fetch for all removable disks, + for (size_t i = 0; i < devices.size(); i++) { + current_device_set.insert(devices[i]); + // Initiate disk property retrieval for each relevant device path. + cros_disks_client_->GetDeviceProperties( + devices[i], + base::Bind(&DiskMountManagerImpl::OnGetDeviceProperties, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + } + } + // Search and remove disks that are no longer present. + for (DiskMap::iterator iter = disks_.begin(); iter != disks_.end(); ) { + if (current_device_set.find(iter->first) == current_device_set.end()) { + Disk* disk = iter->second; + NotifyDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); + delete iter->second; + disks_.erase(iter++); + } else { + ++iter; + } + } + } + + // Callback to handle mount event signals. + void OnMountEvent(MountEventType event, const std::string& device_path_arg) { + // Take a copy of the argument so we can modify it below. + std::string device_path = device_path_arg; + DiskMountManagerEventType type = MOUNT_DEVICE_ADDED; + switch (event) { + case DISK_ADDED: { + cros_disks_client_->GetDeviceProperties( + device_path, + base::Bind(&DiskMountManagerImpl::OnGetDeviceProperties, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&base::DoNothing)); + return; + } + case DISK_REMOVED: { + // Search and remove disks that are no longer present. + DiskMountManager::DiskMap::iterator iter = disks_.find(device_path); + if (iter != disks_.end()) { + Disk* disk = iter->second; + NotifyDiskStatusUpdate(MOUNT_DISK_REMOVED, disk); + delete iter->second; + disks_.erase(iter); + } + return; + } + case DEVICE_ADDED: { + type = MOUNT_DEVICE_ADDED; + system_path_prefixes_.insert(device_path); + break; + } + case DEVICE_REMOVED: { + type = MOUNT_DEVICE_REMOVED; + system_path_prefixes_.erase(device_path); + break; + } + case DEVICE_SCANNED: { + type = MOUNT_DEVICE_SCANNED; + break; + } + case FORMATTING_FINISHED: { + // FORMATTING_FINISHED actually returns file path instead of device + // path. + device_path = FilePathToDevicePath(device_path); + if (device_path.empty()) { + LOG(ERROR) << "Error while handling disks metadata. Cannot find " + << "device that is being formatted."; + return; + } + type = MOUNT_FORMATTING_FINISHED; + break; + } + default: { + LOG(ERROR) << "Unknown event: " << event; + return; + } + } + NotifyDeviceStatusUpdate(type, device_path); + } + + // Notifies all observers about disk status update. + void NotifyDiskStatusUpdate(DiskMountManagerEventType event, + const Disk* disk) { + FOR_EACH_OBSERVER(Observer, observers_, DiskChanged(event, disk)); + } + + // Notifies all observers about device status update. + void NotifyDeviceStatusUpdate(DiskMountManagerEventType event, + const std::string& device_path) { + FOR_EACH_OBSERVER(Observer, observers_, DeviceChanged(event, device_path)); + } + + // Notifies all observers about mount completion. + void NotifyMountCompleted(MountEvent event_type, + MountError error_code, + const MountPointInfo& mount_info) { + FOR_EACH_OBSERVER(Observer, observers_, + MountCompleted(event_type, error_code, mount_info)); + } + + // Converts file path to device path. + std::string FilePathToDevicePath(const std::string& file_path) { + // TODO(hashimoto): Refactor error handling code like here. + // Appending "!" is not the best way to indicate error. This kind of trick + // also makes it difficult to simplify the code paths. crosbug.com/22972 + const int failed = StartsWithASCII(file_path, "!", true); + for (DiskMountManager::DiskMap::iterator it = disks_.begin(); + it != disks_.end(); ++it) { + // Skip the leading '!' on the failure case. + if (it->second->file_path() == file_path.substr(failed)) { + if (failed) + return std::string("!") + it->second->device_path(); + else + return it->second->device_path(); + } + } + return ""; + } + + // Finds system path prefix from |system_path|. + const std::string& FindSystemPathPrefix(const std::string& system_path) { + if (system_path.empty()) + return EmptyString(); + for (SystemPathPrefixSet::const_iterator it = system_path_prefixes_.begin(); + it != system_path_prefixes_.end(); + ++it) { + const std::string& prefix = *it; + if (StartsWithASCII(system_path, prefix, true)) + return prefix; + } + return EmptyString(); + } + + // Mount event change observers. + ObserverList observers_; + + CrosDisksClient* cros_disks_client_; + + // The list of disks found. + DiskMountManager::DiskMap disks_; + + DiskMountManager::MountPointMap mount_points_; + + typedef std::set SystemPathPrefixSet; + SystemPathPrefixSet system_path_prefixes_; + + // A map from device path (e.g. /sys/devices/pci0000:00/.../sdb/sdb1)) to file + // path (e.g. /dev/sdb). + // Devices in this map are supposed to be formatted, but are currently waiting + // to be unmounted. When device is in this map, the formatting process HAVEN'T + // started yet. + typedef std::map PathMap; + PathMap formatting_pending_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DiskMountManagerImpl); +}; + +} // namespace + +DiskMountManager::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& fs_uuid, + const std::string& system_path_prefix, + DeviceType device_type, + uint64 total_size_in_bytes, + bool is_parent, + bool is_read_only, + bool has_media, + bool on_boot_device, + bool is_hidden) + : device_path_(device_path), + mount_path_(mount_path), + system_path_(system_path), + file_path_(file_path), + device_label_(device_label), + drive_label_(drive_label), + fs_uuid_(fs_uuid), + system_path_prefix_(system_path_prefix), + device_type_(device_type), + total_size_in_bytes_(total_size_in_bytes), + is_parent_(is_parent), + is_read_only_(is_read_only), + has_media_(has_media), + on_boot_device_(on_boot_device), + is_hidden_(is_hidden) { +} + +DiskMountManager::Disk::~Disk() {} + +// static +std::string DiskMountManager::MountTypeToString(MountType type) { + switch (type) { + case MOUNT_TYPE_DEVICE: + return "device"; + case MOUNT_TYPE_ARCHIVE: + return "file"; + case MOUNT_TYPE_NETWORK_STORAGE: + return "network"; + case MOUNT_TYPE_GDATA: + return "gdata"; + case MOUNT_TYPE_INVALID: + return "invalid"; + default: + NOTREACHED(); + } + return ""; +} + +// static +std::string DiskMountManager::MountConditionToString(MountCondition condition) { + switch (condition) { + case MOUNT_CONDITION_NONE: + return ""; + case MOUNT_CONDITION_UNKNOWN_FILESYSTEM: + return "unknown_filesystem"; + case MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM: + return "unsupported_filesystem"; + default: + NOTREACHED(); + } + return ""; +} + +// static +MountType DiskMountManager::MountTypeFromString(const std::string& type_str) { + if (type_str == "device") + return MOUNT_TYPE_DEVICE; + else if (type_str == "network") + return MOUNT_TYPE_NETWORK_STORAGE; + else if (type_str == "file") + return MOUNT_TYPE_ARCHIVE; + else if (type_str == "gdata") + return MOUNT_TYPE_GDATA; + else + return MOUNT_TYPE_INVALID; +} + +// static +std::string DiskMountManager::DeviceTypeToString(DeviceType type) { + switch (type) { + case DEVICE_TYPE_USB: + return "usb"; + case DEVICE_TYPE_SD: + return "sd"; + case DEVICE_TYPE_OPTICAL_DISC: + return "optical"; + case DEVICE_TYPE_MOBILE: + return "mobile"; + default: + return "unknown"; + } +} + +// static +void DiskMountManager::Initialize() { + if (g_disk_mount_manager) { + LOG(WARNING) << "DiskMountManager was already initialized"; + return; + } + g_disk_mount_manager = new DiskMountManagerImpl(); + VLOG(1) << "DiskMountManager initialized"; +} + +// static +void DiskMountManager::InitializeForTesting( + DiskMountManager* disk_mount_manager) { + if (g_disk_mount_manager) { + LOG(WARNING) << "DiskMountManager was already initialized"; + return; + } + g_disk_mount_manager = disk_mount_manager; + VLOG(1) << "DiskMountManager initialized"; +} + +// static +void DiskMountManager::Shutdown() { + if (!g_disk_mount_manager) { + LOG(WARNING) << "DiskMountManager::Shutdown() called with NULL manager"; + return; + } + delete g_disk_mount_manager; + g_disk_mount_manager = NULL; + VLOG(1) << "DiskMountManager Shutdown completed"; +} + +// static +DiskMountManager* DiskMountManager::GetInstance() { + return g_disk_mount_manager; +} + +} // namespace disks +} // namespace chromeos diff --git a/chromeos/disks/disk_mount_manager.h b/chromeos/disks/disk_mount_manager.h new file mode 100644 index 0000000..dfe80c3 --- /dev/null +++ b/chromeos/disks/disk_mount_manager.h @@ -0,0 +1,267 @@ +// Copyright (c) 2012 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. + +#ifndef CHROMEOS_DISKS_DISK_MOUNT_MANAGER_H_ +#define CHROMEOS_DISKS_DISK_MOUNT_MANAGER_H_ + +#include + +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/cros_disks_client.h" + +namespace chromeos { +namespace disks { + +// Types of events DiskMountManager sends to its observers. +enum DiskMountManagerEventType { + MOUNT_DISK_ADDED, + MOUNT_DISK_REMOVED, + MOUNT_DISK_CHANGED, + MOUNT_DISK_MOUNTED, + MOUNT_DISK_UNMOUNTED, + MOUNT_DEVICE_ADDED, + MOUNT_DEVICE_REMOVED, + MOUNT_DEVICE_SCANNED, + MOUNT_FORMATTING_STARTED, + MOUNT_FORMATTING_FINISHED, +}; + +// Condition of mounted filesystem. +enum MountCondition { + MOUNT_CONDITION_NONE, + MOUNT_CONDITION_UNKNOWN_FILESYSTEM, + MOUNT_CONDITION_UNSUPPORTED_FILESYSTEM, +}; + +// This class handles the interaction with cros-disks. +// Other classes can add themselves as observers. +class CHROMEOS_EXPORT DiskMountManager { + public: + // Event type given to observers' MountCompleted method. + enum MountEvent { + MOUNTING, + UNMOUNTING, + }; + + // Used to house an instance of each found mount device. + class Disk { + public: + 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& fs_uuid, + const std::string& system_path_prefix, + DeviceType device_type, + uint64 total_size_in_bytes, + bool is_parent, + bool is_read_only, + bool has_media, + bool on_boot_device, + bool is_hidden); + ~Disk(); + + // The path of the device, used by devicekit-disks. + // (e.g. /sys/devices/pci0000:00/.../8:0:0:0/block/sdb/sdb1) + const std::string& device_path() const { return device_path_; } + + // The path to the mount point of this device. Will be empty if not mounted. + // (e.g. /media/removable/VOLUME) + const std::string& mount_path() const { return mount_path_; } + + // The path of the device according to the udev system. + // (e.g. /sys/devices/pci0000:00/.../8:0:0:0/block/sdb/sdb1) + const std::string& system_path() const { return system_path_; } + + // The path of the device according to filesystem. + // (e.g. /dev/sdb) + const std::string& file_path() const { return file_path_; } + + // Device's label. + const std::string& device_label() const { return device_label_; } + + // If disk is a parent, then its label, else parents label. + // (e.g. "TransMemory") + const std::string& drive_label() const { return drive_label_; } + + // Returns the file system uuid string. + const std::string& fs_uuid() const { return fs_uuid_; } + + // Path of the system device this device's block is a part of. + // (e.g. /sys/devices/pci0000:00/.../8:0:0:0/) + const std::string& system_path_prefix() const { + return system_path_prefix_; + } + + // Device type. + DeviceType device_type() const { return device_type_; } + + // Total size of the device in bytes. + uint64 total_size_in_bytes() const { return total_size_in_bytes_; } + + // Is the device is a parent device (i.e. sdb rather than sdb1). + bool is_parent() const { return is_parent_; } + + // Is the device read only. + bool is_read_only() const { return is_read_only_; } + + // Does the device contains media. + bool has_media() const { return has_media_; } + + // Is the device on the boot device. + bool on_boot_device() const { return on_boot_device_; } + + // Shoud the device be shown in the UI, or automounted. + bool is_hidden() const { return is_hidden_; } + + void set_mount_path(const std::string& mount_path) { + mount_path_ = mount_path; + } + + void clear_mount_path() { mount_path_.clear(); } + + private: + std::string device_path_; + std::string mount_path_; + std::string system_path_; + std::string file_path_; + std::string device_label_; + std::string drive_label_; + std::string fs_uuid_; + std::string system_path_prefix_; + DeviceType device_type_; + uint64 total_size_in_bytes_; + bool is_parent_; + bool is_read_only_; + bool has_media_; + bool on_boot_device_; + bool is_hidden_; + }; + typedef std::map DiskMap; + + // A struct to store information about mount point. + struct MountPointInfo { + // Device's path. + std::string source_path; + // Mounted path. + std::string mount_path; + // Type of mount. + MountType mount_type; + // Condition of mount. + MountCondition mount_condition; + + MountPointInfo(const std::string& source, + const std::string& mount, + const MountType type, + MountCondition condition) + : source_path(source), + mount_path(mount), + mount_type(type), + mount_condition(condition) { + } + }; + + // MountPointMap key is mount_path. + typedef std::map MountPointMap; + + // A callback function type which is called after UnmountDeviceRecursive + // finishes. + typedef void(*UnmountDeviceRecursiveCallbackType)(void*, bool); + + // Implement this interface to be notified about disk/mount related events. + class Observer { + public: + virtual ~Observer() {} + + // A function called when disk mount status is changed. + virtual void DiskChanged(DiskMountManagerEventType event, + const Disk* disk) = 0; + // A function called when device status is changed. + virtual void DeviceChanged(DiskMountManagerEventType event, + const std::string& device_path) = 0; + // A function called after mount is completed. + virtual void MountCompleted(MountEvent event_type, + MountError error_code, + const MountPointInfo& mount_info) = 0; + }; + + virtual ~DiskMountManager() {} + + // Adds an observer. + virtual void AddObserver(Observer* observer) = 0; + + // Removes an observer. + virtual void RemoveObserver(Observer* observer) = 0; + + // Gets the list of disks found. + virtual const DiskMap& disks() const = 0; + + // Returns Disk object corresponding to |source_path| or NULL on failure. + virtual const Disk* FindDiskBySourcePath( + const std::string& source_path) const = 0; + + // Gets the list of mount points. + virtual const MountPointMap& mount_points() const = 0; + + // Requests refreshing all the information about mounted disks. + virtual void RequestMountInfoRefresh() = 0; + + // Mounts a device. + virtual void MountPath(const std::string& source_path, + const std::string& source_format, + const std::string& mount_label, + MountType type) = 0; + + // Unmounts a mounted disk. + virtual void UnmountPath(const std::string& mount_path) = 0; + + // Formats device given its file path. + // Example: file_path: /dev/sdb1 + virtual void FormatUnmountedDevice(const std::string& file_path) = 0; + + // Formats Device given its mount path. Unmounts the device. + // Example: mount_path: /media/VOLUME_LABEL + virtual void FormatMountedDevice(const std::string& mount_path) = 0; + + // Unmounts device_path and all of its known children. + virtual void UnmountDeviceRecursive( + const std::string& device_path, + UnmountDeviceRecursiveCallbackType callback, + void* user_data) = 0; + + // Returns corresponding string to |type| like "device" or "file". + static std::string MountTypeToString(MountType type); + + // The inverse function of MountTypeToString. + static MountType MountTypeFromString(const std::string& type_str); + + // Returns corresponding string to |type| like "unknown_filesystem". + static std::string MountConditionToString(MountCondition type); + + // Returns corresponding string to |type|, like "sd", "usb". + static std::string DeviceTypeToString(DeviceType type); + + // Creates the global DiskMountManager instance. + static void Initialize(); + + // Similar to Initialize(), but can inject an alternative + // DiskMountManager such as MockDiskMountManager for testing. + // The injected object will be owned by the internal pointer and deleted + // by Shutdown(). + static void InitializeForTesting(DiskMountManager* disk_mount_manager); + + // Destroys the global DiskMountManager instance if it exists. + static void Shutdown(); + + // Returns a pointer to the global DiskMountManager instance. + // Initialize() should already have been called. + static DiskMountManager* GetInstance(); +}; + +} // namespace disks +} // namespace chromeos + +#endif // CHROMEOS_DISKS_DISK_MOUNT_MANAGER_H_ diff --git a/chromeos/disks/mock_disk_mount_manager.cc b/chromeos/disks/mock_disk_mount_manager.cc new file mode 100644 index 0000000..0b67c9a --- /dev/null +++ b/chromeos/disks/mock_disk_mount_manager.cc @@ -0,0 +1,227 @@ +// Copyright (c) 2012 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 "chromeos/disks/mock_disk_mount_manager.h" + +#include + +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "base/string_util.h" + +using testing::_; +using testing::AnyNumber; +using testing::Invoke; +using testing::ReturnRef; + +namespace chromeos { +namespace disks { + +namespace { + +const char* kTestSystemPath = "/this/system/path"; +const char* kTestSystemPathPrefix = "/this/system"; +const char* kTestDevicePath = "/this/device/path"; +const char* kTestMountPath = "/media/foofoo"; +const char* kTestFilePath = "/this/file/path"; +const char* kTestDeviceLabel = "A label"; +const char* kTestDriveLabel = "Another label"; +const char* kTestUuid = "FFFF-FFFF"; + +} // namespace + +void MockDiskMountManager::AddObserverInternal( + DiskMountManager::Observer* observer) { + observers_.AddObserver(observer); +} + +void MockDiskMountManager::RemoveObserverInternal( + DiskMountManager::Observer* observer) { + observers_.RemoveObserver(observer); +} + +MockDiskMountManager::MockDiskMountManager() { + ON_CALL(*this, AddObserver(_)) + .WillByDefault(Invoke(this, &MockDiskMountManager::AddObserverInternal)); + ON_CALL(*this, RemoveObserver(_)) + .WillByDefault(Invoke(this, + &MockDiskMountManager::RemoveObserverInternal)); + ON_CALL(*this, disks()) + .WillByDefault(Invoke(this, &MockDiskMountManager::disksInternal)); + ON_CALL(*this, mount_points()) + .WillByDefault(Invoke(this, &MockDiskMountManager::mountPointsInternal)); + ON_CALL(*this, FindDiskBySourcePath(_)) + .WillByDefault(Invoke( + this, &MockDiskMountManager::FindDiskBySourcePathInternal)); +} + +MockDiskMountManager::~MockDiskMountManager() { + STLDeleteContainerPairSecondPointers(disks_.begin(), disks_.end()); + disks_.clear(); +} + +void MockDiskMountManager::NotifyDeviceInsertEvents() { + scoped_ptr disk1(new DiskMountManager::Disk( + std::string(kTestDevicePath), + std::string(), + std::string(kTestSystemPath), + std::string(kTestFilePath), + std::string(), + std::string(kTestDriveLabel), + std::string(kTestUuid), + std::string(kTestSystemPathPrefix), + DEVICE_TYPE_USB, + 4294967295U, + false, // is_parent + false, // is_read_only + true, // has_media + false, // on_boot_device + false)); // is_hidden + + disks_.clear(); + disks_.insert(std::pair( + std::string(kTestDevicePath), disk1.get())); + + // Device Added + DiskMountManagerEventType event; + event = MOUNT_DEVICE_ADDED; + NotifyDeviceChanged(event, kTestSystemPath); + + // Disk Added + event = MOUNT_DISK_ADDED; + NotifyDiskChanged(event, disk1.get()); + + // Disk Changed + scoped_ptr disk2(new DiskMountManager::Disk( + std::string(kTestDevicePath), + std::string(kTestMountPath), + std::string(kTestSystemPath), + std::string(kTestFilePath), + std::string(kTestDeviceLabel), + std::string(kTestDriveLabel), + std::string(kTestUuid), + std::string(kTestSystemPathPrefix), + DEVICE_TYPE_MOBILE, + 1073741824, + false, // is_parent + false, // is_read_only + true, // has_media + false, // on_boot_device + false)); // is_hidden + disks_.clear(); + disks_.insert(std::pair( + std::string(kTestDevicePath), disk2.get())); + event = MOUNT_DISK_CHANGED; + NotifyDiskChanged(event, disk2.get()); +} + +void MockDiskMountManager::NotifyDeviceRemoveEvents() { + scoped_ptr disk(new DiskMountManager::Disk( + std::string(kTestDevicePath), + std::string(kTestMountPath), + std::string(kTestSystemPath), + std::string(kTestFilePath), + std::string(kTestDeviceLabel), + std::string(kTestDriveLabel), + std::string(kTestUuid), + std::string(kTestSystemPathPrefix), + DEVICE_TYPE_SD, + 1073741824, + false, // is_parent + false, // is_read_only + true, // has_media + false, // on_boot_device + false)); // is_hidden + disks_.clear(); + disks_.insert(std::pair( + std::string(kTestDevicePath), disk.get())); + NotifyDiskChanged(MOUNT_DISK_REMOVED, disk.get()); +} + +void MockDiskMountManager::SetupDefaultReplies() { + EXPECT_CALL(*this, AddObserver(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, RemoveObserver(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, disks()) + .WillRepeatedly(ReturnRef(disks_)); + EXPECT_CALL(*this, mount_points()) + .WillRepeatedly(ReturnRef(mount_points_)); + EXPECT_CALL(*this, FindDiskBySourcePath(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, RequestMountInfoRefresh()) + .Times(AnyNumber()); + EXPECT_CALL(*this, MountPath(_, _, _, _)) + .Times(AnyNumber()); + EXPECT_CALL(*this, UnmountPath(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, FormatUnmountedDevice(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, FormatMountedDevice(_)) + .Times(AnyNumber()); + EXPECT_CALL(*this, UnmountDeviceRecursive(_, _, _)) + .Times(AnyNumber()); +} + +void MockDiskMountManager::CreateDiskEntryForMountDevice( + const DiskMountManager::MountPointInfo& mount_info, + const std::string& device_id) { + Disk* disk = new DiskMountManager::Disk(std::string(mount_info.source_path), + std::string(mount_info.mount_path), + std::string(), // system_path + std::string(), // file_path + std::string(), // device_label + std::string(), // drive_label + device_id, // fs_uuid + std::string(), // system_path_prefix + DEVICE_TYPE_USB, // device_type + 1073741824, // total_size_in_bytes + false, // is_parent + false, // is_read_only + true, // has_media + false, // on_boot_device + false); // is_hidden + DiskMountManager::DiskMap::iterator it = disks_.find(mount_info.source_path); + if (it == disks_.end()) { + disks_.insert(std::make_pair(std::string(mount_info.source_path), disk)); + } else { + delete it->second; + it->second = disk; + } +} + +void MockDiskMountManager::RemoveDiskEntryForMountDevice( + const DiskMountManager::MountPointInfo& mount_info) { + DiskMountManager::DiskMap::iterator it = disks_.find(mount_info.source_path); + if (it != disks_.end()) { + delete it->second; + disks_.erase(it); + } +} + +const DiskMountManager::MountPointMap& +MockDiskMountManager::mountPointsInternal() const { + return mount_points_; +} + +const DiskMountManager::Disk* +MockDiskMountManager::FindDiskBySourcePathInternal( + const std::string& source_path) const { + DiskMap::const_iterator disk_it = disks_.find(source_path); + return disk_it == disks_.end() ? NULL : disk_it->second; +} + +void MockDiskMountManager::NotifyDiskChanged( + DiskMountManagerEventType event, + const DiskMountManager::Disk* disk) { + FOR_EACH_OBSERVER(Observer, observers_, DiskChanged(event, disk)); +} + +void MockDiskMountManager::NotifyDeviceChanged(DiskMountManagerEventType event, + const std::string& path) { + FOR_EACH_OBSERVER(Observer, observers_, DeviceChanged(event, path)); +} + +} // namespace disks +} // namespace chromeos diff --git a/chromeos/disks/mock_disk_mount_manager.h b/chromeos/disks/mock_disk_mount_manager.h new file mode 100644 index 0000000..169f33d --- /dev/null +++ b/chromeos/disks/mock_disk_mount_manager.h @@ -0,0 +1,101 @@ +// Copyright (c) 2012 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. + +#ifndef CHROMEOS_DISKS_MOCK_DISK_MOUNT_MANAGER_H_ +#define CHROMEOS_DISKS_MOCK_DISK_MOUNT_MANAGER_H_ + +#include + +#include "base/observer_list.h" +#include "base/time.h" +#include "chromeos/disks/disk_mount_manager.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace disks { + +class MockDiskMountManager : public DiskMountManager { + public: + MockDiskMountManager(); + virtual ~MockDiskMountManager(); + + // DiskMountManager override. + MOCK_METHOD0(Init, void(void)); + MOCK_METHOD1(AddObserver, void(DiskMountManager::Observer*)); + MOCK_METHOD1(RemoveObserver, void(DiskMountManager::Observer*)); + MOCK_CONST_METHOD0(disks, const DiskMountManager::DiskMap&(void)); + MOCK_CONST_METHOD1(FindDiskBySourcePath, + const DiskMountManager::Disk*(const std::string&)); + MOCK_CONST_METHOD0(mount_points, + const DiskMountManager::MountPointMap&(void)); + MOCK_METHOD0(RequestMountInfoRefresh, void(void)); + MOCK_METHOD4(MountPath, void(const std::string&, const std::string&, + const std::string&, MountType)); + MOCK_METHOD1(UnmountPath, void(const std::string&)); + MOCK_METHOD1(FormatUnmountedDevice, void(const std::string&)); + MOCK_METHOD1(FormatMountedDevice, void(const std::string&)); + MOCK_METHOD3(UnmountDeviceRecursive, void(const std::string&, + DiskMountManager::UnmountDeviceRecursiveCallbackType, void*)); + + // Invokes fake device insert events. + void NotifyDeviceInsertEvents(); + + // Invokes fake device remove events. + void NotifyDeviceRemoveEvents(); + + // Sets up default results for mock methods. + void SetupDefaultReplies(); + + // Creates a fake disk entry for the mounted device. This function is + // primarily for MediaDeviceNotificationsTest. + void CreateDiskEntryForMountDevice( + const DiskMountManager::MountPointInfo& mount_info, + const std::string& device_id); + + // Removes the fake disk entry associated with the mounted device. This + // function is primarily for MediaDeviceNotificationsTest. + void RemoveDiskEntryForMountDevice( + const DiskMountManager::MountPointInfo& mount_info); + + private: + // Is used to implement AddObserver. + void AddObserverInternal(DiskMountManager::Observer* observer); + + // Is used to implement RemoveObserver. + void RemoveObserverInternal(DiskMountManager::Observer* observer); + + // Is used to implement disks. + const DiskMountManager::DiskMap& disksInternal() const { return disks_; } + + const DiskMountManager::MountPointMap& mountPointsInternal() const; + + // Returns Disk object associated with the |source_path| or NULL on failure. + const DiskMountManager::Disk* FindDiskBySourcePathInternal( + const std::string& source_path) const; + + // Notifies observers about device status update. + void NotifyDeviceChanged(DiskMountManagerEventType event, + const std::string& path); + + // Notifies observers about disk status update. + void NotifyDiskChanged(DiskMountManagerEventType event, + const DiskMountManager::Disk* disk); + + // The list of observers. + ObserverList observers_; + + // The list of disks found. + DiskMountManager::DiskMap disks_; + + // The list of existing mount points. + DiskMountManager::MountPointMap mount_points_; + + DISALLOW_COPY_AND_ASSIGN(MockDiskMountManager); +}; + +} // namespace disks +} // namespace chromeos + +#endif // CHROMEOS_DISKS_MOCK_DISK_MOUNT_MANAGER_H_ -- cgit v1.1