xref: /aosp_15_r20/external/crosvm/fuse/src/mount.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2020 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::ffi::CString;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::ffi::OsStr;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::ffi::OsStrExt;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::io::RawFd;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker /// Mount options to pass to mount(2) for a FUSE filesystem. See the [official document](
13*bb4ee6a4SAndroid Build Coastguard Worker /// https://www.kernel.org/doc/html/latest/filesystems/fuse.html#mount-options) for the
14*bb4ee6a4SAndroid Build Coastguard Worker /// descriptions.
15*bb4ee6a4SAndroid Build Coastguard Worker pub enum MountOption<'a> {
16*bb4ee6a4SAndroid Build Coastguard Worker     FD(RawFd),
17*bb4ee6a4SAndroid Build Coastguard Worker     RootMode(u32),
18*bb4ee6a4SAndroid Build Coastguard Worker     UserId(libc::uid_t),
19*bb4ee6a4SAndroid Build Coastguard Worker     GroupId(libc::gid_t),
20*bb4ee6a4SAndroid Build Coastguard Worker     DefaultPermissions,
21*bb4ee6a4SAndroid Build Coastguard Worker     AllowOther,
22*bb4ee6a4SAndroid Build Coastguard Worker     MaxRead(u32),
23*bb4ee6a4SAndroid Build Coastguard Worker     BlockSize(u32),
24*bb4ee6a4SAndroid Build Coastguard Worker     // General mount options that are not specific to FUSE. Note that the value is not checked
25*bb4ee6a4SAndroid Build Coastguard Worker     // or interpreted by this library, but by kernel.
26*bb4ee6a4SAndroid Build Coastguard Worker     Extra(&'a str),
27*bb4ee6a4SAndroid Build Coastguard Worker }
28*bb4ee6a4SAndroid Build Coastguard Worker 
29*bb4ee6a4SAndroid Build Coastguard Worker // Implement Display for ToString to convert to actual mount options.
30*bb4ee6a4SAndroid Build Coastguard Worker impl<'a> fmt::Display for MountOption<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result31*bb4ee6a4SAndroid Build Coastguard Worker     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32*bb4ee6a4SAndroid Build Coastguard Worker         match &self {
33*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::FD(fd) => write!(f, "fd={}", fd),
34*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::RootMode(mode) => write!(f, "rootmode={:o}", mode),
35*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::UserId(uid) => write!(f, "user_id={}", uid),
36*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::GroupId(gid) => write!(f, "group_id={}", gid),
37*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::DefaultPermissions => write!(f, "default_permissions"),
38*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::AllowOther => write!(f, "allow_other"),
39*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::MaxRead(size) => write!(f, "max_read={}", size),
40*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::BlockSize(size) => write!(f, "blksize={}", size),
41*bb4ee6a4SAndroid Build Coastguard Worker             MountOption::Extra(text) => write!(f, "{}", text),
42*bb4ee6a4SAndroid Build Coastguard Worker         }
43*bb4ee6a4SAndroid Build Coastguard Worker     }
44*bb4ee6a4SAndroid Build Coastguard Worker }
45*bb4ee6a4SAndroid Build Coastguard Worker 
join_mount_options(options: &[MountOption]) -> String46*bb4ee6a4SAndroid Build Coastguard Worker fn join_mount_options(options: &[MountOption]) -> String {
47*bb4ee6a4SAndroid Build Coastguard Worker     if !options.is_empty() {
48*bb4ee6a4SAndroid Build Coastguard Worker         let mut concat = options[0].to_string();
49*bb4ee6a4SAndroid Build Coastguard Worker         for opt in &options[1..] {
50*bb4ee6a4SAndroid Build Coastguard Worker             concat.push(',');
51*bb4ee6a4SAndroid Build Coastguard Worker             concat.push_str(&opt.to_string());
52*bb4ee6a4SAndroid Build Coastguard Worker         }
53*bb4ee6a4SAndroid Build Coastguard Worker         concat
54*bb4ee6a4SAndroid Build Coastguard Worker     } else {
55*bb4ee6a4SAndroid Build Coastguard Worker         String::new()
56*bb4ee6a4SAndroid Build Coastguard Worker     }
57*bb4ee6a4SAndroid Build Coastguard Worker }
58*bb4ee6a4SAndroid Build Coastguard Worker 
59*bb4ee6a4SAndroid Build Coastguard Worker /// Initiates a FUSE mount at `mountpoint` directory with `flags` and `options` via mount(2). The
60*bb4ee6a4SAndroid Build Coastguard Worker /// caller should provide a file descriptor (backed by /dev/fuse) with `MountOption::FD`. After
61*bb4ee6a4SAndroid Build Coastguard Worker /// this function completes, the FUSE filesystem can start to handle the requests, e.g. via
62*bb4ee6a4SAndroid Build Coastguard Worker /// `fuse::worker::start_message_loop()`.
63*bb4ee6a4SAndroid Build Coastguard Worker ///
64*bb4ee6a4SAndroid Build Coastguard Worker /// This operation requires CAP_SYS_ADMIN privilege, but the privilege can be dropped afterward.
mount<P: AsRef<OsStr>>( mountpoint: P, name: &str, flags: libc::c_ulong, options: &[MountOption], ) -> Result<(), io::Error>65*bb4ee6a4SAndroid Build Coastguard Worker pub fn mount<P: AsRef<OsStr>>(
66*bb4ee6a4SAndroid Build Coastguard Worker     mountpoint: P,
67*bb4ee6a4SAndroid Build Coastguard Worker     name: &str,
68*bb4ee6a4SAndroid Build Coastguard Worker     flags: libc::c_ulong,
69*bb4ee6a4SAndroid Build Coastguard Worker     options: &[MountOption],
70*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<(), io::Error> {
71*bb4ee6a4SAndroid Build Coastguard Worker     let mount_name = CString::new(name.as_bytes())?;
72*bb4ee6a4SAndroid Build Coastguard Worker     let fs_type = CString::new(String::from("fuse.") + name)?;
73*bb4ee6a4SAndroid Build Coastguard Worker     let mountpoint = CString::new(mountpoint.as_ref().as_bytes())?;
74*bb4ee6a4SAndroid Build Coastguard Worker     let mount_options = CString::new(join_mount_options(options))?;
75*bb4ee6a4SAndroid Build Coastguard Worker 
76*bb4ee6a4SAndroid Build Coastguard Worker     // SAFETY:
77*bb4ee6a4SAndroid Build Coastguard Worker     // Safe because pointer arguments all points to null-terminiated CStrings.
78*bb4ee6a4SAndroid Build Coastguard Worker     let retval = unsafe {
79*bb4ee6a4SAndroid Build Coastguard Worker         libc::mount(
80*bb4ee6a4SAndroid Build Coastguard Worker             mount_name.as_ptr(),
81*bb4ee6a4SAndroid Build Coastguard Worker             mountpoint.as_ptr(),
82*bb4ee6a4SAndroid Build Coastguard Worker             fs_type.as_ptr(),
83*bb4ee6a4SAndroid Build Coastguard Worker             flags,
84*bb4ee6a4SAndroid Build Coastguard Worker             mount_options.as_ptr() as *const std::ffi::c_void,
85*bb4ee6a4SAndroid Build Coastguard Worker         )
86*bb4ee6a4SAndroid Build Coastguard Worker     };
87*bb4ee6a4SAndroid Build Coastguard Worker     if retval < 0 {
88*bb4ee6a4SAndroid Build Coastguard Worker         Err(io::Error::last_os_error())
89*bb4ee6a4SAndroid Build Coastguard Worker     } else {
90*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
91*bb4ee6a4SAndroid Build Coastguard Worker     }
92*bb4ee6a4SAndroid Build Coastguard Worker }
93*bb4ee6a4SAndroid Build Coastguard Worker 
94*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
95*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
96*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
97*bb4ee6a4SAndroid Build Coastguard Worker 
98*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
basic_options_concatenate_in_order()99*bb4ee6a4SAndroid Build Coastguard Worker     fn basic_options_concatenate_in_order() {
100*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!("".to_string(), join_mount_options(&[]));
101*bb4ee6a4SAndroid Build Coastguard Worker 
102*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
103*bb4ee6a4SAndroid Build Coastguard Worker             "fd=42".to_string(),
104*bb4ee6a4SAndroid Build Coastguard Worker             join_mount_options(&[MountOption::FD(42),])
105*bb4ee6a4SAndroid Build Coastguard Worker         );
106*bb4ee6a4SAndroid Build Coastguard Worker 
107*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
108*bb4ee6a4SAndroid Build Coastguard Worker             "fd=42,rootmode=40111,allow_other,user_id=12,group_id=34,max_read=4096".to_string(),
109*bb4ee6a4SAndroid Build Coastguard Worker             join_mount_options(&[
110*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::FD(42),
111*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::RootMode(
112*bb4ee6a4SAndroid Build Coastguard Worker                     libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH
113*bb4ee6a4SAndroid Build Coastguard Worker                 ),
114*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::AllowOther,
115*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::UserId(12),
116*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::GroupId(34),
117*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::MaxRead(4096),
118*bb4ee6a4SAndroid Build Coastguard Worker             ])
119*bb4ee6a4SAndroid Build Coastguard Worker         );
120*bb4ee6a4SAndroid Build Coastguard Worker 
121*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
122*bb4ee6a4SAndroid Build Coastguard Worker             "fd=42,default_permissions,user_id=12,group_id=34,max_read=4096".to_string(),
123*bb4ee6a4SAndroid Build Coastguard Worker             join_mount_options(&[
124*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::FD(42),
125*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::DefaultPermissions,
126*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::UserId(12),
127*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::GroupId(34),
128*bb4ee6a4SAndroid Build Coastguard Worker                 MountOption::MaxRead(4096),
129*bb4ee6a4SAndroid Build Coastguard Worker             ])
130*bb4ee6a4SAndroid Build Coastguard Worker         );
131*bb4ee6a4SAndroid Build Coastguard Worker 
132*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(
133*bb4ee6a4SAndroid Build Coastguard Worker             "option1=a,option2=b".to_string(),
134*bb4ee6a4SAndroid Build Coastguard Worker             join_mount_options(&[MountOption::Extra("option1=a,option2=b"),])
135*bb4ee6a4SAndroid Build Coastguard Worker         );
136*bb4ee6a4SAndroid Build Coastguard Worker     }
137*bb4ee6a4SAndroid Build Coastguard Worker }
138