123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125 |
- // 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 "ash/components/disks/disk_mount_manager.h"
- #include <stddef.h>
- #include <stdint.h>
- #include <map>
- #include <memory>
- #include <set>
- #include <string>
- #include <type_traits>
- #include <utility>
- #include <vector>
- #include "ash/components/disks/disk.h"
- #include "ash/components/disks/suspend_unmount_manager.h"
- #include "base/barrier_closure.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/logging.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/observer_list.h"
- #include "base/strings/string_util.h"
- #include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
- namespace ash::disks {
- namespace {
- using base::BindOnce;
- DiskMountManager* g_disk_mount_manager = nullptr;
- struct UnmountDeviceRecursivelyCallbackData {
- explicit UnmountDeviceRecursivelyCallbackData(
- DiskMountManager::UnmountDeviceRecursivelyCallbackType in_callback)
- : callback(std::move(in_callback)) {}
- DiskMountManager::UnmountDeviceRecursivelyCallbackType callback;
- MountError error_code = MountError::kNone;
- };
- void OnAllUnmountDeviceRecursively(
- std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data) {
- std::move(cb_data->callback).Run(cb_data->error_code);
- }
- std::string FormatFileSystemTypeToString(FormatFileSystemType filesystem) {
- switch (filesystem) {
- case FormatFileSystemType::kUnknown:
- return "";
- case FormatFileSystemType::kVfat:
- return "vfat";
- case FormatFileSystemType::kExfat:
- return "exfat";
- case FormatFileSystemType::kNtfs:
- return "ntfs";
- }
- NOTREACHED() << "Unknown filesystem type " << static_cast<int>(filesystem);
- return "";
- }
- // The DiskMountManager implementation.
- class DiskMountManagerImpl : public DiskMountManager,
- public CrosDisksClient::Observer {
- public:
- DiskMountManagerImpl() { cros_disks_client_->AddObserver(this); }
- DiskMountManagerImpl(const DiskMountManagerImpl&) = delete;
- DiskMountManagerImpl& operator=(const DiskMountManagerImpl&) = delete;
- ~DiskMountManagerImpl() override { cros_disks_client_->RemoveObserver(this); }
- // DiskMountManager override.
- void AddObserver(DiskMountManager::Observer* observer) override {
- observers_.AddObserver(observer);
- }
- // DiskMountManager override.
- void RemoveObserver(DiskMountManager::Observer* observer) override {
- observers_.RemoveObserver(observer);
- }
- // DiskMountManager override.
- void MountPath(const std::string& source_path,
- const std::string& source_format,
- const std::string& mount_label,
- const std::vector<std::string>& mount_options,
- MountType type,
- MountAccessMode access_mode,
- MountPathCallback callback) override {
- if (const auto [_, ok] =
- mount_callbacks_.try_emplace(source_path, std::move(callback));
- !ok) {
- std::move(callback).Run(MountError::kPathAlreadyMounted,
- {source_path, "", type});
- return;
- }
- // Hidden and non-existent devices should not be mounted.
- if (type == MountType::kDevice) {
- Disks::const_iterator it = disks_.find(source_path);
- if (it == disks_.end() || it->get()->is_hidden()) {
- OnMountCompleted({MountError::kInternal, source_path, type});
- return;
- }
- }
- cros_disks_client_->Mount(
- source_path, source_format, mount_label, mount_options, access_mode,
- RemountOption::kMountNewDevice,
- BindOnce(&DiskMountManagerImpl::OnMount, weak_ptr_factory_.GetWeakPtr(),
- source_path, type));
- // Record the access mode option passed to CrosDisks.
- // This is needed because CrosDisks service methods doesn't return the info
- // via DBus.
- access_modes_.emplace(source_path, access_mode);
- }
- // DiskMountManager override.
- void UnmountPath(const std::string& mount_path,
- UnmountPathCallback callback) override {
- UnmountChildMounts(mount_path);
- cros_disks_client_->Unmount(mount_path,
- BindOnce(&DiskMountManagerImpl::OnUnmountPath,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(callback), mount_path));
- }
- void RemountAllRemovableDrives(MountAccessMode mode) override {
- // TODO(yamaguchi): Retry for tentative remount failures. crbug.com/661455
- for (const auto& disk : disks_) {
- DCHECK(disk);
- if (disk->is_read_only_hardware()) {
- // Read-only devices can be mounted in RO mode only. No need to remount.
- continue;
- }
- if (!disk->is_mounted()) {
- continue;
- }
- RemountRemovableDrive(*disk, mode);
- }
- }
- // DiskMountManager override.
- void FormatMountedDevice(const std::string& mount_path,
- FormatFileSystemType filesystem,
- const std::string& label) override {
- MountPoints::const_iterator mount_point = mount_points_.find(mount_path);
- if (mount_point == mount_points_.end()) {
- LOG(ERROR) << "Cannot find mount point '" << mount_path << "'";
- // We can't call OnFormatCompleted until |pending_format_changes_| has
- // been populated.
- NotifyFormatStatusUpdate(FORMAT_COMPLETED, FormatError::kUnknown,
- mount_path, label);
- return;
- }
- std::string device_path = mount_point->source_path;
- const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
- pending_format_changes_[device_path] = {filesystem_str, label};
- Disks::const_iterator disk = disks_.find(device_path);
- if (disk == disks_.end()) {
- LOG(ERROR) << "Cannot find device '" << device_path << "'";
- OnFormatCompleted(FormatError::kUnknown, device_path);
- return;
- }
- if (disk->get()->is_read_only()) {
- LOG(ERROR) << "Device '" << device_path << "' is read-only";
- OnFormatCompleted(FormatError::kDeviceNotAllowed, device_path);
- return;
- }
- if (filesystem == FormatFileSystemType::kUnknown) {
- LOG(ERROR) << "Unknown filesystem passed to FormatMountedDevice";
- OnFormatCompleted(FormatError::kUnsupportedFilesystem, device_path);
- return;
- }
- UnmountPath(disk->get()->mount_path(),
- BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
- weak_ptr_factory_.GetWeakPtr(), device_path,
- filesystem, label));
- }
- // DiskMountManager override.
- void SinglePartitionFormatDevice(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label) override {
- Disks::const_iterator disk_iter = disks_.find(device_path);
- if (disk_iter == disks_.end()) {
- LOG(ERROR) << "Cannot find device '" << device_path << "'";
- OnPartitionCompleted(device_path, filesystem, label,
- PartitionError::kInvalidDevicePath);
- return;
- }
- UnmountDeviceRecursively(
- device_path,
- BindOnce(&DiskMountManagerImpl::OnUnmountDeviceForSinglePartitionFormat,
- weak_ptr_factory_.GetWeakPtr(), device_path, filesystem,
- label));
- }
- void RenameMountedDevice(const std::string& mount_path,
- const std::string& volume_name) override {
- MountPoints::const_iterator mount_point = mount_points_.find(mount_path);
- if (mount_point == mount_points_.end()) {
- LOG(ERROR) << "Cannot find mount point '" << mount_path << "'";
- // We can't call OnRenameCompleted until |pending_rename_changes_| has
- // been populated.
- NotifyRenameStatusUpdate(RENAME_COMPLETED, RenameError::kUnknown,
- mount_path, volume_name);
- return;
- }
- std::string device_path = mount_point->source_path;
- pending_rename_changes_[device_path] = volume_name;
- Disks::const_iterator iter = disks_.find(device_path);
- if (iter == disks_.end()) {
- LOG(ERROR) << "Cannot find device '" << device_path << "'";
- OnRenameCompleted(RenameError::kUnknown, device_path);
- return;
- }
- if (iter->get()->is_read_only()) {
- LOG(ERROR) << "Device '" << device_path << "' is read-only";
- OnRenameCompleted(RenameError::kDeviceNotAllowed, device_path);
- return;
- }
- UnmountPath(
- iter->get()->mount_path(),
- BindOnce(&DiskMountManagerImpl::OnUnmountPathForRename,
- weak_ptr_factory_.GetWeakPtr(), device_path, volume_name));
- }
- // DiskMountManager override.
- void UnmountDeviceRecursively(
- const std::string& device_path,
- UnmountDeviceRecursivelyCallbackType callback) override {
- std::vector<std::string> devices_to_unmount;
- // Get list of all devices to unmount.
- int device_path_len = device_path.length();
- for (const auto& disk : disks_) {
- if (!disk->mount_path().empty() &&
- strncmp(device_path.c_str(), disk->device_path().c_str(),
- device_path_len) == 0) {
- devices_to_unmount.push_back(disk->mount_path());
- }
- }
- // We should detect at least original device.
- if (devices_to_unmount.empty()) {
- if (disks_.find(device_path) == disks_.end()) {
- LOG(WARNING) << "Cannot find device '" << device_path << "'";
- std::move(callback).Run(MountError::kInvalidDevicePath);
- return;
- }
- // Nothing to unmount.
- std::move(callback).Run(MountError::kNone);
- return;
- }
- std::unique_ptr<UnmountDeviceRecursivelyCallbackData> cb_data =
- std::make_unique<UnmountDeviceRecursivelyCallbackData>(
- std::move(callback));
- UnmountDeviceRecursivelyCallbackData* raw_cb_data = cb_data.get();
- base::RepeatingClosure done_callback = base::BarrierClosure(
- devices_to_unmount.size(),
- BindOnce(&OnAllUnmountDeviceRecursively, std::move(cb_data)));
- for (const std::string& device : devices_to_unmount) {
- cros_disks_client_->Unmount(
- device, BindOnce(&DiskMountManagerImpl::OnUnmountDeviceRecursively,
- weak_ptr_factory_.GetWeakPtr(), raw_cb_data, device,
- done_callback));
- }
- }
- // DiskMountManager override.
- void EnsureMountInfoRefreshed(EnsureMountInfoRefreshedCallback callback,
- bool force) override {
- if (!force && already_refreshed_) {
- std::move(callback).Run(true);
- return;
- }
- refresh_callbacks_.push_back(std::move(callback));
- if (refresh_callbacks_.size() == 1) {
- // If there's no in-flight refreshing task, start it.
- cros_disks_client_->EnumerateDevices(
- BindOnce(&DiskMountManagerImpl::RefreshAfterEnumerateDevices,
- weak_ptr_factory_.GetWeakPtr()),
- BindOnce(&DiskMountManagerImpl::RefreshCompleted,
- weak_ptr_factory_.GetWeakPtr(), false));
- }
- }
- // DiskMountManager override.
- const Disks& disks() const override { return disks_; }
- // DiskMountManager override.
- const Disk* FindDiskBySourcePath(
- const std::string& source_path) const override {
- Disks::const_iterator disk_it = disks_.find(source_path);
- return disk_it == disks_.end() ? nullptr : disk_it->get();
- }
- // DiskMountManager override.
- const MountPoints& mount_points() const override { return mount_points_; }
- // DiskMountManager override.
- bool AddDiskForTest(std::unique_ptr<Disk> disk) override {
- if (disks_.find(disk->device_path()) != disks_.end()) {
- LOG(ERROR) << "Attempt to add a duplicate disk";
- return false;
- }
- disks_.insert(std::move(disk));
- return true;
- }
- // DiskMountManager override.
- // Corresponding disk should be added to the manager before this is called.
- bool AddMountPointForTest(const MountPoint& mount_point) override {
- if (mount_points_.find(mount_point.mount_path) != mount_points_.end()) {
- LOG(ERROR) << "Attempt to add a duplicate mount point";
- return false;
- }
- if (mount_point.mount_type == MountType::kDevice &&
- disks_.find(mount_point.source_path) == disks_.end()) {
- LOG(ERROR) << "Device mount points must have a disk entry";
- return false;
- }
- mount_points_.insert(mount_point);
- return true;
- }
- private:
- // A struct to represent information about a format changes.
- struct FormatChange {
- // new file system type
- std::string file_system_type;
- // New volume name
- std::string volume_name;
- };
- // Stores new volume name and file system type for a device on which
- // formatting is invoked on, so that OnFormatCompleted can set it back to
- // |disks_|. The key is a device_path and the value is a FormatChange.
- std::map<std::string, FormatChange> pending_format_changes_;
- // Stores device path are being partitioning.
- // It allows preventing auto-mount of the disks in this set.
- std::set<std::string> pending_partitioning_disks_;
- // Stores new volume name for a device on which renaming is invoked on, so
- // that OnRenameCompleted can set it back to |disks_|. The key is a
- // device_path and the value is new volume_name.
- std::map<std::string, std::string> pending_rename_changes_;
- // Called on D-Bus CrosDisksClient::Mount() is done.
- void OnMount(const std::string& source_path, MountType type, bool result) {
- // When succeeds, OnMountCompleted will be called by "MountCompleted",
- // signal instead. Do nothing now.
- if (result)
- return;
- OnMountCompleted({MountError::kInternal, source_path, type});
- }
- void RemountRemovableDrive(const Disk& disk, MountAccessMode access_mode) {
- const std::string& mount_path = disk.mount_path();
- MountPoints::const_iterator mount_point = mount_points_.find(mount_path);
- if (mount_point == mount_points_.end()) {
- // Not in mount_points_. This happens when the mount_points and disks_ are
- // inconsistent.
- LOG(ERROR) << "Cannot find mount point '" << mount_path << "'";
- OnMountCompleted({MountError::kPathNotMounted, disk.device_path(),
- MountType::kDevice, mount_path});
- return;
- }
- const std::string& source_path = mount_point->source_path;
- // Update the access mode option passed to CrosDisks.
- // This is needed because CrosDisks service methods doesn't return the info
- // via DBus, and must be updated before issuing Mount command as it'll be
- // read by the handler of MountCompleted signal.
- access_modes_[source_path] = access_mode;
- cros_disks_client_->Mount(
- source_path, std::string(), std::string(), {}, access_mode,
- RemountOption::kRemountExistingDevice,
- BindOnce(&DiskMountManagerImpl::OnMount, weak_ptr_factory_.GetWeakPtr(),
- source_path, mount_point->mount_type));
- }
- // Unmounts all mount points whose source path is transitively parented by
- // |mount_path|.
- void UnmountChildMounts(std::string mount_path) {
- DCHECK(!mount_path.empty());
- // Let's make sure mount path has trailing slash.
- if (mount_path.back() != '/')
- mount_path += '/';
- for (const auto& mount_point : mount_points_) {
- if (base::StartsWith(mount_point.source_path, mount_path,
- base::CompareCase::SENSITIVE)) {
- UnmountPath(mount_point.mount_path,
- BindOnce(
- [](const std::string& path, MountError error) {
- LOG(ERROR)
- << "Cannot unmount '" << path << "': " << error;
- },
- mount_point.mount_path));
- }
- }
- }
- // Callback for UnmountDeviceRecursively.
- void OnUnmountDeviceRecursively(UnmountDeviceRecursivelyCallbackData* cb_data,
- const std::string& mount_path,
- base::OnceClosure done_callback,
- MountError error_code) {
- if (error_code == MountError::kPathNotMounted ||
- error_code == MountError::kInvalidPath) {
- // The path was already unmounted by something else.
- error_code = MountError::kNone;
- }
- if (error_code == MountError::kNone) {
- // Do standard processing for Unmount event.
- OnUnmountPath(UnmountPathCallback(), mount_path, MountError::kNone);
- VLOG(1) << "Unmounted '" << mount_path << "'";
- } else {
- // This causes the last non-success error to be reported.
- cb_data->error_code = error_code;
- }
- std::move(done_callback).Run();
- }
- // CrosDisksClient::Observer override.
- void OnMountCompleted(const MountEntry& entry) override {
- auto iter = deferred_mount_events_.find(entry.source_path);
- if (iter != deferred_mount_events_.end()) {
- iter->second.push_back(entry);
- return;
- }
- MountCondition mount_condition = MountCondition::kNone;
- if (entry.mount_type == MountType::kDevice) {
- if (entry.error_code == MountError::kUnknownFilesystem) {
- mount_condition = MountCondition::kUnknownFilesystem;
- }
- if (entry.error_code == MountError::kUnsupportedFilesystem) {
- mount_condition = MountCondition::kUnsupportedFilesystem;
- }
- }
- const MountPoint mount_info{entry.source_path, entry.mount_path,
- entry.mount_type, mount_condition};
- // If the device is corrupted but it's still possible to format it, it will
- // be fake mounted.
- if (entry.error_code == MountError::kNone ||
- mount_condition != MountCondition::kNone) {
- mount_points_.insert(mount_info);
- }
- Disk* disk = nullptr;
- if ((entry.error_code == MountError::kNone ||
- mount_info.mount_condition != MountCondition::kNone) &&
- mount_info.mount_type == MountType::kDevice &&
- !mount_info.source_path.empty() && !mount_info.mount_path.empty()) {
- Disks::iterator disk_map_iter = disks_.find(mount_info.source_path);
- if (disk_map_iter != disks_.end()) { // disk might have been removed?
- disk = disk_map_iter->get();
- DCHECK(disk);
- // Currently the MountCompleted signal doesn't tell whether the device
- // is mounted in read-only mode or not. Instead use the mount option
- // recorded by DiskMountManagerImpl::MountPath().
- // |source_path| should be same as |disk->device_path| because
- // |VolumeManager::OnDiskEvent()| passes the latter to cros-disks as a
- // source path when mounting a device.
- AccessModeMap::iterator it = access_modes_.find(entry.source_path);
- // Store whether the disk was mounted in read-only mode due to a policy.
- disk->set_write_disabled_by_policy(
- it != access_modes_.end() && !disk->is_read_only_hardware() &&
- it->second == MountAccessMode::kReadOnly);
- disk->SetMountPath(mount_info.mount_path);
- // Only set the mount path if the disk is actually mounted. Right now, a
- // number of code paths (format, rename, unmount) rely on the mount path
- // being set even if the disk isn't mounted. cros-disks also does some
- // tracking of non-mounted mount paths. Making this change is
- // non-trivial.
- // TODO(amistry): Change these code paths to use device path instead of
- // mount path.
- disk->set_mounted(entry.error_code == MountError::kNone);
- }
- }
- // Observers may read the values of disks_. So notify them after tweaking
- // values of disks_.
- auto it = mount_callbacks_.find(entry.source_path);
- if (it != mount_callbacks_.end()) {
- std::move(it->second).Run(entry.error_code, mount_info);
- mount_callbacks_.erase(it);
- }
- NotifyMountStatusUpdate(MOUNTING, entry.error_code, mount_info);
- if (disk) {
- disk->set_is_first_mount(false);
- }
- }
- // CrosDisksClient::Observer override.
- void OnMountProgress(const MountEntry& entry) override {
- VLOG(1) << "OnMountProgress: " << entry;
- }
- // Callback for UnmountPath.
- void OnUnmountPath(UnmountPathCallback callback,
- const std::string& mount_path,
- MountError error) {
- if (error == MountError::kPathNotMounted ||
- error == MountError::kInvalidPath) {
- // The path was already unmounted by something else.
- error = MountError::kNone;
- }
- if (const MountPoints::const_iterator mp_it =
- mount_points_.find(mount_path);
- mp_it != mount_points_.end()) {
- const MountPoint& mp = *mp_it;
- NotifyMountStatusUpdate(UNMOUNTING, error, mp);
- if (error == MountError::kNone) {
- if (const Disks::iterator disk_it = disks_.find(mp.source_path);
- disk_it != disks_.end()) {
- Disk* const disk = disk_it->get();
- DCHECK(disk);
- disk->clear_mount_path();
- disk->set_mounted(false);
- }
- mount_points_.erase(mp_it);
- }
- }
- if (callback)
- std::move(callback).Run(error);
- }
- void OnUnmountPathForFormat(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label,
- MountError error_code) {
- if (error_code == MountError::kNone && disks_.count(device_path) != 0) {
- FormatUnmountedDevice(device_path, filesystem, label);
- } else {
- OnFormatCompleted(FormatError::kUnknown, device_path);
- }
- }
- void OnUnmountDeviceForSinglePartitionFormat(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label,
- MountError error_code) {
- if (error_code != MountError::kNone || disks_.count(device_path) == 0) {
- OnPartitionCompleted(device_path, filesystem, label,
- PartitionError::kUnknown);
- return;
- }
- SinglePartitionFormatUnmountedDevice(device_path, filesystem, label);
- }
- // Starts device formatting.
- void FormatUnmountedDevice(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label) {
- Disks::const_iterator disk = disks_.find(device_path);
- DCHECK(disk != disks_.end() && disk->get()->mount_path().empty());
- base::UmaHistogramEnumeration("FileBrowser.FormatFileSystemType",
- filesystem);
- cros_disks_client_->Format(
- device_path, FormatFileSystemTypeToString(filesystem), label,
- BindOnce(&DiskMountManagerImpl::OnFormatStarted,
- weak_ptr_factory_.GetWeakPtr(), device_path, label));
- }
- // Callback for Format.
- void OnFormatStarted(const std::string& device_path,
- const std::string& device_label,
- bool success) {
- if (!success) {
- OnFormatCompleted(FormatError::kUnknown, device_path);
- return;
- }
- NotifyFormatStatusUpdate(FORMAT_STARTED, FormatError::kNone, device_path,
- device_label);
- }
- // CrosDisksClient::Observer override.
- void OnFormatCompleted(FormatError error_code,
- const std::string& device_path) override {
- std::string device_label;
- auto pending_change = pending_format_changes_.find(device_path);
- if (pending_change != pending_format_changes_.end()) {
- device_label = pending_change->second.volume_name;
- }
- auto iter = disks_.find(device_path);
- // disk might have been removed by now?
- if (iter != disks_.end()) {
- Disk* const disk = iter->get();
- DCHECK(disk);
- if (pending_change != pending_format_changes_.end() &&
- error_code == FormatError::kNone) {
- disk->set_device_label(pending_change->second.volume_name);
- disk->set_file_system_type(pending_change->second.file_system_type);
- }
- }
- pending_format_changes_.erase(device_path);
- EnsureMountInfoRefreshed(base::DoNothing(), true /* force */);
- NotifyFormatStatusUpdate(FORMAT_COMPLETED, error_code, device_path,
- device_label);
- }
- void SinglePartitionFormatUnmountedDevice(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label) {
- Disks::const_iterator disk = disks_.find(device_path);
- DCHECK(disk != disks_.end() && disk->get()->mount_path().empty());
- pending_partitioning_disks_.insert(disk->get()->device_path());
- NotifyPartitionStatusUpdate(PARTITION_STARTED, PartitionError::kNone,
- device_path, label);
- cros_disks_client_->SinglePartitionFormat(
- disk->get()->file_path(),
- BindOnce(&DiskMountManagerImpl::OnPartitionCompleted,
- weak_ptr_factory_.GetWeakPtr(), device_path, filesystem,
- label));
- }
- void OnPartitionCompleted(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label,
- PartitionError error_code) {
- auto iter = disks_.find(device_path);
- // disk might have been removed by now?
- if (iter != disks_.end()) {
- Disk* const disk = iter->get();
- DCHECK(disk);
- if (error_code == PartitionError::kNone) {
- EnsureMountInfoRefreshed(
- BindOnce(&DiskMountManagerImpl::OnRefreshAfterPartition,
- weak_ptr_factory_.GetWeakPtr(), device_path, filesystem,
- label),
- true /* force */);
- }
- } else {
- // Remove disk from pending partitioning list if disk removed.
- pending_partitioning_disks_.erase(device_path);
- }
- NotifyPartitionStatusUpdate(PARTITION_COMPLETED, error_code, device_path,
- label);
- }
- void OnRefreshAfterPartition(const std::string& device_path,
- FormatFileSystemType filesystem,
- const std::string& label,
- bool success) {
- Disks::const_iterator device_disk = disks_.find(device_path);
- if (device_disk == disks_.end()) {
- LOG(ERROR) << "Device not found, maybe ejected";
- pending_partitioning_disks_.erase(device_path);
- NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
- PartitionError::kInvalidDevicePath,
- device_path, label);
- return;
- }
- std::string new_partition_device_path;
- // Find new partition using common storage path with parent device.
- for (const auto& candidate : disks_) {
- if (candidate->storage_device_path() ==
- device_disk->get()->storage_device_path() &&
- !candidate->is_parent()) {
- new_partition_device_path = candidate->device_path();
- break;
- }
- }
- if (new_partition_device_path.empty()) {
- LOG(ERROR) << "New partition couldn't be found";
- pending_partitioning_disks_.erase(device_path);
- NotifyPartitionStatusUpdate(PARTITION_COMPLETED,
- PartitionError::kInvalidDevicePath,
- device_path, label);
- return;
- }
- const std::string filesystem_str = FormatFileSystemTypeToString(filesystem);
- pending_format_changes_[new_partition_device_path] = {filesystem_str,
- label};
- // It's expected the disks (parent device and new partition) are not
- // mounted, but try unmounting before starting format if it got
- // mounted through another flow.
- UnmountDeviceRecursively(
- device_path, BindOnce(&DiskMountManagerImpl::OnUnmountPathForFormat,
- weak_ptr_factory_.GetWeakPtr(),
- new_partition_device_path, filesystem, label));
- // It's ok to remove it from pending partitioning as format flow started.
- pending_partitioning_disks_.erase(device_path);
- }
- void OnUnmountPathForRename(const std::string& device_path,
- const std::string& volume_name,
- MountError error_code) {
- if (error_code != MountError::kNone || disks_.count(device_path) == 0) {
- OnRenameCompleted(RenameError::kUnknown, device_path);
- return;
- }
- RenameUnmountedDevice(device_path, volume_name);
- }
- // Start device renaming
- void RenameUnmountedDevice(const std::string& device_path,
- const std::string& volume_name) {
- const Disks::const_iterator disk = disks_.find(device_path);
- DCHECK(disk != disks_.end() && disk->get()->mount_path().empty());
- cros_disks_client_->Rename(
- device_path, volume_name,
- BindOnce(&DiskMountManagerImpl::OnRenameStarted,
- weak_ptr_factory_.GetWeakPtr(), device_path, volume_name));
- }
- // Callback for Rename.
- void OnRenameStarted(const std::string& device_path,
- const std::string& volume_name,
- bool success) {
- if (!success) {
- OnRenameCompleted(RenameError::kUnknown, device_path);
- return;
- }
- NotifyRenameStatusUpdate(RENAME_STARTED, RenameError::kNone, device_path,
- volume_name);
- }
- // CrosDisksClient::Observer override.
- void OnRenameCompleted(RenameError error_code,
- const std::string& device_path) override {
- std::string device_label;
- auto pending_change = pending_rename_changes_.find(device_path);
- if (pending_change != pending_rename_changes_.end()) {
- device_label = pending_change->second;
- }
- auto iter = disks_.find(device_path);
- // disk might have been removed by now?
- if (iter != disks_.end()) {
- Disk* const disk = iter->get();
- DCHECK(disk);
- if (pending_change != pending_rename_changes_.end() &&
- error_code == RenameError::kNone)
- disk->set_device_label(pending_change->second);
- }
- pending_rename_changes_.erase(device_path);
- NotifyRenameStatusUpdate(RENAME_COMPLETED, error_code, device_path,
- device_label);
- }
- // Fire observer mount events that were deferred due to an in-progress
- // GetDeviceProperties() call.
- void RunDeferredMountEvents(const std::string& device_path) {
- auto mount_events_iter = deferred_mount_events_.find(device_path);
- if (mount_events_iter == deferred_mount_events_.end())
- return;
- std::vector<MountEntry> entries = std::move(mount_events_iter->second);
- deferred_mount_events_.erase(mount_events_iter);
- for (const MountEntry& entry : entries)
- OnMountCompleted(entry);
- }
- // Callback for GetDeviceProperties.
- void OnGetDeviceProperties(const DiskInfo& disk_info) {
- if (disk_info.is_virtual()) {
- RunDeferredMountEvents(disk_info.device_path());
- return;
- }
- DVLOG(1) << "Found disk " << disk_info.device_path();
- // Delete previous disk info for this path:
- bool is_new = true;
- bool is_first_mount = false;
- std::string base_mount_path = std::string();
- Disks::iterator iter = disks_.find(disk_info.device_path());
- if (iter != disks_.end()) {
- is_first_mount = iter->get()->is_first_mount();
- base_mount_path = iter->get()->base_mount_path();
- disks_.erase(iter);
- is_new = false;
- }
- // If the device was mounted by the instance, apply recorded parameter.
- // Otherwise, default to false.
- // Lookup by |device_path| which we pass to cros-disks when mounting a
- // device in |VolumeManager::OnDiskEvent()|.
- auto access_mode = access_modes_.find(disk_info.device_path());
- bool write_disabled_by_policy =
- access_mode != access_modes_.end() &&
- access_mode->second == MountAccessMode::kReadOnly;
- std::unique_ptr<Disk> disk = std::make_unique<Disk>(
- disk_info, write_disabled_by_policy, base_mount_path);
- if (!is_new) {
- disk->set_is_first_mount(is_first_mount);
- }
- const auto [it, ok] = disks_.insert(std::move(disk));
- DCHECK(ok);
- NotifyDiskStatusUpdate(is_new ? DISK_ADDED : DISK_CHANGED, **it);
- RunDeferredMountEvents(disk_info.device_path());
- }
- // Part of EnsureMountInfoRefreshed(). Called after the list of devices are
- // enumerated.
- void RefreshAfterEnumerateDevices(const std::vector<std::string>& devices) {
- std::set<std::string> current_device_set(devices.begin(), devices.end());
- for (Disks::iterator iter = disks_.begin(); iter != disks_.end();) {
- if (current_device_set.count(iter->get()->device_path()) == 0) {
- iter = disks_.erase(iter);
- } else {
- ++iter;
- }
- }
- RefreshDeviceAtIndex(devices, 0);
- }
- // Part of EnsureMountInfoRefreshed(). Called for each device to refresh info.
- void RefreshDeviceAtIndex(const std::vector<std::string>& devices,
- size_t index) {
- if (index == devices.size()) {
- // All devices info retrieved. Proceed to enumerate mount point info.
- cros_disks_client_->EnumerateMountEntries(
- BindOnce(&DiskMountManagerImpl::RefreshAfterEnumerateMountEntries,
- weak_ptr_factory_.GetWeakPtr()),
- BindOnce(&DiskMountManagerImpl::RefreshCompleted,
- weak_ptr_factory_.GetWeakPtr(), false));
- return;
- }
- cros_disks_client_->GetDeviceProperties(
- devices[index],
- BindOnce(&DiskMountManagerImpl::RefreshAfterGetDeviceProperties,
- weak_ptr_factory_.GetWeakPtr(), devices, index + 1),
- BindOnce(&DiskMountManagerImpl::RefreshDeviceAtIndex,
- weak_ptr_factory_.GetWeakPtr(), devices, index + 1));
- }
- // Part of EnsureMountInfoRefreshed().
- void RefreshAfterGetDeviceProperties(const std::vector<std::string>& devices,
- size_t next_index,
- const DiskInfo& disk_info) {
- OnGetDeviceProperties(disk_info);
- RefreshDeviceAtIndex(devices, next_index);
- }
- // Part of EnsureMountInfoRefreshed(). Called after mount entries are listed.
- void RefreshAfterEnumerateMountEntries(
- const std::vector<MountEntry>& entries) {
- for (const auto& entry : entries)
- OnMountCompleted(entry);
- RefreshCompleted(true);
- }
- // Part of EnsureMountInfoRefreshed(). Called when the refreshing is done.
- void RefreshCompleted(bool success) {
- already_refreshed_ = true;
- for (auto& callback : refresh_callbacks_)
- std::move(callback).Run(success);
- refresh_callbacks_.clear();
- }
- // CrosDisksClient::Observer override.
- void OnMountEvent(MountEventType event,
- const std::string& device_path_arg) override {
- // Take a copy of the argument so we can modify it below.
- std::string device_path = device_path_arg;
- switch (event) {
- case MountEventType::kDiskAdded: {
- // Ensure we have an entry indicating we're waiting for
- // GetDeviceProperties() to complete.
- deferred_mount_events_[device_path];
- cros_disks_client_->GetDeviceProperties(
- device_path,
- BindOnce(&DiskMountManagerImpl::OnGetDeviceProperties,
- weak_ptr_factory_.GetWeakPtr()),
- base::DoNothing());
- break;
- }
- case MountEventType::kDiskRemoved: {
- // Search and remove disks that are no longer present.
- DiskMountManager::Disks::iterator iter = disks_.find(device_path);
- if (iter != disks_.end()) {
- Disk* disk = iter->get();
- NotifyDiskStatusUpdate(DISK_REMOVED, *disk);
- disks_.erase(iter);
- }
- break;
- }
- case MountEventType::kDeviceAdded: {
- NotifyDeviceStatusUpdate(DEVICE_ADDED, device_path);
- break;
- }
- case MountEventType::kDeviceRemoved: {
- NotifyDeviceStatusUpdate(DEVICE_REMOVED, device_path);
- break;
- }
- case MountEventType::kDeviceScanned: {
- NotifyDeviceStatusUpdate(DEVICE_SCANNED, device_path);
- break;
- }
- default: {
- LOG(ERROR) << "Unknown event: " << static_cast<int>(event);
- }
- }
- }
- // Notifies all observers about disk status update.
- void NotifyDiskStatusUpdate(DiskEvent event, const Disk& disk) {
- for (auto& observer : observers_) {
- // Skip mounting of new partitioned disks while waiting for the format.
- if (IsPendingPartitioningDisk(disk.device_path())) {
- continue;
- }
- disk.is_auto_mountable() ? observer.OnAutoMountableDiskEvent(event, disk)
- : observer.OnBootDeviceDiskEvent(event, disk);
- }
- }
- // Notifies all observers about device status update.
- void NotifyDeviceStatusUpdate(DeviceEvent event,
- const std::string& device_path) {
- for (auto& observer : observers_)
- observer.OnDeviceEvent(event, device_path);
- }
- // Notifies all observers about mount completion.
- void NotifyMountStatusUpdate(MountEvent event,
- MountError error_code,
- const MountPoint& mount_info) {
- for (auto& observer : observers_)
- observer.OnMountEvent(event, error_code, mount_info);
- }
- void NotifyFormatStatusUpdate(FormatEvent event,
- FormatError error_code,
- const std::string& device_path,
- const std::string& device_label) {
- for (auto& observer : observers_)
- observer.OnFormatEvent(event, error_code, device_path, device_label);
- }
- void NotifyPartitionStatusUpdate(PartitionEvent event,
- PartitionError error_code,
- const std::string& device_path,
- const std::string& device_label) {
- for (auto& observer : observers_)
- observer.OnPartitionEvent(event, error_code, device_path, device_label);
- }
- void NotifyRenameStatusUpdate(RenameEvent event,
- RenameError error_code,
- const std::string& device_path,
- const std::string& device_label) {
- for (auto& observer : observers_)
- observer.OnRenameEvent(event, error_code, device_path, device_label);
- }
- bool IsPendingPartitioningDisk(const std::string& device_path) {
- if (pending_partitioning_disks_.find(device_path) !=
- pending_partitioning_disks_.end()) {
- return true;
- }
- // If device path doesn't match check whether if it's a child path.
- for (const auto& disk : pending_partitioning_disks_) {
- if (base::StartsWith(device_path, disk, base::CompareCase::SENSITIVE)) {
- return true;
- }
- }
- return false;
- }
- // Mount event change observers.
- base::ObserverList<DiskMountManager::Observer> observers_;
- CrosDisksClient* const cros_disks_client_ = CrosDisksClient::Get();
- // The list of disks found.
- DiskMountManager::Disks disks_;
- std::map<std::string, MountPathCallback> mount_callbacks_;
- DiskMountManager::MountPoints mount_points_;
- // A map entry with a key of the device path will be created upon calling
- // GetDeviceProperties(), for deferring mount events, and removed once it has
- // completed. This prevents a race resulting in mount events being fired with
- // the corresponding Disk entry unexpectedly missing.
- std::map<std::string, std::vector<MountEntry>> deferred_mount_events_;
- bool already_refreshed_ = false;
- std::vector<EnsureMountInfoRefreshedCallback> refresh_callbacks_;
- SuspendUnmountManager suspend_unmount_manager_{this};
- // Whether the instance attempted to mount a device in read-only mode for
- // each source path.
- typedef std::map<std::string, MountAccessMode> AccessModeMap;
- AccessModeMap access_modes_;
- base::WeakPtrFactory<DiskMountManagerImpl> weak_ptr_factory_{this};
- };
- } // namespace
- std::ostream& operator<<(std::ostream& out, MountCondition condition) {
- switch (condition) {
- #define PRINT(s) \
- case MountCondition::s: \
- return out << #s;
- PRINT(kNone)
- PRINT(kUnknownFilesystem)
- PRINT(kUnsupportedFilesystem)
- #undef PRINT
- }
- return out << static_cast<std::underlying_type_t<MountCondition>>(condition);
- }
- DiskMountManager::Observer::~Observer() {
- DCHECK(!IsInObserverList());
- }
- bool DiskMountManager::AddDiskForTest(std::unique_ptr<Disk> disk) {
- return false;
- }
- bool DiskMountManager::AddMountPointForTest(const MountPoint& mount_point) {
- return false;
- }
- // 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 = nullptr;
- VLOG(1) << "DiskMountManager Shutdown completed";
- }
- // static
- DiskMountManager* DiskMountManager::GetInstance() {
- return g_disk_mount_manager;
- }
- } // namespace ash::disks
|