1use std::ffi::{OsStr, OsString};
7use std::path::{Path, PathBuf};
8use std::sync::{Arc, RwLock};
9use std::time::SystemTime;
10
11use fuser::{
12 AccessFlags, Errno, FileHandle, FopenFlags, Generation, INodeNo, LockOwner, OpenFlags,
13 RenameFlags, TimeOrNow, WriteFlags,
14};
15
16use crate::FileType;
17use crate::directory_cache::*;
18use crate::inode_table::{InodeTable, InodeToPath};
19use crate::types::*;
20
21trait IntoRequestInfo {
22 fn info(&self) -> RequestInfo;
23}
24
25impl IntoRequestInfo for fuser::Request {
26 fn info(&self) -> RequestInfo {
27 RequestInfo {
28 unique: self.unique().0,
29 uid: self.uid(),
30 gid: self.gid(),
31 pid: self.pid(),
32 }
33 }
34}
35
36fn fuse_fileattr(attr: FileAttr, ino: INodeNo) -> fuser::FileAttr {
37 fuser::FileAttr {
38 ino,
39 size: attr.size,
40 blocks: attr.blocks,
41 atime: attr.atime,
42 mtime: attr.mtime,
43 ctime: attr.ctime,
44 crtime: attr.crtime,
45 kind: attr.kind,
46 perm: attr.perm,
47 nlink: attr.nlink,
48 uid: attr.uid,
49 gid: attr.gid,
50 rdev: attr.rdev,
51 blksize: attr.blksize,
52 flags: attr.flags,
53 }
54}
55
56trait TimeOrNowExt {
57 fn time(self) -> SystemTime;
58}
59
60impl TimeOrNowExt for TimeOrNow {
61 fn time(self) -> SystemTime {
62 match self {
63 TimeOrNow::SpecificTime(t) => t,
64 TimeOrNow::Now => SystemTime::now(),
65 }
66 }
67}
68
69#[derive(Debug)]
89pub struct FuserNG<T> {
90 target: Arc<T>,
91 table: InodeTable,
92 directory_cache: RwLock<DirectoryCache>,
93}
94
95impl<T: Filesystem + Sync + Send + 'static> FuserNG<T> {
96 pub fn new(target_fs: T) -> FuserNG<T> {
98 FuserNG {
99 target: Arc::new(target_fs),
100 table: InodeTable::new(),
101 directory_cache: DirectoryCache::new().into(),
102 }
103 }
104 fn get_path(&self, ino: INodeNo) -> Option<EntryName> {
105 self.table.get_path(ino.0)
106 }
107 fn add_or_get_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
108 self.table.add_or_get_dir(parent.0, name)
109 }
110 fn add_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
111 self.table.add_or_get_leaf(parent.0, name)
112 }
113 fn create_or_get_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(bool, u64, u64)> {
114 self.table.create_or_get_leaf(parent.0, name)
115 }
116 fn lookup(&self, ino: u64) {
117 self.table.lookup(ino);
118 }
119 fn forget(&self, ino: INodeNo, n: u64) -> u64 {
120 self.table.forget(ino.0, n)
121 }
122 fn add_leaf(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
123 self.table.add_leaf(parent.0, name)
124 }
125 fn add_dir(&self, parent: INodeNo, name: &OsStr) -> Option<(u64, u64)> {
126 self.table.add_dir(parent.0, name)
127 }
128 fn inode_unlink(&self, parent: INodeNo, name: &OsStr) {
129 self.table.unlink(parent.0, name)
130 }
131 fn get_parent_inode(&self, ino: INodeNo) -> Option<u64> {
132 self.table.get_parent_inode(ino.0)
133 }
134 fn inode_rename(
135 &self,
136 oldparent: INodeNo,
137 oldname: &OsStr,
138 newparent: INodeNo,
139 newname: &OsStr,
140 ) -> Option<()> {
141 self.table
142 .rename(oldparent.0, oldname, newparent.0, newname)
143 }
144}
145
146macro_rules! get_entry_name {
147 ($s:expr, $ino:expr, $reply:expr) => {
148 if let Some(path) = $s.get_path($ino) {
149 path
150 } else {
151 $reply.error(Errno::EINVAL);
152 return;
153 }
154 };
155}
156
157macro_rules! resolve_from_parent {
158 ($s:expr, $ino:expr, $name:expr, $reply:expr) => {
159 if let Some(path) = $s.table.resolve_from_parent($ino.0, $name.into()) {
160 path
161 } else {
162 $reply.error(Errno::EINVAL);
163 return;
164 }
165 };
166}
167
168macro_rules! get_resolved_path {
169 ($s:expr, $ino:expr, $reply:expr) => {{ get_entry_name!($s, $ino, $reply).with($ino.0) }};
170}
171
172impl<T: Filesystem + Sync + Send + 'static> fuser::Filesystem for FuserNG<T> {
173 fn init(
174 &mut self,
175 req: &fuser::Request,
176 config: &mut fuser::KernelConfig,
177 ) -> Result<(), std::io::Error> {
178 debug!("init");
179 self.target.init(req.info(), config)
180 }
181
182 fn destroy(&mut self) {
183 debug!("destroy");
184 self.target.destroy();
185 }
186
187 fn lookup(
188 &self,
189 req: &fuser::Request,
190 parent: INodeNo,
191 name: &OsStr,
192 reply: fuser::ReplyEntry,
193 ) {
194 let path = resolve_from_parent!(self, parent, name, reply);
195 debug!("lookup: {:?}", path);
196 let path = EntryRef::Lookup(path);
197 match self.target.getattr(req.info(), &path, None) {
201 Ok((ttl, attr)) => {
202 let value = if attr.kind == FileType::Directory {
203 self.add_or_get_dir(parent, name)
204 } else {
205 self.add_or_get_leaf(parent, name)
206 };
207 if let Some((ino, generation)) = value {
208 self.lookup(ino);
209 reply.entry(
210 &ttl,
211 &fuse_fileattr(attr, INodeNo(ino)),
212 Generation(generation),
213 );
214 } else {
215 reply.error(Errno::EINVAL)
216 }
217 }
218 Err(e) => reply.error(e.into()),
219 }
220 }
221
222 fn forget(&self, _req: &fuser::Request, ino: INodeNo, nlookup: u64) {
223 let lookups = self.forget(ino, nlookup);
224 let path = self.get_path(ino).unwrap_or_else(|| {
225 EntryName::new(
226 Arc::new(PathBuf::from(OsStr::new(""))).into(),
227 OsString::from("[unknown]").into(),
228 )
229 });
230 debug!(
231 "forget: inode {} ({:?}) now at {} lookups",
232 ino, path, lookups
233 );
234 }
235
236 fn getattr(
237 &self,
238 req: &fuser::Request,
239 ino: INodeNo,
240 fh: Option<fuser::FileHandle>,
241 reply: fuser::ReplyAttr,
242 ) {
243 let path = get_resolved_path!(self, ino, reply);
244 debug!("getattr: {:?}", path);
245 let path = EntryRef::Resolved(path);
246 match self.target.getattr(req.info(), &path, fh.map(|fh| fh.0)) {
247 Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
248 Err(e) => reply.error(e.into()),
249 }
250 }
251
252 fn setattr(
253 &self,
254 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,
269 ) {
270 let path = get_resolved_path!(self, ino, reply);
271 debug!("setattr: {:?}", path);
272
273 debug!("\tino:\t{:?}", ino);
274 debug!("\tmode:\t{:?}", mode);
275 debug!("\tuid:\t{:?}", uid);
276 debug!("\tgid:\t{:?}", gid);
277 debug!("\tsize:\t{:?}", size);
278 debug!("\tatime:\t{:?}", atime);
279 debug!("\tmtime:\t{:?}", mtime);
280 debug!("\tfh:\t{:?}", fh);
281
282 if let Some(mode) = mode
285 && let Err(e) = self
286 .target
287 .chmod(req.info(), &path, fh.map(|fh| fh.0), mode)
288 {
289 reply.error(e.into());
290 return;
291 }
292
293 if (uid.is_some() || gid.is_some())
294 && let Err(e) = self
295 .target
296 .chown(req.info(), &path, fh.map(|fh| fh.0), uid, gid)
297 {
298 reply.error(e.into());
299 return;
300 }
301
302 if let Some(size) = size
303 && let Err(e) = self
304 .target
305 .truncate(req.info(), &path, fh.map(|fh| fh.0), size)
306 {
307 reply.error(e.into());
308 return;
309 }
310
311 if atime.is_some() || mtime.is_some() {
312 let atime = atime.map(TimeOrNowExt::time);
313 let mtime = mtime.map(TimeOrNowExt::time);
314 if let Err(e) = self
315 .target
316 .utimens(req.info(), &path, fh.map(|fh| fh.0), atime, mtime)
317 {
318 reply.error(e.into());
319 return;
320 }
321 }
322
323 if (crtime.is_some() || chgtime.is_some() || bkuptime.is_some() || flags.is_some())
324 && let Err(e) = self.target.utimens_macos(
325 req.info(),
326 &path,
327 fh.map(|fh| fh.0),
328 crtime,
329 chgtime,
330 bkuptime,
331 flags.map(|flags| flags.bits()),
332 )
333 {
334 reply.error(e.into());
335 return;
336 }
337
338 let path = EntryRef::Resolved(path);
339 match self.target.getattr(req.info(), &path, fh.map(|fh| fh.0)) {
340 Ok((ttl, attr)) => reply.attr(&ttl, &fuse_fileattr(attr, ino)),
341 Err(e) => reply.error(e.into()),
342 }
343 }
344
345 fn readlink(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyData) {
346 let path = get_resolved_path!(self, ino, reply);
347 debug!("readlink: {:?}", path);
348 match self.target.readlink(req.info(), &path) {
349 Ok(data) => reply.data(&data),
350 Err(e) => reply.error(e.into()),
351 }
352 }
353
354 fn mknod(
355 &self,
356 req: &fuser::Request,
357 parent: INodeNo,
358 name: &OsStr,
359 mode: u32,
360 _umask: u32, rdev: u32,
362 reply: fuser::ReplyEntry,
363 ) {
364 let entry = resolve_from_parent!(self, parent, name, reply);
365 debug!("mknod: {:?}", entry);
366 match self.target.mknod(req.info(), &entry, mode, rdev) {
367 Ok((ttl, attr)) => {
368 if let Some((ino, generation)) = self.add_leaf(parent, name) {
369 reply.entry(
370 &ttl,
371 &fuse_fileattr(attr, INodeNo(ino)),
372 Generation(generation),
373 )
374 } else {
375 reply.error(Errno::from_i32(libc::EINVAL))
376 }
377 }
378 Err(e) => reply.error(e.into()),
379 }
380 }
381
382 fn mkdir(
383 &self,
384 req: &fuser::Request,
385 parent: INodeNo,
386 name: &OsStr,
387 mode: u32,
388 _umask: u32, reply: fuser::ReplyEntry,
390 ) {
391 let entry = resolve_from_parent!(self, parent, name, reply);
392 debug!("mkdir: {:?} (mode={:#o})", entry, mode);
393 match self.target.mkdir(req.info(), &entry, mode) {
394 Ok((ttl, attr)) => {
395 if let Some((ino, generation)) = self.add_dir(parent, name) {
396 reply.entry(
397 &ttl,
398 &fuse_fileattr(attr, INodeNo(ino)),
399 Generation(generation),
400 )
401 } else {
402 reply.error(Errno::from_i32(libc::EINVAL))
403 }
404 }
405 Err(e) => reply.error(e.into()),
406 }
407 }
408
409 fn unlink(
410 &self,
411 req: &fuser::Request,
412 parent: INodeNo,
413 name: &OsStr,
414 reply: fuser::ReplyEmpty,
415 ) {
416 let entry = resolve_from_parent!(self, parent, name, reply);
417 debug!("unlink: {:?}", entry);
418 match self.target.unlink(req.info(), &entry) {
419 Ok(()) => {
420 self.inode_unlink(parent, name);
421 reply.ok()
422 }
423 Err(e) => reply.error(e.into()),
424 }
425 }
426
427 fn rmdir(&self, req: &fuser::Request, parent: INodeNo, name: &OsStr, reply: fuser::ReplyEmpty) {
428 let entry = resolve_from_parent!(self, parent, name, reply);
429 debug!("rmdir: {:?}", entry);
430 match self.target.rmdir(req.info(), &entry) {
431 Ok(()) => {
432 self.inode_unlink(parent, name);
433 reply.ok()
434 }
435 Err(e) => reply.error(e.into()),
436 }
437 }
438
439 fn symlink(
440 &self,
441 req: &fuser::Request,
442 parent: INodeNo,
443 name: &OsStr,
444 link: &Path,
445 reply: fuser::ReplyEntry,
446 ) {
447 let entry = resolve_from_parent!(self, parent, name, reply);
448 debug!("symlink: {:?} -> {:?}", entry, link);
449 match self.target.symlink(req.info(), &entry, link) {
450 Ok((ttl, attr)) => {
451 if let Some((ino, generation)) = self.add_leaf(parent, name) {
452 reply.entry(
453 &ttl,
454 &fuse_fileattr(attr, INodeNo(ino)),
455 Generation(generation),
456 )
457 } else {
458 reply.error(Errno::EINVAL)
459 }
460 }
461 Err(e) => reply.error(e.into()),
462 }
463 }
464
465 fn rename(
466 &self,
467 req: &fuser::Request,
468 parent: INodeNo,
469 name: &OsStr,
470 newparent: INodeNo,
471 newname: &OsStr,
472 _flags: RenameFlags, reply: fuser::ReplyEmpty,
474 ) {
475 let entry = resolve_from_parent!(self, parent, name, reply);
476 let new_entry = resolve_from_parent!(self, newparent, newname, reply);
477 debug!("rename: {:?} -> {:?}", entry, new_entry);
478 match self.target.rename(req.info(), &entry, &new_entry) {
479 Ok(()) => {
480 self.inode_rename(parent, name, newparent, newname);
481 reply.ok()
482 }
483 Err(e) => reply.error(e.into()),
484 }
485 }
486
487 fn link(
488 &self,
489 req: &fuser::Request,
490 ino: INodeNo,
491 newparent: INodeNo,
492 newname: &OsStr,
493 reply: fuser::ReplyEntry,
494 ) {
495 let path = get_resolved_path!(self, ino, reply);
496 let new_entry = resolve_from_parent!(self, newparent, newname, reply);
497 debug!("link: {:?} -> {:?}", path, new_entry);
498 match self.target.link(req.info(), &path, &new_entry) {
499 Ok((ttl, attr)) => {
500 if let Some((new_ino, generation)) = self.add_leaf(newparent, newname) {
503 reply.entry(
504 &ttl,
505 &fuse_fileattr(attr, INodeNo(new_ino)),
506 Generation(generation),
507 );
508 } else {
509 reply.error(Errno::EINVAL);
510 }
511 }
512 Err(e) => reply.error(e.into()),
513 }
514 }
515
516 fn open(&self, req: &fuser::Request, ino: INodeNo, flags: OpenFlags, reply: fuser::ReplyOpen) {
517 let path = get_resolved_path!(self, ino, reply);
518 debug!("open: {:?}", path);
519 match self.target.open(req.info(), &path, flags.0 as u32) {
520 Ok((fh, flags)) => reply.opened(FileHandle(fh), FopenFlags::from_bits_retain(flags)),
522 Err(e) => reply.error(e.into()),
523 }
524 }
525
526 fn read(
527 &self,
528 req: &fuser::Request,
529 ino: INodeNo,
530 fh: FileHandle,
531 offset: u64,
532 size: u32,
533 _flags: OpenFlags, _lock_owner: Option<LockOwner>, reply: fuser::ReplyData,
536 ) {
537 let path = get_resolved_path!(self, ino, reply);
538 debug!("read: {:?} {:#x} @ {:#x}", path, size, offset);
539 self.target
540 .read(req.info(), &path, fh.0, offset, size, |result| {
541 match result {
542 Ok(data) => reply.data(data),
543 Err(e) => reply.error(e.into()),
544 }
545 CallbackResult {
546 _private: std::marker::PhantomData {},
547 }
548 });
549 }
550
551 fn write(
552 &self,
553 req: &fuser::Request,
554 ino: INodeNo,
555 fh: FileHandle,
556 offset: u64,
557 data: &[u8],
558 _write_flags: WriteFlags, flags: OpenFlags,
560 _lock_owner: Option<LockOwner>, reply: fuser::ReplyWrite,
562 ) {
563 let path = get_resolved_path!(self, ino, reply);
564 debug!("write: {:?} {:#x} @ {:#x}", path, data.len(), offset);
565 let data_buf = Vec::from(data);
567 match self
568 .target
569 .write(req.info(), &path, fh.0, offset, data_buf, flags.0 as u32)
570 {
571 Ok(written) => reply.written(written),
572 Err(e) => reply.error(e.into()),
573 }
574 }
575
576 fn flush(
577 &self,
578 req: &fuser::Request,
579 ino: INodeNo,
580 fh: FileHandle,
581 lock_owner: LockOwner,
582 reply: fuser::ReplyEmpty,
583 ) {
584 let path = get_resolved_path!(self, ino, reply);
585 debug!("flush: {:?}", path);
586 match self.target.flush(req.info(), &path, fh.0, lock_owner.0) {
587 Ok(()) => reply.ok(),
588 Err(e) => reply.error(e.into()),
589 }
590 }
591
592 fn release(
593 &self,
594 req: &fuser::Request,
595 ino: INodeNo,
596 fh: FileHandle,
597 flags: OpenFlags,
598 lock_owner: Option<LockOwner>,
599 flush: bool,
600 reply: fuser::ReplyEmpty,
601 ) {
602 let path = get_resolved_path!(self, ino, reply);
603 debug!("release: {:?}", path);
604 match self.target.release(
605 req.info(),
606 &path,
607 fh.0,
608 flags.0 as u32,
609 lock_owner.map(|owner| owner.0).unwrap_or(0), flush,
611 ) {
612 Ok(()) => reply.ok(),
613 Err(e) => reply.error(e.into()),
614 }
615 }
616
617 fn fsync(
618 &self,
619 req: &fuser::Request,
620 ino: INodeNo,
621 fh: FileHandle,
622 datasync: bool,
623 reply: fuser::ReplyEmpty,
624 ) {
625 let path = get_resolved_path!(self, ino, reply);
626 debug!("fsync: {:?}", path);
627 match self.target.fsync(req.info(), &path, fh.0, datasync) {
628 Ok(()) => reply.ok(),
629 Err(e) => reply.error(e.into()),
630 }
631 }
632
633 fn opendir(
634 &self,
635 req: &fuser::Request,
636 ino: INodeNo,
637 flags: OpenFlags,
638 reply: fuser::ReplyOpen,
639 ) {
640 let path = get_resolved_path!(self, ino, reply);
641 debug!("opendir: {:?}", path);
642 match self.target.opendir(req.info(), &path, flags.0 as u32) {
643 Ok((fh, flags)) => {
644 let dcache_key = self.directory_cache.write().unwrap().new_entry(fh);
645 reply.opened(FileHandle(dcache_key), FopenFlags::from_bits_retain(flags));
646 }
647 Err(e) => reply.error(e.into()),
648 }
649 }
650
651 fn readdir(
652 &self,
653 req: &fuser::Request,
654 ino: INodeNo,
655 fh: FileHandle,
656 offset: u64,
657 mut reply: fuser::ReplyDirectory,
658 ) {
659 let path = get_resolved_path!(self, ino, reply);
660 debug!("readdir: {:?} @ {}", path, offset);
661
662 let parent_inode = if ino == INodeNo::ROOT {
663 ino
664 } else {
665 match self.get_parent_inode(ino) {
666 Some(inode) => INodeNo(inode),
667 None => {
668 error!("readdir: unable to get parent inode for {:?}", path);
669 reply.error(Errno::EIO);
670 return;
671 }
672 }
673 };
674
675 let cached_entries = {
676 self.directory_cache
677 .write()
678 .unwrap()
679 .get_mut(fh.0)
680 .entries
681 .clone()
682 };
683
684 let entries = match cached_entries {
685 Some(entries) => entries,
686 None => {
687 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
688 debug!("entries not yet fetched; requesting with fh {}", real_fh);
689 match self.target.readdir(req.info(), &path, real_fh) {
690 Ok(entries) => {
691 self.directory_cache.write().unwrap().get_mut(fh.0).entries =
692 Some(entries.clone());
693 entries
694 }
695 Err(e) => {
696 reply.error(e.into());
697 return;
698 }
699 }
700 }
701 };
702
703 debug!("directory has {} entries", entries.len());
704
705 for (index, entry) in entries.iter().skip(offset as usize).enumerate() {
706 let entry_inode = if entry.name == Path::new(".") {
707 ino
708 } else if entry.name == Path::new("..") {
709 parent_inode
710 } else {
711 INodeNo(!1u64)
715 };
716
717 debug!(
718 "readdir: adding entry #{}, {:?}",
719 offset + index as u64,
720 entry.name
721 );
722
723 let buffer_full: bool = reply.add(
724 entry_inode,
725 offset + index as u64 + 1,
726 entry.kind,
727 entry.name.as_os_str(),
728 );
729
730 if buffer_full {
731 debug!("readdir: reply buffer is full");
732 break;
733 }
734 }
735
736 reply.ok();
737 }
738
739 fn releasedir(
740 &self,
741 req: &fuser::Request,
742 ino: INodeNo,
743 fh: FileHandle,
744 flags: OpenFlags,
745 reply: fuser::ReplyEmpty,
746 ) {
747 let path = get_resolved_path!(self, ino, reply);
748 debug!("releasedir: {:?}", path);
749 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
750 match self
751 .target
752 .releasedir(req.info(), &path, real_fh, flags.0 as u32)
753 {
754 Ok(()) => reply.ok(),
755 Err(e) => reply.error(e.into()),
756 }
757 self.directory_cache.write().unwrap().delete(fh.0);
758 }
759
760 fn fsyncdir(
761 &self,
762 req: &fuser::Request,
763 ino: INodeNo,
764 fh: FileHandle,
765 datasync: bool,
766 reply: fuser::ReplyEmpty,
767 ) {
768 let path = get_resolved_path!(self, ino, reply);
769 debug!("fsyncdir: {:?} (datasync: {:?})", path, datasync);
770 let real_fh = self.directory_cache.read().unwrap().real_fh(fh.0);
771 match self.target.fsyncdir(req.info(), &path, real_fh, datasync) {
772 Ok(()) => reply.ok(),
773 Err(e) => reply.error(e.into()),
774 }
775 }
776
777 fn statfs(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyStatfs) {
778 let path = get_resolved_path!(self, ino, reply);
779 debug!("statfs: {:?}", path);
780 match self.target.statfs(req.info(), &path) {
781 Ok(statfs) => reply.statfs(
782 statfs.blocks,
783 statfs.bfree,
784 statfs.bavail,
785 statfs.files,
786 statfs.ffree,
787 statfs.bsize,
788 statfs.namelen,
789 statfs.frsize,
790 ),
791 Err(e) => reply.error(e.into()),
792 }
793 }
794
795 fn setxattr(
796 &self,
797 req: &fuser::Request,
798 ino: INodeNo,
799 name: &OsStr,
800 value: &[u8],
801 flags: i32,
802 position: u32,
803 reply: fuser::ReplyEmpty,
804 ) {
805 let path = get_resolved_path!(self, ino, reply);
806 debug!(
807 "setxattr: {:?} {:?} ({} bytes, flags={:#x}, pos={:#x}",
808 path,
809 name,
810 value.len(),
811 flags,
812 position
813 );
814 match self
815 .target
816 .setxattr(req.info(), &path, name, value, flags as u32, position)
817 {
818 Ok(()) => reply.ok(),
819 Err(e) => reply.error(e.into()),
820 }
821 }
822
823 fn getxattr(
824 &self,
825 req: &fuser::Request,
826 ino: INodeNo,
827 name: &OsStr,
828 size: u32,
829 reply: fuser::ReplyXattr,
830 ) {
831 let path = get_resolved_path!(self, ino, reply);
832 debug!("getxattr: {:?} {:?}", path, name);
833 match self.target.getxattr(req.info(), &path, name, size) {
834 Ok(Xattr::Size(size)) => {
835 debug!("getxattr: sending size {}", size);
836 reply.size(size)
837 }
838 Ok(Xattr::Data(vec)) => {
839 debug!("getxattr: sending {} bytes", vec.len());
840 reply.data(&vec)
841 }
842 Err(e) => {
843 debug!("getxattr: error {}", e);
844 reply.error(e.into())
845 }
846 }
847 }
848
849 fn listxattr(&self, req: &fuser::Request, ino: INodeNo, size: u32, reply: fuser::ReplyXattr) {
850 let path = get_resolved_path!(self, ino, reply);
851 debug!("listxattr: {:?}", path);
852 match self.target.listxattr(req.info(), &path, size) {
853 Ok(Xattr::Size(size)) => {
854 debug!("listxattr: sending size {}", size);
855 reply.size(size)
856 }
857 Ok(Xattr::Data(vec)) => {
858 debug!("listxattr: sending {} bytes", vec.len());
859 reply.data(&vec)
860 }
861 Err(e) => reply.error(e.into()),
862 }
863 }
864
865 fn removexattr(
866 &self,
867 req: &fuser::Request,
868 ino: INodeNo,
869 name: &OsStr,
870 reply: fuser::ReplyEmpty,
871 ) {
872 let path = get_resolved_path!(self, ino, reply);
873 debug!("removexattr: {:?}, {:?}", path, name);
874 match self.target.removexattr(req.info(), &path, name) {
875 Ok(()) => reply.ok(),
876 Err(e) => reply.error(e.into()),
877 }
878 }
879
880 fn access(
881 &self,
882 req: &fuser::Request,
883 ino: INodeNo,
884 mask: AccessFlags,
885 reply: fuser::ReplyEmpty,
886 ) {
887 let path = get_resolved_path!(self, ino, reply);
888 debug!("access: {:?}, mask={:#o}", path, mask.bits());
889 match self.target.access(req.info(), &path, mask.bits() as u32) {
890 Ok(()) => reply.ok(),
891 Err(e) => reply.error(e.into()),
892 }
893 }
894
895 fn create(
896 &self,
897 req: &fuser::Request,
898 parent: INodeNo,
899 name: &OsStr,
900 mode: u32,
901 _umask: u32, flags: i32,
903 reply: fuser::ReplyCreate,
904 ) {
905 let (created, path, generation) = match self.create_or_get_leaf(parent, name) {
906 Some((created, ino, generation)) => (
907 created,
908 get_resolved_path!(self, INodeNo(ino), reply),
909 generation,
910 ),
911 _ => {
912 reply.error(Errno::EINVAL);
913 return;
914 }
915 };
916 debug!("create: {:?} (mode={:#o}, flags={:#x})", path, mode, flags);
917 match self.target.create(req.info(), &path, mode, flags as u32) {
918 Ok(create) => {
919 if !created {
920 self.lookup(path.ino());
921 }
922 let attr = fuse_fileattr(create.attr, INodeNo(path.ino()));
923 reply.created(
924 &create.ttl,
925 &attr,
926 Generation(generation),
927 FileHandle(create.fh),
928 FopenFlags::from_bits_retain(create.flags),
929 );
930 }
931 Err(e) => {
932 if created {
933 self.inode_unlink(parent, name);
934 self.forget(INodeNo(path.ino()), 1);
935 }
936 reply.error(e.into())
937 }
938 }
939 }
940
941 #[cfg(target_os = "macos")]
948 fn setvolname(&self, req: &fuser::Request, name: &OsStr, reply: fuser::ReplyEmpty) {
949 debug!("setvolname: {:?}", name);
950 match self.target.setvolname(req.info(), name) {
951 Ok(()) => reply.ok(),
952 Err(e) => reply.error(e.into()),
953 }
954 }
955
956 #[cfg(target_os = "macos")]
959 fn getxtimes(&self, req: &fuser::Request, ino: INodeNo, reply: fuser::ReplyXTimes) {
960 let path = get_resolved_path!(self, ino, reply);
961 debug!("getxtimes: {:?}", path);
962 match self.target.getxtimes(req.info(), &path) {
963 Ok(xtimes) => {
964 reply.xtimes(xtimes.bkuptime, xtimes.crtime);
965 }
966 Err(e) => reply.error(e.into()),
967 }
968 }
969}