main.rs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. // SPDX-License-Identifier: AGPL-3.0-or-later
  2. mod fuse_daemon;
  3. use btconfig::{CredStoreConfig, FigmentExt, NodeCredConsumer};
  4. use figment::{providers::Serialized, Figment};
  5. use fuse_daemon::FuseDaemon;
  6. mod fuse_fs;
  7. use btfproto::{client::FsClient, local_fs::LocalFs, server::FsProvider};
  8. use btlib::{
  9. bterr,
  10. crypto::{Creds, CredsPriv},
  11. Result,
  12. };
  13. use btmsg::{BlockAddr, Transmitter};
  14. use serde::{Deserialize, Serialize};
  15. use std::{
  16. fs::{self},
  17. io,
  18. num::NonZeroUsize,
  19. path::{Path, PathBuf},
  20. sync::Arc,
  21. };
  22. use tokio::sync::oneshot;
  23. #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
  24. #[serde(tag = "type")]
  25. pub enum FsKind {
  26. /// Indicates that a local filesystem provider should be used.
  27. Local { path: PathBuf },
  28. /// Indicates that a remote filesystem provider should be used.
  29. Remote { addr: BlockAddr },
  30. }
  31. impl Default for FsKind {
  32. fn default() -> Self {
  33. Self::Local {
  34. path: btconfig::default_block_dir(),
  35. }
  36. }
  37. }
  38. #[derive(Debug, Clone, Serialize, Deserialize)]
  39. pub struct BtfuseConfig {
  40. /// Configures the credential store used.
  41. #[serde(rename = "credstore")]
  42. pub cred_store: CredStoreConfig,
  43. /// Configures the filesystem provider used.
  44. #[serde(rename = "fskind")]
  45. pub fs_kind: FsKind,
  46. /// Sets the directory where the filesystem will be mounted.
  47. #[serde(rename = "mntdir")]
  48. pub mnt_dir: PathBuf,
  49. /// Specifies the options to use for the filesystem mount.
  50. #[serde(rename = "mntoptions")]
  51. pub mnt_options: String,
  52. /// Prescribes the number of threads to use for handling FUSE messages from the kernel. If
  53. /// not specified, then the number of threads will be equal to the number of logical cores on
  54. /// the system.
  55. pub threads: Option<NonZeroUsize>,
  56. }
  57. impl BtfuseConfig {
  58. pub fn new() -> Result<Self> {
  59. Figment::new()
  60. .merge(Serialized::defaults(BtfuseConfig::default()))
  61. .btconfig()?
  62. .extract()
  63. .map_err(|err| err.into())
  64. }
  65. }
  66. impl Default for BtfuseConfig {
  67. fn default() -> Self {
  68. Self {
  69. cred_store: CredStoreConfig::default(),
  70. fs_kind: FsKind::default(),
  71. mnt_dir: "./btfuse_mnt".into(),
  72. mnt_options: "default_permissions".into(),
  73. threads: None,
  74. }
  75. }
  76. }
  77. trait PathExt {
  78. fn try_create_dir(&self) -> io::Result<()>;
  79. }
  80. impl<T: AsRef<Path>> PathExt for T {
  81. fn try_create_dir(&self) -> io::Result<()> {
  82. match fs::create_dir(self) {
  83. Ok(_) => Ok(()),
  84. Err(err) => match err.kind() {
  85. io::ErrorKind::AlreadyExists => Ok(()),
  86. _ => Err(err),
  87. },
  88. }
  89. }
  90. }
  91. async fn local_provider(btdir: PathBuf, node_creds: Arc<dyn Creds>) -> Result<impl FsProvider> {
  92. btdir.try_create_dir()?;
  93. let empty = fs::read_dir(&btdir)?.next().is_none();
  94. if empty {
  95. LocalFs::new_empty(btdir, 0, node_creds, btfproto::local_fs::ModeAuthorizer {}).await
  96. } else {
  97. LocalFs::new_existing(btdir, node_creds, btfproto::local_fs::ModeAuthorizer {})
  98. }
  99. }
  100. async fn remote_provider<C: 'static + Creds + Send + Sync>(
  101. remote_addr: BlockAddr,
  102. node_creds: C,
  103. ) -> Result<impl FsProvider> {
  104. let tx = Transmitter::new(Arc::new(remote_addr), Arc::new(node_creds)).await?;
  105. let client = FsClient::new(tx);
  106. Ok(client)
  107. }
  108. async fn run_daemon(config: BtfuseConfig, mounted_signal: Option<oneshot::Sender<()>>) {
  109. let node_creds = config
  110. .cred_store
  111. .consume(NodeCredConsumer)
  112. .unwrap()
  113. .unwrap();
  114. let fallback_path = {
  115. let writecap = node_creds
  116. .writecap()
  117. .ok_or(btlib::BlockError::MissingWritecap)
  118. .unwrap();
  119. Arc::new(writecap.bind_path())
  120. };
  121. let mut daemon = match config.fs_kind {
  122. FsKind::Local { path: btdir } => {
  123. log::info!("starting daemon with local provider using {:?}", btdir);
  124. let provider = local_provider(btdir.clone(), node_creds)
  125. .await
  126. .map_err(|err| {
  127. bterr!(
  128. "failed to create local provider using path '{}': {err}",
  129. btdir.display()
  130. )
  131. })
  132. .unwrap();
  133. FuseDaemon::new(
  134. config.mnt_dir,
  135. &config.mnt_options,
  136. config.threads,
  137. fallback_path,
  138. mounted_signal,
  139. provider,
  140. )
  141. }
  142. FsKind::Remote { addr: remote_addr } => {
  143. log::info!(
  144. "starting daemon with remote provider using {:?}",
  145. remote_addr.socket_addr()
  146. );
  147. let provider = remote_provider(remote_addr, node_creds)
  148. .await
  149. .expect("failed to create remote provider");
  150. FuseDaemon::new(
  151. config.mnt_dir,
  152. &config.mnt_options,
  153. config.threads,
  154. fallback_path,
  155. mounted_signal,
  156. provider,
  157. )
  158. }
  159. }
  160. .expect("failed to create FUSE daemon");
  161. daemon.finished().await;
  162. }
  163. #[tokio::main]
  164. async fn main() {
  165. env_logger::init();
  166. let config = BtfuseConfig::new().unwrap();
  167. run_daemon(config, None).await;
  168. }
  169. #[cfg(test)]
  170. mod test {
  171. use super::*;
  172. use btconfig::CredStoreConfig;
  173. use btfproto::{local_fs::ModeAuthorizer, server::new_fs_server};
  174. use btlib::{
  175. crypto::{tpm::TpmCredStore, CredStore, CredStoreMut, Creds},
  176. log::BuilderExt,
  177. Epoch, Principaled,
  178. };
  179. use btmsg::Receiver;
  180. use ctor::ctor;
  181. use std::{
  182. ffi::{OsStr, OsString},
  183. fs::Permissions,
  184. io::{BufRead, SeekFrom},
  185. net::{IpAddr, Ipv6Addr},
  186. num::NonZeroUsize,
  187. os::unix::fs::PermissionsExt,
  188. time::Duration,
  189. };
  190. use swtpm_harness::SwtpmHarness;
  191. use tempdir::TempDir;
  192. use tokio::{
  193. fs::{
  194. create_dir, hard_link, metadata, read, read_dir, remove_dir, remove_file, rename,
  195. set_permissions, write, OpenOptions, ReadDir,
  196. },
  197. io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
  198. task::JoinHandle,
  199. };
  200. /// An optional timeout to limit the time spent waiting for the FUSE daemon to start in tests.
  201. const TIMEOUT: Option<Duration> = Some(Duration::from_millis(1250));
  202. /// The log level to use when running tests.
  203. /// Note that the debug log level significantly reduces performance.
  204. const LOG_LEVEL: &str = "warn";
  205. #[ctor]
  206. fn ctor() {
  207. std::env::set_var("RUST_LOG", LOG_LEVEL);
  208. env_logger::Builder::from_default_env().btformat().init();
  209. }
  210. /// Reads `/etc/mtab` to determine if `mnt_path` is mounted. Returns true if it is and false
  211. /// otherwise.
  212. fn mounted(mnt_path: &str) -> bool {
  213. let file = std::fs::OpenOptions::new()
  214. .read(true)
  215. .write(false)
  216. .create(false)
  217. .open("/etc/mtab")
  218. .unwrap();
  219. let mut reader = std::io::BufReader::new(file);
  220. let mut line = String::with_capacity(64);
  221. loop {
  222. line.clear();
  223. let read = reader.read_line(&mut line).unwrap();
  224. if 0 == read {
  225. break;
  226. }
  227. let path = line.split(' ').skip(1).next().unwrap();
  228. if path == mnt_path {
  229. return true;
  230. }
  231. }
  232. false
  233. }
  234. /// Unmounts the file system at the given path.
  235. fn unmount<P: AsRef<Path>>(mnt_path: P) {
  236. let mnt_path = mnt_path.as_ref();
  237. if !mounted(mnt_path.to_str().unwrap()) {
  238. return;
  239. }
  240. const PROG: &str = "fusermount";
  241. let mnt_path = mnt_path
  242. .as_os_str()
  243. .to_str()
  244. .expect("failed to convert mnt_path to `str`");
  245. let code = std::process::Command::new(PROG)
  246. .args(["-z", "-u", mnt_path])
  247. .status()
  248. .expect("waiting for exit status failed")
  249. .code()
  250. .expect("code returned None");
  251. if code != 0 {
  252. panic!("{PROG} exited with a non-zero status: {code}");
  253. }
  254. }
  255. async fn file_names(mut read_dir: ReadDir) -> Vec<OsString> {
  256. let mut output = Vec::new();
  257. while let Some(entry) = read_dir.next_entry().await.unwrap() {
  258. output.push(entry.file_name());
  259. }
  260. output
  261. }
  262. const ROOT_PASSWD: &str = "password";
  263. struct TestCase {
  264. config: BtfuseConfig,
  265. handle: Option<JoinHandle<()>>,
  266. node_principal: OsString,
  267. stop_flag: Option<()>,
  268. // Note that the drop order of these fields is significant.
  269. _receiver: Option<Receiver>,
  270. _cred_store: TpmCredStore,
  271. _swtpm: SwtpmHarness,
  272. _temp_dir: TempDir,
  273. }
  274. async fn new_local() -> TestCase {
  275. new(false).await
  276. }
  277. async fn new_remote() -> TestCase {
  278. new(true).await
  279. }
  280. async fn new(remote: bool) -> TestCase {
  281. let tmp = TempDir::new("btfuse").unwrap();
  282. let (mounted_tx, mounted_rx) = oneshot::channel();
  283. let (swtpm, cred_store) = swtpm();
  284. let block_dir = tmp.path().join("bt");
  285. let (fs_kind, receiver) = if remote {
  286. let node_creds = Arc::new(cred_store.node_creds().unwrap());
  287. let bind_path = node_creds.bind_path().unwrap();
  288. block_dir.try_create_dir().unwrap();
  289. let local_fs = LocalFs::new_empty(block_dir, 0, node_creds.clone(), ModeAuthorizer)
  290. .await
  291. .unwrap();
  292. let ip_addr = IpAddr::V6(Ipv6Addr::LOCALHOST);
  293. let receiver = new_fs_server(ip_addr, node_creds.clone(), Arc::new(local_fs)).unwrap();
  294. let fs_kind = FsKind::Remote {
  295. addr: BlockAddr::new(ip_addr, Arc::new(bind_path)),
  296. };
  297. (fs_kind, Some(receiver))
  298. } else {
  299. (FsKind::Local { path: block_dir }, None)
  300. };
  301. let config = BtfuseConfig {
  302. threads: Some(NonZeroUsize::new(1).unwrap()),
  303. mnt_dir: tmp.path().join("mnt"),
  304. cred_store: CredStoreConfig::Tpm {
  305. path: swtpm.state_path().to_owned().into(),
  306. tabrmd: swtpm.tabrmd_config().to_owned(),
  307. },
  308. fs_kind,
  309. mnt_options: "default_permissions".to_string(),
  310. };
  311. let config_clone = config.clone();
  312. let handle = tokio::spawn(async move {
  313. run_daemon(config_clone, Some(mounted_tx)).await;
  314. });
  315. if let Some(timeout) = TIMEOUT {
  316. tokio::time::timeout(timeout, mounted_rx)
  317. .await
  318. .unwrap()
  319. .unwrap();
  320. } else {
  321. mounted_rx.await.unwrap();
  322. }
  323. let node_principal =
  324. OsString::from(cred_store.node_creds().unwrap().principal().to_string());
  325. TestCase {
  326. config,
  327. handle: Some(handle),
  328. node_principal,
  329. stop_flag: Some(()),
  330. _receiver: receiver,
  331. _temp_dir: tmp,
  332. _swtpm: swtpm,
  333. _cred_store: cred_store,
  334. }
  335. }
  336. fn swtpm() -> (SwtpmHarness, TpmCredStore) {
  337. let swtpm = SwtpmHarness::new().unwrap();
  338. let state_path: PathBuf = swtpm.state_path().to_owned();
  339. let cred_store = {
  340. let context = swtpm.context().unwrap();
  341. TpmCredStore::from_context(context, state_path.clone()).unwrap()
  342. };
  343. let root_creds = cred_store.gen_root_creds(ROOT_PASSWD).unwrap();
  344. let mut node_creds = cred_store.node_creds().unwrap();
  345. let expires = Epoch::now() + Duration::from_secs(3600);
  346. let writecap = root_creds
  347. .issue_writecap(node_creds.principal(), &mut std::iter::empty(), expires)
  348. .unwrap();
  349. cred_store
  350. .assign_node_writecap(&mut node_creds, writecap)
  351. .unwrap();
  352. (swtpm, cred_store)
  353. }
  354. impl TestCase {
  355. fn mnt_dir(&self) -> &Path {
  356. &self.config.mnt_dir
  357. }
  358. /// Signals to the daemon that it must stop.
  359. fn signal_stop(&mut self) {
  360. if let Some(_) = self.stop_flag.take() {
  361. unmount(&self.config.mnt_dir)
  362. }
  363. }
  364. /// Returns a future that resolves when the daemon has stopped.
  365. async fn stopped(&mut self) {
  366. if let Some(handle) = self.handle.take() {
  367. handle.await.expect("join failed");
  368. }
  369. }
  370. /// Signals to the daemon to stop and returns a future that resolves when after it has
  371. /// stopped.
  372. async fn stop(&mut self) {
  373. self.signal_stop();
  374. self.stopped().await;
  375. }
  376. fn initial_contents(&self) -> Vec<&OsStr> {
  377. vec![&self.node_principal]
  378. }
  379. }
  380. impl Drop for TestCase {
  381. fn drop(&mut self) {
  382. self.signal_stop()
  383. }
  384. }
  385. /// Creates a new file system and mounts it at `/tmp/btfuse.<random>/mnt` so it can be
  386. /// tested manually.
  387. //#[tokio::test]
  388. #[allow(dead_code)]
  389. async fn manual_test() {
  390. let mut case = new_local().await;
  391. case.stopped().await
  392. }
  393. async fn write_read(mut case: TestCase) -> Result<()> {
  394. const EXPECTED: &[u8] =
  395. b"The paths to failure are uncountable, yet to success there are few.";
  396. let file_path = case.mnt_dir().join("file");
  397. write(&file_path, EXPECTED).await?;
  398. let actual = read(&file_path).await?;
  399. assert_eq!(EXPECTED, actual);
  400. case.stop().await;
  401. Ok(())
  402. }
  403. #[tokio::test]
  404. async fn write_read_local() -> Result<()> {
  405. write_read(new_local().await).await
  406. }
  407. // When the current thread runtime is used the test executable does not exit after the test
  408. // method returns because one of the FuseDaemon blocking threads is blocked calling
  409. // `FuseChannel::get_request`.
  410. #[tokio::test(flavor = "multi_thread")]
  411. async fn write_read_remote() -> Result<()> {
  412. write_read(new_remote().await).await
  413. }
  414. async fn create_file_then_readdir(mut case: TestCase) {
  415. const DATA: &[u8] = b"Au revoir Shoshanna!";
  416. let file_name = OsStr::new("landa_dialog.txt");
  417. let mut expected = case.initial_contents();
  418. expected.push(file_name);
  419. let mnt_path = case.mnt_dir();
  420. let file_path = mnt_path.join(file_name);
  421. write(&file_path, DATA).await.expect("write failed");
  422. let first = file_names(read_dir(&mnt_path).await.expect("read_dir failed")).await;
  423. assert_eq!(expected, first);
  424. let second = file_names(read_dir(&mnt_path).await.expect("read_dir failed")).await;
  425. assert_eq!(expected, second);
  426. case.stop().await;
  427. }
  428. #[tokio::test]
  429. async fn create_file_then_readdir_local() {
  430. create_file_then_readdir(new_local().await).await
  431. }
  432. #[tokio::test(flavor = "multi_thread")]
  433. async fn create_file_then_readdir_remote() {
  434. create_file_then_readdir(new_remote().await).await
  435. }
  436. async fn create_then_delete_file(mut case: TestCase) {
  437. const DATA: &[u8] = b"The universe is hostile, so impersonal. Devour to survive";
  438. let file_name = OsStr::new("tool_lyrics.txt");
  439. let mnt_path = case.mnt_dir();
  440. let file_path = mnt_path.join(file_name);
  441. write(&file_path, DATA).await.expect("write failed");
  442. remove_file(&file_path).await.expect("remove_file failed");
  443. let expected = case.initial_contents();
  444. let actual = file_names(read_dir(&mnt_path).await.expect("read_dir failed")).await;
  445. assert_eq!(expected, actual);
  446. case.stop().await;
  447. }
  448. #[tokio::test]
  449. async fn create_then_delete_file_local() {
  450. create_then_delete_file(new_local().await).await
  451. }
  452. #[tokio::test(flavor = "multi_thread")]
  453. async fn create_then_delete_file_remote() {
  454. create_then_delete_file(new_remote().await).await
  455. }
  456. async fn hard_link_then_remove(mut case: TestCase) {
  457. const EXPECTED: &[u8] = b"And the lives we've reclaimed";
  458. let name1 = OsStr::new("refugee_lyrics.txt");
  459. let name2 = OsStr::new("rise_against_lyrics.txt");
  460. let mnt_path = case.mnt_dir();
  461. let path1 = mnt_path.join(name1);
  462. let path2 = mnt_path.join(name2);
  463. write(&path1, EXPECTED).await.expect("write failed");
  464. hard_link(&path1, &path2).await.expect("hard_link failed");
  465. remove_file(&path1).await.expect("remove_file failed");
  466. let actual = read(&path2).await.expect("read failed");
  467. assert_eq!(EXPECTED, actual);
  468. case.stop().await;
  469. }
  470. #[tokio::test]
  471. async fn hard_link_then_remove_local() {
  472. hard_link_then_remove(new_local().await).await
  473. }
  474. #[tokio::test(flavor = "multi_thread")]
  475. async fn hard_link_then_remove_remote() {
  476. hard_link_then_remove(new_remote().await).await
  477. }
  478. async fn hard_link_then_remove_both(mut case: TestCase) {
  479. const EXPECTED: &[u8] = b"And the lives we've reclaimed";
  480. let name1 = OsStr::new("refugee_lyrics.txt");
  481. let name2 = OsStr::new("rise_against_lyrics.txt");
  482. let mnt_path = case.mnt_dir();
  483. let path1 = mnt_path.join(name1);
  484. let path2 = mnt_path.join(name2);
  485. write(&path1, EXPECTED).await.expect("write failed");
  486. hard_link(&path1, &path2).await.expect("hard_link failed");
  487. remove_file(&path1)
  488. .await
  489. .expect("remove_file on path1 failed");
  490. remove_file(&path2)
  491. .await
  492. .expect("remove_file on path2 failed");
  493. let expected = case.initial_contents();
  494. let actual = file_names(read_dir(&mnt_path).await.expect("read_dir failed")).await;
  495. assert_eq!(expected, actual);
  496. case.stop().await;
  497. }
  498. #[tokio::test]
  499. async fn hard_link_then_remove_both_local() {
  500. hard_link_then_remove_both(new_local().await).await
  501. }
  502. #[tokio::test(flavor = "multi_thread")]
  503. async fn hard_link_then_remove_both_remote() {
  504. hard_link_then_remove_both(new_remote().await).await
  505. }
  506. async fn set_mode_bits(mut case: TestCase) {
  507. const EXPECTED: u32 = libc::S_IFREG | 0o777;
  508. let file_path = case.mnt_dir().join("bagobits");
  509. write(&file_path, []).await.expect("write failed");
  510. let original = metadata(&file_path)
  511. .await
  512. .expect("metadata failed")
  513. .permissions()
  514. .mode();
  515. assert_ne!(EXPECTED, original);
  516. set_permissions(&file_path, Permissions::from_mode(EXPECTED))
  517. .await
  518. .expect("set_permissions failed");
  519. let actual = metadata(&file_path)
  520. .await
  521. .expect("metadata failed")
  522. .permissions()
  523. .mode();
  524. assert_eq!(EXPECTED, actual);
  525. case.stop().await;
  526. }
  527. #[tokio::test]
  528. async fn set_mode_bits_local() {
  529. set_mode_bits(new_local().await).await
  530. }
  531. #[tokio::test(flavor = "multi_thread")]
  532. async fn set_mode_bits_remote() {
  533. set_mode_bits(new_remote().await).await
  534. }
  535. async fn create_directory(mut case: TestCase) {
  536. const EXPECTED: &str = "etc";
  537. let mnt_path = case.mnt_dir();
  538. let dir_path = mnt_path.join(EXPECTED);
  539. let mut expected = case.initial_contents();
  540. expected.push(OsStr::new(EXPECTED));
  541. create_dir(&dir_path).await.expect("create_dir failed");
  542. let actual = file_names(read_dir(mnt_path).await.expect("read_dir failed")).await;
  543. assert_eq!(expected, actual);
  544. case.stop().await;
  545. }
  546. #[tokio::test]
  547. async fn create_directory_local() {
  548. create_directory(new_local().await).await
  549. }
  550. #[tokio::test(flavor = "multi_thread")]
  551. async fn create_directory_remote() {
  552. create_directory(new_remote().await).await
  553. }
  554. async fn create_file_under_new_directory(mut case: TestCase) {
  555. const DIR_NAME: &str = "etc";
  556. const FILE_NAME: &str = "file";
  557. let mnt_path = case.mnt_dir();
  558. let dir_path = mnt_path.join(DIR_NAME);
  559. let file_path = dir_path.join(FILE_NAME);
  560. create_dir(&dir_path).await.expect("create_dir failed");
  561. write(&file_path, []).await.expect("write failed");
  562. let actual = file_names(read_dir(dir_path).await.expect("read_dir failed")).await;
  563. assert_eq!([FILE_NAME].as_slice(), actual.as_slice());
  564. case.stop().await;
  565. }
  566. #[tokio::test]
  567. async fn create_file_under_new_directory_local() {
  568. create_file_under_new_directory(new_local().await).await
  569. }
  570. #[tokio::test(flavor = "multi_thread")]
  571. async fn create_file_under_new_directory_remote() {
  572. create_file_under_new_directory(new_remote().await).await
  573. }
  574. async fn create_then_remove_directory(mut case: TestCase) {
  575. const DIR_NAME: &str = "etc";
  576. let mnt_path = case.mnt_dir();
  577. let dir_path = mnt_path.join(DIR_NAME);
  578. create_dir(&dir_path).await.expect("create_dir failed");
  579. remove_dir(&dir_path).await.expect("remove_dir failed");
  580. let actual = file_names(read_dir(&mnt_path).await.expect("read_dir failed")).await;
  581. assert_eq!(case.initial_contents(), actual);
  582. case.stop().await;
  583. }
  584. #[tokio::test]
  585. async fn create_then_remote_directory_local() {
  586. create_then_remove_directory(new_local().await).await
  587. }
  588. #[tokio::test(flavor = "multi_thread")]
  589. async fn create_then_remote_directory_remote() {
  590. create_then_remove_directory(new_remote().await).await
  591. }
  592. async fn read_only_dir_cant_create_subdir(mut case: TestCase) {
  593. const DIR_NAME: &str = "etc";
  594. let dir_path = case.mnt_dir().join(DIR_NAME);
  595. create_dir(&dir_path).await.expect("create_dir failed");
  596. set_permissions(&dir_path, Permissions::from_mode(libc::S_IFDIR | 0o444))
  597. .await
  598. .expect("set_permissions failed");
  599. let result = create_dir(dir_path.join("sub")).await;
  600. let err = result.err().expect("create_dir returned `Ok`");
  601. let os_err = err.raw_os_error().expect("raw_os_error was empty");
  602. assert_eq!(os_err, libc::EACCES);
  603. case.stop().await;
  604. }
  605. #[tokio::test]
  606. async fn read_only_dir_cant_create_subdir_local() {
  607. read_only_dir_cant_create_subdir(new_local().await).await
  608. }
  609. #[tokio::test(flavor = "multi_thread")]
  610. async fn read_only_dir_cant_create_subdir_remote() {
  611. read_only_dir_cant_create_subdir(new_remote().await).await
  612. }
  613. async fn read_only_dir_cant_remove_subdir(mut case: TestCase) {
  614. const DIR_NAME: &str = "etc";
  615. let dir_path = case.mnt_dir().join(DIR_NAME);
  616. let sub_path = dir_path.join("sub");
  617. create_dir(&dir_path).await.expect("create_dir failed");
  618. create_dir(&sub_path).await.expect("create_dir failed");
  619. set_permissions(&dir_path, Permissions::from_mode(libc::S_IFDIR | 0o444))
  620. .await
  621. .expect("set_permissions failed");
  622. let result = remove_dir(&sub_path).await;
  623. let err = result.err().expect("remove_dir returned `Ok`");
  624. let os_err = err.raw_os_error().expect("raw_os_error was empty");
  625. assert_eq!(os_err, libc::EACCES);
  626. case.stop().await;
  627. }
  628. #[tokio::test]
  629. async fn read_only_dir_cant_remove_subdir_local() {
  630. read_only_dir_cant_remove_subdir(new_local().await).await
  631. }
  632. #[tokio::test(flavor = "multi_thread")]
  633. async fn read_only_dir_cant_remove_subdir_remote() {
  634. read_only_dir_cant_remove_subdir(new_remote().await).await
  635. }
  636. async fn rename_file(mut case: TestCase) {
  637. const FILE_NAME: &str = "parabola.txt";
  638. const EXPECTED: &[u8] = b"We are eternal all this pain is an illusion";
  639. let src_path = case.mnt_dir().join(FILE_NAME);
  640. let dst_path = case.mnt_dir().join("parabola_lyrics.txt");
  641. write(&src_path, EXPECTED).await.unwrap();
  642. rename(&src_path, &dst_path).await.unwrap();
  643. let actual = read(&dst_path).await.unwrap();
  644. assert_eq!(EXPECTED, actual);
  645. case.stop().await;
  646. }
  647. #[tokio::test]
  648. async fn rename_file_local() {
  649. rename_file(new_local().await).await
  650. }
  651. #[tokio::test(flavor = "multi_thread")]
  652. async fn rename_file_remote() {
  653. rename_file(new_remote().await).await
  654. }
  655. async fn write_read_with_file_struct(mut case: TestCase) {
  656. const FILE_NAME: &str = "big.dat";
  657. const LEN: usize = btlib::SECTOR_SZ_DEFAULT + 1;
  658. fn fill(buf: &mut Vec<u8>, value: u8) {
  659. buf.clear();
  660. buf.extend(std::iter::repeat(value).take(buf.capacity()));
  661. }
  662. let file_path = case.mnt_dir().join(FILE_NAME);
  663. let mut buf = vec![1u8; LEN];
  664. let mut file = OpenOptions::new()
  665. .create(true)
  666. .read(true)
  667. .write(true)
  668. .open(&file_path)
  669. .await
  670. .unwrap();
  671. file.write_all(&buf).await.unwrap();
  672. fill(&mut buf, 2);
  673. file.write_all(&buf).await.unwrap();
  674. file.rewind().await.unwrap();
  675. let mut actual = vec![0u8; LEN];
  676. file.read_exact(&mut actual).await.unwrap();
  677. fill(&mut buf, 1);
  678. assert_eq!(buf, actual);
  679. drop(file);
  680. case.stop().await;
  681. }
  682. #[tokio::test]
  683. async fn write_read_with_file_struct_local() {
  684. write_read_with_file_struct(new_local().await).await
  685. }
  686. #[tokio::test(flavor = "multi_thread")]
  687. async fn write_read_with_file_struct_remote() {
  688. write_read_with_file_struct(new_remote().await).await
  689. }
  690. /// KMC: This test is currently not working, and I've not been able to figure out why, nor
  691. /// reproduce it at a lower layer of the stack.
  692. async fn read_more_than_whats_buffered(mut case: TestCase) {
  693. const FILE_NAME: &str = "big.dat";
  694. const SECT_SZ: usize = btlib::SECTOR_SZ_DEFAULT;
  695. const DIVISOR: usize = 8;
  696. const READ_SZ: usize = SECT_SZ / DIVISOR;
  697. let file_path = case.mnt_dir().join(FILE_NAME);
  698. let mut file = OpenOptions::new()
  699. .create(true)
  700. .read(true)
  701. .write(true)
  702. .open(&file_path)
  703. .await
  704. .unwrap();
  705. let mut buf = vec![1u8; 2 * SECT_SZ];
  706. file.write_all(&buf).await.unwrap();
  707. file.flush().await.unwrap();
  708. let mut file = OpenOptions::new()
  709. .read(true)
  710. .write(true)
  711. .open(&file_path)
  712. .await
  713. .unwrap();
  714. file.seek(SeekFrom::Start(SECT_SZ as u64)).await.unwrap();
  715. let mut actual = vec![0u8; READ_SZ];
  716. file.read_exact(&mut actual).await.unwrap();
  717. buf.truncate(READ_SZ);
  718. assert!(buf == actual);
  719. case.stop().await;
  720. }
  721. //#[tokio::test]
  722. #[allow(dead_code)]
  723. async fn read_more_than_whats_buffered_local() {
  724. read_more_than_whats_buffered(new_local().await).await
  725. }
  726. //#[tokio::test(flavor = "multi_thread")]
  727. #[allow(dead_code)]
  728. async fn read_more_than_whats_buffered_remote() {
  729. read_more_than_whats_buffered(new_remote().await).await
  730. }
  731. }
  732. #[cfg(test)]
  733. mod config_tests {
  734. use super::{BtfuseConfig, FsKind};
  735. use std::{net::IpAddr, num::NonZeroUsize, path::PathBuf, sync::Arc};
  736. use btconfig::CredStoreConfig;
  737. use btlib::BlockPath;
  738. use btmsg::BlockAddr;
  739. use figment::Jail;
  740. #[test]
  741. fn fs_kind_local() {
  742. Jail::expect_with(|jail| {
  743. const EXPECTED_PATH: &str = "/tmp/blocks";
  744. let expected = FsKind::Local {
  745. path: EXPECTED_PATH.into(),
  746. };
  747. jail.set_env("BT_FSKIND_TYPE", "Local");
  748. jail.set_env("BT_FSKIND_PATH", EXPECTED_PATH);
  749. let config = BtfuseConfig::new().unwrap();
  750. assert_eq!(expected, config.fs_kind);
  751. Ok(())
  752. })
  753. }
  754. #[test]
  755. fn fs_kind_remote() {
  756. Jail::expect_with(|jail| {
  757. let expected_ip = IpAddr::from([127, 0, 0, 42]);
  758. let expected_path = Arc::new(BlockPath::try_from(
  759. "/0!zX_LMUVQO2Y7mgDomQB8ZdNsXKlykpHs-zPX9C3ztII/0!vVB5rOb3NFjzaZl_wlH3jqhBaYV7uuxrk3_s42xLnzg"
  760. ).unwrap());
  761. let expected_addr = BlockAddr::new(expected_ip, expected_path.clone());
  762. let expected = FsKind::Remote {
  763. addr: expected_addr,
  764. };
  765. jail.set_env("BT_FSKIND_TYPE", "Remote");
  766. jail.set_env("BT_FSKIND_ADDR_IPADDR", expected_ip);
  767. jail.set_env("BT_FSKIND_ADDR_PATH", expected_path.as_ref());
  768. let config = BtfuseConfig::new().unwrap();
  769. assert_eq!(expected, config.fs_kind);
  770. Ok(())
  771. })
  772. }
  773. #[test]
  774. fn mnt_dir() {
  775. Jail::expect_with(|jail| {
  776. let expected = PathBuf::from("/tmp/btfuse_mnt");
  777. jail.set_env("BT_MNTDIR", expected.display());
  778. let config = BtfuseConfig::new().unwrap();
  779. assert_eq!(expected, config.mnt_dir);
  780. Ok(())
  781. })
  782. }
  783. #[test]
  784. fn mnt_options() {
  785. Jail::expect_with(|jail| {
  786. let expected = "default_permissions";
  787. jail.set_env("BT_MNTOPTIONS", expected);
  788. let config = BtfuseConfig::new().unwrap();
  789. assert_eq!(expected, &config.mnt_options);
  790. Ok(())
  791. })
  792. }
  793. #[test]
  794. fn threads_is_set() {
  795. Jail::expect_with(|jail| {
  796. let expected = Some(NonZeroUsize::new(8).unwrap());
  797. jail.set_env("BT_THREADS", expected.unwrap().get());
  798. let config = BtfuseConfig::new().unwrap();
  799. assert_eq!(expected, config.threads);
  800. Ok(())
  801. })
  802. }
  803. #[test]
  804. fn threads_is_not_set() {
  805. Jail::expect_with(|_jail| {
  806. let expected = None;
  807. let config = BtfuseConfig::new().unwrap();
  808. assert_eq!(expected, config.threads);
  809. Ok(())
  810. })
  811. }
  812. #[test]
  813. fn cred_store_path() {
  814. Jail::expect_with(|jail| {
  815. let expected = PathBuf::from("/tmp/secrets/file_credstore");
  816. jail.set_env("BT_CREDSTORE_TYPE", "File");
  817. jail.set_env("BT_CREDSTORE_PATH", expected.display());
  818. let config = BtfuseConfig::new().unwrap();
  819. let success = if let CredStoreConfig::File { path: actual } = config.cred_store {
  820. expected == actual
  821. } else {
  822. false
  823. };
  824. assert!(success);
  825. Ok(())
  826. })
  827. }
  828. }