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 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), #[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), #[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), #[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 ..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; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 unsafe {
1265 assert_eq!(init(std::ptr::null_mut()), std::ptr::null_mut());
1266 }
1267
1268 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 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 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 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 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 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 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 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 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 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 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 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 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 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}