]> git.sesse.net Git - bcachefs-tools-debian/blob - rust-src/mount/src/filesystem.rs
36af8c058c7a0044c4d4ee464dccfe2de6d34c89
[bcachefs-tools-debian] / rust-src / mount / src / filesystem.rs
1 extern "C" {
2         pub static stdout: *mut libc::FILE;
3 }
4
5 use getset::{CopyGetters, Getters};
6 use std::path::PathBuf;
7 #[derive(Getters, CopyGetters)]
8 pub struct FileSystem {
9         /// External UUID of the bcachefs
10         #[getset(get = "pub")]
11         uuid: uuid::Uuid,
12         /// Whether filesystem is encrypted
13         #[getset(get_copy = "pub")]
14         encrypted: bool,
15         /// Super block
16         #[getset(get = "pub")]
17         sb: bcachefs::bch_sb_handle,
18         /// Member devices for this filesystem
19         #[getset(get = "pub")]
20         devices: Vec<PathBuf>,
21 }
22
23 /// Parse a comma-separated mount options and split out mountflags and filesystem
24 /// specific options.
25 fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
26         use either::Either::*;
27         let (opts, flags) = options
28                 .as_ref()
29                 .split(",")
30                 .map(|o| match o {
31                         "dirsync" => Left(libc::MS_DIRSYNC),
32                         "lazytime" => Left(1 << 25), // MS_LAZYTIME
33                         "mand" => Left(libc::MS_MANDLOCK),
34                         "noatime" => Left(libc::MS_NOATIME),
35                         "nodev" => Left(libc::MS_NODEV),
36                         "nodiratime" => Left(libc::MS_NODIRATIME),
37                         "noexec" => Left(libc::MS_NOEXEC),
38                         "nosuid" => Left(libc::MS_NOSUID),
39                         "ro" => Left(libc::MS_RDONLY),
40                         "rw" => Left(0),
41                         "relatime" => Left(libc::MS_RELATIME),
42                         "strictatime" => Left(libc::MS_STRICTATIME),
43                         "sync" => Left(libc::MS_SYNCHRONOUS),
44                         "" => Left(0),
45                         o @ _ => Right(o),
46                 })
47                 .fold((Vec::new(), 0), |(mut opts, flags), next| match next {
48                         Left(f) => (opts, flags | f),
49                         Right(o) => {
50                                 opts.push(o);
51                                 (opts, flags)
52                         }
53                 });
54
55         use itertools::Itertools;
56         (
57                 if opts.len() == 0 {
58                         None
59                 } else {
60                         Some(opts.iter().join(","))
61                 },
62                 flags,
63         )
64 }
65
66 impl FileSystem {
67         pub(crate) fn new(sb: bcachefs::bch_sb_handle) -> Self {
68                 Self {
69                         uuid: sb.sb().uuid(),
70                         encrypted: sb.sb().crypt().is_some(),
71                         sb: sb,
72                         devices: vec![],
73                 }
74         }
75
76         pub fn mount(
77                 &self,
78                 target: impl AsRef<std::path::Path>,
79                 options: impl AsRef<str>,
80         ) -> anyhow::Result<()> {
81                 use itertools::Itertools;
82                 use std::ffi::c_void;
83                 use std::os::raw::c_char;
84                 use std::os::unix::ffi::OsStrExt;
85                 let src = self.devices.iter().map(|d| d.display()).join(":");
86                 let (data, mountflags) = parse_mount_options(options);
87                 let fstype = c_str!("bcachefs");
88
89                 let src = std::ffi::CString::new(src)?; // bind the CString to keep it alive
90                 let target = std::ffi::CString::new(target.as_ref().as_os_str().as_bytes())?; // ditto
91                 let data = data.map(|data| std::ffi::CString::new(data)).transpose()?; // ditto
92
93                 let src = src.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
94                 let target = target.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
95                 let data = data.as_ref().map_or(std::ptr::null(), |data| {
96                         data.as_c_str().to_bytes_with_nul().as_ptr() as *const c_void
97                 });
98
99                 let ret = unsafe { libc::mount(src, target, fstype, mountflags, data) };
100                 if ret == 0 {
101                         Ok(())
102                 } else {
103                         Err(crate::ErrnoError(errno::errno()).into())
104                 }
105         }
106 }
107
108 use crate::bcachefs;
109 use std::collections::HashMap;
110 use uuid::Uuid;
111 pub fn probe_filesystems() -> anyhow::Result<HashMap<Uuid, FileSystem>> {
112         use std::os::unix::ffi::OsStrExt;
113         let mut udev = udev::Enumerator::new()?;
114         let mut fss = HashMap::new();
115         udev.match_subsystem("block")?;
116
117         {
118                 // Stop libbcachefs from spamming the output
119                 let _gag = gag::Gag::stdout().unwrap();
120                 for dev in udev.scan_devices()? {
121                         if let Some(p) = dev.devnode() {
122                                 let path =
123                                         std::ffi::CString::new(p.as_os_str().as_bytes()).unwrap();
124                                 let result = unsafe {
125                                         let mut opts = std::mem::MaybeUninit::zeroed();
126                                         let mut sb = std::mem::MaybeUninit::zeroed();
127                                         let ret = bcachefs::bch2_read_super(
128                                                 path.as_ptr(),
129                                                 opts.as_mut_ptr(),
130                                                 sb.as_mut_ptr(),
131                                         );
132                                         if ret == -libc::EACCES {
133                                                 Err(std::io::Error::new(
134                                                         std::io::ErrorKind::PermissionDenied,
135                                                         "no permission",
136                                                 ))
137                                         } else if ret != 0 {
138                                                 Err(std::io::Error::new(
139                                                         std::io::ErrorKind::Other,
140                                                         "failed to read super",
141                                                 ))
142                                         } else {
143                                                 Ok((opts.assume_init(), sb.assume_init()))
144                                         }
145                                 };
146                                 match result {
147                                         Ok((_, sb)) => match fss.get_mut(&sb.sb().uuid()) {
148                                                 None => {
149                                                         let mut fs = FileSystem::new(sb);
150                                                         fs.devices.push(p.to_owned());
151                                                         fss.insert(fs.uuid, fs);
152                                                 }
153                                                 Some(fs) => {
154                                                         fs.devices.push(p.to_owned());
155                                                 }
156                                         },
157                                         Err(e) if e.kind()
158                                                 != std::io::ErrorKind::PermissionDenied =>
159                                         {
160                                                 ()
161                                         }
162                                         e @ Err(_) => {
163                                                 e?;
164                                         }
165                                 }
166                         }
167                 }
168                 // Flush stdout so buffered output don't get printed after we remove the gag
169                 unsafe {
170                         libc::fflush(stdout);
171                 }
172         }
173         Ok(fss)
174 }