1use std::ffi::CString;
6use std::ffi::OsStr;
7use std::fmt;
8use std::io;
9use std::os::unix::ffi::OsStrExt;
10use std::os::unix::io::RawFd;
11
12pub 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 Extra(&'a str),
27}
28
29impl fmt::Display for MountOption<'_> {
31 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={mode:o}"),
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
46fn 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
59pub 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 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)]
95mod tests {
96 use super::*;
97
98 #[test]
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}