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