2 pub static stdout: *mut libc::FILE;
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")]
12 /// Whether filesystem is encrypted
13 #[getset(get_copy = "pub")]
16 #[getset(get = "pub")]
17 sb: bcachefs::bch_sb_handle,
18 /// Member devices for this filesystem
19 #[getset(get = "pub")]
20 devices: Vec<PathBuf>,
23 /// Parse a comma-separated mount options and split out mountflags and filesystem
25 fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
26 use either::Either::*;
27 let (opts, flags) = options
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),
41 "relatime" => Left(libc::MS_RELATIME),
42 "strictatime" => Left(libc::MS_STRICTATIME),
43 "sync" => Left(libc::MS_SYNCHRONOUS),
47 .fold((Vec::new(), 0), |(mut opts, flags), next| match next {
48 Left(f) => (opts, flags | f),
55 use itertools::Itertools;
60 Some(opts.iter().join(","))
67 pub(crate) fn new(sb: bcachefs::bch_sb_handle) -> Self {
70 encrypted: sb.sb().crypt().is_some(),
78 target: impl AsRef<std::path::Path>,
79 options: impl AsRef<str>,
80 ) -> anyhow::Result<()> {
81 use itertools::Itertools;
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");
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
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
99 let ret = unsafe { libc::mount(src, target, fstype, mountflags, data) };
103 Err(crate::ErrnoError(errno::errno()).into())
109 use std::collections::HashMap;
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")?;
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() {
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(
132 if ret == -libc::EACCES {
133 Err(std::io::Error::new(
134 std::io::ErrorKind::PermissionDenied,
138 Err(std::io::Error::new(
139 std::io::ErrorKind::Other,
140 "failed to read super",
143 Ok((opts.assume_init(), sb.assume_init()))
147 Ok((_, sb)) => match fss.get_mut(&sb.sb().uuid()) {
149 let mut fs = FileSystem::new(sb);
150 fs.devices.push(p.to_owned());
151 fss.insert(fs.uuid, fs);
154 fs.devices.push(p.to_owned());
158 != std::io::ErrorKind::PermissionDenied =>
168 // Flush stdout so buffered output don't get printed after we remove the gag
170 libc::fflush(stdout);