| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 | use btlib::{    blocktree::Blocktree,    crypto::{tpm::TpmCredStore, CredStore},};use fuse_backend_rs::{api::server::Server, transport::{FuseSession, Error}};use log::error;use std::{    ffi::{c_char, CString},    fs::{self, File},    io,    os::fd::FromRawFd,    os::{raw::c_int, unix::ffi::OsStrExt},    path::{Path, PathBuf},    str::FromStr,};use tss_esapi::{    tcti_ldr::{TabrmdConfig, TctiNameConf},    Context,};const DEFAULT_TABRMD: &str = "bus_type=session";const MOUNT_OPTIONS: &str = "default_permissions";const FSNAME: &str = "btfuse";const FSTYPE: &str = "bt";trait PathExt {    fn try_create_dir(&self) -> io::Result<()>;}impl<T: AsRef<Path>> PathExt for T {    fn try_create_dir(&self) -> io::Result<()> {        match fs::create_dir(self) {            Ok(_) => Ok(()),            Err(err) => match err.kind() {                io::ErrorKind::AlreadyExists => Ok(()),                _ => Err(err),            },        }    }}#[link(name = "fuse3")]extern "C" {    /// Opens a channel to the kernel.    fn fuse_open_channel(mountpoint: *const c_char, options: *const c_char) -> c_int;}/// Calls into libfuse3 to mount this file system at the given path. The file descriptor to use/// to communicate with the kernel is returned.fn mount_at<P: AsRef<Path>>(mountpoint: P) -> File {    let mountpoint = CString::new(mountpoint.as_ref().as_os_str().as_bytes()).unwrap();    let options = CString::new(MOUNT_OPTIONS).unwrap();    let raw_fd = unsafe { fuse_open_channel(mountpoint.as_ptr(), options.as_ptr()) };    unsafe { File::from_raw_fd(raw_fd) }}/// A fuse daemon process.struct FuseDaemon<'a> {    /// The path of the directory to store all file system information in.    path: &'a Path,    /// The configuration string to use to connect to a Tabrmd instance.    tabrmd_config: &'a str,}impl<'a> FuseDaemon<'a> {    fn new(path: &'a Path, tabrmd_config: &'a str) -> FuseDaemon<'_> {        FuseDaemon {            path,            tabrmd_config,        }    }    fn start(&self) {        self.path            .try_create_dir()            .expect("failed to create main directory");        let mnt_path = self.path.join("mnt");        mnt_path            .try_create_dir()            .expect("failed to create mount directory");        let empty = fs::read_dir(&mnt_path)            .expect("failed to read mountdir")            .next()            .is_none();        let context = Context::new(TctiNameConf::Tabrmd(            TabrmdConfig::from_str(self.tabrmd_config).expect("failed to parse Tabrmd config"),        ))        .expect("failed to connect to Tabrmd");        let cred_store = TpmCredStore::new(context, self.path.join("tpm_state"))            .expect("failed to create TpmCredStore");        let node_creds = cred_store            .node_creds()            .expect("failed to retrieve node creds");        let bt_path = self.path.join("bt");        bt_path            .try_create_dir()            .expect("failed to create blocktree directory");        let fs = if empty {            Blocktree::new_empty(bt_path, 0, node_creds)        } else {            Blocktree::new_existing(bt_path, node_creds)        }        .expect("failed to create blocktree");        let server = Server::new(fs);        let mut session = FuseSession::new(&mnt_path, FSNAME, FSTYPE, false)            .expect("failed to create FUSE session");        session.set_fuse_file(mount_at(&mnt_path));        let mut channel = session            .new_channel()            .expect("failed to create FUSE channel");        loop {            match channel.get_request() {                Ok(Some((reader, writer))) => {                    if let Err(err) = server.handle_message(reader, writer.into(), None, None) {                        error!("error while handling FUSE message: {err}");                    }                }                Ok(None) => break,                Err(err) => {                    match err {                        // Occurs when the file system is unmounted.                        Error::SessionFailure(_) => break,                        _ => error!("{err}"),                    }                }            }        }    }}fn main() {    env_logger::init();    let main_dir = std::env::args().nth(1).expect("no mount point given");    let main_dir =        PathBuf::from_str(main_dir.as_str()).expect("failed to convert mount point to PathBuf");    let tabrmd_string = std::env::var("BT_TABRMD").ok();    let tabrmd_str = tabrmd_string        .as_ref()        .map_or(DEFAULT_TABRMD, |s| s.as_str());    let daemon = FuseDaemon::new(&main_dir, tabrmd_str);    daemon.start();}#[cfg(test)]mod test {    use btlib::test_helpers;    use tempdir::TempDir;    use super::*;    /// Starts the `FuseDaemon` in a new temporary directory prefixed by "btfuse".    #[test]    fn server_start() {        std::env::set_var("RUST_LOG", "info");        env_logger::init();        let temp_dir = TempDir::new("btfuse").expect("failed to create TempDir");        let swtpm = test_helpers::SwtpmHarness::new().expect("failed to start swtpm");        let daemon = FuseDaemon::new(temp_dir.path(), swtpm.tabrmd_config());        daemon.start();    }}
 |