blob: 40eb2b58ca19515dd1275d409b4d2e5e1bac4032 [file] [log] [blame] [edit]
// 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