mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 20:19:07 +00:00
fuse: provide a mount API
The mount API is useful especially in an environment without typical fusermount setuid program (e.g. Android). BUG=b:173507504 TEST=create a FUSE fs, run with cap_sys_admin, can read from the mount point TEST=build_test.py Change-Id: Ibfc220e8cf59b54d55f5d030d2e4c4375d3654cb Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2556079 Commit-Queue: Victor Hsieh <victorhsieh@chromium.org> Tested-by: Victor Hsieh <victorhsieh@chromium.org> Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
This commit is contained in:
parent
f70350ba51
commit
4799680841
2 changed files with 128 additions and 0 deletions
|
@ -10,11 +10,13 @@ use thiserror::Error as ThisError;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
#[cfg(fuzzing)]
|
#[cfg(fuzzing)]
|
||||||
pub mod fuzzing;
|
pub mod fuzzing;
|
||||||
|
pub mod mount;
|
||||||
mod server;
|
mod server;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
pub mod worker;
|
pub mod worker;
|
||||||
|
|
||||||
|
pub use mount::mount;
|
||||||
pub use server::{Reader, Server, Writer};
|
pub use server::{Reader, Server, Writer};
|
||||||
|
|
||||||
/// Errors that may occur during the creation or operation of an Fs device.
|
/// Errors that may occur during the creation or operation of an Fs device.
|
||||||
|
|
126
fuse/src/mount.rs
Normal file
126
fuse/src/mount.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// 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),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue