main.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. use btlib::{
  2. blocktree::Blocktree,
  3. crypto::{tpm::TpmCredStore, CredStore},
  4. };
  5. use fuse_backend_rs::{api::server::Server, transport::{FuseSession, Error}};
  6. use log::error;
  7. use std::{
  8. ffi::{c_char, CString},
  9. fs::{self, File},
  10. io,
  11. os::fd::FromRawFd,
  12. os::{raw::c_int, unix::ffi::OsStrExt},
  13. path::{Path, PathBuf},
  14. str::FromStr,
  15. };
  16. use tss_esapi::{
  17. tcti_ldr::{TabrmdConfig, TctiNameConf},
  18. Context,
  19. };
  20. const DEFAULT_TABRMD: &str = "bus_type=session";
  21. const MOUNT_OPTIONS: &str = "default_permissions";
  22. const FSNAME: &str = "btfuse";
  23. const FSTYPE: &str = "bt";
  24. trait PathExt {
  25. fn try_create_dir(&self) -> io::Result<()>;
  26. }
  27. impl<T: AsRef<Path>> PathExt for T {
  28. fn try_create_dir(&self) -> io::Result<()> {
  29. match fs::create_dir(self) {
  30. Ok(_) => Ok(()),
  31. Err(err) => match err.kind() {
  32. io::ErrorKind::AlreadyExists => Ok(()),
  33. _ => Err(err),
  34. },
  35. }
  36. }
  37. }
  38. #[link(name = "fuse3")]
  39. extern "C" {
  40. /// Opens a channel to the kernel.
  41. fn fuse_open_channel(mountpoint: *const c_char, options: *const c_char) -> c_int;
  42. }
  43. /// Calls into libfuse3 to mount this file system at the given path. The file descriptor to use
  44. /// to communicate with the kernel is returned.
  45. fn mount_at<P: AsRef<Path>>(mountpoint: P) -> File {
  46. let mountpoint = CString::new(mountpoint.as_ref().as_os_str().as_bytes()).unwrap();
  47. let options = CString::new(MOUNT_OPTIONS).unwrap();
  48. let raw_fd = unsafe { fuse_open_channel(mountpoint.as_ptr(), options.as_ptr()) };
  49. unsafe { File::from_raw_fd(raw_fd) }
  50. }
  51. /// A fuse daemon process.
  52. struct FuseDaemon<'a> {
  53. /// The path of the directory to store all file system information in.
  54. path: &'a Path,
  55. /// The configuration string to use to connect to a Tabrmd instance.
  56. tabrmd_config: &'a str,
  57. }
  58. impl<'a> FuseDaemon<'a> {
  59. fn new(path: &'a Path, tabrmd_config: &'a str) -> FuseDaemon<'_> {
  60. FuseDaemon {
  61. path,
  62. tabrmd_config,
  63. }
  64. }
  65. fn start(&self) {
  66. self.path
  67. .try_create_dir()
  68. .expect("failed to create main directory");
  69. let mnt_path = self.path.join("mnt");
  70. mnt_path
  71. .try_create_dir()
  72. .expect("failed to create mount directory");
  73. let empty = fs::read_dir(&mnt_path)
  74. .expect("failed to read mountdir")
  75. .next()
  76. .is_none();
  77. let context = Context::new(TctiNameConf::Tabrmd(
  78. TabrmdConfig::from_str(self.tabrmd_config).expect("failed to parse Tabrmd config"),
  79. ))
  80. .expect("failed to connect to Tabrmd");
  81. let cred_store = TpmCredStore::new(context, self.path.join("tpm_state"))
  82. .expect("failed to create TpmCredStore");
  83. let node_creds = cred_store
  84. .node_creds()
  85. .expect("failed to retrieve node creds");
  86. let bt_path = self.path.join("bt");
  87. bt_path
  88. .try_create_dir()
  89. .expect("failed to create blocktree directory");
  90. let fs = if empty {
  91. Blocktree::new_empty(bt_path, 0, node_creds)
  92. } else {
  93. Blocktree::new_existing(bt_path, node_creds)
  94. }
  95. .expect("failed to create blocktree");
  96. let server = Server::new(fs);
  97. let mut session = FuseSession::new(&mnt_path, FSNAME, FSTYPE, false)
  98. .expect("failed to create FUSE session");
  99. session.set_fuse_file(mount_at(&mnt_path));
  100. let mut channel = session
  101. .new_channel()
  102. .expect("failed to create FUSE channel");
  103. loop {
  104. match channel.get_request() {
  105. Ok(Some((reader, writer))) => {
  106. if let Err(err) = server.handle_message(reader, writer.into(), None, None) {
  107. error!("error while handling FUSE message: {err}");
  108. }
  109. }
  110. Ok(None) => break,
  111. Err(err) => {
  112. match err {
  113. // Occurs when the file system is unmounted.
  114. Error::SessionFailure(_) => break,
  115. _ => error!("{err}"),
  116. }
  117. }
  118. }
  119. }
  120. }
  121. }
  122. fn main() {
  123. env_logger::init();
  124. let main_dir = std::env::args().nth(1).expect("no mount point given");
  125. let main_dir =
  126. PathBuf::from_str(main_dir.as_str()).expect("failed to convert mount point to PathBuf");
  127. let tabrmd_string = std::env::var("BT_TABRMD").ok();
  128. let tabrmd_str = tabrmd_string
  129. .as_ref()
  130. .map_or(DEFAULT_TABRMD, |s| s.as_str());
  131. let daemon = FuseDaemon::new(&main_dir, tabrmd_str);
  132. daemon.start();
  133. }
  134. #[cfg(test)]
  135. mod test {
  136. use btlib::test_helpers;
  137. use tempdir::TempDir;
  138. use super::*;
  139. /// Starts the `FuseDaemon` in a new temporary directory prefixed by "btfuse".
  140. #[test]
  141. fn server_start() {
  142. std::env::set_var("RUST_LOG", "info");
  143. env_logger::init();
  144. let temp_dir = TempDir::new("btfuse").expect("failed to create TempDir");
  145. let swtpm = test_helpers::SwtpmHarness::new().expect("failed to start swtpm");
  146. let daemon = FuseDaemon::new(temp_dir.path(), swtpm.tabrmd_config());
  147. daemon.start();
  148. }
  149. }