blob: 66276d4d4e574dca05a250c52e2eb35962ecbcc8 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::ffi::{CString, OsStr};
use std::fmt;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::RawFd;
/// Mount options to pass to mount(2) for a FUSE filesystem. See the [official document](
/// https://www.kernel.org/doc/html/latest/filesystems/fuse.html#mount-options) for the
/// descriptions.
pub enum MountOption {
FD(RawFd),
RootMode(u32),
UserId(libc::uid_t),
GroupId(libc::gid_t),
DefaultPermissions,
AllowOther,
MaxRead(u32),
BlockSize(u32),
}
// Implement Display for ToString to convert to actual mount options.
impl fmt::Display for MountOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
MountOption::FD(fd) => write!(f, "fd={}", fd),
MountOption::RootMode(mode) => write!(f, "rootmode={:o}", mode),
MountOption::UserId(uid) => write!(f, "user_id={}", uid),
MountOption::GroupId(gid) => write!(f, "group_id={}", gid),
MountOption::DefaultPermissions => write!(f, "default_permissions"),
MountOption::AllowOther => write!(f, "allow_other"),
MountOption::MaxRead(size) => write!(f, "max_read={}", size),
MountOption::BlockSize(size) => write!(f, "blksize={}", size),
}
}
}
fn join_mount_options(options: &[MountOption]) -> String {
if !options.is_empty() {
let mut concat = options[0].to_string();
for opt in &options[1..] {
concat.push(',');
concat.push_str(&opt.to_string());
}
concat
} else {
String::new()
}
}
/// Initiates a FUSE mount at `mountpoint` directory with `flags` and `options` via mount(2). The
/// caller should provide a file descriptor (backed by /dev/fuse) with `MountOption::FD`. After
/// this function completes, the FUSE filesystem can start to handle the requests, e.g. via
/// `fuse::worker::start_message_loop()`.
///
/// This operation requires CAP_SYS_ADMIN privilege, but the privilege can be dropped afterward.
pub fn mount<P: AsRef<OsStr>>(
mountpoint: P,
name: &str,
flags: libc::c_ulong,
options: &[MountOption],
) -> Result<(), io::Error> {
let mount_name = CString::new(name.as_bytes())?;
let fs_type = CString::new(String::from("fuse.") + name)?;
let mountpoint = CString::new(mountpoint.as_ref().as_bytes())?;
let mount_options = CString::new(join_mount_options(options))?;
// Safe because pointer arguments all points to null-terminiated CStrings.
let retval = unsafe {
libc::mount(
mount_name.as_ptr(),
mountpoint.as_ptr(),
fs_type.as_ptr(),
flags,
mount_options.as_ptr() as *const std::ffi::c_void,
)
};
if retval < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_options_concatenate_in_order() {
assert_eq!("".to_string(), join_mount_options(&[]));
assert_eq!(
"fd=42".to_string(),
join_mount_options(&[MountOption::FD(42),])
);
assert_eq!(
"fd=42,rootmode=40111,allow_other,user_id=12,group_id=34,max_read=4096".to_string(),
join_mount_options(&[
MountOption::FD(42),
MountOption::RootMode(
libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH
),
MountOption::AllowOther,
MountOption::UserId(12),
MountOption::GroupId(34),
MountOption::MaxRead(4096),
])
);
assert_eq!(
"fd=42,default_permissions,user_id=12,group_id=34,max_read=4096".to_string(),
join_mount_options(&[
MountOption::FD(42),
MountOption::DefaultPermissions,
MountOption::UserId(12),
MountOption::GroupId(34),
MountOption::MaxRead(4096),
])
);
}
}