1use std::ffi::{OsStr, OsString};
7use std::path::{Path, PathBuf};
8use std::sync::{Arc, Mutex, RwLock};
9use std::time::SystemTime;
10
11#[cfg(not(feature = "legacy_readdir"))]
12use fuser::InitFlags;
13use fuser::{
14 AccessFlags, Errno, FileHandle, FopenFlags, Generation, INodeNo, LockOwner, OpenFlags,
15 RenameFlags, TimeOrNow, WriteFlags,
16};
17
18use crate::FileType;
19use crate::directory_cache::*;
20use crate::inode_table::{InodeTable, InodeToPath};
21use crate::types::*;
22
23type ReaddirIterator = Box<dyn Iterator<Item = std::io::Result<Vec<DirectoryEntry>>> + Send>;
24type ReaddirSlot = Mutex<Option<ReaddirState<ReaddirIterator, DirectoryEntry>>>;
25
26#[cfg(feature = "legacy_readdir")]
27type LegacyReaddirIterator =
28 Box<dyn Iterator<Item = std::io::Result<Vec<LegacyDirectoryEntry>>> + Send>;
29#[cfg(feature = "legacy_readdir")]
30type LegacyReaddirSlot = Mutex<Option<ReaddirState<LegacyReaddirIterator, LegacyDirectoryEntry>>>;
31
32enum ReaddirReply {
34 #[cfg(not(feature = "legacy_readdir"))]
35 Plain(fuser::ReplyDirectory),
36 Plus(fuser::ReplyDirectoryPlus),
37}
38
39impl ReaddirReply {
40 fn ok(self) {
42 match self {
43 #[cfg(not(feature = "legacy_readdir"))]
44 Self::Plain(reply) => reply.ok(),
45 Self::Plus(reply) => reply.ok(),
46 }
47 }
48
49 fn error(self, error: Errno) {
51 match self {
52 #[cfg(not(feature = "legacy_readdir"))]
53 Self::Plain(reply) => reply.error(error),
54 Self::Plus(reply) => reply.error(error),
55 }
56 }
57}
58
59trait IntoRequestInfo {
60 fn info(&self) -> RequestInfo;
61}
62
63impl IntoRequestInfo for fuser::Request {
64 fn info(&self) -> RequestInfo {
65 RequestInfo {
66 unique: self.unique().0,
67 uid: self.uid(),
68 gid: self.gid(),
69 pid: self.pid(),
70 }
71 }
72}
73
74fn fuse_fileattr(attr: FileAttr, ino: INodeNo) -> fuser::FileAttr {
75 fuser::FileAttr {
76 ino,
77 size: attr.size,
78 blocks: attr.blocks,
79 atime: attr.atime,
80 mtime: attr.mtime,
81 ctime: attr.ctime,
82 crtime: attr.crtime,
83 kind: attr.kind,
84 perm: attr.perm,
85 nlink: attr.nlink,
86 uid: attr.uid,
87 gid: attr.gid,
88 rdev: attr.rdev,
89 blksize: attr.blksize,
90 flags: attr.flags,
91 }
92}
93
94trait TimeOrNowExt {
95 fn time(self) -> SystemTime;
96}
97
98impl TimeOrNowExt for TimeOrNow {
99 fn time(self) -> SystemTime {
100 match self {
101 TimeOrNow::SpecificTime(t) => t,
102 TimeOrNow::Now => SystemTime::now(),
103 }
104 }
105}
106
107#[derive(Debug)]
127pub struct FuserNG<T> {
128 target: Arc<T>,
129 table: InodeTable,
130 directory_cache: RwLock<DirectoryCache>,
131 readdir_cache: RwLock<ReaddirCache<ReaddirSlot>>,
132 #[cfg(feature = "legacy_readdir")]
133 legacy_readdir_cache: RwLock<ReaddirCache<LegacyReaddirSlot>>,
134}
135
136impl<T: Filesystem + Sync + Send + 'static> FuserNG<T> {
137 pub fn new(target_fs: T) -> FuserNG<T> {
139 FuserNG {
140 target: Arc::new(target_fs),
141 table: InodeTable::new(),
142 directory_cache: DirectoryCache::new().into(),
143 readdir_cache: ReaddirCache::new().into(),
144 #[cfg(feature = "legacy_readdir")]
145 legacy_readdir_cache: ReaddirCache::new().into(),
146 }
147 }
148 fn get_path(&self, ino: INodeNo) -> Option<EntryName> {
149 self.table.get_path(ino.0)
150 }
151 fn add_or_get_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
152 self.table.add_or_get_dir(parent.0, name)
153 }
154 fn add_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
155 self.table.add_or_get_leaf(parent.0, name)
156 }
157 fn create_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(bool, u64, u64)> {
158 self.table.create_or_get_leaf(parent.0, name)
159 }
160 fn lookup(&self, ino: u64) {
161 self.table.lookup(ino);
162 }
163 fn forget(&self, ino: INodeNo, n: u64) -> u64 {
164 self.table.forget(ino.0, n)
165 }
166 fn add_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
167 self.table.add_leaf(parent.0, name)
168 }
169 fn add_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
170 self.table.add_dir(parent.0, name)
171 }
172 fn inode_unlink(&self, parent: INodeNo, name: &OsStr) {
173 self.table.unlink(parent.0, name)
174 }
175 fn get_parent_inode(&self, ino: INodeNo) -> Option<u64> {
176 self.table.get_parent_inode(ino.0)
177 }
178 fn inode_rename(
179 &self,
180 oldparent: INodeNo,
181 oldname: &OsStr,
182 newparent: INodeNo,
183 newname: &OsStr,
184 ) -> Option<()> {
185 self.table
186 .rename(oldparent.0, oldname, newparent.0, newname)
187 }
188
189 #[cfg(not(feature = "legacy_readdir"))]
191 fn add_plain_readdir_entry(
192 &self,
193 reply: &mut fuser::ReplyDirectory,
194 entry: &DirectoryEntry,
195 ino: INodeNo,
196 parent_inode: INodeNo,
197 entry_offset: u64,
198 ) -> bool {
199 let entry_inode = if entry.name == Path::new(".") {
200 ino
201 } else if entry.name == Path::new("..") {
202 parent_inode
203 } else {
204 INodeNo(!1u64)
205 };
206 reply.add(
207 entry_inode,
208 entry_offset,
209 entry.attr.kind,
210 entry.name.as_os_str(),
211 )
212 }
213
214 fn add_readdirplus_entry(
216 &self,
217 reply: &mut fuser::ReplyDirectoryPlus,
218 entry: &DirectoryEntry,
219 ino: INodeNo,
220 parent_inode: INodeNo,
221 entry_offset: u64,
222 ) -> Result<bool, Errno> {
223 let (entry_inode, generation, count_lookup) = if entry.name == Path::new(".") {
224 (ino, Generation(0), false)
225 } else if entry.name == Path::new("..") {
226 (parent_inode, Generation(0), false)
227 } else {
228 let inode = if entry.attr.kind == FileType::Directory {
229 self.add_or_get_dir(ino, &entry.name)
230 } else {
231 self.add_or_get_leaf(ino, &entry.name)
232 };
233 let Some((inode, generation)) = inode else {
234 return Err(Errno::EINVAL);
235 };
236 (INodeNo(inode), Generation(generation), true)
237 };
238
239 let attr = fuse_fileattr(entry.attr, entry_inode);
240 let buffer_full = reply.add(
241 entry_inode,
242 entry_offset,
243 entry.name.as_os_str(),
244 &entry.ttl,
245 &attr,
246 generation,
247 );
248 if !buffer_full && count_lookup {
249 self.lookup(entry_inode.0);
250 }
251 Ok(buffer_full)
252 }
253
254 fn add_readdir_entry(
256 &self,
257 reply: &mut ReaddirReply,
258 entry: &DirectoryEntry,
259 ino: INodeNo,
260 parent_inode: INodeNo,
261 entry_offset: u64,
262 ) -> Result<bool, Errno> {
263 match reply {
264 #[cfg(not(feature = "legacy_readdir"))]
265 ReaddirReply::Plain(reply) => {
266 Ok(self.add_plain_readdir_entry(reply, entry, ino, parent_inode, entry_offset))
267 }
268 ReaddirReply::Plus(reply) => {
269 self.add_readdirplus_entry(reply, entry, ino, parent_inode, entry_offset)
270 }
271 }
272 }
273
274 fn fill_readdir<I>(
276 &self,
277 state: &mut ReaddirState<ReaddirIterator, DirectoryEntry>,
278 mut producer: Option<&mut I>,
279 ino: INodeNo,
280 parent_inode: INodeNo,
281 offset: u64,
282 mut reply: ReaddirReply,
283 ) -> bool
284 where
285 I: Iterator<Item = std::io::Result<Vec<DirectoryEntry>>> + ?Sized,
286 {
287 let Ok(mut index) = usize::try_from(offset) else {
288 reply.error(Errno::EINVAL);
289 return producer.is_some();
290 };
291 if index > state.entries.len() {
292 reply.error(Errno::EINVAL);
293 return producer.is_some();
294 }
295
296 let mut added = false;
297 loop {
298 if index < state.entries.len() {
299 let entry = &state.entries[index];
300 let entry_offset = match u64::try_from(index)
301 .ok()
302 .and_then(|value| value.checked_add(1))
303 {
304 Some(offset) => offset,
305 None => {
306 if added {
307 reply.ok();
308 } else {
309 reply.error(Errno::EOVERFLOW);
310 }
311 return producer.is_some();
312 }
313 };
314
315 let buffer_full = match self.add_readdir_entry(
316 &mut reply,
317 entry,
318 ino,
319 parent_inode,
320 entry_offset,
321 ) {
322 Ok(buffer_full) => buffer_full,
323 Err(error) => {
324 if added {
325 reply.ok();
326 } else {
327 reply.error(error);
328 }
329 return producer.is_some();
330 }
331 };
332 if buffer_full {
333 if added {
334 reply.ok();
335 } else {
336 reply.error(Errno::EOVERFLOW);
337 }
338 return producer.is_some();
339 }
340 added = true;
341 index += 1;
342 continue;
343 }
344
345 if let Some(error) = state.pending_error {
346 if added {
347 reply.ok();
348 } else {
349 reply.error(error);
350 }
351 return false;
352 }
353
354 let Some(source) = producer.as_deref_mut() else {
355 reply.ok();
356 return false;
357 };
358 match source.next() {
359 Some(Ok(entries)) => state.entries.extend(entries),
360 Some(Err(error)) => {
361 producer = None;
362 state.pending_error = Some(error.into());
363 }
364 None => producer = None,
365 }
366 }
367 }
368
369 #[cfg(feature = "legacy_readdir")]
371 fn fill_legacy_readdir<I>(
372 &self,
373 state: &mut ReaddirState<LegacyReaddirIterator, LegacyDirectoryEntry>,
374 mut producer: Option<&mut I>,
375 ino: INodeNo,
376 parent_inode: INodeNo,
377 offset: u64,
378 mut reply: fuser::ReplyDirectory,
379 ) -> bool
380 where
381 I: Iterator<Item = std::io::Result<Vec<LegacyDirectoryEntry>>> + ?Sized,
382 {
383 let Ok(mut index) = usize::try_from(offset) else {
384 reply.error(Errno::EINVAL);
385 return producer.is_some();
386 };
387 if index > state.entries.len() {
388 reply.error(Errno::EINVAL);
389 return producer.is_some();
390 }
391
392 let mut added = false;
393 loop {
394 if index < state.entries.len() {
395 let entry = &state.entries[index];
396 let entry_offset = match u64::try_from(index)
397 .ok()
398 .and_then(|value| value.checked_add(1))
399 {
400 Some(offset) => offset,
401 None => {
402 if added {
403 reply.ok();
404 } else {
405 reply.error(Errno::EOVERFLOW);
406 }
407 return producer.is_some();
408 }
409 };
410 let entry_inode = if entry.name == Path::new(".") {
411 ino
412 } else if entry.name == Path::new("..") {
413 parent_inode
414 } else {
415 INodeNo(!1u64)
416 };
417 if reply.add(
418 entry_inode,
419 entry_offset,
420 entry.kind,
421 entry.name.as_os_str(),
422 ) {
423 if added {
424 reply.ok();
425 } else {
426 reply.error(Errno::EOVERFLOW);
427 }
428 return producer.is_some();
429 }
430 added = true;
431 index += 1;
432 continue;
433 }
434
435 if let Some(error) = state.pending_error {
436 if added {
437 reply.ok();
438 } else {
439 reply.error(error);
440 }
441 return false;
442 }
443
444 let Some(source) = producer.as_deref_mut() else {
445 reply.ok();
446 return false;
447 };
448 match source.next() {
449 Some(Ok(entries)) => state.entries.extend(entries),
450 Some(Err(error)) => {
451 producer = None;
452 state.pending_error = Some(error.into());
453 }
454 None => producer = None,
455 }
456 }
457 }
458}
459
460macro_rules! get_entry_name {
461 ($s:expr, $ino:expr, $reply:expr) => {
462 if let Some(path) = $s.get_path($ino) {
463 path
464 } else {
465 $reply.error(Errno::EINVAL);
466 return;
467 }
468 };
469}
470
471macro_rules! resolve_from_parent {
472 ($s:expr, $ino:expr, $name:expr, $reply:expr) => {
473 if let Some(path) = $s.table.resolve_from_parent($ino.0, $name.into()) {
474 path
475 } else {
476 $reply.error(Errno::EINVAL);
477 return;
478 }
479 };
480}
481
482macro_rules! get_resolved_path {
483 ($s:expr, $ino:expr, $reply:expr) => {{ get_entry_name!($s, $ino, $reply).with($ino.0) }};
484}
485
486impl<T: Filesystem + Sync + Send + 'static> fuser::Filesystem for FuserNG<T> {
487 fn init(
488 &mut self,
489 req: &fuser::Request,
490 config: &mut fuser::KernelConfig,
491 ) -> Result<(), std::io::Error> {
492 debug!("init");
493 self.target.init(req.info(), config)?;
494 #[cfg(not(feature = "legacy_readdir"))]
495 if let Err(unsupported) = config.add_capabilities(InitFlags::FUSE_DO_READDIRPLUS) {
496 warn!("kernel does not support FUSE_READDIRPLUS: {unsupported:?}");
497 }
498 Ok(())
499 }
500
501 fn destroy(&mut self) {
502 debug!("destroy");
503 self.target.destroy();
504 }
505
506 fn lookup(
507 &self,
508 req: &fuser::Request,
509 parent: INodeNo,
510 name: &OsStr,
511 reply: fuser::ReplyEntry,
512 ) {
513 let path = resolve_from_parent!(self, parent, name, reply);
514 debug!("lookup: {:?}", path);
515 let path = EntryRef::Lookup(path);
516 match self.target.getattr(req.info(), &path, None) {
520 Ok((ttl, attr)) => {
521 let value = if attr.kind == FileType::Directory {
522 self.add_or_get_dir(parent, name)
523 } else {
524 self.add_or_get_leaf(parent, name)
525 };
526 if let Some((ino, generation)) = value {
527 self.lookup(ino);
528 reply.entry(
529 &ttl,
530 &fuse_fileattr(attr, INodeNo(ino)),
531 Generation(generation),
532 );
533 } else {
534 reply.error(Errno::EINVAL)
535 }
536 }
537 Err(e) => reply.error(e.into()),
538 }
539 }
540
541 fn forget(&self, _req: &fuser::Request, ino: INodeNo, nlookup: u64) {
542 let lookups = self.forget(ino, nlookup);
543 let path = self.get_path(ino).unwrap_or_else(|| {
544 EntryName::new(
545 Arc::new(PathBuf::from(OsStr::new(""))).into(),
546 OsString::from("[unknown]").into(),
547 )
548 });
549 debug!(
550 "forget: inode {} ({:?}) now at {} lookups",
551 ino, path, lookups
552 );
553 }
554
555 fn getattr(
556 &self,
557 req: &fuser::Request,
558 ino: INodeNo,
559 fh: Option<fuser::FileHandle>,
560 reply: fuser::ReplyAttr,
561 ) {
562 let path = get_resolved_path!(self, ino, reply);
563 debug!("getattr: {:?}", path);
564 let path = EntryRef::Resolved(path);
565 match self.target.getattr(req.info(), &path, fh.map(|fh| fh.0)) {
566 Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
567 Err(e) => reply.error(e.into()),
568 }
569 }
570
571 fn setattr(
572 &self,
573 req: &fuser::Request, ino: INodeNo, mode: Option<u32>, uid: Option<u32>, gid: Option<u32>, size: Option<u64>, atime: Option<TimeOrNow>, mtime: Option<TimeOrNow>, _ctime: Option<SystemTime>, fh: Option<fuser::FileHandle>, crtime: Option<SystemTime>, chgtime: Option<SystemTime>, bkuptime: Option<SystemTime>, flags: Option<fuser::BsdFileFlags>, reply: fuser::ReplyAttr,
588 ) {
589 let path = get_resolved_path!(self, ino, reply);
590 debug!("setattr: {:?}", path);
591
592 debug!("\tino:\t{:?}", ino);
593 debug!("\tmode:\t{:?}", mode);
594 debug!("\tuid:\t{:?}", uid);
595 debug!("\tgid:\t{:?}", gid);
596 debug!("\tsize:\t{:?}", size);
597 debug!("\tatime:\t{:?}", atime);
598 debug!("\tmtime:\t{:?}", mtime);
599 debug!("\tfh:\t{:?}", fh);
600
601 if let Some(mode) = mode
604 && let Err(e) = self
605 .target
606 .chmod(req.info(), &path, fh.map(|fh| fh.0), mode)
607 {
608 reply.error(e.into());
609 return;
610 }
611
612 if (uid.is_some() || gid.is_some())
613 && let Err(e) = self
614 .target
615 .chown(req.info(), &path, fh.map(|fh| fh.0), uid, gid)
616 {
617 reply.error(e.into());
618 return;
619 }
620
621 if let Some(size) = size
622 && let Err(e) = self
623 .target
624 .truncate(req.info(), &path, fh.map(|fh| fh.0), size)
625 {
626 reply.error(e.into());
627 return;
628 }
629
630 if atime.is_some() || mtime.is_some() {
631 let atime = atime.map(TimeOrNowExt::time);
632 let mtime = mtime.map(TimeOrNowExt::time);
633 if let Err(e) = self
634 .target
635 .utimens(req.info(), &path, fh.map(|fh| fh.0), atime, mtime)
636 {
637 reply.error(e.into());
638 return;
639 }
640 }
641
642 if (crtime.is_some() || chgtime.is_some() || bkuptime.is_some() || flags.is_some())
643 && let Err(e) = self.target.utimens_macos(
644 req.info(),
645 &path,
646 fh.map(|fh| fh.0),
647 crtime,
648 chgtime,
649 bkuptime,
650 flags.map(|flags| flags.bits()),
651 )
652 {
653 reply.error(e.into());
654 return;
655 }
656
657 let path = EntryRef::Resolved(path);
658 match self.target.getattr(req.info(), &path, fh.map(|fh| fh.0)) {
659 Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
660 Err(e) => reply.error(e.into()),
661 }
662 }
663
664 fn readlink(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyData) {
665 let path = get_resolved_path!(self, ino, reply);
666 debug!("readlink: {:?}", path);
667 match self.target.readlink(req.info(), &path) {
668 Ok(data) => reply.data(&data),
669 Err(e) => reply.error(e.into()),
670 }
671 }
672
673 fn mknod(
674 &self,
675 req: &fuser::Request,
676 parent: INodeNo,
677 name: &OsStr,
678 mode: u32,
679 _umask: u32, rdev: u32,
681 reply: fuser::ReplyEntry,
682 ) {
683 let entry = resolve_from_parent!(self, parent, name, reply);
684 debug!("mknod: {:?}", entry);
685 match self.target.mknod(req.info(), &entry, mode, rdev) {
686 Ok((ttl, attr)) => {
687 if let Some((ino, generation)) = self.add_leaf(parent, name) {
688 reply.entry(
689 &ttl,
690 &fuse_fileattr(attr, INodeNo(ino)),
691 Generation(generation),
692 )
693 } else {
694 reply.error(Errno::from_i32(libc::EINVAL))
695 }
696 }
697 Err(e) => reply.error(e.into()),
698 }
699 }
700
701 fn mkdir(
702 &self,
703 req: &fuser::Request,
704 parent: INodeNo,
705 name: &OsStr,
706 mode: u32,
707 _umask: u32, reply: fuser::ReplyEntry,
709 ) {
710 let entry = resolve_from_parent!(self, parent, name, reply);
711 debug!("mkdir: {:?} (mode={:#o})", entry, mode);
712 match self.target.mkdir(req.info(), &entry, mode) {
713 Ok((ttl, attr)) => {
714 if let Some((ino, generation)) = self.add_dir(parent, name) {
715 reply.entry(
716 &ttl,
717 &fuse_fileattr(attr, INodeNo(ino)),
718 Generation(generation),
719 )
720 } else {
721 reply.error(Errno::from_i32(libc::EINVAL))
722 }
723 }
724 Err(e) => reply.error(e.into()),
725 }
726 }
727
728 fn unlink(
729 &self,
730 req: &fuser::Request,
731 parent: INodeNo,
732 name: &OsStr,
733 reply: fuser::ReplyEmpty,
734 ) {
735 let entry = resolve_from_parent!(self, parent, name, reply);
736 debug!("unlink: {:?}", entry);
737 match self.target.unlink(req.info(), &entry) {
738 Ok(()) => {
739 self.inode_unlink(parent, name);
740 reply.ok()
741 }
742 Err(e) => reply.error(e.into()),
743 }
744 }
745
746 fn rmdir(&self, req: &fuser::Request, parent: INodeNo, name: &OsStr, reply: fuser::ReplyEmpty) {
747 let entry = resolve_from_parent!(self, parent, name, reply);
748 debug!("rmdir: {:?}", entry);
749 match self.target.rmdir(req.info(), &entry) {
750 Ok(()) => {
751 self.inode_unlink(parent, name);
752 reply.ok()
753 }
754 Err(e) => reply.error(e.into()),
755 }
756 }
757
758 fn symlink(
759 &self,
760 req: &fuser::Request,
761 parent: INodeNo,
762 name: &OsStr,
763 link: &Path,
764 reply: fuser::ReplyEntry,
765 ) {
766 let entry = resolve_from_parent!(self, parent, name, reply);
767 debug!("symlink: {:?} -> {:?}", entry, link);
768 match self.target.symlink(req.info(), &entry, link) {
769 Ok((ttl, attr)) => {
770 if let Some((ino, generation)) = self.add_leaf(parent, name) {
771 reply.entry(
772 &ttl,
773 &fuse_fileattr(attr, INodeNo(ino)),
774 Generation(generation),
775 )
776 } else {
777 reply.error(Errno::EINVAL)
778 }
779 }
780 Err(e) => reply.error(e.into()),
781 }
782 }
783
784 fn rename(
785 &self,
786 req: &fuser::Request,
787 parent: INodeNo,
788 name: &OsStr,
789 newparent: INodeNo,
790 newname: &OsStr,
791 _flags: RenameFlags, reply: fuser::ReplyEmpty,
793 ) {
794 let entry = resolve_from_parent!(self, parent, name, reply);
795 let new_entry = resolve_from_parent!(self, newparent, newname, reply);
796 debug!("rename: {:?} -> {:?}", entry, new_entry);
797 match self.target.rename(req.info(), &entry, &new_entry) {
798 Ok(()) => {
799 self.inode_rename(parent, name, newparent, newname);
800 reply.ok()
801 }
802 Err(e) => reply.error(e.into()),
803 }
804 }
805
806 fn link(
807 &self,
808 req: &fuser::Request,
809 ino: INodeNo,
810 newparent: INodeNo,
811 newname: &OsStr,
812 reply: fuser::ReplyEntry,
813 ) {
814 let path = get_resolved_path!(self, ino, reply);
815 let new_entry = resolve_from_parent!(self, newparent, newname, reply);
816 debug!("link: {:?} -> {:?}", path, new_entry);
817 match self.target.link(req.info(), &path, &new_entry) {
818 Ok((ttl, attr)) => {
819 if let Some((new_ino, generation)) = self.add_leaf(newparent, newname) {
822 reply.entry(
823 &ttl,
824 &fuse_fileattr(attr, INodeNo(new_ino)),
825 Generation(generation),
826 );
827 } else {
828 reply.error(Errno::EINVAL);
829 }
830 }
831 Err(e) => reply.error(e.into()),
832 }
833 }
834
835 fn open(&self, req: &fuser::Request, ino: INodeNo, flags: OpenFlags, reply: fuser::ReplyOpen) {
836 let path = get_resolved_path!(self, ino, reply);
837 debug!("open: {:?}", path);
838 match self.target.open(req.info(), &path, flags.0 as u32) {
839 Ok((fh, flags)) => reply.opened(FileHandle(fh), FopenFlags::from_bits_retain(flags)),
841 Err(e) => reply.error(e.into()),
842 }
843 }
844
845 fn read(
846 &self,
847 req: &fuser::Request,
848 ino: INodeNo,
849 fh: FileHandle,
850 offset: u64,
851 size: u32,
852 _flags: OpenFlags, _lock_owner: Option<LockOwner>, reply: fuser::ReplyData,
855 ) {
856 let path = get_resolved_path!(self, ino, reply);
857 debug!("read: {:?} {:#x} @ {:#x}", path, size, offset);
858 self.target
859 .read(req.info(), &path, fh.0, offset, size, |result| {
860 match result {
861 Ok(data) => reply.data(data),
862 Err(e) => reply.error(e.into()),
863 }
864 CallbackResult {
865 _private: std::marker::PhantomData {},
866 }
867 });
868 }
869
870 fn write(
871 &self,
872 req: &fuser::Request,
873 ino: INodeNo,
874 fh: FileHandle,
875 offset: u64,
876 data: &[u8],
877 _write_flags: WriteFlags, flags: OpenFlags,
879 _lock_owner: Option<LockOwner>, reply: fuser::ReplyWrite,
881 ) {
882 let path = get_resolved_path!(self, ino, reply);
883 debug!("write: {:?} {:#x} @ {:#x}", path, data.len(), offset);
884 let data_buf = Vec::from(data);
886 match self
887 .target
888 .write(req.info(), &path, fh.0, offset, data_buf, flags.0 as u32)
889 {
890 Ok(written) => reply.written(written),
891 Err(e) => reply.error(e.into()),
892 }
893 }
894
895 fn flush(
896 &self,
897 req: &fuser::Request,
898 ino: INodeNo,
899 fh: FileHandle,
900 lock_owner: LockOwner,
901 reply: fuser::ReplyEmpty,
902 ) {
903 let path = get_resolved_path!(self, ino, reply);
904 debug!("flush: {:?}", path);
905 match self.target.flush(req.info(), &path, fh.0, lock_owner.0) {
906 Ok(()) => reply.ok(),
907 Err(e) => reply.error(e.into()),
908 }
909 }
910
911 fn release(
912 &self,
913 req: &fuser::Request,
914 ino: INodeNo,
915 fh: FileHandle,
916 flags: OpenFlags,
917 lock_owner: Option<LockOwner>,
918 flush: bool,
919 reply: fuser::ReplyEmpty,
920 ) {
921 let path = get_resolved_path!(self, ino, reply);
922 debug!("release: {:?}", path);
923 match self.target.release(
924 req.info(),
925 &path,
926 fh.0,
927 flags.0 as u32,
928 lock_owner.map(|owner| owner.0).unwrap_or(0), flush,
930 ) {
931 Ok(()) => reply.ok(),
932 Err(e) => reply.error(e.into()),
933 }
934 }
935
936 fn fsync(
937 &self,
938 req: &fuser::Request,
939 ino: INodeNo,
940 fh: FileHandle,
941 datasync: bool,
942 reply: fuser::ReplyEmpty,
943 ) {
944 let path = get_resolved_path!(self, ino, reply);
945 debug!("fsync: {:?}", path);
946 match self.target.fsync(req.info(), &path, fh.0, datasync) {
947 Ok(()) => reply.ok(),
948 Err(e) => reply.error(e.into()),
949 }
950 }
951
952 fn opendir(
953 &self,
954 req: &fuser::Request,
955 ino: INodeNo,
956 flags: OpenFlags,
957 reply: fuser::ReplyOpen,
958 ) {
959 let path = get_resolved_path!(self, ino, reply);
960 debug!("opendir: {:?}", path);
961 match self.target.opendir(req.info(), &path, flags.0 as u32) {
962 Ok((fh, flags)) => {
963 let dcache_key = self.directory_cache.write().unwrap().new_entry(fh);
964 self.readdir_cache
965 .write()
966 .unwrap()
967 .insert(dcache_key, Mutex::new(None));
968 #[cfg(feature = "legacy_readdir")]
969 self.legacy_readdir_cache
970 .write()
971 .unwrap()
972 .insert(dcache_key, Mutex::new(None));
973 reply.opened(FileHandle(dcache_key), FopenFlags::from_bits_retain(flags));
974 }
975 Err(e) => reply.error(e.into()),
976 }
977 }
978
979 fn readdir(
980 &self,
981 req: &fuser::Request,
982 ino: INodeNo,
983 fh: FileHandle,
984 offset: u64,
985 reply: fuser::ReplyDirectory,
986 ) {
987 let path = get_resolved_path!(self, ino, reply);
988 debug!("readdir: {:?} @ {}", path, offset);
989
990 let parent_inode = if ino == INodeNo::ROOT {
991 ino
992 } else {
993 match self.get_parent_inode(ino) {
994 Some(inode) => INodeNo(inode),
995 None => {
996 error!("readdir: unable to get parent inode for {:?}", path);
997 reply.error(Errno::EIO);
998 return;
999 }
1000 }
1001 };
1002
1003 #[cfg(feature = "legacy_readdir")]
1004 {
1005 let Some(slot) = self.legacy_readdir_cache.read().unwrap().get(fh.0) else {
1006 reply.error(Errno::EINVAL);
1007 return;
1008 };
1009 let mut slot = slot.lock().unwrap();
1010 if slot.is_none() {
1011 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
1012 let producer = self.target.legacy_readdir(req.info(), &path, real_fh);
1013 *slot = Some(ReaddirState::new(Box::new(producer)));
1014 }
1015
1016 let state = slot.as_mut().unwrap();
1017 let mut producer = state.producer.take();
1018 if self.fill_legacy_readdir(
1019 state,
1020 producer.as_deref_mut(),
1021 ino,
1022 parent_inode,
1023 offset,
1024 reply,
1025 ) {
1026 state.producer = producer;
1027 }
1028 }
1029
1030 #[cfg(not(feature = "legacy_readdir"))]
1031 {
1032 let Some(slot) = self.readdir_cache.read().unwrap().get(fh.0) else {
1033 reply.error(Errno::EINVAL);
1034 return;
1035 };
1036 let mut slot = slot.lock().unwrap();
1037 if slot.is_none() {
1038 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
1039 let producer = self.target.readdir(req.info(), &path, real_fh);
1040 *slot = Some(ReaddirState::new(Box::new(producer)));
1041 }
1042
1043 let state = slot.as_mut().unwrap();
1044 let mut producer = state.producer.take();
1045 if self.fill_readdir(
1046 state,
1047 producer.as_deref_mut(),
1048 ino,
1049 parent_inode,
1050 offset,
1051 ReaddirReply::Plain(reply),
1052 ) {
1053 state.producer = producer;
1054 }
1055 }
1056 }
1057
1058 fn readdirplus(
1059 &self,
1060 req: &fuser::Request,
1061 ino: INodeNo,
1062 fh: FileHandle,
1063 offset: u64,
1064 reply: fuser::ReplyDirectoryPlus,
1065 ) {
1066 let path = get_resolved_path!(self, ino, reply);
1067 debug!("readdirplus: {:?} @ {}", path, offset);
1068
1069 let parent_inode = if ino == INodeNo::ROOT {
1070 ino
1071 } else {
1072 match self.get_parent_inode(ino) {
1073 Some(inode) => INodeNo(inode),
1074 None => {
1075 error!("readdirplus: unable to get parent inode for {:?}", path);
1076 reply.error(Errno::EIO);
1077 return;
1078 }
1079 }
1080 };
1081
1082 let Some(slot) = self.readdir_cache.read().unwrap().get(fh.0) else {
1083 reply.error(Errno::EINVAL);
1084 return;
1085 };
1086 let mut slot = slot.lock().unwrap();
1087
1088 if slot.is_none() {
1089 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
1090 let producer = self.target.readdir(req.info(), &path, real_fh);
1091 *slot = Some(ReaddirState::new(Box::new(producer)));
1092 }
1093
1094 let state = slot.as_mut().unwrap();
1095 let mut producer = state.producer.take();
1096 if self.fill_readdir(
1097 state,
1098 producer.as_deref_mut(),
1099 ino,
1100 parent_inode,
1101 offset,
1102 ReaddirReply::Plus(reply),
1103 ) {
1104 state.producer = producer;
1105 }
1106 }
1107
1108 fn releasedir(
1109 &self,
1110 req: &fuser::Request,
1111 ino: INodeNo,
1112 fh: FileHandle,
1113 flags: OpenFlags,
1114 reply: fuser::ReplyEmpty,
1115 ) {
1116 let path = get_resolved_path!(self, ino, reply);
1117 debug!("releasedir: {:?}", path);
1118 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
1119 match self
1120 .target
1121 .releasedir(req.info(), &path, real_fh, flags.0 as u32)
1122 {
1123 Ok(()) => reply.ok(),
1124 Err(e) => reply.error(e.into()),
1125 }
1126 self.directory_cache.write().unwrap().delete(fh.0);
1127 self.readdir_cache.write().unwrap().delete(fh.0);
1128 #[cfg(feature = "legacy_readdir")]
1129 self.legacy_readdir_cache.write().unwrap().delete(fh.0);
1130 }
1131
1132 fn fsyncdir(
1133 &self,
1134 req: &fuser::Request,
1135 ino: INodeNo,
1136 fh: FileHandle,
1137 datasync: bool,
1138 reply: fuser::ReplyEmpty,
1139 ) {
1140 let path = get_resolved_path!(self, ino, reply);
1141 debug!("fsyncdir: {:?} (datasync: {:?})", path, datasync);
1142 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
1143 match self.target.fsyncdir(req.info(), &path, real_fh, datasync) {
1144 Ok(()) => reply.ok(),
1145 Err(e) => reply.error(e.into()),
1146 }
1147 }
1148
1149 fn statfs(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyStatfs) {
1150 let path = get_resolved_path!(self, ino, reply);
1151 debug!("statfs: {:?}", path);
1152 match self.target.statfs(req.info(), &path) {
1153 Ok(statfs) => reply.statfs(
1154 statfs.blocks,
1155 statfs.bfree,
1156 statfs.bavail,
1157 statfs.files,
1158 statfs.ffree,
1159 statfs.bsize,
1160 statfs.namelen,
1161 statfs.frsize,
1162 ),
1163 Err(e) => reply.error(e.into()),
1164 }
1165 }
1166
1167 fn setxattr(
1168 &self,
1169 req: &fuser::Request,
1170 ino: INodeNo,
1171 name: &OsStr,
1172 value: &[u8],
1173 flags: i32,
1174 position: u32,
1175 reply: fuser::ReplyEmpty,
1176 ) {
1177 let path = get_resolved_path!(self, ino, reply);
1178 debug!(
1179 "setxattr: {:?} {:?} ({} bytes, flags={:#x}, pos={:#x}",
1180 path,
1181 name,
1182 value.len(),
1183 flags,
1184 position
1185 );
1186 match self
1187 .target
1188 .setxattr(req.info(), &path, name, value, flags as u32, position)
1189 {
1190 Ok(()) => reply.ok(),
1191 Err(e) => reply.error(e.into()),
1192 }
1193 }
1194
1195 fn getxattr(
1196 &self,
1197 req: &fuser::Request,
1198 ino: INodeNo,
1199 name: &OsStr,
1200 size: u32,
1201 reply: fuser::ReplyXattr,
1202 ) {
1203 let path = get_resolved_path!(self, ino, reply);
1204 debug!("getxattr: {:?} {:?}", path, name);
1205 match self.target.getxattr(req.info(), &path, name, size) {
1206 Ok(Xattr::Size(size)) => {
1207 debug!("getxattr: sending size {}", size);
1208 reply.size(size)
1209 }
1210 Ok(Xattr::Data(vec)) => {
1211 debug!("getxattr: sending {} bytes", vec.len());
1212 reply.data(&vec)
1213 }
1214 Err(e) => {
1215 debug!("getxattr: error {}", e);
1216 reply.error(e.into())
1217 }
1218 }
1219 }
1220
1221 fn listxattr(&self, req: &fuser::Request, ino: INodeNo, size: u32, reply: fuser::ReplyXattr) {
1222 let path = get_resolved_path!(self, ino, reply);
1223 debug!("listxattr: {:?}", path);
1224 match self.target.listxattr(req.info(), &path, size) {
1225 Ok(Xattr::Size(size)) => {
1226 debug!("listxattr: sending size {}", size);
1227 reply.size(size)
1228 }
1229 Ok(Xattr::Data(vec)) => {
1230 debug!("listxattr: sending {} bytes", vec.len());
1231 reply.data(&vec)
1232 }
1233 Err(e) => reply.error(e.into()),
1234 }
1235 }
1236
1237 fn removexattr(
1238 &self,
1239 req: &fuser::Request,
1240 ino: INodeNo,
1241 name: &OsStr,
1242 reply: fuser::ReplyEmpty,
1243 ) {
1244 let path = get_resolved_path!(self, ino, reply);
1245 debug!("removexattr: {:?}, {:?}", path, name);
1246 match self.target.removexattr(req.info(), &path, name) {
1247 Ok(()) => reply.ok(),
1248 Err(e) => reply.error(e.into()),
1249 }
1250 }
1251
1252 fn access(
1253 &self,
1254 req: &fuser::Request,
1255 ino: INodeNo,
1256 mask: AccessFlags,
1257 reply: fuser::ReplyEmpty,
1258 ) {
1259 let path = get_resolved_path!(self, ino, reply);
1260 debug!("access: {:?}, mask={:#o}", path, mask.bits());
1261 match self.target.access(req.info(), &path, mask.bits() as u32) {
1262 Ok(()) => reply.ok(),
1263 Err(e) => reply.error(e.into()),
1264 }
1265 }
1266
1267 fn create(
1268 &self,
1269 req: &fuser::Request,
1270 parent: INodeNo,
1271 name: &OsStr,
1272 mode: u32,
1273 _umask: u32, flags: i32,
1275 reply: fuser::ReplyCreate,
1276 ) {
1277 let (created, path, generation) = match self.create_or_get_leaf(parent, name) {
1278 Some((created, ino, generation)) => (
1279 created,
1280 get_resolved_path!(self, INodeNo(ino), reply),
1281 generation,
1282 ),
1283 _ => {
1284 reply.error(Errno::EINVAL);
1285 return;
1286 }
1287 };
1288 debug!("create: {:?} (mode={:#o}, flags={:#x})", path, mode, flags);
1289 match self.target.create(req.info(), &path, mode, flags as u32) {
1290 Ok(create) => {
1291 if !created {
1292 self.lookup(path.ino());
1293 }
1294 let attr = fuse_fileattr(create.attr, INodeNo(path.ino()));
1295 reply.created(
1296 &create.ttl,
1297 &attr,
1298 Generation(generation),
1299 FileHandle(create.fh),
1300 FopenFlags::from_bits_retain(create.flags),
1301 );
1302 }
1303 Err(e) => {
1304 if created {
1305 self.inode_unlink(parent, name);
1306 self.forget(INodeNo(path.ino()), 1);
1307 }
1308 reply.error(e.into())
1309 }
1310 }
1311 }
1312
1313 #[cfg(target_os = "macos")]
1320 fn setvolname(&self, req: &fuser::Request, name: &OsStr, reply: fuser::ReplyEmpty) {
1321 debug!("setvolname: {:?}", name);
1322 match self.target.setvolname(req.info(), name) {
1323 Ok(()) => reply.ok(),
1324 Err(e) => reply.error(e.into()),
1325 }
1326 }
1327
1328 #[cfg(target_os = "macos")]
1331 fn getxtimes(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyXTimes) {
1332 let path = get_resolved_path!(self, ino, reply);
1333 debug!("getxtimes: {:?}", path);
1334 match self.target.getxtimes(req.info(), &path) {
1335 Ok(xtimes) => {
1336 reply.xtimes(xtimes.bkuptime, xtimes.crtime);
1337 }
1338 Err(e) => reply.error(e.into()),
1339 }
1340 }
1341}