| // Copyright 2022 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "rmad/executor/mount.h" | |
| #include <sys/mount.h> | |
| #include <sys/stat.h> | |
| #include <sys/statvfs.h> | |
| #include <sys/vfs.h> | |
| #include <algorithm> | |
| #include <array> | |
| #include <memory> | |
| #include <utility> | |
| #include <base/files/file_path.h> | |
| #include <base/logging.h> | |
| #include "rmad/udev/udev_device.h" | |
| #include "rmad/udev/udev_utils.h" | |
| namespace { | |
| constexpr uint32_t kDefaultMountFlags = | |
| MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOSYMFOLLOW; | |
| bool IsSupportedFileSystemType(const std::string& fs_type) { | |
| // Currently only FAT32 and ext* are supported. | |
| static constexpr std::array<const char*, 4> kSupportedFileSystems = { | |
| "vfat", "ext2", "ext3", "ext4"}; | |
| return std::find(kSupportedFileSystems.begin(), kSupportedFileSystems.end(), | |
| fs_type) != kSupportedFileSystems.end(); | |
| } | |
| } // namespace | |
| namespace rmad { | |
| Mount::Mount() : valid_(false) {} | |
| Mount::Mount(const base::FilePath& device_path, | |
| const base::FilePath& mount_point, | |
| const std::string& fs_type, | |
| bool read_only) | |
| : mount_point_(mount_point) { | |
| udev_utils_ = std::make_unique<UdevUtilsImpl>(); | |
| valid_ = AttemptMount(device_path, mount_point, fs_type, read_only); | |
| } | |
| Mount::Mount(const base::FilePath& device_path, | |
| const base::FilePath& mount_point, | |
| const std::string& fs_type, | |
| bool read_only, | |
| std::unique_ptr<UdevUtils> udev_utils) | |
| : mount_point_(mount_point), udev_utils_(std::move(udev_utils)) { | |
| valid_ = AttemptMount(device_path, mount_point, fs_type, read_only); | |
| } | |
| Mount::Mount(Mount&& mount) | |
| : mount_point_(mount.mount_point_), | |
| valid_(mount.valid_), | |
| udev_utils_(std::move(mount.udev_utils_)) { | |
| mount.valid_ = false; | |
| } | |
| Mount& Mount::operator=(Mount&& mount) { | |
| mount_point_ = mount.mount_point_; | |
| udev_utils_ = std::move(mount.udev_utils_); | |
| valid_ = mount.valid_; | |
| mount.valid_ = false; | |
| return *this; | |
| } | |
| bool Mount::AttemptMount(const base::FilePath& device_path, | |
| const base::FilePath& mount_point, | |
| const std::string& fs_type, | |
| bool read_only) { | |
| if (!IsSupportedFileSystemType(fs_type)) { | |
| return false; | |
| } | |
| // |device_path| is restricted to device paths for block devices with | |
| // supported filesystems. | |
| std::unique_ptr<UdevDevice> dev; | |
| if (!udev_utils_->GetBlockDeviceFromDevicePath(device_path.value(), &dev) || | |
| !dev->IsRemovable() || | |
| !IsSupportedFileSystemType(dev->GetFileSystemType())) { | |
| return false; | |
| } | |
| if (!VerifyMountPoint(mount_point)) { | |
| return false; | |
| } | |
| uint32_t mount_flags = kDefaultMountFlags; | |
| if (read_only) { | |
| mount_flags |= MS_RDONLY; | |
| } | |
| if (mount(device_path.value().c_str(), mount_point.value().c_str(), | |
| fs_type.c_str(), mount_flags, "") != 0) { | |
| PLOG(ERROR) << "Failed to mount " << device_path.value() << " to " | |
| << mount_point.value(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| Mount::~Mount() { | |
| if (IsValid() && umount(mount_point_.value().c_str()) != 0) { | |
| PLOG(ERROR) << "Failed to unmount " << mount_point_.value(); | |
| } | |
| } | |
| bool Mount::VerifyMountPoint(const base::FilePath& mount_point) { | |
| struct stat st, st_parent; | |
| if (lstat(mount_point.value().c_str(), &st) != 0) { | |
| PLOG(ERROR) << "Could not lstat the mount point " << mount_point.value(); | |
| return false; | |
| } | |
| if (!S_ISDIR(st.st_mode)) { | |
| LOG(ERROR) << "Mount point " << mount_point.value() | |
| << " exists but is not a directory"; | |
| return false; | |
| } | |
| base::FilePath mount_parent = mount_point.DirName(); | |
| if (stat(mount_parent.value().c_str(), &st_parent) != 0) { | |
| PLOG(ERROR) << "Could not stat the mount point parent " | |
| << mount_parent.value(); | |
| return false; | |
| } | |
| return st.st_dev == st_parent.st_dev; | |
| } | |
| } // namespace rmad |