main.rs 26 KB

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