summaryrefslogtreecommitdiff
path: root/rust-src/mount/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust-src/mount/src')
-rw-r--r--rust-src/mount/src/filesystem.rs238
-rw-r--r--rust-src/mount/src/key.rs23
-rw-r--r--rust-src/mount/src/keyutils_wrapper.h1
-rw-r--r--rust-src/mount/src/lib.rs181
-rw-r--r--rust-src/mount/src/libbcachefs_wrapper.h4
-rw-r--r--rust-src/mount/src/main.rs63
6 files changed, 252 insertions, 258 deletions
diff --git a/rust-src/mount/src/filesystem.rs b/rust-src/mount/src/filesystem.rs
index 36af8c05..b1575c26 100644
--- a/rust-src/mount/src/filesystem.rs
+++ b/rust-src/mount/src/filesystem.rs
@@ -19,11 +19,103 @@ pub struct FileSystem {
#[getset(get = "pub")]
devices: Vec<PathBuf>,
}
+impl std::fmt::Debug for FileSystem {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FileSystem")
+ .field("uuid", &self.uuid)
+ .field("encrypted", &self.encrypted)
+ .field("devices", &self.device_string())
+ .finish()
+ }
+}
+use std::fmt;
+impl std::fmt::Display for FileSystem {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let devs = self.device_string();
+ write!(
+ f,
+ "{:?}: locked?={lock} ({}) ",
+ self.uuid,
+ devs,
+ lock = self.encrypted
+ )
+ }
+}
+
+impl FileSystem {
+ pub(crate) fn new(sb: bcachefs::bch_sb_handle) -> Self {
+ Self {
+ uuid: sb.sb().uuid(),
+ encrypted: sb.sb().crypt().is_some(),
+ sb: sb,
+ devices: Vec::new(),
+ }
+ }
+
+ pub fn device_string(&self) -> String {
+ use itertools::Itertools;
+ self.devices.iter().map(|d| d.display()).join(":")
+ }
+
+ pub fn mount(
+ &self,
+ target: impl AsRef<std::path::Path>,
+ options: impl AsRef<str>,
+ ) -> anyhow::Result<()> {
+ tracing::info_span!("mount").in_scope(|| {
+ let src = self.device_string();
+ let (data, mountflags) = parse_mount_options(options);
+ // let fstype = c_str!("bcachefs");
+
+ tracing::info!(msg="mounting bcachefs filesystem", target=%target.as_ref().display());
+ mount_inner(src, target, "bcachefs", mountflags, data)
+ })
+ }
+}
+
+fn mount_inner(
+ src: String,
+ target: impl AsRef<std::path::Path>,
+ fstype: &str,
+ mountflags: u64,
+ data: Option<String>,
+) -> anyhow::Result<()> {
+ use std::{
+ ffi::{c_void, CString},
+ os::{raw::c_char, unix::ffi::OsStrExt},
+ };
+
+ // bind the CStrings to keep them alive
+ let src = CString::new(src)?;
+ let target = CString::new(target.as_ref().as_os_str().as_bytes())?;
+ let data = data.map(CString::new).transpose()?;
+ let fstype = CString::new(fstype)?;
+
+ // convert to pointers for ffi
+ let src = src.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
+ let target = target.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
+ let data = data.as_ref().map_or(std::ptr::null(), |data| {
+ data.as_c_str().to_bytes_with_nul().as_ptr() as *const c_void
+ });
+ let fstype = fstype.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
+
+ let ret = {let _entered = tracing::info_span!("libc::mount").entered();
+ tracing::info!("mounting filesystem");
+ // REQUIRES: CAP_SYS_ADMIN
+ unsafe { libc::mount(src, target, fstype, mountflags, data) }
+ };
+ match ret {
+ 0 => Ok(()),
+ _ => Err(crate::ErrnoError(errno::errno()).into()),
+ }
+}
/// Parse a comma-separated mount options and split out mountflags and filesystem
/// specific options.
+#[tracing_attributes::instrument(skip(options))]
fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
use either::Either::*;
+ tracing::debug!(msg="parsing mount options", options=?options.as_ref());
let (opts, flags) = options
.as_ref()
.split(",")
@@ -63,112 +155,54 @@ fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
)
}
-impl FileSystem {
- pub(crate) fn new(sb: bcachefs::bch_sb_handle) -> Self {
- Self {
- uuid: sb.sb().uuid(),
- encrypted: sb.sb().crypt().is_some(),
- sb: sb,
- devices: vec![],
- }
- }
-
- pub fn mount(
- &self,
- target: impl AsRef<std::path::Path>,
- options: impl AsRef<str>,
- ) -> anyhow::Result<()> {
- use itertools::Itertools;
- use std::ffi::c_void;
- use std::os::raw::c_char;
- use std::os::unix::ffi::OsStrExt;
- let src = self.devices.iter().map(|d| d.display()).join(":");
- let (data, mountflags) = parse_mount_options(options);
- let fstype = c_str!("bcachefs");
-
- let src = std::ffi::CString::new(src)?; // bind the CString to keep it alive
- let target = std::ffi::CString::new(target.as_ref().as_os_str().as_bytes())?; // ditto
- let data = data.map(|data| std::ffi::CString::new(data)).transpose()?; // ditto
-
- let src = src.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
- let target = target.as_c_str().to_bytes_with_nul().as_ptr() as *const c_char;
- let data = data.as_ref().map_or(std::ptr::null(), |data| {
- data.as_c_str().to_bytes_with_nul().as_ptr() as *const c_void
- });
-
- let ret = unsafe { libc::mount(src, target, fstype, mountflags, data) };
- if ret == 0 {
- Ok(())
- } else {
- Err(crate::ErrnoError(errno::errno()).into())
- }
- }
-}
-
-use crate::bcachefs;
+use bch_bindgen::bcachefs;
use std::collections::HashMap;
use uuid::Uuid;
+
+#[tracing_attributes::instrument]
pub fn probe_filesystems() -> anyhow::Result<HashMap<Uuid, FileSystem>> {
- use std::os::unix::ffi::OsStrExt;
+ tracing::trace!("enumerating udev devices");
let mut udev = udev::Enumerator::new()?;
- let mut fss = HashMap::new();
- udev.match_subsystem("block")?;
-
- {
- // Stop libbcachefs from spamming the output
- let _gag = gag::Gag::stdout().unwrap();
- for dev in udev.scan_devices()? {
- if let Some(p) = dev.devnode() {
- let path =
- std::ffi::CString::new(p.as_os_str().as_bytes()).unwrap();
- let result = unsafe {
- let mut opts = std::mem::MaybeUninit::zeroed();
- let mut sb = std::mem::MaybeUninit::zeroed();
- let ret = bcachefs::bch2_read_super(
- path.as_ptr(),
- opts.as_mut_ptr(),
- sb.as_mut_ptr(),
- );
- if ret == -libc::EACCES {
- Err(std::io::Error::new(
- std::io::ErrorKind::PermissionDenied,
- "no permission",
- ))
- } else if ret != 0 {
- Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "failed to read super",
- ))
- } else {
- Ok((opts.assume_init(), sb.assume_init()))
- }
- };
- match result {
- Ok((_, sb)) => match fss.get_mut(&sb.sb().uuid()) {
- None => {
- let mut fs = FileSystem::new(sb);
- fs.devices.push(p.to_owned());
- fss.insert(fs.uuid, fs);
- }
- Some(fs) => {
- fs.devices.push(p.to_owned());
- }
- },
- Err(e) if e.kind()
- != std::io::ErrorKind::PermissionDenied =>
- {
- ()
- }
- e @ Err(_) => {
- e?;
- }
- }
- }
- }
- // Flush stdout so buffered output don't get printed after we remove the gag
- unsafe {
- libc::fflush(stdout);
+
+ udev.match_subsystem("block")?; // find kernel block devices
+
+ let mut fs_map = HashMap::new();
+ let devresults =
+ udev.scan_devices()?
+ .into_iter()
+ .filter_map(|dev| dev.devnode().map(ToOwned::to_owned));
+
+ for pathbuf in devresults {
+ match get_super_block_uuid(&pathbuf)? {
+
+ Ok((uuid_key, superblock)) => {
+ let fs = fs_map.entry(uuid_key).or_insert_with(|| {
+ tracing::info!(msg="found bcachefs pool", uuid=?uuid_key);
+ FileSystem::new(superblock)
+ });
+
+ fs.devices.push(pathbuf);
+ },
+
+ Err(e) => { tracing::debug!(inner2_error=?e);}
}
}
- Ok(fss)
+
+
+ tracing::info!(msg = "found filesystems", count = fs_map.len());
+ Ok(fs_map)
+}
+
+// #[tracing_attributes::instrument(skip(dev, fs_map))]
+fn get_super_block_uuid(path: &std::path::Path) -> std::io::Result<std::io::Result<(Uuid, bcachefs::bch_sb_handle)>> {
+ let sb = bch_bindgen::rs::read_super(&path)?;
+ let super_block = match sb {
+ Err(e) => { return Ok(Err(e)); }
+ Ok(sb) => sb,
+ };
+
+ let uuid = (&super_block).sb().uuid();
+ tracing::debug!(found="bcachefs superblock", devnode=?path, ?uuid);
+
+ Ok(Ok((uuid, super_block)))
}
diff --git a/rust-src/mount/src/key.rs b/rust-src/mount/src/key.rs
index 6769f52c..91c92d1c 100644
--- a/rust-src/mount/src/key.rs
+++ b/rust-src/mount/src/key.rs
@@ -1,12 +1,11 @@
-use log::info;
+use tracing::info;
fn check_for_key(key_name: &std::ffi::CStr) -> anyhow::Result<bool> {
- use crate::keyutils::{self, keyctl_search};
+ use bch_bindgen::keyutils::{self, keyctl_search};
let key_name = key_name.to_bytes_with_nul().as_ptr() as *const _;
let key_type = c_str!("logon");
- let key_id =
- unsafe { keyctl_search(keyutils::KEY_SPEC_USER_KEYRING, key_type, key_name, 0) };
+ let key_id = unsafe { keyctl_search(keyutils::KEY_SPEC_USER_KEYRING, key_type, key_name, 0) };
if key_id > 0 {
info!("Key has became avaiable");
Ok(true)
@@ -31,9 +30,9 @@ fn wait_for_key(uuid: &uuid::Uuid) -> anyhow::Result<()> {
const BCH_KEY_MAGIC: &str = "bch**key";
use crate::filesystem::FileSystem;
fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> {
- use crate::bcachefs::{self, bch2_chacha_encrypt_key, bch_encrypted_key, bch_key};
use anyhow::anyhow;
use byteorder::{LittleEndian, ReadBytesExt};
+ use bch_bindgen::bcachefs::{self, bch2_chacha_encrypt_key, bch_encrypted_key, bch_key};
use std::os::raw::c_char;
let key_name = std::ffi::CString::new(format!("bcachefs:{}", fs.uuid())).unwrap();
@@ -62,19 +61,18 @@ fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> {
)
};
if ret != 0 {
- Err(anyhow!("chache decryption failure"))
+ Err(anyhow!("chacha decryption failure"))
} else if key.magic != bch_key_magic {
Err(anyhow!("failed to verify the password"))
} else {
let key_type = c_str!("logon");
let ret = unsafe {
- crate::keyutils::add_key(
+ bch_bindgen::keyutils::add_key(
key_type,
- key_name.as_c_str().to_bytes_with_nul() as *const _
- as *const c_char,
+ key_name.as_c_str().to_bytes_with_nul() as *const _ as *const c_char,
&output as *const _ as *const _,
std::mem::size_of::<bch_key>() as u64,
- crate::keyutils::KEY_SPEC_USER_KEYRING,
+ bch_bindgen::keyutils::KEY_SPEC_USER_KEYRING,
)
};
if ret == -1 {
@@ -85,9 +83,12 @@ fn ask_for_key(fs: &FileSystem) -> anyhow::Result<()> {
}
}
-pub(crate) fn prepare_key(fs: &FileSystem, password: crate::KeyLocation) -> anyhow::Result<()> {
+#[tracing_attributes::instrument]
+pub fn prepare_key(fs: &FileSystem, password: crate::KeyLocation) -> anyhow::Result<()> {
use crate::KeyLocation::*;
use anyhow::anyhow;
+
+ tracing::info!(msg = "checking if key exists for filesystem");
match password {
Fail => Err(anyhow!("no key available")),
Wait => Ok(wait_for_key(fs.uuid())?),
diff --git a/rust-src/mount/src/keyutils_wrapper.h b/rust-src/mount/src/keyutils_wrapper.h
deleted file mode 100644
index 857cee2e..00000000
--- a/rust-src/mount/src/keyutils_wrapper.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <keyutils.h>
diff --git a/rust-src/mount/src/lib.rs b/rust-src/mount/src/lib.rs
index 751eab38..4e918e13 100644
--- a/rust-src/mount/src/lib.rs
+++ b/rust-src/mount/src/lib.rs
@@ -1,12 +1,24 @@
-use structopt::StructOpt;
use anyhow::anyhow;
+use structopt::StructOpt;
+
+pub mod err {
+ pub enum GError {
+ Unknown{
+ message: std::borrow::Cow<'static, String>
+ }
+ }
+ pub type GResult<T, E, OE> =::core::result::Result< ::core::result::Result<T, E>, OE>;
+ pub type Result<T, E> = GResult<T, E, GError>;
+}
#[macro_export]
macro_rules! c_str {
($lit:expr) => {
- unsafe { std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const std::os::raw::c_char)
- .to_bytes_with_nul()
- .as_ptr() as *const std::os::raw::c_char }
+ unsafe {
+ std::ffi::CStr::from_ptr(concat!($lit, "\0").as_ptr() as *const std::os::raw::c_char)
+ .to_bytes_with_nul()
+ .as_ptr() as *const std::os::raw::c_char
+ }
};
}
@@ -20,171 +32,60 @@ impl std::fmt::Display for ErrnoError {
impl std::error::Error for ErrnoError {}
#[derive(Debug)]
-pub(crate) enum KeyLocation {
+pub enum KeyLocation {
Fail,
Wait,
Ask,
}
-impl std::str::FromStr for KeyLocation {
+#[derive(Debug)]
+pub struct KeyLoc(pub Option<KeyLocation>);
+impl std::ops::Deref for KeyLoc {
+ type Target = Option<KeyLocation>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl std::str::FromStr for KeyLoc {
type Err = anyhow::Error;
fn from_str(s: &str) -> anyhow::Result<Self> {
- use anyhow::anyhow;
+ // use anyhow::anyhow;
match s {
- "fail" => Ok(Self::Fail),
- "wait" => Ok(Self::Wait),
- "ask" => Ok(Self::Ask),
- _ => Err(anyhow!("invalid password option"))
+ "" => Ok(KeyLoc(None)),
+ "fail" => Ok(KeyLoc(Some(KeyLocation::Fail))),
+ "wait" => Ok(KeyLoc(Some(KeyLocation::Wait))),
+ "ask" => Ok(KeyLoc(Some(KeyLocation::Ask))),
+ _ => Err(anyhow!("invalid password option")),
}
}
}
#[derive(StructOpt, Debug)]
/// Mount a bcachefs filesystem by its UUID.
-struct Options {
+pub struct Options {
/// Where the password would be loaded from.
///
/// Possible values are:
/// "fail" - don't ask for password, fail if filesystem is encrypted;
/// "wait" - wait for password to become available before mounting;
/// "ask" - prompt the user for password;
- #[structopt(short, long, default_value = "fail")]
- key_location: KeyLocation,
+ #[structopt(short, long, default_value = "")]
+ pub key_location: KeyLoc,
/// External UUID of the bcachefs filesystem
- uuid: uuid::Uuid,
+ pub uuid: uuid::Uuid,
/// Where the filesystem should be mounted. If not set, then the filesystem
/// won't actually be mounted. But all steps preceeding mounting the
/// filesystem (e.g. asking for passphrase) will still be performed.
- mountpoint: Option<std::path::PathBuf>,
+ pub mountpoint: Option<std::path::PathBuf>,
/// Mount options
#[structopt(short, default_value = "")]
- options: String,
-}
-
-mod filesystem;
-mod key;
-mod keyutils {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(unused)]
-
- include!(concat!(env!("OUT_DIR"), "/keyutils.rs"));
+ pub options: String,
}
-mod bcachefs {
- #![allow(non_upper_case_globals)]
- #![allow(non_camel_case_types)]
- #![allow(non_snake_case)]
- #![allow(unused)]
-
- include!(concat!(env!("OUT_DIR"), "/bcachefs.rs"));
+pub mod filesystem;
+pub mod key;
- use bitfield::bitfield;
- bitfield! {
- pub struct bch_scrypt_flags(u64);
- pub N, _: 15, 0;
- pub R, _: 31, 16;
- pub P, _: 47, 32;
- }
- bitfield! {
- pub struct bch_crypt_flags(u64);
- TYPE, _: 4, 0;
- }
- use memoffset::offset_of;
- impl bch_sb_field_crypt {
- pub fn scrypt_flags(&self) -> Option<bch_scrypt_flags> {
- let t = bch_crypt_flags(self.flags);
- if t.TYPE() != bch_kdf_types::BCH_KDF_SCRYPT as u64 {
- None
- } else {
- Some(bch_scrypt_flags(self.kdf_flags))
- }
- }
- pub fn key(&self) -> &bch_encrypted_key {
- &self.key
- }
- }
- impl bch_sb {
- pub fn crypt(&self) -> Option<&bch_sb_field_crypt> {
- unsafe {
- let ptr = bch2_sb_field_get(
- self as *const _ as *mut _,
- bch_sb_field_type::BCH_SB_FIELD_crypt,
- ) as *const u8;
- if ptr.is_null() {
- None
- } else {
- let offset = offset_of!(bch_sb_field_crypt, field);
- Some(&*((ptr.sub(offset)) as *const _))
- }
- }
- }
- pub fn uuid(&self) -> uuid::Uuid {
- uuid::Uuid::from_bytes(self.user_uuid.b)
- }
-
- /// Get the nonce used to encrypt the superblock
- pub fn nonce(&self) -> nonce {
- use byteorder::{ReadBytesExt, LittleEndian};
- let mut internal_uuid = &self.uuid.b[..];
- let dword1 = internal_uuid.read_u32::<LittleEndian>().unwrap();
- let dword2 = internal_uuid.read_u32::<LittleEndian>().unwrap();
- nonce { d: [0, 0, dword1, dword2] }
- }
- }
- impl bch_sb_handle {
- pub fn sb(&self) -> &bch_sb {
- unsafe { &*self.sb }
- }
- }
-}
-
-fn main_inner() -> anyhow::Result<()> {
- use itertools::Itertools;
- use log::{info, trace};
-
- env_logger::init();
- let opt = Options::from_args();
- trace!("{:?}", opt);
-
- let fss = filesystem::probe_filesystems()?;
- info!("Found {} bcachefs filesystems: ", fss.len());
- for fs in fss.values() {
- info!(
- "{} ({}): {}",
- fs.uuid(),
- if fs.encrypted() {
- "encrypted"
- } else {
- "unencrypted"
- },
- fs.devices().iter().map(|d| d.display()).join(" ")
- );
- }
-
- if let Some(fs) = fss.get(&opt.uuid) {
- if fs.encrypted() {
- info!("Making sure key is loaded for this filesystem");
- key::prepare_key(&fs, opt.key_location)?;
- }
-
- if let Some(p) = opt.mountpoint {
- fs.mount(&p, &opt.options)
- } else {
- Ok(())
- }
- } else {
- Err(anyhow!("Filesystem {} is not found", opt.uuid))
- }
-}
-
-#[no_mangle]
-pub extern "C" fn main() {
- if let Err(e) = main_inner() {
- println!("Error: {:?}", e);
- }
-}
+// pub fn mnt_in_use()
diff --git a/rust-src/mount/src/libbcachefs_wrapper.h b/rust-src/mount/src/libbcachefs_wrapper.h
deleted file mode 100644
index 9d9754c1..00000000
--- a/rust-src/mount/src/libbcachefs_wrapper.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "../libbcachefs/super-io.h"
-#include "../libbcachefs/checksum.h"
-#include "../libbcachefs/bcachefs_format.h"
-#include "../crypto.h"
diff --git a/rust-src/mount/src/main.rs b/rust-src/mount/src/main.rs
new file mode 100644
index 00000000..92b69170
--- /dev/null
+++ b/rust-src/mount/src/main.rs
@@ -0,0 +1,63 @@
+fn main() {
+ // convert existing log statements to tracing events
+ // tracing_log::LogTracer::init().expect("logtracer init failed!");
+ // format tracing log data to env_logger like stdout
+ tracing_subscriber::fmt::init();
+
+ if let Err(e) = crate::main_inner() {
+ tracing::error!(fatal_error = ?e);
+ }
+}
+
+
+
+#[tracing_attributes::instrument("main")]
+pub fn main_inner() -> anyhow::Result<()> {
+ use structopt::StructOpt;
+ use bcachefs_mount::{Options, filesystem, key};
+ unsafe {
+ libc::setvbuf(
+ filesystem::stdout,
+ std::ptr::null_mut(),
+ libc::_IONBF,
+ 0,
+ );
+ // libc::fflush(filesystem::stdout);
+ }
+ let opt = Options::from_args();
+
+
+ tracing::trace!(?opt);
+
+ let fss = filesystem::probe_filesystems()?;
+ let fs = fss
+ .get(&opt.uuid)
+ .ok_or_else(|| anyhow::anyhow!("filesystem was not found"))?;
+
+ tracing::info!(msg="found filesystem", %fs);
+ if fs.encrypted() {
+ let key = opt
+ .key_location
+ .0
+ .ok_or_else(|| anyhow::anyhow!("no keyoption specified for locked filesystem"))?;
+
+ key::prepare_key(&fs, key)?;
+ }
+
+ let mountpoint = opt
+ .mountpoint
+ .ok_or_else(|| anyhow::anyhow!("mountpoint option was not specified"))?;
+
+ fs.mount(&mountpoint, &opt.options)?;
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod test {
+ // use insta::assert_debug_snapshot;
+ // #[test]
+ // fn snapshot_testing() {
+ // insta::assert_debug_snapshot!();
+ // }
+}