local_fs_tests.rs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. // SPDX-License-Identifier: AGPL-3.0-or-later
  2. use btfproto::{
  3. local_fs::{LocalFs, ModeAuthorizer},
  4. msg::{GrantAccess, SpecInodes},
  5. server::FsProvider,
  6. };
  7. use btlib::{
  8. crypto::{Creds, CredsPriv, CredsPub},
  9. AuthzAttrs, BlockError, BlockPath, IssuedProcRec,
  10. };
  11. use std::{
  12. net::{IpAddr, Ipv6Addr},
  13. path::PathBuf,
  14. sync::Arc,
  15. };
  16. use tempdir::TempDir;
  17. pub fn bind_path<C: CredsPriv>(creds: C) -> BlockPath {
  18. let writecap = creds
  19. .writecap()
  20. .ok_or(btlib::BlockError::MissingWritecap)
  21. .unwrap();
  22. writecap.bind_path()
  23. }
  24. pub type ConcreteFs = LocalFs<ModeAuthorizer>;
  25. pub struct LocalFsTest {
  26. dir: TempDir,
  27. fs: ConcreteFs,
  28. node_bind_path: Arc<BlockPath>,
  29. }
  30. fn node_creds() -> Arc<dyn Creds> {
  31. Arc::new(super::node_creds())
  32. }
  33. fn root_creds() -> Arc<dyn Creds> {
  34. Arc::new(super::root_creds())
  35. }
  36. impl LocalFsTest {
  37. pub const NODE_UID: u32 = 1000;
  38. pub const NODE_GID: u32 = 1000;
  39. pub async fn new_empty() -> LocalFsTest {
  40. let dir = TempDir::new("fuse").expect("failed to create temp dir");
  41. let node_creds = node_creds();
  42. Self::grant_node_access(dir.path().to_owned()).await;
  43. let node_writecap = node_creds
  44. .writecap()
  45. .ok_or(BlockError::MissingWritecap)
  46. .unwrap();
  47. let node_bind_path = Arc::new(node_writecap.bind_path());
  48. let fs = LocalFs::new_existing(dir.path().to_owned(), node_creds, ModeAuthorizer)
  49. .expect("failed to create empty blocktree");
  50. LocalFsTest {
  51. dir,
  52. fs,
  53. node_bind_path,
  54. }
  55. }
  56. pub fn new_existing(dir: TempDir) -> LocalFsTest {
  57. let fs = LocalFs::new_existing(dir.path().to_owned(), node_creds(), ModeAuthorizer)
  58. .expect("failed to create blocktree from existing directory");
  59. let from = Arc::new(bind_path(node_creds()));
  60. LocalFsTest {
  61. dir,
  62. fs,
  63. node_bind_path: from,
  64. }
  65. }
  66. async fn grant_node_access(path: PathBuf) {
  67. let root_creds = root_creds();
  68. let root_writecap = root_creds
  69. .writecap()
  70. .ok_or(BlockError::MissingWritecap)
  71. .unwrap();
  72. let root_bind_path = Arc::new(root_writecap.bind_path());
  73. let node_creds = node_creds();
  74. let node_writecap = node_creds
  75. .writecap()
  76. .ok_or(BlockError::MissingWritecap)
  77. .unwrap();
  78. let fs = LocalFs::new_empty(path, 0, root_creds, ModeAuthorizer)
  79. .await
  80. .unwrap();
  81. let proc_rec = IssuedProcRec {
  82. addr: IpAddr::V6(Ipv6Addr::LOCALHOST),
  83. pub_creds: node_creds.concrete_pub(),
  84. writecap: node_writecap.to_owned(),
  85. authz_attrs: AuthzAttrs {
  86. uid: Self::NODE_UID,
  87. gid: Self::NODE_GID,
  88. supp_gids: Vec::new(),
  89. },
  90. };
  91. let msg = GrantAccess {
  92. inode: SpecInodes::RootDir.into(),
  93. record: proc_rec,
  94. };
  95. fs.grant_access(&root_bind_path, msg).await.unwrap();
  96. }
  97. pub fn into_parts(self) -> (TempDir, ConcreteFs, Arc<BlockPath>) {
  98. (self.dir, self.fs, self.node_bind_path)
  99. }
  100. pub fn fs(&self) -> &ConcreteFs {
  101. &self.fs
  102. }
  103. pub fn from(&self) -> &Arc<BlockPath> {
  104. &self.node_bind_path
  105. }
  106. }
  107. #[cfg(test)]
  108. mod tests {
  109. use super::*;
  110. use btfproto::{local_fs::Error, msg::*};
  111. use btlib::{Inode, Result, SECTOR_SZ_DEFAULT};
  112. use btlib_tests::fs_queries::num_files;
  113. use std::{
  114. fs::read_dir,
  115. io::{self, Cursor, Write as IoWrite},
  116. ops::Deref,
  117. sync::Arc,
  118. };
  119. /// Tests that a new file can be created, written to and the written data can be read from it.
  120. #[tokio::test]
  121. async fn create_write_read() {
  122. let case = LocalFsTest::new_empty().await;
  123. let bt = &case.fs;
  124. let from = case.from();
  125. let name = "README.md";
  126. let flags = Flags::new(libc::O_RDWR);
  127. let create_msg = Create {
  128. parent: SpecInodes::RootDir.into(),
  129. name,
  130. flags,
  131. mode: libc::S_IFREG | 0o644,
  132. umask: 0,
  133. };
  134. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  135. const LEN: usize = 32;
  136. let expected = [1u8; LEN];
  137. let write_msg = Write {
  138. inode,
  139. handle,
  140. offset: 0,
  141. data: expected.as_slice(),
  142. };
  143. let WriteReply { written, .. } = bt.write(from, write_msg).await.unwrap();
  144. assert_eq!(LEN as u64, written);
  145. let read_msg = Read {
  146. inode,
  147. handle,
  148. offset: 0,
  149. size: LEN as u64,
  150. };
  151. let guard = bt.read(from, read_msg).await.unwrap();
  152. assert_eq!(expected, guard.deref())
  153. }
  154. #[tokio::test]
  155. async fn lookup() {
  156. let case = LocalFsTest::new_empty().await;
  157. let bt = &case.fs;
  158. let from = case.from();
  159. let name = "README.md";
  160. let create_msg = Create {
  161. parent: SpecInodes::RootDir.into(),
  162. name,
  163. flags: Flags::default(),
  164. mode: 0,
  165. umask: 0,
  166. };
  167. let create_reply = bt.create(from, create_msg).await.unwrap();
  168. let lookup_msg = Lookup {
  169. parent: SpecInodes::RootDir.into(),
  170. name,
  171. };
  172. let lookup_reply = bt.lookup(from, lookup_msg).await.unwrap();
  173. assert_eq!(create_reply.inode, lookup_reply.inode);
  174. }
  175. /// Tests that data written by one instance of [Blocktree] can be read by a subsequent
  176. /// instance.
  177. #[tokio::test]
  178. async fn new_existing() {
  179. const EXPECTED: &[u8] = b"cool as cucumbers";
  180. let name = "RESIGNATION.docx";
  181. let case = LocalFsTest::new_empty().await;
  182. let bt = &case.fs;
  183. let from = case.from();
  184. let flags = Flags::new(libc::O_RDWR);
  185. {
  186. let create_msg = Create {
  187. parent: SpecInodes::RootDir.into(),
  188. name,
  189. mode: libc::S_IFREG | 0o644,
  190. flags,
  191. umask: 0,
  192. };
  193. let CreateReply { handle, inode, .. } = bt.create(from, create_msg).await.unwrap();
  194. let write_msg = Write {
  195. inode,
  196. handle,
  197. offset: 0,
  198. data: EXPECTED,
  199. };
  200. let WriteReply { written, .. } = bt.write(from, write_msg).await.unwrap();
  201. assert_eq!(EXPECTED.len() as u64, written);
  202. let close_msg = Close { inode, handle };
  203. bt.close(from, close_msg).await.unwrap();
  204. }
  205. let case = LocalFsTest::new_existing(case.dir);
  206. let from = case.from();
  207. let bt = &case.fs;
  208. let lookup_msg = Lookup {
  209. parent: SpecInodes::RootDir.into(),
  210. name,
  211. };
  212. let LookupReply { inode, .. } = bt.lookup(from, lookup_msg).await.unwrap();
  213. let open_msg = Open {
  214. inode,
  215. flags: Flags::new(libc::O_RDONLY),
  216. };
  217. let OpenReply { handle, .. } = bt.open(from, open_msg).await.unwrap();
  218. let read_msg = Read {
  219. inode,
  220. handle,
  221. offset: 0,
  222. size: EXPECTED.len() as u64,
  223. };
  224. let guard = bt.read(from, read_msg).await.unwrap();
  225. assert_eq!(EXPECTED, guard.deref())
  226. }
  227. /// Tests that an error is returned by the `Blocktree::write` method if the file was opened
  228. /// read-only.
  229. #[tokio::test]
  230. async fn open_read_only_write_is_error() {
  231. let name = "books.ods";
  232. let case = LocalFsTest::new_empty().await;
  233. let bt = &case.fs;
  234. let from = case.from();
  235. let create_msg = Create {
  236. parent: SpecInodes::RootDir.into(),
  237. name,
  238. flags: Flags::default(),
  239. mode: libc::S_IFREG | 0o644,
  240. umask: 0,
  241. };
  242. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  243. let close_msg = Close { inode, handle };
  244. bt.close(from, close_msg).await.unwrap();
  245. let open_msg = Open {
  246. inode,
  247. flags: libc::O_RDONLY.into(),
  248. };
  249. let OpenReply { handle, .. } = bt.open(from, open_msg).await.unwrap();
  250. let data = [1u8; 32];
  251. let write_msg = Write {
  252. inode,
  253. handle,
  254. offset: 0,
  255. data: data.as_slice(),
  256. };
  257. let result = bt.write(from, write_msg).await;
  258. let err = result.err().unwrap();
  259. let err = err.downcast::<Error>().unwrap();
  260. let actual_handle = if let Error::ReadOnlyHandle(actual_handle) = err {
  261. Some(actual_handle)
  262. } else {
  263. None
  264. };
  265. assert_eq!(Some(handle), actual_handle);
  266. }
  267. /// Asserts that the given [Result] is an [Err] and that it contains an [io::Error] which
  268. /// corresponds to the [libc::ENOENT] error code.
  269. fn assert_enoent<T>(result: Result<T>) {
  270. let err = result.err().unwrap().downcast::<io::Error>().unwrap();
  271. assert_eq!(libc::ENOENT, err.raw_os_error().unwrap());
  272. }
  273. /// Tests that multiple handles see consistent metadata associated with a block.
  274. #[tokio::test]
  275. async fn ensure_metadata_consistency() {
  276. let case = LocalFsTest::new_empty().await;
  277. let from = case.from();
  278. let trash = ".Trash";
  279. let file = "file.txt";
  280. let bt = &case.fs;
  281. let root = SpecInodes::RootDir.into();
  282. let open_msg = Open {
  283. inode: root,
  284. flags: libc::O_DIRECTORY.into(),
  285. };
  286. let OpenReply { handle, .. } = bt.open(from, open_msg).await.unwrap();
  287. // Because the directory is open, this will cause a new handle for this block to be opened.
  288. let lookup_msg = Lookup {
  289. parent: root,
  290. name: trash,
  291. };
  292. let result = bt.lookup(from, lookup_msg).await;
  293. assert_enoent(result);
  294. let close_msg = Close {
  295. inode: root,
  296. handle,
  297. };
  298. bt.close(from, close_msg).await.unwrap();
  299. let create_msg = Create {
  300. parent: root,
  301. name: file,
  302. flags: Flags::default(),
  303. mode: libc::S_IFREG | 0o644,
  304. umask: 0,
  305. };
  306. bt.create(from, create_msg).await.unwrap();
  307. let open_msg = Open {
  308. inode: root,
  309. flags: libc::O_DIRECTORY.into(),
  310. };
  311. bt.open(from, open_msg).await.unwrap();
  312. // Since the directory is open, the second handle will be used for this lookup.
  313. let lookup_msg = Lookup {
  314. parent: root,
  315. name: trash,
  316. };
  317. let result = bt.lookup(from, lookup_msg).await;
  318. assert!(result.is_err());
  319. }
  320. /// Tests that the `size` parameter actually limits the number of read bytes.
  321. #[tokio::test]
  322. async fn read_with_smaller_size() {
  323. const DATA: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
  324. let case = LocalFsTest::new_empty().await;
  325. let from = case.from();
  326. let file = "file.txt";
  327. let bt = &case.fs;
  328. let root: Inode = SpecInodes::RootDir.into();
  329. let create_msg = Create {
  330. parent: root,
  331. name: file,
  332. flags: libc::O_RDWR.into(),
  333. mode: libc::S_IFREG | 0o644,
  334. umask: 0,
  335. };
  336. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  337. let write_msg = Write {
  338. inode,
  339. handle,
  340. offset: 0,
  341. data: DATA.as_slice(),
  342. };
  343. let WriteReply { written, .. } = bt.write(from, write_msg).await.unwrap();
  344. assert_eq!(DATA.len() as u64, written);
  345. const SIZE: usize = DATA.len() / 2;
  346. let read_msg = Read {
  347. inode,
  348. handle,
  349. offset: 0,
  350. size: SIZE as u64,
  351. };
  352. let guard = bt.read(from, read_msg).await.unwrap();
  353. assert_eq!(&[0, 1, 2, 3], guard.deref());
  354. }
  355. /// Returns an integer array starting at the given value and increasing by one for each
  356. /// subsequent entry.
  357. pub const fn integer_array<const N: usize>(start: u8) -> [u8; N] {
  358. let mut array = [0u8; N];
  359. let mut k = 0usize;
  360. while k < N {
  361. array[k] = start.wrapping_add(k as u8);
  362. k += 1;
  363. }
  364. array
  365. }
  366. #[tokio::test]
  367. async fn concurrent_reads() {
  368. // The size of each of the reads.
  369. const SIZE: usize = 4;
  370. // The number of concurrent reads.
  371. const NREADS: usize = 32;
  372. const DATA_LEN: usize = SIZE * NREADS;
  373. const DATA: [u8; DATA_LEN] = integer_array::<DATA_LEN>(0);
  374. let case = LocalFsTest::new_empty().await;
  375. let from = case.from();
  376. let file = "file.txt";
  377. let bt = &case.fs;
  378. let root: Inode = SpecInodes::RootDir.into();
  379. let mode = libc::S_IFREG | 0o644;
  380. let create_msg = Create {
  381. parent: root,
  382. name: file,
  383. flags: libc::O_RDWR.into(),
  384. mode,
  385. umask: 0,
  386. };
  387. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  388. let write_msg = Write {
  389. inode,
  390. handle,
  391. offset: 0,
  392. data: DATA.as_slice(),
  393. };
  394. let WriteReply { written, .. } = bt.write(from, write_msg).await.unwrap();
  395. assert_eq!(DATA.len() as u64, written);
  396. let case = Arc::new(case);
  397. let mut handles = Vec::with_capacity(NREADS);
  398. for offset in (0..NREADS).map(|e| e * SIZE) {
  399. let case = case.clone();
  400. handles.push(tokio::spawn(async move {
  401. // Notice that we have concurrent reads to different offsets using the same handle.
  402. // Without proper synchronization, this shouldn't work.
  403. let read_msg = Read {
  404. inode,
  405. handle,
  406. offset: offset as u64,
  407. size: SIZE as u64,
  408. };
  409. let guard = case.fs.read(case.from(), read_msg).await.unwrap();
  410. let expected = integer_array::<SIZE>(offset as u8);
  411. assert_eq!(&expected, guard.deref());
  412. }));
  413. }
  414. for handle in handles {
  415. handle.await.unwrap();
  416. }
  417. }
  418. #[tokio::test]
  419. async fn rename_in_same_directory() {
  420. let case = LocalFsTest::new_empty().await;
  421. let bt = &case.fs;
  422. let from = case.from();
  423. let root: Inode = SpecInodes::RootDir.into();
  424. let src_name = "src";
  425. let dst_name = "dst";
  426. let create_msg = Create {
  427. parent: root,
  428. name: src_name,
  429. flags: Flags::default(),
  430. mode: libc::S_IFREG | 0o644,
  431. umask: 0,
  432. };
  433. let CreateReply { inode, .. } = bt.create(from, create_msg).await.unwrap();
  434. let link_msg = Link {
  435. inode,
  436. new_parent: root,
  437. name: dst_name,
  438. };
  439. bt.link(from, link_msg).await.unwrap();
  440. let unlink_msg = Unlink {
  441. parent: root,
  442. name: src_name,
  443. };
  444. bt.unlink(from, unlink_msg).await.unwrap();
  445. let lookup_msg = Lookup {
  446. parent: root,
  447. name: dst_name,
  448. };
  449. let LookupReply {
  450. inode: actual_inode,
  451. ..
  452. } = bt.lookup(from, lookup_msg).await.unwrap();
  453. assert_eq!(inode, actual_inode);
  454. let lookup_msg = Lookup {
  455. parent: root,
  456. name: src_name,
  457. };
  458. let result = bt.lookup(from, lookup_msg).await;
  459. assert_enoent(result);
  460. }
  461. #[tokio::test]
  462. async fn rename_to_different_directory() {
  463. let case = LocalFsTest::new_empty().await;
  464. let bt = &case.fs;
  465. let from = case.from();
  466. let root = SpecInodes::RootDir.into();
  467. let dir_name = "dir";
  468. let file_name = "file";
  469. let create_msg = Create {
  470. parent: root,
  471. name: dir_name,
  472. flags: libc::O_DIRECTORY.into(),
  473. mode: libc::S_IFDIR | 0o755,
  474. umask: 0,
  475. };
  476. let CreateReply { inode: dir, .. } = bt.create(from, create_msg).await.unwrap();
  477. let create_msg = Create {
  478. parent: root,
  479. name: file_name,
  480. flags: Flags::default(),
  481. mode: libc::S_IFREG | 0o644,
  482. umask: 0,
  483. };
  484. let CreateReply { inode: file, .. } = bt.create(from, create_msg).await.unwrap();
  485. let link_msg = Link {
  486. inode: file,
  487. new_parent: dir,
  488. name: file_name,
  489. };
  490. bt.link(from, link_msg).await.unwrap();
  491. let unlink_msg = Unlink {
  492. parent: root,
  493. name: file_name,
  494. };
  495. bt.unlink(from, unlink_msg).await.unwrap();
  496. let lookup_msg = Lookup {
  497. parent: dir,
  498. name: file_name,
  499. };
  500. let LookupReply {
  501. inode: actual_inode,
  502. ..
  503. } = bt.lookup(from, lookup_msg).await.unwrap();
  504. assert_eq!(file, actual_inode);
  505. let lookup_msg = Lookup {
  506. parent: root,
  507. name: file_name,
  508. };
  509. let result = bt.lookup(from, lookup_msg).await;
  510. assert_enoent(result);
  511. }
  512. #[tokio::test]
  513. async fn rename_no_replace_same_directory() {
  514. let case = LocalFsTest::new_empty().await;
  515. let bt = &case.fs;
  516. let from = case.from();
  517. let root: Inode = SpecInodes::RootDir.into();
  518. let oldname = "old";
  519. let newname = "new";
  520. let create_msg = Create {
  521. parent: root,
  522. name: oldname,
  523. flags: Flags::default(),
  524. mode: 0o644,
  525. umask: 0,
  526. };
  527. let CreateReply { inode, .. } = bt.create(from, create_msg).await.unwrap();
  528. let create_msg = Create {
  529. parent: root,
  530. name: newname,
  531. flags: Flags::default(),
  532. mode: 0o644,
  533. umask: 0,
  534. };
  535. bt.create(from, create_msg).await.unwrap();
  536. let link_msg = Link {
  537. inode,
  538. new_parent: root,
  539. name: newname,
  540. };
  541. let result = bt.link(from, link_msg).await;
  542. let err = result.err().unwrap().downcast::<io::Error>().unwrap();
  543. assert_eq!(io::ErrorKind::AlreadyExists, err.kind());
  544. }
  545. #[tokio::test]
  546. async fn rename_no_replace_different_directory() {
  547. let case = LocalFsTest::new_empty().await;
  548. let bt = &case.fs;
  549. let from = case.from();
  550. let root = SpecInodes::RootDir.into();
  551. let dir_name = "dir";
  552. let file_name = "file";
  553. let create_msg = Create {
  554. parent: root,
  555. name: dir_name,
  556. flags: libc::O_DIRECTORY.into(),
  557. mode: 0o755,
  558. umask: 0,
  559. };
  560. let CreateReply { inode: dir, .. } = bt.create(from, create_msg).await.unwrap();
  561. let create_msg = Create {
  562. parent: root,
  563. name: file_name,
  564. flags: Flags::default(),
  565. mode: 0o644,
  566. umask: 0,
  567. };
  568. let CreateReply { inode, .. } = bt.create(from, create_msg).await.unwrap();
  569. let create_msg = Create {
  570. parent: dir,
  571. name: file_name,
  572. flags: Flags::default(),
  573. mode: 0o644,
  574. umask: 0,
  575. };
  576. bt.create(from, create_msg).await.unwrap();
  577. let link_msg = Link {
  578. inode,
  579. new_parent: dir,
  580. name: file_name,
  581. };
  582. let result = bt.link(from, link_msg).await;
  583. let err = result.err().unwrap().downcast::<io::Error>().unwrap();
  584. assert_eq!(io::ErrorKind::AlreadyExists, err.kind());
  585. }
  586. #[tokio::test]
  587. async fn read_from_non_owner_is_err() {
  588. let case = LocalFsTest::new_empty().await;
  589. let bt = &case.fs;
  590. let name = "file.txt";
  591. let owner = case.from();
  592. let mut other = owner.as_ref().clone();
  593. other.push_component("subdir".to_owned());
  594. let other = Arc::new(other);
  595. let create_msg = Create {
  596. parent: SpecInodes::RootDir.into(),
  597. name,
  598. flags: libc::O_RDWR.into(),
  599. mode: 0o644,
  600. umask: 0,
  601. };
  602. let CreateReply { inode, handle, .. } = bt.create(owner, create_msg).await.unwrap();
  603. let write_msg = Write {
  604. inode,
  605. handle,
  606. offset: 0,
  607. data: [1, 2, 3].as_slice(),
  608. };
  609. let result = bt.write(&other, write_msg).await;
  610. let err = result.err().unwrap().downcast::<Error>().unwrap();
  611. let matched = if let Error::WrongOwner = err {
  612. true
  613. } else {
  614. false
  615. };
  616. assert!(matched)
  617. }
  618. #[tokio::test]
  619. async fn allocate_full_sectors_zero_remain_non_zero() {
  620. let case = LocalFsTest::new_empty().await;
  621. let bt = &case.fs;
  622. let name = "file.txt";
  623. let from = case.from();
  624. let create_msg = Create {
  625. parent: SpecInodes::RootDir.into(),
  626. name,
  627. flags: libc::O_RDWR.into(),
  628. mode: 0o644,
  629. umask: 0,
  630. };
  631. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  632. const LEN: u64 = 8;
  633. let alloc_msg = Allocate {
  634. inode,
  635. handle,
  636. offset: None,
  637. size: LEN,
  638. };
  639. bt.allocate(from, alloc_msg).await.unwrap();
  640. let read_meta_msg = ReadMeta {
  641. inode,
  642. handle: Some(handle),
  643. };
  644. let ReadMetaReply { attrs, .. } = bt.read_meta(from, read_meta_msg).await.unwrap();
  645. let read_msg = Read {
  646. inode,
  647. handle,
  648. offset: 0,
  649. size: LEN,
  650. };
  651. let guard = bt.read(from, read_msg).await.unwrap();
  652. assert_eq!([0u8; 8], guard.deref());
  653. assert_eq!(guard.len() as u64, attrs.size);
  654. }
  655. #[tokio::test]
  656. async fn allocate_full_sectors_non_zero_remain_non_zero() {
  657. let case = LocalFsTest::new_empty().await;
  658. let bt = &case.fs;
  659. let name = "file.txt";
  660. let from = case.from();
  661. let create_msg = Create {
  662. parent: SpecInodes::RootDir.into(),
  663. name,
  664. flags: libc::O_RDWR.into(),
  665. mode: 0o644,
  666. umask: 0,
  667. };
  668. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  669. const LEN: usize = SECTOR_SZ_DEFAULT + 1;
  670. let mut size = LEN as u64;
  671. let alloc_msg = Allocate {
  672. inode,
  673. handle,
  674. offset: None,
  675. size,
  676. };
  677. bt.allocate(from, alloc_msg).await.unwrap();
  678. let mut actual = Cursor::new(Vec::with_capacity(LEN));
  679. while size > 0 {
  680. let read_msg = Read {
  681. inode,
  682. handle,
  683. offset: 0,
  684. size,
  685. };
  686. let guard = bt.read(from, read_msg).await.unwrap();
  687. let data = guard.deref();
  688. actual.write(data).unwrap();
  689. size -= data.len() as u64;
  690. }
  691. let read_meta_msg = ReadMeta {
  692. inode,
  693. handle: Some(handle),
  694. };
  695. let ReadMetaReply { attrs, .. } = bt.read_meta(from, read_meta_msg).await.unwrap();
  696. assert!(vec![0u8; LEN].eq(&actual.into_inner()));
  697. assert_eq!(LEN as u64, attrs.size);
  698. }
  699. #[tokio::test]
  700. async fn allocate_full_sectors_non_zero_remain_zero() {
  701. let case = LocalFsTest::new_empty().await;
  702. let bt = &case.fs;
  703. let name = "file.txt";
  704. let from = case.from();
  705. let create_msg = Create {
  706. parent: SpecInodes::RootDir.into(),
  707. name,
  708. flags: libc::O_RDWR.into(),
  709. mode: 0o644,
  710. umask: 0,
  711. };
  712. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  713. const LEN: usize = SECTOR_SZ_DEFAULT;
  714. let size = LEN as u64;
  715. let alloc_msg = Allocate {
  716. inode,
  717. handle,
  718. offset: None,
  719. size,
  720. };
  721. bt.allocate(from, alloc_msg).await.unwrap();
  722. let read_meta_msg = ReadMeta {
  723. inode,
  724. handle: Some(handle),
  725. };
  726. let ReadMetaReply { attrs, .. } = bt.read_meta(from, read_meta_msg).await.unwrap();
  727. let read_msg = Read {
  728. inode,
  729. handle,
  730. offset: 0,
  731. size,
  732. };
  733. let guard = bt.read(from, read_msg).await.unwrap();
  734. assert_eq!(vec![0u8; LEN], guard.deref());
  735. assert_eq!(LEN as u64, attrs.size);
  736. }
  737. /// Tests that when the new_size of the block is not greater than the current size of the block,
  738. /// then no change to the block occurs.
  739. #[tokio::test]
  740. async fn allocate_new_size_not_greater_than_curr_size() {
  741. let case = LocalFsTest::new_empty().await;
  742. let bt = &case.fs;
  743. let name = "file.txt";
  744. let from = case.from();
  745. let create_msg = Create {
  746. parent: SpecInodes::RootDir.into(),
  747. name,
  748. flags: libc::O_RDWR.into(),
  749. mode: 0o644,
  750. umask: 0,
  751. };
  752. let CreateReply { inode, handle, .. } = bt.create(from, create_msg).await.unwrap();
  753. const LEN: usize = 8;
  754. let write_msg = Write {
  755. inode,
  756. handle,
  757. offset: 0,
  758. data: [1u8; LEN].as_slice(),
  759. };
  760. let WriteReply { written, .. } = bt.write(from, write_msg).await.unwrap();
  761. assert_eq!(LEN as u64, written);
  762. let alloc_msg = Allocate {
  763. inode,
  764. handle,
  765. offset: None,
  766. size: (LEN / 2) as u64,
  767. };
  768. bt.allocate(from, alloc_msg).await.unwrap();
  769. let read_meta_msg = ReadMeta {
  770. inode,
  771. handle: Some(handle),
  772. };
  773. let ReadMetaReply { attrs, .. } = bt.read_meta(from, read_meta_msg).await.unwrap();
  774. let read_msg = Read {
  775. inode,
  776. handle,
  777. offset: 0,
  778. size: LEN as u64,
  779. };
  780. let actual = bt.read(from, read_msg).await.unwrap();
  781. assert_eq!([1u8; LEN], actual.deref());
  782. assert_eq!(LEN as u64, attrs.size);
  783. }
  784. #[tokio::test]
  785. async fn read_at_non_current_position() {
  786. const FILENAME: &str = "MANIFESTO.rtf";
  787. let case = LocalFsTest::new_empty().await;
  788. let bt = &case.fs;
  789. let from = case.from();
  790. let msg = Create {
  791. parent: SpecInodes::RootDir.into(),
  792. name: FILENAME,
  793. flags: FlagValue::ReadWrite.into(),
  794. mode: 0o644,
  795. umask: 0,
  796. };
  797. let CreateReply {
  798. inode,
  799. handle,
  800. entry,
  801. ..
  802. } = bt.create(from, msg).await.unwrap();
  803. let sect_sz64 = entry.attr.sect_sz;
  804. let sect_sz: usize = sect_sz64.try_into().unwrap();
  805. let mut data = vec![1u8; sect_sz];
  806. let msg = Write {
  807. inode,
  808. handle,
  809. offset: 0,
  810. data: data.as_slice(),
  811. };
  812. let WriteReply { written, .. } = bt.write(from, msg).await.unwrap();
  813. assert_eq!(sect_sz64, written);
  814. data.truncate(0);
  815. data.extend(std::iter::repeat(2).take(sect_sz));
  816. let msg = Write {
  817. inode,
  818. handle,
  819. offset: sect_sz64,
  820. data: data.as_slice(),
  821. };
  822. let WriteReply { written, .. } = bt.write(from, msg).await.unwrap();
  823. assert_eq!(sect_sz64, written);
  824. // The Accessor for this block should now have the second sector loaded, so it will have to
  825. // seek back to the first in order to respond to this read request.
  826. let msg = Read {
  827. inode,
  828. handle,
  829. offset: 0,
  830. size: sect_sz64,
  831. };
  832. let guard = bt.read(from, msg).await.unwrap();
  833. assert!(guard
  834. .deref()
  835. .iter()
  836. .map(|e| *e)
  837. .eq(std::iter::repeat(1u8).take(sect_sz)));
  838. }
  839. #[tokio::test]
  840. async fn unlink_after_forget_file_is_deleted() {
  841. const FILENAME: &str = "file";
  842. let case = LocalFsTest::new_empty().await;
  843. let block_dir = case.dir.path();
  844. let bt = &case.fs;
  845. let from = case.from();
  846. let expected = num_files(block_dir).unwrap();
  847. let msg = Create {
  848. parent: SpecInodes::RootDir.into(),
  849. name: FILENAME,
  850. flags: FlagValue::ReadWrite.into(),
  851. mode: 0o644,
  852. umask: 0,
  853. };
  854. let CreateReply { inode, handle, .. } = bt.create(from, msg).await.unwrap();
  855. const DATA: [u8; 8] = [1u8; 8];
  856. let msg = Write {
  857. inode,
  858. handle,
  859. offset: 0,
  860. data: DATA.as_slice(),
  861. };
  862. bt.write(from, msg).await.unwrap();
  863. let msg = Close { inode, handle };
  864. bt.close(from, msg).await.unwrap();
  865. let more = num_files(block_dir).unwrap();
  866. assert!(more > expected);
  867. let msg = Forget { inode, count: 1 };
  868. bt.forget(from, msg).await.unwrap();
  869. let msg = Unlink {
  870. parent: SpecInodes::RootDir.into(),
  871. name: FILENAME,
  872. };
  873. bt.unlink(from, msg).await.unwrap();
  874. let actual = num_files(block_dir).unwrap();
  875. assert_eq!(expected, actual);
  876. }
  877. #[tokio::test]
  878. async fn forget_after_unlink_file_is_deleted() {
  879. const FILENAME: &str = "file";
  880. let case = LocalFsTest::new_empty().await;
  881. let block_dir = case.dir.path();
  882. let bt = &case.fs;
  883. let from = case.from();
  884. let expected = num_files(block_dir).unwrap();
  885. let msg = Create {
  886. parent: SpecInodes::RootDir.into(),
  887. name: FILENAME,
  888. flags: FlagValue::ReadWrite.into(),
  889. mode: 0o644,
  890. umask: 0,
  891. };
  892. let CreateReply { inode, handle, .. } = bt.create(from, msg).await.unwrap();
  893. const DATA: [u8; 8] = [1u8; 8];
  894. let msg = Write {
  895. inode,
  896. handle,
  897. offset: 0,
  898. data: DATA.as_slice(),
  899. };
  900. bt.write(from, msg).await.unwrap();
  901. let msg = Close { inode, handle };
  902. bt.close(from, msg).await.unwrap();
  903. let more = num_files(block_dir).unwrap();
  904. let msg = Unlink {
  905. parent: SpecInodes::RootDir.into(),
  906. name: FILENAME,
  907. };
  908. bt.unlink(from, msg).await.unwrap();
  909. assert!(more > expected);
  910. let msg = Forget { inode, count: 1 };
  911. bt.forget(from, msg).await.unwrap();
  912. let actual = num_files(block_dir).unwrap();
  913. assert_eq!(expected, actual);
  914. }
  915. #[tokio::test]
  916. async fn after_unlink_no_empty_directories() {
  917. const FILENAME: &str = "file";
  918. let case = LocalFsTest::new_empty().await;
  919. let bt = &case.fs;
  920. let from = case.from();
  921. let msg = Create {
  922. parent: SpecInodes::RootDir.into(),
  923. name: FILENAME,
  924. flags: FlagValue::ReadWrite.into(),
  925. mode: 0o644,
  926. umask: 0,
  927. };
  928. let CreateReply { inode, handle, .. } = bt.create(from, msg).await.unwrap();
  929. const DATA: [u8; 8] = [1u8; 8];
  930. let msg = Write {
  931. inode,
  932. handle,
  933. offset: 0,
  934. data: DATA.as_slice(),
  935. };
  936. bt.write(from, msg).await.unwrap();
  937. let msg = Close { inode, handle };
  938. bt.close(from, msg).await.unwrap();
  939. let msg = Forget { inode, count: 1 };
  940. bt.forget(from, msg).await.unwrap();
  941. let msg = Unlink {
  942. parent: SpecInodes::RootDir.into(),
  943. name: FILENAME,
  944. };
  945. bt.unlink(from, msg).await.unwrap();
  946. let mut path = case.dir.path().to_owned();
  947. let entries = read_dir(&path).unwrap();
  948. path.push("x");
  949. for entry in entries {
  950. let entry = entry.unwrap();
  951. if !entry.file_type().unwrap().is_dir() {
  952. continue;
  953. }
  954. path.pop();
  955. path.push(entry.file_name());
  956. let empty = read_dir(&path).unwrap().next().is_none();
  957. assert!(!empty);
  958. }
  959. }
  960. }