fuse_rs/
operations.rs

1extern crate once_cell;
2
3use libfuse_sys as ffi;
4
5use crate::fs::{
6    ConnectionInfo, FileInfo, Filesystem, FlushFileInfo, OpenFileInfo, ReleaseFileInfo,
7    WriteFileInfo,
8};
9use ffi::fuse;
10use libc::{c_char, c_int, gid_t, mode_t, off_t, stat, uid_t};
11use nix::{
12    errno::Errno::{self, EINVAL},
13    sys::stat::Mode,
14    unistd::{AccessFlags, Gid, Uid},
15};
16use once_cell::sync::OnceCell;
17use std::{
18    ffi::{c_void, CStr, CString, OsString},
19    ops::{Deref, DerefMut},
20    os::unix::ffi::{OsStrExt, OsStringExt},
21    path::Path,
22    sync::{RwLock, RwLockReadGuard},
23};
24use Errno::ENOSYS;
25
26static mut FS: OnceCell<RwLock<FilesystemImpl>> = OnceCell::new();
27
28#[derive(Debug)]
29pub enum Error {
30    AlreadyMountedError,
31    MountError,
32}
33
34pub fn mount<P, I>(
35    name: OsString,
36    mountpoint: P,
37    filesystem: &'static mut dyn Filesystem,
38    args: I,
39) -> Result<(), Error>
40where
41    P: AsRef<Path>,
42    I: IntoIterator<Item = OsString>,
43{
44    unsafe {
45        setup_fs(filesystem)?;
46
47        let mut argv = vec![
48            CString::from_vec_unchecked(name.into_vec()).into_raw(),
49            CString::from_vec_unchecked(mountpoint.as_ref().as_os_str().to_os_string().into_vec())
50                .into_raw(),
51        ];
52
53        argv.append(
54            &mut args
55                .into_iter()
56                .map(|a| CString::from_vec_unchecked(a.into_vec()).into_raw())
57                .collect::<Vec<*mut c_char>>(),
58        );
59
60        match fuse::fuse_main(
61            argv.len() as c_int,
62            argv.as_mut_ptr(),
63            &build_operations(),
64            std::ptr::null_mut(),
65        ) {
66            0 => Ok(()),
67            _ => Err(Error::MountError),
68        }
69    }
70}
71
72unsafe extern "C" fn getattr(p: *const c_char, stat: *mut stat) -> c_int {
73    if stat.is_null() {
74        return negate_errno(EINVAL);
75    }
76
77    match build_path(p) {
78        Ok(path) => match get_fs().metadata(path) {
79            Ok(file_stat) => file_stat.fill(stat),
80            Err(err) => negate_errno(err),
81        },
82        Err(err) => err,
83    }
84}
85
86unsafe extern "C" fn readlink(p: *const c_char, buffer: *mut c_char, len: usize) -> c_int {
87    if buffer.is_null() {
88        return negate_errno(EINVAL);
89    }
90
91    match build_path(p) {
92        Ok(path) => match get_fs().read_link(path) {
93            Ok(path) => {
94                let cstr = CString::new(&path.as_bytes()[..len - 1]).unwrap();
95                std::ptr::copy_nonoverlapping(
96                    cstr.into_bytes_with_nul().as_ptr() as *const _,
97                    buffer,
98                    len,
99                );
100                0
101            }
102            Err(err) => negate_errno(err),
103        },
104        Err(err) => err,
105    }
106}
107
108unsafe extern "C" fn mkdir(p: *const c_char, mode: mode_t) -> c_int {
109    match build_path(p) {
110        // according to fuse.h, it can send S_IFDIR so we don't check the bits here.
111        Ok(path) => unit_op!(get_mut_fs().create_dir(path, Mode::from_bits_unchecked(mode))),
112        Err(err) => err,
113    }
114}
115
116unsafe extern "C" fn unlink(p: *const c_char) -> c_int {
117    match build_path(p) {
118        Ok(path) => unit_op!(get_mut_fs().remove_file(path)),
119        Err(err) => err,
120    }
121}
122
123unsafe extern "C" fn rmdir(p: *const c_char) -> c_int {
124    match build_path(p) {
125        Ok(path) => unit_op!(get_mut_fs().remove_dir(path)),
126        Err(err) => err,
127    }
128}
129
130unsafe extern "C" fn symlink(src: *const c_char, dst: *const c_char) -> c_int {
131    match build_path(src) {
132        Ok(src) => match build_path(dst) {
133            Ok(dst) => unit_op!(get_mut_fs().symlink(src, dst)),
134            Err(err) => err,
135        },
136        Err(err) => err,
137    }
138}
139
140unsafe extern "C" fn rename(from: *const c_char, to: *const c_char) -> c_int {
141    match build_path(from) {
142        Ok(from) => match build_path(to) {
143            Ok(to) => unit_op!(get_mut_fs().rename(from, to)),
144            Err(err) => err,
145        },
146        Err(err) => err,
147    }
148}
149
150unsafe extern "C" fn link(src: *const c_char, dst: *const c_char) -> c_int {
151    match build_path(src) {
152        Ok(src) => match build_path(dst) {
153            Ok(dst) => unit_op!(get_mut_fs().hard_link(src, dst)),
154            Err(err) => err,
155        },
156        Err(err) => err,
157    }
158}
159
160unsafe extern "C" fn chmod(p: *const c_char, mode: mode_t) -> c_int {
161    match build_path(p) {
162        Ok(path) => unit_op!(get_mut_fs().set_permissions(path, Mode::from_bits_unchecked(mode))),
163        Err(err) => err,
164    }
165}
166
167unsafe extern "C" fn chown(p: *const c_char, uid: uid_t, gid: gid_t) -> c_int {
168    match build_path(p) {
169        Ok(path) => unit_op!(get_mut_fs().set_owner(path, Uid::from_raw(uid), Gid::from_raw(gid))),
170        Err(err) => err,
171    }
172}
173
174unsafe extern "C" fn truncate(p: *const c_char, len: off_t) -> c_int {
175    match build_path(p) {
176        Ok(path) => unit_op!(get_mut_fs().set_len(path, len as _)),
177        Err(err) => err,
178    }
179}
180
181unsafe extern "C" fn open(p: *const c_char, fi: *mut fuse::fuse_file_info) -> c_int {
182    if fi.is_null() {
183        return negate_errno(EINVAL);
184    }
185
186    let mut open_fi = OpenFileInfo::default();
187    match build_path(p) {
188        Ok(path) => match get_mut_fs().open(path, &mut open_fi) {
189            Ok(_) => open_fi.file_info().fill(fi),
190            Err(err) => negate_errno(err),
191        },
192        Err(err) => err,
193    }
194}
195
196unsafe extern "C" fn read(
197    p: *const c_char,
198    buffer: *mut c_char,
199    len: usize,
200    offset: off_t,
201    fi: *mut fuse::fuse_file_info,
202) -> c_int {
203    if buffer.is_null() {
204        return negate_errno(EINVAL);
205    }
206
207    let mut buf = Vec::with_capacity(len);
208    buf.set_len(len);
209    match build_path(p) {
210        Ok(path) => match get_mut_fs().read(path, &mut buf, offset as _, FileInfo::from_raw(fi)) {
211            Ok(length) => {
212                let len = len.min(length);
213                buf.split_off(len).clear();
214                std::ptr::copy_nonoverlapping(buf.as_ptr() as *const _, buffer, len);
215                len as _
216            }
217            Err(err) => negate_errno(err),
218        },
219        Err(err) => err,
220    }
221}
222
223unsafe extern "C" fn write(
224    p: *const c_char,
225    buffer: *const c_char,
226    len: usize,
227    offset: off_t,
228    fi: *mut fuse::fuse_file_info,
229) -> c_int {
230    if buffer.is_null() {
231        return negate_errno(EINVAL);
232    }
233
234    let buf: &[u8] = std::slice::from_raw_parts(buffer as _, len);
235    let mut write_fi = WriteFileInfo::from_file_info(FileInfo::from_raw(fi));
236    match build_path(p) {
237        Ok(path) => match get_mut_fs().write(path, &buf[..len], offset as _, &mut write_fi) {
238            Ok(len) => {
239                write_fi.file_info().fill(fi);
240                len as _
241            }
242            Err(err) => negate_errno(err),
243        },
244        Err(err) => err,
245    }
246}
247
248unsafe extern "C" fn statfs(p: *const c_char, stbuf: *mut libc::statvfs) -> c_int {
249    if stbuf.is_null() {
250        return negate_errno(EINVAL);
251    }
252
253    match build_path(p) {
254        Ok(path) => match get_fs().statfs(path) {
255            Ok(stats) => {
256                let _ = std::ptr::replace(stbuf, stats);
257                0
258            }
259            Err(err) => negate_errno(err),
260        },
261        Err(err) => err,
262    }
263}
264
265unsafe extern "C" fn flush(p: *const c_char, fi: *mut fuse::fuse_file_info) -> c_int {
266    if fi.is_null() {
267        return negate_errno(EINVAL);
268    }
269
270    let mut flush_fi = FlushFileInfo::from_file_info(FileInfo::from_raw(fi));
271    match build_path(p) {
272        Ok(path) => match get_mut_fs().flush(path, &mut flush_fi) {
273            Ok(_) => flush_fi.file_info().fill(fi),
274            Err(err) => negate_errno(err),
275        },
276        Err(err) => err,
277    }
278}
279
280unsafe extern "C" fn release(p: *const c_char, fi: *mut fuse::fuse_file_info) -> c_int {
281    if fi.is_null() {
282        return negate_errno(EINVAL);
283    }
284
285    let mut release_fi = ReleaseFileInfo::from_file_info(FileInfo::from_raw(fi));
286    match build_path(p) {
287        Ok(path) => match get_mut_fs().release(path, &mut release_fi) {
288            Ok(_) => release_fi.file_info().fill(fi),
289            Err(err) => negate_errno(err),
290        },
291        Err(err) => err,
292    }
293}
294
295unsafe extern "C" fn fsync(
296    p: *const c_char,
297    data_sync: c_int,
298    fi: *mut fuse::fuse_file_info,
299) -> c_int {
300    if fi.is_null() {
301        return negate_errno(EINVAL);
302    }
303
304    match build_path(p) {
305        Ok(path) => {
306            if data_sync == 1 {
307                unit_op!(get_mut_fs().sync_data(path, FileInfo::from_raw(fi)))
308            } else {
309                unit_op!(get_mut_fs().sync_all(path, FileInfo::from_raw(fi)))
310            }
311        }
312        Err(err) => err,
313    }
314}
315
316unsafe extern "C" fn opendir(p: *const c_char, fi: *mut fuse::fuse_file_info) -> c_int {
317    if fi.is_null() {
318        return negate_errno(EINVAL);
319    }
320
321    let mut open_fi = OpenFileInfo::from_file_info(FileInfo::from_raw(fi));
322    match build_path(p) {
323        Ok(path) => match get_mut_fs().open_dir(path, &mut open_fi) {
324            Ok(_) => open_fi.file_info().fill(fi),
325            Err(err) => negate_errno(err),
326        },
327        Err(err) => err,
328    }
329}
330
331unsafe extern "C" fn readdir(
332    p: *const c_char,
333    buf: *mut c_void,
334    filler: fuse::fuse_fill_dir_t,
335    offset: off_t,
336    fi: *mut fuse::fuse_file_info,
337) -> c_int {
338    match build_path(p) {
339        Ok(path) => match get_mut_fs().read_dir(path, offset as _, FileInfo::from_raw(fi)) {
340            Ok(entries) => match filler {
341                Some(f) => {
342                    for e in entries {
343                        let stat = if let Some(s) = e.metadata {
344                            s.as_raw()
345                        } else {
346                            std::ptr::null()
347                        };
348                        let res = f(
349                            buf,
350                            CString::from_vec_unchecked(e.name.into_vec()).as_ptr(),
351                            stat,
352                            e.offset.unwrap_or(0) as _,
353                        );
354                        if res != 0 {
355                            return res;
356                        }
357                    }
358                    0
359                }
360                None => 0,
361            },
362            Err(err) => negate_errno(err),
363        },
364        Err(err) => err,
365    }
366}
367
368unsafe extern "C" fn fsyncdir(
369    p: *const c_char,
370    data_sync: c_int,
371    fi: *mut fuse::fuse_file_info,
372) -> c_int {
373    if fi.is_null() {
374        return negate_errno(EINVAL);
375    }
376
377    match build_path(p) {
378        Ok(path) => {
379            if data_sync == 1 {
380                unit_op!(get_mut_fs().sync_dir_data(path, FileInfo::from_raw(fi)))
381            } else {
382                unit_op!(get_mut_fs().sync_dir_all(path, FileInfo::from_raw(fi)))
383            }
384        }
385        Err(err) => err,
386    }
387}
388
389unsafe extern "C" fn releasedir(p: *const c_char, fi: *mut fuse::fuse_file_info) -> c_int {
390    if fi.is_null() {
391        return negate_errno(EINVAL);
392    }
393
394    let mut release_fi = ReleaseFileInfo::from_file_info(FileInfo::from_raw(fi));
395    match build_path(p) {
396        Ok(path) => match get_mut_fs().release_dir(path, &mut release_fi) {
397            Ok(_) => release_fi.file_info().fill(fi),
398            Err(err) => negate_errno(err),
399        },
400        Err(err) => err,
401    }
402}
403
404unsafe extern "C" fn init(conn: *mut fuse::fuse_conn_info) -> *mut c_void {
405    let null_ptr = std::ptr::null_mut();
406    if conn.is_null() {
407        return null_ptr;
408    }
409
410    let mut conn_info = ConnectionInfo::from_raw(conn);
411    if get_mut_fs().init(&mut conn_info).is_ok() {
412        conn_info.fill(conn)
413    }
414
415    null_ptr
416}
417
418unsafe extern "C" fn destroy(_private_data: *mut c_void) {
419    unit_op!(get_mut_fs().destroy());
420}
421
422unsafe extern "C" fn access(p: *const c_char, flags: c_int) -> c_int {
423    match build_path(p) {
424        Ok(path) => match get_fs().check_permissions(path, AccessFlags::from_bits_unchecked(flags))
425        {
426            Ok(b) => b as c_int,
427            Err(err) => negate_errno(err),
428        },
429        Err(err) => err,
430    }
431}
432
433unsafe extern "C" fn create(
434    p: *const c_char,
435    mode: mode_t,
436    fi: *mut fuse::fuse_file_info,
437) -> c_int {
438    if fi.is_null() {
439        return negate_errno(EINVAL);
440    }
441
442    let mut open_fi = OpenFileInfo::from_file_info(FileInfo::from_raw(fi));
443    match build_path(p) {
444        Ok(path) => {
445            match get_mut_fs().create(path, Mode::from_bits_unchecked(mode), &mut open_fi) {
446                Ok(_) => open_fi.file_info().fill(fi),
447                Err(err) => negate_errno(err),
448            }
449        }
450        Err(err) => err,
451    }
452}
453
454unsafe extern "C" fn ftruncate(
455    p: *const c_char,
456    len: off_t,
457    fi: *mut fuse::fuse_file_info,
458) -> c_int {
459    if fi.is_null() {
460        return negate_errno(EINVAL);
461    }
462
463    match build_path(p) {
464        Ok(path) => unit_op!(get_mut_fs().ftruncate(path, len as _, FileInfo::from_raw(fi))),
465        Err(err) => err,
466    }
467}
468
469unsafe extern "C" fn fgetattr(
470    p: *const c_char,
471    stat: *mut stat,
472    fi: *mut fuse::fuse_file_info,
473) -> c_int {
474    if fi.is_null() || stat.is_null() {
475        return negate_errno(EINVAL);
476    }
477
478    match build_path(p) {
479        Ok(path) => match get_fs().fmetadata(path, FileInfo::from_raw(fi)) {
480            Ok(file_stat) => file_stat.fill(stat),
481            Err(err) => negate_errno(err),
482        },
483        Err(err) => err,
484    }
485}
486
487unsafe extern "C" fn lock(
488    _arg1: *const c_char,
489    _arg2: *mut fuse::fuse_file_info,
490    _cmd: c_int,
491    _arg3: *mut libc::flock,
492) -> c_int {
493    negate_errno(ENOSYS)
494}
495
496unsafe fn build_path<'a>(p: *const c_char) -> Result<&'a Path, c_int> {
497    if p.is_null() {
498        return Err(negate_errno(EINVAL));
499    }
500
501    CStr::from_ptr(p)
502        .to_str()
503        .map(|p| Path::new(p))
504        .map_err(|_| negate_errno(EINVAL))
505}
506
507fn build_operations() -> fuse::fuse_operations {
508    fuse::fuse_operations {
509        #[cfg(any(feature = "full", feature = "getattr"))]
510        getattr: Some(getattr),
511        #[cfg(any(feature = "full", feature = "readlink"))]
512        readlink: Some(readlink),
513        #[cfg(any(feature = "full", feature = "mkdir"))]
514        mkdir: Some(mkdir),
515        #[cfg(any(feature = "full", feature = "unlink"))]
516        unlink: Some(unlink),
517        #[cfg(any(feature = "full", feature = "rmdir"))]
518        rmdir: Some(rmdir),
519        #[cfg(any(feature = "full", feature = "symlink"))]
520        symlink: Some(symlink),
521        #[cfg(any(feature = "full", feature = "rename"))]
522        rename: Some(rename),
523        #[cfg(any(feature = "full", feature = "link"))]
524        link: Some(link),
525        #[cfg(any(feature = "full", feature = "chmod"))]
526        chmod: Some(chmod),
527        #[cfg(any(feature = "full", feature = "chown"))]
528        chown: Some(chown),
529        #[cfg(any(feature = "full", feature = "truncate"))]
530        truncate: Some(truncate),
531        #[cfg(any(feature = "full", feature = "open"))]
532        open: Some(open),
533        #[cfg(any(feature = "full", feature = "read"))]
534        read: Some(read),
535        #[cfg(any(feature = "full", feature = "write"))]
536        write: Some(write),
537        #[cfg(any(feature = "full", feature = "statfs"))]
538        statfs: Some(statfs), // not fine
539        #[cfg(any(feature = "full", feature = "flush"))]
540        flush: Some(flush),
541        #[cfg(any(feature = "full", feature = "release"))]
542        release: Some(release),
543        #[cfg(any(feature = "full", feature = "fsync"))]
544        fsync: Some(fsync),
545        #[cfg(any(feature = "full", feature = "opendir"))]
546        opendir: Some(opendir), // not fine
547        #[cfg(any(feature = "full", feature = "readdir"))]
548        readdir: Some(readdir),
549        #[cfg(any(feature = "full", feature = "fsyncdir"))]
550        fsyncdir: Some(fsyncdir),
551        #[cfg(any(feature = "full", feature = "releasedir"))]
552        releasedir: Some(releasedir),
553        #[cfg(any(feature = "full", feature = "init"))]
554        init: Some(init),
555        #[cfg(any(feature = "full", feature = "destroy"))]
556        destroy: Some(destroy),
557        #[cfg(any(feature = "full", feature = "access"))]
558        access: Some(access),
559        #[cfg(any(feature = "full", feature = "create"))]
560        create: Some(create), // ok
561        #[cfg(any(feature = "full", feature = "ftruncate"))]
562        ftruncate: Some(ftruncate),
563        #[cfg(any(feature = "full", feature = "fgetattr"))]
564        fgetattr: Some(fgetattr),
565        #[cfg(any(feature = "full", feature = "lock"))]
566        lock: Some(lock),
567        // TODO: lock, utimens, bmap, ext_metadata
568        ..Default::default()
569    }
570}
571
572fn negate_errno(err: Errno) -> c_int {
573    let e = err as c_int;
574    if e < 0 {
575        e
576    } else {
577        -e
578    }
579}
580
581unsafe fn setup_fs(fs: &'static mut dyn Filesystem) -> Result<(), Error> {
582    FS.set(RwLock::new(FilesystemImpl(fs)))
583        .map_err(|_| Error::AlreadyMountedError)
584}
585
586unsafe fn get_fs<'a>() -> RwLockReadGuard<'a, FilesystemImpl> {
587    FS.get()
588        .expect("fetching FS")
589        .read()
590        .expect("acquiring read lock")
591}
592
593unsafe fn get_mut_fs<'a>() -> &'a mut FilesystemImpl {
594    FS.get_mut()
595        .expect("fetching mut FS")
596        .get_mut()
597        .expect("acquiring mut lock")
598}
599
600struct FilesystemImpl(&'static mut dyn Filesystem);
601
602impl Deref for FilesystemImpl {
603    type Target = &'static mut dyn Filesystem;
604
605    fn deref(&self) -> &Self::Target {
606        &self.0
607    }
608}
609
610impl DerefMut for FilesystemImpl {
611    fn deref_mut(&mut self) -> &mut Self::Target {
612        &mut self.0
613    }
614}
615
616#[cfg(test)]
617mod tests {
618    use super::*;
619    use crate::{
620        fs::{CapabilityFlags, DirEntry, FileStat},
621        Result,
622    };
623    use nix::errno::Errno::{EFAULT, ENOENT};
624    use std::io::Read;
625    use std::{ffi::OsStr, mem};
626    use Errno::EACCES;
627
628    static mut DUMMY_FS: DummyFS = DummyFS {};
629    const FOO_PATH: &str = "/path/to/foo.txt";
630    const BAR_PATH: &str = "/path/to/bar.xyz";
631
632    #[test]
633    fn test_build_path() {
634        assert_eq!(
635            unsafe { build_path(std::ptr::null()) }.err(),
636            Some(negate_errno(EINVAL))
637        );
638
639        let p = CString::new(FOO_PATH).unwrap();
640        let ptr = p.as_ptr();
641        assert_eq!(unsafe { build_path(ptr) }.ok(), Some(Path::new(FOO_PATH)));
642    }
643
644    #[test]
645    fn test_getattr() {
646        unsafe { setup_test_fs(&mut DUMMY_FS) };
647
648        let p = CString::new(FOO_PATH).unwrap();
649        let ptr = p.as_ptr();
650        let stat = std::ptr::null_mut();
651        assert_eq!(unsafe { getattr(ptr, stat) }, negate_errno(EINVAL));
652
653        let p = CString::new(BAR_PATH).unwrap();
654        let ptr = p.as_ptr();
655        let mut stat = mem::MaybeUninit::uninit();
656        assert_eq!(
657            unsafe { getattr(ptr, stat.as_mut_ptr()) },
658            negate_errno(ENOENT)
659        );
660
661        let p = CString::new(FOO_PATH).unwrap();
662        let ptr = p.as_ptr();
663        let mut stat = mem::MaybeUninit::uninit();
664        unsafe {
665            assert_eq!(getattr(ptr, stat.as_mut_ptr()), 0);
666
667            let stat = stat.assume_init();
668            assert_eq!(stat.st_nlink, 3);
669        };
670    }
671
672    #[test]
673    fn test_readlink() {
674        unsafe { setup_test_fs(&mut DUMMY_FS) };
675
676        let len = 13; // BAR_PATH - extension + nul byte
677        let p = CString::new(FOO_PATH).unwrap();
678        let ptr = p.as_ptr();
679        assert_eq!(
680            unsafe { readlink(ptr, std::ptr::null_mut(), len) },
681            negate_errno(EINVAL)
682        );
683
684        let p = CString::new(BAR_PATH).unwrap();
685        let ptr = p.as_ptr();
686        let mut buf = mem::MaybeUninit::uninit();
687        assert_eq!(
688            unsafe { readlink(ptr, buf.as_mut_ptr(), len) },
689            negate_errno(ENOENT)
690        );
691
692        let p = CString::new(FOO_PATH).unwrap();
693        let ptr = p.as_ptr();
694        let mut vec = Vec::with_capacity(len);
695        unsafe {
696            vec.set_len(len);
697            let buf = CString::from_vec_unchecked(vec).into_raw();
698            assert_eq!(readlink(ptr, buf, len), 0);
699
700            let got = CString::from_raw(buf);
701            assert_eq!(got.to_bytes_with_nul(), b"/path/to/bar\0");
702        };
703    }
704
705    #[test]
706    fn test_mkdir() {
707        unsafe { setup_test_fs(&mut DUMMY_FS) };
708
709        let p = CString::new(BAR_PATH).unwrap();
710        let ptr = p.as_ptr();
711        let mode: libc::mode_t = libc::S_IRWXU | 0o755;
712        assert_eq!(unsafe { mkdir(ptr, mode) }, negate_errno(ENOENT));
713
714        let p = CString::new(FOO_PATH).unwrap();
715        let ptr = p.as_ptr();
716        assert_eq!(unsafe { mkdir(ptr, mode) }, 0);
717    }
718
719    #[test]
720    fn test_unlink() {
721        unsafe { setup_test_fs(&mut DUMMY_FS) };
722
723        let p = CString::new(BAR_PATH).unwrap();
724        let ptr = p.as_ptr();
725        assert_eq!(unsafe { unlink(ptr) }, negate_errno(ENOENT));
726
727        let p = CString::new(FOO_PATH).unwrap();
728        let ptr = p.as_ptr();
729        assert_eq!(unsafe { unlink(ptr) }, 0);
730    }
731
732    #[test]
733    fn test_rmdir() {
734        unsafe { setup_test_fs(&mut DUMMY_FS) };
735
736        let p = CString::new(BAR_PATH).unwrap();
737        let ptr = p.as_ptr();
738        assert_eq!(unsafe { rmdir(ptr) }, negate_errno(ENOENT));
739
740        let p = CString::new(FOO_PATH).unwrap();
741        let ptr = p.as_ptr();
742        assert_eq!(unsafe { rmdir(ptr) }, 0);
743    }
744
745    #[test]
746    fn test_symlink() {
747        unsafe { setup_test_fs(&mut DUMMY_FS) };
748
749        let src = CString::new(BAR_PATH).unwrap();
750        let src_ptr = src.as_ptr();
751        let dst = CString::new(FOO_PATH).unwrap();
752        let dst_ptr = dst.as_ptr();
753        assert_eq!(unsafe { symlink(src_ptr, dst_ptr) }, negate_errno(ENOENT));
754
755        let src = CString::new(FOO_PATH).unwrap();
756        let src_ptr = src.as_ptr();
757        assert_eq!(unsafe { symlink(src_ptr, dst_ptr) }, 0);
758    }
759
760    #[test]
761    fn test_rename() {
762        unsafe { setup_test_fs(&mut DUMMY_FS) };
763
764        let from = CString::new(BAR_PATH).unwrap();
765        let from_ptr = from.as_ptr();
766        let to = CString::new(FOO_PATH).unwrap();
767        let to_ptr = to.as_ptr();
768        assert_eq!(unsafe { rename(from_ptr, to_ptr) }, negate_errno(ENOENT));
769
770        let from = CString::new(FOO_PATH).unwrap();
771        let from_ptr = from.as_ptr();
772        assert_eq!(unsafe { rename(from_ptr, to_ptr) }, 0);
773    }
774
775    #[test]
776    fn test_link() {
777        unsafe { setup_test_fs(&mut DUMMY_FS) };
778
779        let src = CString::new(BAR_PATH).unwrap();
780        let src_ptr = src.as_ptr();
781        let dst = CString::new(FOO_PATH).unwrap();
782        let dst_ptr = dst.as_ptr();
783        assert_eq!(unsafe { link(src_ptr, dst_ptr) }, negate_errno(ENOENT));
784
785        let src = CString::new(FOO_PATH).unwrap();
786        let src_ptr = src.as_ptr();
787        assert_eq!(unsafe { link(src_ptr, dst_ptr) }, 0);
788    }
789
790    #[test]
791    fn test_chmod() {
792        unsafe { setup_test_fs(&mut DUMMY_FS) };
793
794        let p = CString::new(BAR_PATH).unwrap();
795        let ptr = p.as_ptr();
796        let mode: libc::mode_t = libc::S_IRWXO | 0o755;
797        assert_eq!(unsafe { chmod(ptr, mode) }, negate_errno(ENOENT));
798
799        let p = CString::new(FOO_PATH).unwrap();
800        let ptr = p.as_ptr();
801        assert_eq!(unsafe { chmod(ptr, mode) }, 0);
802    }
803
804    #[test]
805    fn test_chown() {
806        unsafe { setup_test_fs(&mut DUMMY_FS) };
807
808        let uid = 123;
809        let gid = 456;
810
811        let p = CString::new(BAR_PATH).unwrap();
812        let ptr = p.as_ptr();
813        assert_eq!(unsafe { chown(ptr, uid, gid) }, negate_errno(ENOENT));
814
815        let p = CString::new(FOO_PATH).unwrap();
816        let ptr = p.as_ptr();
817        assert_eq!(unsafe { chown(ptr, uid, gid) }, 0);
818    }
819
820    #[test]
821    fn test_truncate() {
822        unsafe { setup_test_fs(&mut DUMMY_FS) };
823
824        let p = CString::new(BAR_PATH).unwrap();
825        let ptr = p.as_ptr();
826        let offset = 128;
827        assert_eq!(unsafe { truncate(ptr, offset) }, negate_errno(ENOENT));
828
829        let p = CString::new(FOO_PATH).unwrap();
830        let ptr = p.as_ptr();
831        assert_eq!(unsafe { truncate(ptr, offset) }, 0);
832    }
833
834    #[test]
835    fn test_open() {
836        unsafe { setup_test_fs(&mut DUMMY_FS) };
837
838        let p = CString::new(FOO_PATH).unwrap();
839        let ptr = p.as_ptr();
840        assert_eq!(
841            unsafe { open(ptr, std::ptr::null_mut()) },
842            negate_errno(EINVAL)
843        );
844
845        let p = CString::new(BAR_PATH).unwrap();
846        let ptr = p.as_ptr();
847        let mut fi = mem::MaybeUninit::uninit();
848        assert_eq!(unsafe { open(ptr, fi.as_mut_ptr()) }, negate_errno(ENOENT));
849
850        let p = CString::new(FOO_PATH).unwrap();
851        let ptr = p.as_ptr();
852        let mut fi = mem::MaybeUninit::uninit();
853        unsafe {
854            assert_eq!(open(ptr, fi.as_mut_ptr()), 0);
855
856            let fi = fi.assume_init();
857            assert_eq!(fi.direct_io(), 1);
858        }
859    }
860
861    #[test]
862    fn test_read() {
863        unsafe { setup_test_fs(&mut DUMMY_FS) };
864
865        let len = 8;
866        let offset = 0;
867
868        // Invalid buffer: null
869        let p = CString::new(FOO_PATH).unwrap();
870        let ptr = p.as_ptr();
871        let mut fi = mem::MaybeUninit::uninit();
872        assert_eq!(
873            unsafe { read(ptr, std::ptr::null_mut(), len, offset, fi.as_mut_ptr()) },
874            negate_errno(EINVAL)
875        );
876
877        // Wrong path
878        let p = CString::new(BAR_PATH).unwrap();
879        let ptr = p.as_ptr();
880        let mut buf = mem::MaybeUninit::uninit();
881        assert_eq!(
882            unsafe { read(ptr, buf.as_mut_ptr(), len, offset, fi.as_mut_ptr()) },
883            negate_errno(ENOENT)
884        );
885
886        // Truncate if fs wrote more than specified len in the buffer.
887        let p = CString::new(FOO_PATH).unwrap();
888        let ptr = p.as_ptr();
889        let mut vec = Vec::with_capacity(len);
890        unsafe {
891            vec.set_len(len);
892            let buf = CString::from_vec_unchecked(vec).into_raw();
893
894            assert_eq!(read(ptr, buf, len, offset, fi.as_mut_ptr()), len as _);
895
896            let got = CString::from_raw(buf);
897            assert_eq!(got.to_bytes(), b"Hello Wo");
898        }
899
900        // Returns actual bytes read if fs read less than specified len. EOF case.
901        let p = CString::new(FOO_PATH).unwrap();
902        let ptr = p.as_ptr();
903        let mut vec = Vec::with_capacity(len);
904        unsafe {
905            vec.set_len(len);
906            let buf = CString::from_vec_unchecked(vec).into_raw();
907            let ret = 12;
908
909            assert_eq!(read(ptr, buf, 2 * len, offset, fi.as_mut_ptr()), ret);
910
911            let got = CString::from_raw(buf);
912            assert_eq!(&got.to_bytes_with_nul()[..ret as usize], b"Hello World!");
913        }
914    }
915
916    #[test]
917    fn test_write() {
918        unsafe { setup_test_fs(&mut DUMMY_FS) };
919
920        let len = 12;
921        let offset = 0;
922
923        // Invalid buffer: null
924        let p = CString::new(FOO_PATH).unwrap();
925        let ptr = p.as_ptr();
926        let mut fi = mem::MaybeUninit::uninit();
927        assert_eq!(
928            unsafe { write(ptr, std::ptr::null_mut(), len, offset, fi.as_mut_ptr()) },
929            negate_errno(EINVAL)
930        );
931
932        // Wrong path
933        let p = CString::new(BAR_PATH).unwrap();
934        let ptr = p.as_ptr();
935        let mut buf = mem::MaybeUninit::uninit();
936        assert_eq!(
937            unsafe { write(ptr, buf.as_mut_ptr(), len, offset, fi.as_mut_ptr()) },
938            negate_errno(ENOENT)
939        );
940
941        // Valid write.
942        let p = CString::new(FOO_PATH).unwrap();
943        let ptr = p.as_ptr();
944        let buf = CString::new("Hello World!").unwrap();
945        unsafe {
946            assert_eq!(
947                write(ptr, buf.into_raw(), len, offset, fi.as_mut_ptr()),
948                len as _
949            );
950
951            let fi = fi.assume_init();
952            assert_eq!(fi.writepage, 1);
953        }
954    }
955
956    #[test]
957    fn test_statfs() {
958        unsafe { setup_test_fs(&mut DUMMY_FS) };
959
960        // Invalid stbuf
961        let p = CString::new(FOO_PATH).unwrap();
962        let ptr = p.as_ptr();
963        assert_eq!(
964            unsafe { statfs(ptr, std::ptr::null_mut()) },
965            negate_errno(EINVAL)
966        );
967
968        // Wrong path
969        let p = CString::new(BAR_PATH).unwrap();
970        let ptr = p.as_ptr();
971        let mut stbuf = mem::MaybeUninit::uninit();
972        assert_eq!(
973            unsafe { statfs(ptr, stbuf.as_mut_ptr()) },
974            negate_errno(ENOENT)
975        );
976
977        // OK
978        let p = CString::new(FOO_PATH).unwrap();
979        let ptr = p.as_ptr();
980        let mut stbuf = mem::MaybeUninit::uninit();
981        unsafe {
982            assert_eq!(statfs(ptr, stbuf.as_mut_ptr()), 0);
983
984            let stbuf = stbuf.assume_init();
985            assert_eq!(stbuf.f_bsize, 512);
986        }
987    }
988
989    #[test]
990    fn test_flush() {
991        unsafe { setup_test_fs(&mut DUMMY_FS) };
992
993        // Invalid stbuf
994        let p = CString::new(FOO_PATH).unwrap();
995        let ptr = p.as_ptr();
996        assert_eq!(
997            unsafe { flush(ptr, std::ptr::null_mut()) },
998            negate_errno(EINVAL)
999        );
1000
1001        // OK
1002        let p = CString::new(FOO_PATH).unwrap();
1003        let ptr = p.as_ptr();
1004        let mut fi = mem::MaybeUninit::uninit();
1005        unsafe {
1006            assert_eq!(flush(ptr, fi.as_mut_ptr()), 0);
1007            let fi = fi.assume_init();
1008
1009            assert_eq!(fi.flush(), 1);
1010        }
1011    }
1012
1013    #[test]
1014    fn test_release() {
1015        unsafe { setup_test_fs(&mut DUMMY_FS) };
1016
1017        // Invalid stbuf
1018        let p = CString::new(FOO_PATH).unwrap();
1019        let ptr = p.as_ptr();
1020        assert_eq!(
1021            unsafe { release(ptr, std::ptr::null_mut()) },
1022            negate_errno(EINVAL)
1023        );
1024
1025        // OK
1026        let p = CString::new(FOO_PATH).unwrap();
1027        let ptr = p.as_ptr();
1028        let mut fi = mem::MaybeUninit::uninit();
1029        unsafe {
1030            assert_eq!(release(ptr, fi.as_mut_ptr()), 0);
1031            let fi = fi.assume_init();
1032
1033            assert_eq!(fi.flock_release(), 1);
1034        }
1035    }
1036
1037    #[test]
1038    fn test_fsync_data() {
1039        unsafe { setup_test_fs(&mut DUMMY_FS) };
1040
1041        // Invalid fi
1042        let p = CString::new(FOO_PATH).unwrap();
1043        let ptr = p.as_ptr();
1044        assert_eq!(
1045            unsafe { fsync(ptr, 1, std::ptr::null_mut()) },
1046            negate_errno(EINVAL)
1047        );
1048
1049        // Wrong path
1050        let p = CString::new(BAR_PATH).unwrap();
1051        let ptr = p.as_ptr();
1052        let mut fi = mem::MaybeUninit::uninit();
1053        assert_eq!(
1054            unsafe { fsync(ptr, 1, fi.as_mut_ptr()) },
1055            negate_errno(ENOENT)
1056        );
1057
1058        // OK
1059        let p = CString::new(FOO_PATH).unwrap();
1060        let ptr = p.as_ptr();
1061        let mut fi = mem::MaybeUninit::uninit();
1062        assert_eq!(unsafe { fsync(ptr, 1, fi.as_mut_ptr()) }, 0);
1063    }
1064
1065    #[test]
1066    fn test_fsync_all() {
1067        unsafe { setup_test_fs(&mut DUMMY_FS) };
1068
1069        // Invalid fi
1070        let p = CString::new(FOO_PATH).unwrap();
1071        let ptr = p.as_ptr();
1072        assert_eq!(
1073            unsafe { fsync(ptr, 0, std::ptr::null_mut()) },
1074            negate_errno(EINVAL)
1075        );
1076
1077        // Wrong path
1078        let p = CString::new(BAR_PATH).unwrap();
1079        let ptr = p.as_ptr();
1080        let mut fi = mem::MaybeUninit::uninit();
1081        assert_eq!(
1082            unsafe { fsync(ptr, 0, fi.as_mut_ptr()) },
1083            negate_errno(ENOENT)
1084        );
1085
1086        // OK
1087        let p = CString::new(FOO_PATH).unwrap();
1088        let ptr = p.as_ptr();
1089        let mut fi = mem::MaybeUninit::uninit();
1090        assert_eq!(unsafe { fsync(ptr, 0, fi.as_mut_ptr()) }, 0);
1091    }
1092
1093    #[test]
1094    fn test_opendir() {
1095        unsafe { setup_test_fs(&mut DUMMY_FS) };
1096
1097        // Invalid fi
1098        let p = CString::new(FOO_PATH).unwrap();
1099        let ptr = p.as_ptr();
1100        assert_eq!(
1101            unsafe { opendir(ptr, std::ptr::null_mut()) },
1102            negate_errno(EINVAL)
1103        );
1104
1105        // Wrong path
1106        let p = CString::new(BAR_PATH).unwrap();
1107        let ptr = p.as_ptr();
1108        let mut fi = mem::MaybeUninit::uninit();
1109        assert_eq!(
1110            unsafe { opendir(ptr, fi.as_mut_ptr()) },
1111            negate_errno(ENOENT)
1112        );
1113
1114        // OK
1115        let p = CString::new(FOO_PATH).unwrap();
1116        let ptr = p.as_ptr();
1117        let mut fi = mem::MaybeUninit::uninit();
1118        unsafe {
1119            assert_eq!(opendir(ptr, fi.as_mut_ptr()), 0);
1120
1121            let fi = fi.assume_init();
1122            assert_eq!(fi.direct_io(), 1);
1123        }
1124    }
1125
1126    #[test]
1127    fn test_readdir() {
1128        unsafe { setup_test_fs(&mut DUMMY_FS) };
1129
1130        let offset = 0;
1131
1132        // Wrong path
1133        let p = CString::new(BAR_PATH).unwrap();
1134        let ptr = p.as_ptr();
1135        let mut fi = mem::MaybeUninit::uninit();
1136        let mut buf = mem::MaybeUninit::uninit();
1137        assert_eq!(
1138            unsafe {
1139                readdir(
1140                    ptr,
1141                    buf.as_mut_ptr(),
1142                    Some(fake_fill_dir),
1143                    offset,
1144                    fi.as_mut_ptr(),
1145                )
1146            },
1147            negate_errno(ENOENT)
1148        );
1149
1150        // Filler function not given
1151        let p = CString::new(FOO_PATH).unwrap();
1152        let ptr = p.as_ptr();
1153        let mut fi = mem::MaybeUninit::uninit();
1154        let mut buf = mem::MaybeUninit::uninit();
1155        assert_eq!(
1156            unsafe { readdir(ptr, buf.as_mut_ptr(), None, offset, fi.as_mut_ptr(),) },
1157            0,
1158        );
1159
1160        // OK
1161        let p = CString::new(FOO_PATH).unwrap();
1162        let ptr = p.as_ptr();
1163        let mut fi = mem::MaybeUninit::uninit();
1164        let mut buf = mem::MaybeUninit::uninit();
1165        assert_eq!(
1166            unsafe {
1167                readdir(
1168                    ptr,
1169                    buf.as_mut_ptr(),
1170                    Some(fake_fill_dir),
1171                    offset,
1172                    fi.as_mut_ptr(),
1173                )
1174            },
1175            0,
1176        );
1177    }
1178
1179    #[test]
1180    fn test_releasedir() {
1181        unsafe { setup_test_fs(&mut DUMMY_FS) };
1182
1183        // Invalid stbuf
1184        let p = CString::new(FOO_PATH).unwrap();
1185        let ptr = p.as_ptr();
1186        assert_eq!(
1187            unsafe { releasedir(ptr, std::ptr::null_mut()) },
1188            negate_errno(EINVAL)
1189        );
1190
1191        // OK
1192        let p = CString::new(FOO_PATH).unwrap();
1193        let ptr = p.as_ptr();
1194        let mut fi = mem::MaybeUninit::uninit();
1195        unsafe {
1196            assert_eq!(releasedir(ptr, fi.as_mut_ptr()), 0);
1197            let fi = fi.assume_init();
1198
1199            assert_eq!(fi.flock_release(), 1);
1200        }
1201    }
1202
1203    #[test]
1204    fn test_fsyncdir_data() {
1205        unsafe { setup_test_fs(&mut DUMMY_FS) };
1206
1207        // Invalid fi
1208        let p = CString::new(FOO_PATH).unwrap();
1209        let ptr = p.as_ptr();
1210        assert_eq!(
1211            unsafe { fsyncdir(ptr, 1, std::ptr::null_mut()) },
1212            negate_errno(EINVAL)
1213        );
1214
1215        // Wrong path
1216        let p = CString::new(BAR_PATH).unwrap();
1217        let ptr = p.as_ptr();
1218        let mut fi = mem::MaybeUninit::uninit();
1219        assert_eq!(
1220            unsafe { fsyncdir(ptr, 1, fi.as_mut_ptr()) },
1221            negate_errno(ENOENT)
1222        );
1223
1224        // OK
1225        let p = CString::new(FOO_PATH).unwrap();
1226        let ptr = p.as_ptr();
1227        let mut fi = mem::MaybeUninit::uninit();
1228        assert_eq!(unsafe { fsyncdir(ptr, 1, fi.as_mut_ptr()) }, 0);
1229    }
1230
1231    #[test]
1232    fn test_fsyncdir_all() {
1233        unsafe { setup_test_fs(&mut DUMMY_FS) };
1234
1235        // Invalid fi
1236        let p = CString::new(FOO_PATH).unwrap();
1237        let ptr = p.as_ptr();
1238        assert_eq!(
1239            unsafe { fsyncdir(ptr, 0, std::ptr::null_mut()) },
1240            negate_errno(EINVAL)
1241        );
1242
1243        // Wrong path
1244        let p = CString::new(BAR_PATH).unwrap();
1245        let ptr = p.as_ptr();
1246        let mut fi = mem::MaybeUninit::uninit();
1247        assert_eq!(
1248            unsafe { fsyncdir(ptr, 0, fi.as_mut_ptr()) },
1249            negate_errno(ENOENT)
1250        );
1251
1252        // OK
1253        let p = CString::new(FOO_PATH).unwrap();
1254        let ptr = p.as_ptr();
1255        let mut fi = mem::MaybeUninit::uninit();
1256        assert_eq!(unsafe { fsyncdir(ptr, 0, fi.as_mut_ptr()) }, 0);
1257    }
1258
1259    #[test]
1260    fn test_init() {
1261        unsafe { setup_test_fs(&mut DUMMY_FS) };
1262
1263        // Null conn
1264        unsafe {
1265            assert_eq!(init(std::ptr::null_mut()), std::ptr::null_mut());
1266        }
1267
1268        // Valid connection
1269        let mut conn = mem::MaybeUninit::uninit();
1270        unsafe {
1271            assert_eq!(init(conn.as_mut_ptr()), std::ptr::null_mut());
1272
1273            let conn = conn.assume_init();
1274            assert_eq!(conn.async_read, 1);
1275            assert_eq!(conn.want, CapabilityFlags::FUSE_CAP_BIG_WRITES.bits());
1276        }
1277    }
1278
1279    #[test]
1280    fn test_destroy() {
1281        unsafe {
1282            setup_test_fs(&mut DUMMY_FS);
1283            destroy(std::ptr::null_mut());
1284        }
1285    }
1286
1287    #[test]
1288    fn test_access() {
1289        unsafe { setup_test_fs(&mut DUMMY_FS) };
1290
1291        // Wrong path
1292        let p = CString::new(BAR_PATH).unwrap();
1293        let ptr = p.as_ptr();
1294        let mode = AccessFlags::W_OK.bits();
1295        assert_eq!(unsafe { access(ptr, mode) }, negate_errno(ENOENT));
1296
1297        // OK
1298        let p = CString::new(FOO_PATH).unwrap();
1299        let ptr = p.as_ptr();
1300        let mode = AccessFlags::W_OK.bits();
1301        assert_eq!(unsafe { access(ptr, mode) }, 1);
1302    }
1303
1304    #[test]
1305    fn test_create() {
1306        unsafe { setup_test_fs(&mut DUMMY_FS) };
1307
1308        // Invalid fi
1309        let p = CString::new(FOO_PATH).unwrap();
1310        let ptr = p.as_ptr();
1311        let mode = Mode::S_IRWXU.bits();
1312        assert_eq!(
1313            unsafe { create(ptr, mode, std::ptr::null_mut()) },
1314            negate_errno(EINVAL)
1315        );
1316
1317        // Wrong path
1318        let p = CString::new(BAR_PATH).unwrap();
1319        let ptr = p.as_ptr();
1320        let mut fi = mem::MaybeUninit::uninit();
1321        assert_eq!(
1322            unsafe { create(ptr, mode, fi.as_mut_ptr()) },
1323            negate_errno(ENOENT)
1324        );
1325
1326        // OK
1327        let p = CString::new(FOO_PATH).unwrap();
1328        let ptr = p.as_ptr();
1329        let mut fi = mem::MaybeUninit::uninit();
1330        unsafe {
1331            assert_eq!(create(ptr, mode, fi.as_mut_ptr()), 0);
1332
1333            let fi = fi.assume_init();
1334            assert_eq!(fi.fh, 123);
1335        }
1336    }
1337
1338    #[test]
1339    fn test_ftruncate() {
1340        unsafe { setup_test_fs(&mut DUMMY_FS) };
1341
1342        // Invalid fi
1343        let p = CString::new(FOO_PATH).unwrap();
1344        let ptr = p.as_ptr();
1345        let offset = 64;
1346        assert_eq!(
1347            unsafe { ftruncate(ptr, offset, std::ptr::null_mut()) },
1348            negate_errno(EINVAL)
1349        );
1350
1351        // Wrong path
1352        let p = CString::new(BAR_PATH).unwrap();
1353        let ptr = p.as_ptr();
1354        let mut fi = mem::MaybeUninit::uninit();
1355        assert_eq!(
1356            unsafe { ftruncate(ptr, offset, fi.as_mut_ptr()) },
1357            negate_errno(ENOENT)
1358        );
1359
1360        // OK
1361        let p = CString::new(FOO_PATH).unwrap();
1362        let ptr = p.as_ptr();
1363        let mut fi = mem::MaybeUninit::uninit();
1364        unsafe {
1365            assert_eq!(ftruncate(ptr, offset, fi.as_mut_ptr()), 0);
1366        }
1367    }
1368
1369    #[test]
1370    fn test_fgetattr() {
1371        unsafe { setup_test_fs(&mut DUMMY_FS) };
1372
1373        // Invalid fi
1374        let p = CString::new(FOO_PATH).unwrap();
1375        let ptr = p.as_ptr();
1376        let mut stat = mem::MaybeUninit::uninit();
1377        assert_eq!(
1378            unsafe { fgetattr(ptr, stat.as_mut_ptr(), std::ptr::null_mut()) },
1379            negate_errno(EINVAL)
1380        );
1381
1382        // Invalid stat
1383        let p = CString::new(FOO_PATH).unwrap();
1384        let ptr = p.as_ptr();
1385        let mut fi = mem::MaybeUninit::uninit();
1386        assert_eq!(
1387            unsafe { fgetattr(ptr, std::ptr::null_mut(), fi.as_mut_ptr()) },
1388            negate_errno(EINVAL)
1389        );
1390
1391        // Wrong path
1392        let p = CString::new(BAR_PATH).unwrap();
1393        let ptr = p.as_ptr();
1394        assert_eq!(
1395            unsafe { fgetattr(ptr, stat.as_mut_ptr(), fi.as_mut_ptr()) },
1396            negate_errno(ENOENT)
1397        );
1398
1399        // OK
1400        let p = CString::new(FOO_PATH).unwrap();
1401        let ptr = p.as_ptr();
1402        unsafe {
1403            assert_eq!(fgetattr(ptr, stat.as_mut_ptr(), fi.as_mut_ptr()), 0);
1404
1405            let stat = stat.assume_init();
1406            assert_eq!(stat.st_nlink, 3);
1407        }
1408    }
1409
1410    #[allow(unused_must_use)]
1411    unsafe fn setup_test_fs(fs: &'static mut dyn Filesystem) {
1412        setup_fs(fs);
1413    }
1414
1415    unsafe extern "C" fn fake_fill_dir(
1416        _buf: *mut c_void,
1417        name: *const c_char,
1418        stbuf: *const stat,
1419        offset: off_t,
1420    ) -> c_int {
1421        let file_name = CStr::from_ptr(name).to_str().unwrap();
1422        assert_eq!(file_name, "hello.txt");
1423
1424        assert_eq!((*stbuf).st_ino, 3);
1425        assert_eq!(offset, 10);
1426
1427        0
1428    }
1429
1430    struct DummyFS;
1431
1432    impl Filesystem for DummyFS {
1433        fn metadata(&self, path: &Path) -> Result<FileStat> {
1434            if path.ends_with("foo.txt") {
1435                let mut fstat = FileStat::new();
1436                fstat.st_nlink = 3;
1437                Ok(fstat)
1438            } else {
1439                Err(ENOENT)
1440            }
1441        }
1442
1443        fn read_link(&self, path: &Path) -> Result<&OsStr> {
1444            if path.ends_with("foo.txt") {
1445                Ok(Path::new(BAR_PATH).as_os_str())
1446            } else {
1447                Err(ENOENT)
1448            }
1449        }
1450
1451        fn create_dir(&mut self, path: &Path, _mode: Mode) -> Result<()> {
1452            if path.ends_with("foo.txt") {
1453                Ok(())
1454            } else {
1455                Err(ENOENT)
1456            }
1457        }
1458
1459        fn remove_file(&mut self, path: &Path) -> Result<()> {
1460            if path.ends_with("foo.txt") {
1461                Ok(())
1462            } else {
1463                Err(ENOENT)
1464            }
1465        }
1466
1467        fn remove_dir(&mut self, path: &Path) -> Result<()> {
1468            if path.ends_with("foo.txt") {
1469                Ok(())
1470            } else {
1471                Err(ENOENT)
1472            }
1473        }
1474
1475        fn symlink(&mut self, src: &Path, _dst: &Path) -> Result<()> {
1476            if src.ends_with("foo.txt") {
1477                Ok(())
1478            } else {
1479                Err(ENOENT)
1480            }
1481        }
1482
1483        fn rename(&mut self, from: &Path, _to: &Path) -> Result<()> {
1484            if from.ends_with("foo.txt") {
1485                Ok(())
1486            } else {
1487                Err(ENOENT)
1488            }
1489        }
1490
1491        fn hard_link(&mut self, src: &Path, _dst: &Path) -> Result<()> {
1492            if src.ends_with("foo.txt") {
1493                Ok(())
1494            } else {
1495                Err(ENOENT)
1496            }
1497        }
1498
1499        fn set_permissions(&mut self, path: &Path, _mode: Mode) -> Result<()> {
1500            if path.ends_with("foo.txt") {
1501                Ok(())
1502            } else {
1503                Err(ENOENT)
1504            }
1505        }
1506
1507        fn set_owner(&mut self, path: &Path, _uid: Uid, _gid: Gid) -> Result<()> {
1508            if path.ends_with("foo.txt") {
1509                Ok(())
1510            } else {
1511                Err(ENOENT)
1512            }
1513        }
1514
1515        fn set_len(&mut self, path: &Path, _len: u64) -> Result<()> {
1516            if path.ends_with("foo.txt") {
1517                Ok(())
1518            } else {
1519                Err(ENOENT)
1520            }
1521        }
1522
1523        fn open(&mut self, path: &Path, file_info: &mut OpenFileInfo) -> Result<()> {
1524            if path.ends_with("foo.txt") {
1525                file_info.set_direct_io(true);
1526                Ok(())
1527            } else {
1528                Err(ENOENT)
1529            }
1530        }
1531
1532        fn read(
1533            &mut self,
1534            path: &Path,
1535            buf: &mut [u8],
1536            _offset: u64,
1537            _file_info: FileInfo,
1538        ) -> Result<usize> {
1539            if path.ends_with("foo.txt") {
1540                (&String::from("Hello World!").into_bytes()[..])
1541                    .read(buf)
1542                    .map_err(|_| EFAULT)
1543            } else {
1544                Err(ENOENT)
1545            }
1546        }
1547
1548        fn write(
1549            &mut self,
1550            path: &Path,
1551            buf: &[u8],
1552            _offset: u64,
1553            file_info: &mut WriteFileInfo,
1554        ) -> Result<usize> {
1555            if path.ends_with("foo.txt") {
1556                if buf.len() == 12 {
1557                    // "Hello World!" len
1558                    file_info.set_writepage(true);
1559                    Ok(buf.len())
1560                } else {
1561                    Err(EFAULT)
1562                }
1563            } else {
1564                Err(ENOENT)
1565            }
1566        }
1567
1568        fn statfs(&self, path: &Path) -> Result<libc::statvfs> {
1569            if path.ends_with("foo.txt") {
1570                let mut stats = mem::MaybeUninit::<libc::statvfs>::uninit();
1571                unsafe {
1572                    (*stats.as_mut_ptr()).f_bsize = 512;
1573                }
1574
1575                let stats = unsafe { stats.assume_init() };
1576                Ok(stats)
1577            } else {
1578                Err(ENOENT)
1579            }
1580        }
1581
1582        fn flush(&mut self, path: &Path, file_info: &mut FlushFileInfo) -> Result<()> {
1583            if path.ends_with("foo.txt") {
1584                file_info.set_flush(true);
1585                Ok(())
1586            } else {
1587                Err(ENOENT)
1588            }
1589        }
1590
1591        fn release(&mut self, path: &Path, file_info: &mut ReleaseFileInfo) -> Result<()> {
1592            if path.ends_with("foo.txt") {
1593                file_info.set_release_flock(true);
1594                Ok(())
1595            } else {
1596                Err(ENOENT)
1597            }
1598        }
1599
1600        fn sync_data(&mut self, path: &Path, _file_info: FileInfo) -> Result<()> {
1601            if path.ends_with("foo.txt") {
1602                Ok(())
1603            } else {
1604                Err(ENOENT)
1605            }
1606        }
1607
1608        fn sync_all(&mut self, path: &Path, _file_info: FileInfo) -> Result<()> {
1609            if path.ends_with("foo.txt") {
1610                Ok(())
1611            } else {
1612                Err(ENOENT)
1613            }
1614        }
1615
1616        fn open_dir(&mut self, path: &Path, file_info: &mut OpenFileInfo) -> Result<()> {
1617            if path.ends_with("foo.txt") {
1618                file_info.set_direct_io(true);
1619                Ok(())
1620            } else {
1621                Err(ENOENT)
1622            }
1623        }
1624
1625        fn read_dir(
1626            &mut self,
1627            path: &Path,
1628            _offset: u64,
1629            _file_info: FileInfo,
1630        ) -> Result<Vec<DirEntry>> {
1631            if path.ends_with("foo.txt") {
1632                let mut file_stat = FileStat::new();
1633                file_stat.st_ino = 3;
1634                Ok(vec!["hello.txt"]
1635                    .into_iter()
1636                    .map(|n| DirEntry {
1637                        name: OsString::from(n),
1638                        metadata: Some(file_stat.clone()),
1639                        offset: Some(10),
1640                    })
1641                    .collect())
1642            } else {
1643                Err(ENOENT)
1644            }
1645        }
1646
1647        fn release_dir(&mut self, path: &Path, file_info: &mut ReleaseFileInfo) -> Result<()> {
1648            if path.ends_with("foo.txt") {
1649                file_info.set_release_flock(true);
1650                Ok(())
1651            } else {
1652                Err(ENOENT)
1653            }
1654        }
1655
1656        fn sync_dir_data(&mut self, path: &Path, _file_info: FileInfo) -> Result<()> {
1657            if path.ends_with("foo.txt") {
1658                Ok(())
1659            } else {
1660                Err(ENOENT)
1661            }
1662        }
1663
1664        fn sync_dir_all(&mut self, path: &Path, _file_info: FileInfo) -> Result<()> {
1665            if path.ends_with("foo.txt") {
1666                Ok(())
1667            } else {
1668                Err(ENOENT)
1669            }
1670        }
1671
1672        fn init(&mut self, connection_info: &mut ConnectionInfo) -> Result<()> {
1673            connection_info
1674                .enable_async_read()
1675                .set_capability_flags(CapabilityFlags::FUSE_CAP_BIG_WRITES);
1676            Ok(())
1677        }
1678
1679        fn check_permissions(&self, path: &Path, permissions: AccessFlags) -> Result<bool> {
1680            if path.ends_with("foo.txt") {
1681                Ok(permissions == AccessFlags::W_OK)
1682            } else {
1683                Err(ENOENT)
1684            }
1685        }
1686
1687        fn create(
1688            &mut self,
1689            path: &Path,
1690            permissions: Mode,
1691            file_info: &mut OpenFileInfo,
1692        ) -> Result<()> {
1693            if path.ends_with("foo.txt") {
1694                if permissions == Mode::S_IRWXU {
1695                    file_info.set_handle(123);
1696                    Ok(())
1697                } else {
1698                    Err(EACCES)
1699                }
1700            } else {
1701                Err(ENOENT)
1702            }
1703        }
1704
1705        fn ftruncate(&mut self, path: &Path, _len: u64, _file_info: FileInfo) -> Result<()> {
1706            if path.ends_with("foo.txt") {
1707                Ok(())
1708            } else {
1709                Err(ENOENT)
1710            }
1711        }
1712
1713        fn fmetadata(&self, path: &Path, _file_info: FileInfo) -> Result<FileStat> {
1714            if path.ends_with("foo.txt") {
1715                let mut fstat = FileStat::new();
1716                fstat.st_nlink = 3;
1717                Ok(fstat)
1718            } else {
1719                Err(ENOENT)
1720            }
1721        }
1722    }
1723}