fuse_fs.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // SPDX-License-Identifier: AGPL-3.0-or-later
  2. use btfproto::{msg::*, server::FsProvider};
  3. use btlib::{
  4. bterr, collections::Bijection, error::DisplayErr, AuthzAttrs, BlockId, BlockMetaSecrets,
  5. BlockPath, Epoch, Result,
  6. };
  7. use btserde::read_from;
  8. use core::{ffi::CStr, future::Future, time::Duration};
  9. use fuse_backend_rs::{
  10. abi::fuse_abi::{stat64, Attr, CreateIn},
  11. api::filesystem::{
  12. Context, DirEntry, Entry, FileSystem, FsOptions, OpenOptions, SetattrValid, ZeroCopyReader,
  13. ZeroCopyWriter,
  14. },
  15. };
  16. use log::{debug, error};
  17. use std::{
  18. io::{self, Result as IoResult},
  19. sync::{Arc, RwLock},
  20. };
  21. use tokio::runtime::Handle;
  22. pub use private::FuseFs;
  23. mod private {
  24. use super::*;
  25. trait BlockMetaSecretsExt {
  26. fn attr(&self) -> Result<Attr>;
  27. fn stat(&self) -> Result<stat64> {
  28. self.attr().map(|e| e.into())
  29. }
  30. }
  31. impl BlockMetaSecretsExt for BlockMetaSecrets {
  32. fn attr(&self) -> Result<Attr> {
  33. Ok(Attr {
  34. ino: self.block_id.inode,
  35. size: self.size,
  36. atime: self.atime.value(),
  37. mtime: self.mtime.value(),
  38. ctime: self.ctime.value(),
  39. atimensec: 0,
  40. mtimensec: 0,
  41. ctimensec: 0,
  42. mode: self.mode,
  43. nlink: self.nlink,
  44. uid: self.uid,
  45. gid: self.gid,
  46. rdev: 0,
  47. blksize: self.sect_sz.try_into().map_err(|_| {
  48. bterr!("BlockMetaSecrets::sect_sz could not be converted to a u32")
  49. })?,
  50. blocks: self.sectors(),
  51. flags: 0,
  52. })
  53. }
  54. }
  55. fn block_on<F: Future>(future: F) -> F::Output {
  56. Handle::current().block_on(future)
  57. }
  58. pub struct FuseFs<P> {
  59. provider: P,
  60. path_by_luid: Bijection<u32, Arc<BlockPath>>,
  61. ruid_by_path: RwLock<Bijection<Arc<BlockPath>, u32>>,
  62. }
  63. impl<P> FuseFs<P> {
  64. pub fn new(provider: P, fallback_path: Arc<BlockPath>) -> Self {
  65. let proc_uid = unsafe { libc::geteuid() };
  66. Self {
  67. provider,
  68. /// luid: Local UID
  69. path_by_luid: Bijection::new(proc_uid, fallback_path.clone()),
  70. // ruid: Remote UID
  71. ruid_by_path: RwLock::new(Bijection::new(fallback_path, 0)),
  72. }
  73. }
  74. fn path_from_luid(&self, luid: u32) -> &Arc<BlockPath> {
  75. self.path_by_luid.value(&luid)
  76. }
  77. fn luid_from_ruid(&self, ruid: u32) -> Result<u32> {
  78. let guard = self.ruid_by_path.read().display_err()?;
  79. let path = guard.key(&ruid);
  80. Ok(*self.path_by_luid.key(path))
  81. }
  82. fn convert_ruid(&self, mut attrs: BlockMetaSecrets) -> Result<BlockMetaSecrets> {
  83. attrs.uid = self.luid_from_ruid(attrs.uid)?;
  84. Ok(attrs)
  85. }
  86. fn fuse_entry(&self, attrs: btfproto::msg::Entry) -> Result<Entry> {
  87. let btfproto::msg::Entry {
  88. attr,
  89. attr_timeout,
  90. entry_timeout,
  91. } = attrs;
  92. let attr = self.convert_ruid(attr)?;
  93. let BlockId { inode, generation } = attr.block_id;
  94. let attr = attr.stat()?;
  95. Ok(Entry {
  96. inode,
  97. generation,
  98. attr,
  99. attr_flags: 0,
  100. attr_timeout,
  101. entry_timeout,
  102. })
  103. }
  104. }
  105. impl<P: 'static + FsProvider> FuseFs<P> {
  106. async fn load_authz_attrs(
  107. provider: &P,
  108. from: &Arc<BlockPath>,
  109. path: &BlockPath,
  110. ) -> Result<AuthzAttrs> {
  111. let mut parent = SpecInodes::RootDir.into();
  112. for component in path.components() {
  113. let msg = Lookup {
  114. parent,
  115. name: component,
  116. };
  117. let LookupReply { inode, .. } = provider.lookup(from, msg).await?;
  118. parent = inode;
  119. }
  120. let msg = Open {
  121. inode: parent,
  122. flags: FlagValue::ReadOnly.into(),
  123. };
  124. let OpenReply { handle, .. } = provider.open(from, msg).await?;
  125. let msg = Read {
  126. inode: parent,
  127. handle,
  128. offset: 0,
  129. size: u64::MAX,
  130. };
  131. let guard = provider.read(from, msg).await?;
  132. let mut slice: &[u8] = &guard;
  133. read_from(&mut slice).map_err(|err| err.into())
  134. }
  135. }
  136. unsafe impl<P: Sync> Sync for FuseFs<P> {}
  137. impl<P: 'static + FsProvider> FileSystem for FuseFs<P> {
  138. type Inode = btfproto::Inode;
  139. type Handle = btfproto::Handle;
  140. fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
  141. log::debug!("init called");
  142. let provider_ref = &self.provider;
  143. let default_path = self.path_by_luid.k2v().default();
  144. let default_path_clone = default_path.clone();
  145. let attrs = block_on(async move {
  146. Self::load_authz_attrs(
  147. provider_ref,
  148. &default_path_clone,
  149. default_path_clone.as_ref(),
  150. )
  151. .await
  152. })?;
  153. let mut guard = self.ruid_by_path.write().display_err()?;
  154. guard.insert(default_path.clone(), attrs.uid);
  155. Ok(FsOptions::empty())
  156. }
  157. fn lookup(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> IoResult<Entry> {
  158. log::debug!("lookup called");
  159. block_on(async move {
  160. let path = self.path_from_luid(ctx.uid);
  161. let name = name.to_str().display_err()?;
  162. let msg = Lookup { parent, name };
  163. let entry = match self.provider.lookup(path, msg).await {
  164. Ok(LookupReply { entry, .. }) => entry,
  165. Err(err) => {
  166. return match err.downcast::<io::Error>() {
  167. Ok(err) => {
  168. debug!("lookup returned io::Error: {err}");
  169. Err(err)
  170. }
  171. Err(err) => {
  172. debug!("lookup returned an unknown error: {err}");
  173. Err(io::Error::new(io::ErrorKind::Other, err.to_string()))
  174. }
  175. }
  176. }
  177. };
  178. let entry = self.fuse_entry(entry)?;
  179. Ok(entry)
  180. })
  181. }
  182. fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64) {
  183. block_on(async move {
  184. let path = self.path_from_luid(ctx.uid);
  185. let msg = Forget { inode, count };
  186. if let Err(err) = self.provider.forget(path, msg).await {
  187. error!("error when sending the Forget message: {err}");
  188. }
  189. })
  190. }
  191. fn create(
  192. &self,
  193. ctx: &Context,
  194. parent: Self::Inode,
  195. name: &CStr,
  196. args: CreateIn,
  197. ) -> IoResult<(Entry, Option<Self::Handle>, OpenOptions)> {
  198. block_on(async move {
  199. let path = self.path_from_luid(ctx.uid);
  200. let name = name.to_str().display_err()?;
  201. let msg = Create {
  202. parent,
  203. name,
  204. flags: Flags::new(args.flags as i32),
  205. mode: args.mode,
  206. umask: args.umask,
  207. };
  208. let CreateReply { entry, handle, .. } = self.provider.create(path, msg).await?;
  209. let entry = self.fuse_entry(entry)?;
  210. Ok((entry, Some(handle), OpenOptions::empty()))
  211. })
  212. }
  213. fn mkdir(
  214. &self,
  215. ctx: &Context,
  216. parent: Self::Inode,
  217. name: &CStr,
  218. mode: u32,
  219. umask: u32,
  220. ) -> io::Result<Entry> {
  221. let args = CreateIn {
  222. flags: FlagValue::Directory.value() as u32,
  223. mode,
  224. umask,
  225. fuse_flags: 0,
  226. };
  227. let (entry, ..) = self.create(ctx, parent, name, args)?;
  228. Ok(entry)
  229. }
  230. fn open(
  231. &self,
  232. ctx: &Context,
  233. inode: Self::Inode,
  234. flags: u32,
  235. _fuse_flags: u32,
  236. ) -> IoResult<(Option<Self::Handle>, OpenOptions)> {
  237. block_on(async move {
  238. let path = self.path_from_luid(ctx.uid);
  239. let msg = Open {
  240. inode,
  241. flags: Flags::new(flags as i32),
  242. };
  243. let handle = match self.provider.open(path, msg).await {
  244. Ok(OpenReply { handle, .. }) => handle,
  245. Err(err) => {
  246. error!("FsProvider::open returned an error: {err}");
  247. return Err(err.into());
  248. }
  249. };
  250. Ok((Some(handle), OpenOptions::empty()))
  251. })
  252. }
  253. fn release(
  254. &self,
  255. ctx: &Context,
  256. inode: Self::Inode,
  257. flags: u32,
  258. handle: Self::Handle,
  259. _flush: bool,
  260. _flock_release: bool,
  261. _lock_owner: Option<u64>,
  262. ) -> io::Result<()> {
  263. self.releasedir(ctx, inode, flags, handle)
  264. }
  265. fn opendir(
  266. &self,
  267. ctx: &Context,
  268. inode: Self::Inode,
  269. flags: u32,
  270. ) -> IoResult<(Option<Self::Handle>, OpenOptions)> {
  271. let flags = flags | libc::O_DIRECTORY as u32;
  272. self.open(ctx, inode, flags, 0)
  273. }
  274. fn releasedir(
  275. &self,
  276. ctx: &Context,
  277. inode: Self::Inode,
  278. _flags: u32,
  279. handle: Self::Handle,
  280. ) -> io::Result<()> {
  281. block_on(async move {
  282. let path = self.path_from_luid(ctx.uid);
  283. let msg = Close { inode, handle };
  284. self.provider.close(path, msg).await?;
  285. Ok(())
  286. })
  287. }
  288. fn read(
  289. &self,
  290. ctx: &Context,
  291. inode: Self::Inode,
  292. handle: Self::Handle,
  293. w: &mut dyn ZeroCopyWriter,
  294. size: u32,
  295. mut offset: u64,
  296. _lock_owner: Option<u64>,
  297. _flags: u32,
  298. ) -> IoResult<usize> {
  299. block_on(async move {
  300. let path = self.path_from_luid(ctx.uid);
  301. let total_size: usize = size.try_into().display_err()?;
  302. let mut total_written = 0;
  303. while total_written < total_size {
  304. let msg = Read {
  305. inode,
  306. handle,
  307. offset,
  308. size: size as u64,
  309. };
  310. let guard = self.provider.read(path, msg).await?;
  311. let slice: &[u8] = &guard;
  312. if slice.is_empty() {
  313. break;
  314. }
  315. w.write_all(&guard)?;
  316. total_written += slice.len();
  317. let len: u64 = slice.len().try_into().display_err()?;
  318. offset += len;
  319. }
  320. Ok(total_written)
  321. })
  322. }
  323. fn write(
  324. &self,
  325. ctx: &Context,
  326. inode: Self::Inode,
  327. handle: Self::Handle,
  328. r: &mut dyn ZeroCopyReader,
  329. size: u32,
  330. offset: u64,
  331. _lock_owner: Option<u64>,
  332. _delayed_write: bool,
  333. _flags: u32,
  334. _fuse_flags: u32,
  335. ) -> IoResult<usize> {
  336. block_on(async move {
  337. let path = self.path_from_luid(ctx.uid);
  338. let size: usize = size.try_into().display_err()?;
  339. // TODO: Eliminate this copying, or at least use a pool of buffers to avoid
  340. // allocating on every write. We could pass `r` to the provider if it were Send.
  341. let mut buf = Vec::with_capacity(size);
  342. r.read_to_end(&mut buf)?;
  343. let msg = Write {
  344. inode,
  345. handle,
  346. offset,
  347. data: buf.as_slice(),
  348. };
  349. let WriteReply { written, .. } = self.provider.write(path, msg).await?;
  350. Ok(written.try_into().display_err()?)
  351. })
  352. }
  353. fn flush(
  354. &self,
  355. ctx: &Context,
  356. inode: Self::Inode,
  357. handle: Self::Handle,
  358. _lock_owner: u64,
  359. ) -> io::Result<()> {
  360. block_on(async move {
  361. let path = self.path_from_luid(ctx.uid);
  362. let msg = Flush { inode, handle };
  363. self.provider.flush(path, msg).await?;
  364. Ok(())
  365. })
  366. }
  367. fn readdir(
  368. &self,
  369. ctx: &Context,
  370. inode: Self::Inode,
  371. handle: Self::Handle,
  372. size: u32,
  373. offset: u64,
  374. add_entry: &mut dyn FnMut(DirEntry) -> io::Result<usize>,
  375. ) -> io::Result<()> {
  376. block_on(async move {
  377. let path = self.path_from_luid(ctx.uid);
  378. let msg = ReadDir {
  379. inode,
  380. handle,
  381. limit: 0,
  382. state: offset,
  383. };
  384. let ReadDirReply { entries, .. } = self.provider.read_dir(path, msg).await?;
  385. let mut size: usize = size.try_into().display_err()?;
  386. for (index, (name, entry)) in entries.into_iter().enumerate() {
  387. // We do not expose non-standard directory entries via FUSE.
  388. if entry.kind() == libc::DT_UNKNOWN {
  389. continue;
  390. }
  391. let inode = entry.inode();
  392. let offset = (index as u64) + 1;
  393. let dir_entry = DirEntry {
  394. ino: inode,
  395. offset,
  396. type_: entry.kind() as u32,
  397. name: name.as_bytes(),
  398. };
  399. size = size.saturating_sub(add_entry(dir_entry)?);
  400. if size == 0 {
  401. break;
  402. }
  403. }
  404. Ok(())
  405. })
  406. }
  407. fn link(
  408. &self,
  409. ctx: &Context,
  410. inode: Self::Inode,
  411. new_parent: Self::Inode,
  412. new_name: &CStr,
  413. ) -> io::Result<Entry> {
  414. debug!("link called");
  415. block_on(async move {
  416. let path = self.path_from_luid(ctx.uid);
  417. let name = new_name.to_str().display_err()?;
  418. let msg = Link {
  419. inode,
  420. new_parent,
  421. name,
  422. };
  423. let LinkReply { entry, .. } = self.provider.link(path, msg).await?;
  424. let entry = self.fuse_entry(entry)?;
  425. Ok(entry)
  426. })
  427. }
  428. fn unlink(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
  429. block_on(async move {
  430. let path = self.path_from_luid(ctx.uid);
  431. let name = name.to_str().display_err()?;
  432. let msg = Unlink { parent, name };
  433. self.provider.unlink(path, msg).await?;
  434. Ok(())
  435. })
  436. }
  437. fn rmdir(&self, ctx: &Context, parent: Self::Inode, name: &CStr) -> io::Result<()> {
  438. self.unlink(ctx, parent, name)
  439. }
  440. fn rename(
  441. &self,
  442. ctx: &Context,
  443. olddir: Self::Inode,
  444. oldname: &CStr,
  445. newdir: Self::Inode,
  446. newname: &CStr,
  447. _flags: u32,
  448. ) -> io::Result<()> {
  449. let Entry { inode, .. } = self.lookup(ctx, olddir, oldname)?;
  450. let result = (move || {
  451. self.link(ctx, inode, newdir, newname)?;
  452. self.unlink(ctx, olddir, oldname)?;
  453. Ok(())
  454. })();
  455. self.forget(ctx, inode, 1);
  456. result
  457. }
  458. fn getattr(
  459. &self,
  460. ctx: &Context,
  461. inode: Self::Inode,
  462. handle: Option<Self::Handle>,
  463. ) -> IoResult<(stat64, Duration)> {
  464. block_on(async move {
  465. let path = self.path_from_luid(ctx.uid);
  466. let msg = ReadMeta { inode, handle };
  467. let ReadMetaReply {
  468. attrs, valid_for, ..
  469. } = self.provider.read_meta(path, msg).await?;
  470. let attrs = self.convert_ruid(attrs)?;
  471. let stat = attrs.stat()?;
  472. Ok((stat, valid_for))
  473. })
  474. }
  475. fn setattr(
  476. &self,
  477. ctx: &Context,
  478. inode: Self::Inode,
  479. attr: stat64,
  480. handle: Option<Self::Handle>,
  481. valid: SetattrValid,
  482. ) -> IoResult<(stat64, Duration)> {
  483. block_on(async move {
  484. let path = self.path_from_luid(ctx.uid);
  485. let mut msg_attrs = Attrs::default();
  486. let mut attrs_set = AttrsSet::none();
  487. if valid.intersects(SetattrValid::MODE) {
  488. msg_attrs.mode = attr.st_mode;
  489. attrs_set |= AttrsSet::MODE;
  490. }
  491. if valid.intersects(SetattrValid::UID) {
  492. msg_attrs.uid = attr.st_uid;
  493. attrs_set |= AttrsSet::UID;
  494. }
  495. if valid.intersects(SetattrValid::GID) {
  496. msg_attrs.gid = attr.st_gid;
  497. attrs_set |= AttrsSet::GID;
  498. }
  499. if valid.intersects(SetattrValid::ATIME) {
  500. let atime: u64 = attr.st_atime.try_into().display_err()?;
  501. msg_attrs.atime = Epoch::from(atime);
  502. attrs_set |= AttrsSet::ATIME;
  503. }
  504. if valid.intersects(SetattrValid::MTIME) {
  505. let mtime: u64 = attr.st_mtime.try_into().display_err()?;
  506. msg_attrs.mtime = Epoch::from(mtime);
  507. attrs_set |= AttrsSet::MTIME;
  508. }
  509. if valid.intersects(SetattrValid::CTIME) {
  510. let ctime: u64 = attr.st_ctime.try_into().display_err()?;
  511. msg_attrs.ctime = Epoch::from(ctime);
  512. attrs_set |= AttrsSet::CTIME;
  513. }
  514. let msg = WriteMeta {
  515. inode,
  516. handle,
  517. attrs: msg_attrs,
  518. attrs_set,
  519. };
  520. let WriteMetaReply {
  521. attrs, valid_for, ..
  522. } = self.provider.write_meta(path, msg).await?;
  523. Ok((attrs.stat()?, valid_for))
  524. })
  525. }
  526. fn fsync(
  527. &self,
  528. ctx: &Context,
  529. inode: Self::Inode,
  530. _datasync: bool,
  531. handle: Self::Handle,
  532. ) -> IoResult<()> {
  533. block_on(async move {
  534. let path = self.path_from_luid(ctx.uid);
  535. let msg = Flush { inode, handle };
  536. self.provider.flush(path, msg).await?;
  537. Ok(())
  538. })
  539. }
  540. fn fsyncdir(
  541. &self,
  542. ctx: &Context,
  543. inode: Self::Inode,
  544. datasync: bool,
  545. handle: Self::Handle,
  546. ) -> IoResult<()> {
  547. self.fsync(ctx, inode, datasync, handle)
  548. }
  549. fn fallocate(
  550. &self,
  551. ctx: &Context,
  552. inode: Self::Inode,
  553. handle: Self::Handle,
  554. mode: u32,
  555. offset: u64,
  556. length: u64,
  557. ) -> IoResult<()> {
  558. block_on(async move {
  559. if mode != 0 {
  560. error!("a non-zero mode argument was given to async_fallocate: {mode}");
  561. return Err(io::Error::from_raw_os_error(libc::ENOTSUP));
  562. }
  563. let path = self.path_from_luid(ctx.uid);
  564. let msg = Allocate {
  565. inode,
  566. handle,
  567. offset: Some(offset),
  568. size: length,
  569. };
  570. self.provider.allocate(path, msg).await?;
  571. Ok(())
  572. })
  573. }
  574. }
  575. }
  576. #[cfg(test)]
  577. mod tests {
  578. use super::*;
  579. use btfproto::Inode;
  580. use btfproto_tests::local_fs_tests::{ConcreteFs, LocalFsTest};
  581. use fuse_backend_rs::api::filesystem::Context;
  582. use std::ffi::CString;
  583. use tempdir::TempDir;
  584. struct FuseFsTest {
  585. _dir: TempDir,
  586. fs: FuseFs<ConcreteFs>,
  587. }
  588. impl FuseFsTest {
  589. async fn new_empty() -> Self {
  590. let case = LocalFsTest::new_empty().await;
  591. let from = case.from().to_owned();
  592. let (dir, fs, ..) = case.into_parts();
  593. let fs = FuseFs::new(fs, from);
  594. Self { _dir: dir, fs }
  595. }
  596. fn fs(&self) -> &FuseFs<ConcreteFs> {
  597. &self.fs
  598. }
  599. fn ctx(&self) -> Context {
  600. Context {
  601. uid: 0,
  602. gid: 0,
  603. pid: 0,
  604. }
  605. }
  606. }
  607. #[tokio::test]
  608. async fn lookup_file_exists() {
  609. let case = FuseFsTest::new_empty().await;
  610. tokio::task::spawn_blocking(move || {
  611. let fuse_fs = case.fs();
  612. let root: Inode = SpecInodes::RootDir.into();
  613. let ctx = case.ctx();
  614. let name = CString::new("file.txt").unwrap();
  615. let args = CreateIn {
  616. flags: libc::O_RDWR as u32,
  617. mode: 0o644,
  618. umask: 0,
  619. fuse_flags: 0,
  620. };
  621. let (expected, ..) = fuse_fs.create(&ctx, root, &name, args).unwrap();
  622. let actual = fuse_fs.lookup(&ctx, root, &name).unwrap();
  623. assert_eq!(expected.inode, actual.inode);
  624. assert_eq!(expected.generation, actual.generation);
  625. assert_eq!(expected.attr, actual.attr);
  626. assert_eq!(expected.attr_flags, actual.attr_flags);
  627. assert_eq!(expected.attr_timeout, actual.attr_timeout);
  628. assert_eq!(expected.entry_timeout, actual.entry_timeout);
  629. })
  630. .await
  631. .unwrap();
  632. }
  633. #[tokio::test]
  634. async fn setattr() {
  635. macro_rules! check {
  636. (
  637. $actual:ident,
  638. $entry:ident,
  639. $getattr_return:ident,
  640. $setattr_return:ident,
  641. $field:ident
  642. ) => {
  643. assert_ne!($actual.$field, $entry.attr.$field);
  644. assert_eq!($actual.$field, $setattr_return.$field);
  645. assert_eq!($actual.$field, $getattr_return.$field);
  646. };
  647. }
  648. let case = FuseFsTest::new_empty().await;
  649. tokio::task::spawn_blocking(move || {
  650. let fuse_fs = case.fs();
  651. let root: Inode = SpecInodes::RootDir.into();
  652. let ctx = case.ctx();
  653. let name = CString::new("file.txt").unwrap();
  654. let args = CreateIn {
  655. flags: libc::O_RDWR as u32,
  656. mode: 0o644,
  657. umask: 0,
  658. fuse_flags: 0,
  659. };
  660. let (entry, handle, ..) = fuse_fs.create(&ctx, root, &name, args).unwrap();
  661. let inode = entry.inode;
  662. let actual = {
  663. let mut actual = stat64::from(Attr::default());
  664. actual.st_mode = 0o777;
  665. actual.st_atime = 21323;
  666. actual.st_mtime = 21290;
  667. actual.st_ctime = 119200;
  668. actual
  669. };
  670. let valid = SetattrValid::MODE
  671. | SetattrValid::UID
  672. | SetattrValid::GID
  673. | SetattrValid::ATIME
  674. | SetattrValid::MTIME
  675. | SetattrValid::CTIME;
  676. let (setattr_return, ..) = fuse_fs.setattr(&ctx, inode, actual, handle, valid).unwrap();
  677. let (getattr_return, ..) = fuse_fs.getattr(&ctx, inode, handle).unwrap();
  678. check!(actual, entry, getattr_return, setattr_return, st_mode);
  679. check!(actual, entry, getattr_return, setattr_return, st_atime);
  680. check!(actual, entry, getattr_return, setattr_return, st_mtime);
  681. check!(actual, entry, getattr_return, setattr_return, st_ctime);
  682. })
  683. .await
  684. .unwrap();
  685. }
  686. }