1#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
8
9use libc::{ENOSYS, EPERM, c_int};
10use log::warn;
11use mnt::mount_options::parse_options_from_args;
12#[cfg(feature = "serializable")]
13use serde::{Deserialize, Serialize};
14use std::ffi::OsStr;
15use std::io;
16use std::path::Path;
17#[cfg(feature = "abi-7-23")]
18use std::time::Duration;
19use std::time::SystemTime;
20use std::{convert::AsRef, io::ErrorKind};
21
22pub use crate::ll::fuse_abi::FUSE_ROOT_ID;
23use crate::ll::fuse_abi::consts::*;
24pub use crate::ll::{TimeOrNow, fuse_abi::consts};
25use crate::mnt::mount_options::check_option_conflicts;
26use crate::session::MAX_WRITE_SIZE;
27pub use ll::fuse_abi::fuse_forget_one;
28pub use mnt::{Mount, mount_options::MountOption};
29pub use notify::{Notifier, PollHandle};
30#[cfg(feature = "abi-7-40")]
31pub use passthrough::BackingId;
32pub use reply::ReplyPoll;
33#[cfg(target_os = "macos")]
34pub use reply::ReplyXTimes;
35pub use reply::ReplyXattr;
36pub use reply::{Reply, ReplyAttr, ReplyData, ReplyEmpty, ReplyEntry, ReplyOpen};
37pub use reply::{
38 ReplyBmap, ReplyCreate, ReplyDirectory, ReplyDirectoryPlus, ReplyIoctl, ReplyLock, ReplyLseek,
39 ReplyStatfs, ReplyWrite,
40};
41pub use request::Request;
42pub use session::{BackgroundSession, Session, SessionACL, SessionUnmounter};
43#[cfg(feature = "abi-7-28")]
44use std::cmp::max;
45use std::cmp::min;
46
47mod channel;
48mod ll;
49mod mnt;
50mod notify;
51#[cfg(feature = "abi-7-40")]
52mod passthrough;
53mod reply;
54mod request;
55mod session;
56
57#[cfg(not(target_os = "macos"))]
59const INIT_FLAGS: u64 = FUSE_ASYNC_READ | FUSE_BIG_WRITES;
60#[cfg(target_os = "macos")]
65const INIT_FLAGS: u64 = FUSE_ASYNC_READ | FUSE_CASE_INSENSITIVE | FUSE_VOL_RENAME | FUSE_XTIMES;
66const fn default_init_flags(#[allow(unused_variables)] capabilities: u64) -> u64 {
69 #[cfg(not(feature = "abi-7-28"))]
70 {
71 INIT_FLAGS
72 }
73
74 #[cfg(feature = "abi-7-28")]
75 {
76 let mut flags = INIT_FLAGS;
77 if capabilities & FUSE_MAX_PAGES != 0 {
78 flags |= FUSE_MAX_PAGES;
79 }
80 flags
81 }
82}
83
84#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
86#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
87pub enum FileType {
88 NamedPipe,
90 CharDevice,
92 BlockDevice,
94 Directory,
96 RegularFile,
98 Symlink,
100 Socket,
102}
103
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
107pub struct FileAttr {
108 pub ino: u64,
110 pub size: u64,
112 pub blocks: u64,
114 pub atime: SystemTime,
116 pub mtime: SystemTime,
118 pub ctime: SystemTime,
120 pub crtime: SystemTime,
122 pub kind: FileType,
124 pub perm: u16,
126 pub nlink: u32,
128 pub uid: u32,
130 pub gid: u32,
132 pub rdev: u32,
134 pub blksize: u32,
136 pub flags: u32,
138}
139
140#[derive(Debug)]
142pub struct KernelConfig {
143 capabilities: u64,
144 requested: u64,
145 max_readahead: u32,
146 max_max_readahead: u32,
147 max_background: u16,
148 congestion_threshold: Option<u16>,
149 max_write: u32,
150 #[cfg(feature = "abi-7-23")]
151 time_gran: Duration,
152 #[cfg(feature = "abi-7-40")]
153 max_stack_depth: u32,
154}
155
156impl KernelConfig {
157 fn new(capabilities: u64, max_readahead: u32) -> Self {
158 Self {
159 capabilities,
160 requested: default_init_flags(capabilities),
161 max_readahead,
162 max_max_readahead: max_readahead,
163 max_background: 16,
164 congestion_threshold: None,
165 max_write: MAX_WRITE_SIZE as u32,
167 #[cfg(feature = "abi-7-23")]
169 time_gran: Duration::new(0, 1),
170 #[cfg(feature = "abi-7-40")]
171 max_stack_depth: 0,
172 }
173 }
174
175 #[cfg(feature = "abi-7-40")]
188 pub fn set_max_stack_depth(&mut self, value: u32) -> Result<u32, u32> {
189 const FILESYSTEM_MAX_STACK_DEPTH: u32 = 2;
191
192 if value > FILESYSTEM_MAX_STACK_DEPTH {
193 return Err(FILESYSTEM_MAX_STACK_DEPTH);
194 }
195
196 let previous = self.max_stack_depth;
197 self.max_stack_depth = value;
198 Ok(previous)
199 }
200
201 #[cfg(feature = "abi-7-23")]
207 pub fn set_time_granularity(&mut self, value: Duration) -> Result<Duration, Duration> {
208 if value.as_nanos() == 0 {
209 return Err(Duration::new(0, 1));
210 }
211 if value.as_secs() > 1 || (value.as_secs() == 1 && value.subsec_nanos() > 0) {
212 return Err(Duration::new(1, 0));
213 }
214 let mut power_of_10 = 1;
215 while power_of_10 < value.as_nanos() {
216 if value.as_nanos() < power_of_10 * 10 {
217 return Err(Duration::new(0, power_of_10 as u32));
219 }
220 power_of_10 *= 10;
221 }
222 let previous = self.time_gran;
223 self.time_gran = value;
224 Ok(previous)
225 }
226
227 pub fn set_max_write(&mut self, value: u32) -> Result<u32, u32> {
231 if value == 0 {
232 return Err(1);
233 }
234 if value > MAX_WRITE_SIZE as u32 {
235 return Err(MAX_WRITE_SIZE as u32);
236 }
237 let previous = self.max_write;
238 self.max_write = value;
239 Ok(previous)
240 }
241
242 pub fn set_max_readahead(&mut self, value: u32) -> Result<u32, u32> {
246 if value == 0 {
247 return Err(1);
248 }
249 if value > self.max_max_readahead {
250 return Err(self.max_max_readahead);
251 }
252 let previous = self.max_readahead;
253 self.max_readahead = value;
254 Ok(previous)
255 }
256
257 pub fn add_capabilities(&mut self, capabilities_to_add: u64) -> Result<(), u64> {
261 if capabilities_to_add & self.capabilities != capabilities_to_add {
262 return Err(capabilities_to_add - (capabilities_to_add & self.capabilities));
263 }
264 self.requested |= capabilities_to_add;
265 Ok(())
266 }
267
268 pub fn set_max_background(&mut self, value: u16) -> Result<u16, u16> {
272 if value == 0 {
273 return Err(1);
274 }
275 let previous = self.max_background;
276 self.max_background = value;
277 Ok(previous)
278 }
279
280 pub fn set_congestion_threshold(&mut self, value: u16) -> Result<u16, u16> {
285 if value == 0 {
286 return Err(1);
287 }
288 let previous = self.congestion_threshold();
289 self.congestion_threshold = Some(value);
290 Ok(previous)
291 }
292
293 fn congestion_threshold(&self) -> u16 {
294 match self.congestion_threshold {
295 None => (self.max_background as u32 * 3 / 4) as u16,
297 Some(value) => min(value, self.max_background),
298 }
299 }
300
301 #[cfg(feature = "abi-7-28")]
302 fn max_pages(&self) -> u16 {
303 ((max(self.max_write, self.max_readahead) - 1) / page_size::get() as u32) as u16 + 1
304 }
305}
306
307#[allow(clippy::too_many_arguments)]
314pub trait Filesystem {
315 fn init(&self, _req: &Request<'_>, _config: &mut KernelConfig) -> Result<(), c_int> {
319 Ok(())
320 }
321
322 fn destroy(&self) {}
325
326 fn lookup(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEntry) {
328 warn!("[Not Implemented] lookup(parent: {parent:#x?}, name {name:?})");
329 reply.error(ENOSYS);
330 }
331
332 fn forget(&self, _req: &Request<'_>, _ino: u64, _nlookup: u64) {}
340
341 fn batch_forget(&self, req: &Request<'_>, nodes: &[fuse_forget_one]) {
344 for node in nodes {
345 self.forget(req, node.nodeid, node.nlookup);
346 }
347 }
348
349 fn getattr(&self, _req: &Request<'_>, ino: u64, fh: Option<u64>, reply: ReplyAttr) {
351 warn!("[Not Implemented] getattr(ino: {ino:#x?}, fh: {fh:#x?})");
352 reply.error(ENOSYS);
353 }
354
355 fn setattr(
357 &self,
358 _req: &Request<'_>,
359 ino: u64,
360 mode: Option<u32>,
361 uid: Option<u32>,
362 gid: Option<u32>,
363 size: Option<u64>,
364 _atime: Option<TimeOrNow>,
365 _mtime: Option<TimeOrNow>,
366 _ctime: Option<SystemTime>,
367 fh: Option<u64>,
368 _crtime: Option<SystemTime>,
369 _chgtime: Option<SystemTime>,
370 _bkuptime: Option<SystemTime>,
371 flags: Option<u32>,
372 reply: ReplyAttr,
373 ) {
374 warn!(
375 "[Not Implemented] setattr(ino: {ino:#x?}, mode: {mode:?}, uid: {uid:?}, \
376 gid: {gid:?}, size: {size:?}, fh: {fh:?}, flags: {flags:?})"
377 );
378 reply.error(ENOSYS);
379 }
380
381 fn readlink(&self, _req: &Request<'_>, ino: u64, reply: ReplyData) {
383 warn!("[Not Implemented] readlink(ino: {ino:#x?})");
384 reply.error(ENOSYS);
385 }
386
387 fn mknod(
390 &self,
391 _req: &Request<'_>,
392 parent: u64,
393 name: &OsStr,
394 mode: u32,
395 umask: u32,
396 rdev: u32,
397 reply: ReplyEntry,
398 ) {
399 warn!(
400 "[Not Implemented] mknod(parent: {parent:#x?}, name: {name:?}, \
401 mode: {mode}, umask: {umask:#x?}, rdev: {rdev})"
402 );
403 reply.error(ENOSYS);
404 }
405
406 fn mkdir(
408 &self,
409 _req: &Request<'_>,
410 parent: u64,
411 name: &OsStr,
412 mode: u32,
413 umask: u32,
414 reply: ReplyEntry,
415 ) {
416 warn!(
417 "[Not Implemented] mkdir(parent: {parent:#x?}, name: {name:?}, mode: {mode}, umask: {umask:#x?})"
418 );
419 reply.error(ENOSYS);
420 }
421
422 fn unlink(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
424 warn!("[Not Implemented] unlink(parent: {parent:#x?}, name: {name:?})",);
425 reply.error(ENOSYS);
426 }
427
428 fn rmdir(&self, _req: &Request<'_>, parent: u64, name: &OsStr, reply: ReplyEmpty) {
430 warn!("[Not Implemented] rmdir(parent: {parent:#x?}, name: {name:?})",);
431 reply.error(ENOSYS);
432 }
433
434 fn symlink(
436 &self,
437 _req: &Request<'_>,
438 parent: u64,
439 link_name: &OsStr,
440 target: &Path,
441 reply: ReplyEntry,
442 ) {
443 warn!(
444 "[Not Implemented] symlink(parent: {parent:#x?}, link_name: {link_name:?}, target: {target:?})",
445 );
446 reply.error(EPERM);
447 }
448
449 fn rename(
451 &self,
452 _req: &Request<'_>,
453 parent: u64,
454 name: &OsStr,
455 newparent: u64,
456 newname: &OsStr,
457 flags: u32,
458 reply: ReplyEmpty,
459 ) {
460 warn!(
461 "[Not Implemented] rename(parent: {parent:#x?}, name: {name:?}, \
462 newparent: {newparent:#x?}, newname: {newname:?}, flags: {flags})",
463 );
464 reply.error(ENOSYS);
465 }
466
467 fn link(
469 &self,
470 _req: &Request<'_>,
471 ino: u64,
472 newparent: u64,
473 newname: &OsStr,
474 reply: ReplyEntry,
475 ) {
476 warn!(
477 "[Not Implemented] link(ino: {ino:#x?}, newparent: {newparent:#x?}, newname: {newname:?})"
478 );
479 reply.error(EPERM);
480 }
481
482 fn open(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) {
491 reply.opened(0, 0);
492 }
493
494 fn read(
505 &self,
506 _req: &Request<'_>,
507 ino: u64,
508 fh: u64,
509 offset: i64,
510 size: u32,
511 flags: i32,
512 lock_owner: Option<u64>,
513 reply: ReplyData,
514 ) {
515 warn!(
516 "[Not Implemented] read(ino: {ino:#x?}, fh: {fh}, offset: {offset}, \
517 size: {size}, flags: {flags:#x?}, lock_owner: {lock_owner:?})"
518 );
519 reply.error(ENOSYS);
520 }
521
522 fn write(
535 &self,
536 _req: &Request<'_>,
537 ino: u64,
538 fh: u64,
539 offset: i64,
540 data: &[u8],
541 write_flags: u32,
542 flags: i32,
543 lock_owner: Option<u64>,
544 reply: ReplyWrite,
545 ) {
546 warn!(
547 "[Not Implemented] write(ino: {ino:#x?}, fh: {fh}, offset: {offset}, \
548 data.len(): {}, write_flags: {write_flags:#x?}, flags: {flags:#x?}, \
549 lock_owner: {lock_owner:?})",
550 data.len()
551 );
552 reply.error(ENOSYS);
553 }
554
555 fn flush(&self, _req: &Request<'_>, ino: u64, fh: u64, lock_owner: u64, reply: ReplyEmpty) {
566 warn!("[Not Implemented] flush(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner:?})");
567 reply.error(ENOSYS);
568 }
569
570 fn release(
579 &self,
580 _req: &Request<'_>,
581 _ino: u64,
582 _fh: u64,
583 _flags: i32,
584 _lock_owner: Option<u64>,
585 _flush: bool,
586 reply: ReplyEmpty,
587 ) {
588 reply.ok();
589 }
590
591 fn fsync(&self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) {
595 warn!("[Not Implemented] fsync(ino: {ino:#x?}, fh: {fh}, datasync: {datasync})");
596 reply.error(ENOSYS);
597 }
598
599 fn opendir(&self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) {
607 reply.opened(0, 0);
608 }
609
610 fn readdir(&self, _req: &Request<'_>, ino: u64, fh: u64, offset: i64, reply: ReplyDirectory) {
616 warn!("[Not Implemented] readdir(ino: {ino:#x?}, fh: {fh}, offset: {offset})");
617 reply.error(ENOSYS);
618 }
619
620 fn readdirplus(
626 &self,
627 _req: &Request<'_>,
628 ino: u64,
629 fh: u64,
630 offset: i64,
631 reply: ReplyDirectoryPlus,
632 ) {
633 warn!("[Not Implemented] readdirplus(ino: {ino:#x?}, fh: {fh}, offset: {offset})");
634 reply.error(ENOSYS);
635 }
636
637 fn releasedir(&self, _req: &Request<'_>, _ino: u64, _fh: u64, _flags: i32, reply: ReplyEmpty) {
642 reply.ok();
643 }
644
645 fn fsyncdir(&self, _req: &Request<'_>, ino: u64, fh: u64, datasync: bool, reply: ReplyEmpty) {
650 warn!("[Not Implemented] fsyncdir(ino: {ino:#x?}, fh: {fh}, datasync: {datasync})");
651 reply.error(ENOSYS);
652 }
653
654 fn statfs(&self, _req: &Request<'_>, _ino: u64, reply: ReplyStatfs) {
656 reply.statfs(0, 0, 0, 0, 0, 512, 255, 0);
657 }
658
659 fn setxattr(
661 &self,
662 _req: &Request<'_>,
663 ino: u64,
664 name: &OsStr,
665 _value: &[u8],
666 flags: i32,
667 position: u32,
668 reply: ReplyEmpty,
669 ) {
670 warn!(
671 "[Not Implemented] setxattr(ino: {ino:#x?}, name: {name:?}, \
672 flags: {flags:#x?}, position: {position})"
673 );
674 reply.error(ENOSYS);
675 }
676
677 fn getxattr(&self, _req: &Request<'_>, ino: u64, name: &OsStr, size: u32, reply: ReplyXattr) {
682 warn!("[Not Implemented] getxattr(ino: {ino:#x?}, name: {name:?}, size: {size})");
683 reply.error(ENOSYS);
684 }
685
686 fn listxattr(&self, _req: &Request<'_>, ino: u64, size: u32, reply: ReplyXattr) {
691 warn!("[Not Implemented] listxattr(ino: {ino:#x?}, size: {size})");
692 reply.error(ENOSYS);
693 }
694
695 fn removexattr(&self, _req: &Request<'_>, ino: u64, name: &OsStr, reply: ReplyEmpty) {
697 warn!("[Not Implemented] removexattr(ino: {ino:#x?}, name: {name:?})");
698 reply.error(ENOSYS);
699 }
700
701 fn access(&self, _req: &Request<'_>, ino: u64, mask: i32, reply: ReplyEmpty) {
706 warn!("[Not Implemented] access(ino: {ino:#x?}, mask: {mask})");
707 reply.error(ENOSYS);
708 }
709
710 fn create(
721 &self,
722 _req: &Request<'_>,
723 parent: u64,
724 name: &OsStr,
725 mode: u32,
726 umask: u32,
727 flags: i32,
728 reply: ReplyCreate,
729 ) {
730 warn!(
731 "[Not Implemented] create(parent: {parent:#x?}, name: {name:?}, mode: {mode}, \
732 umask: {umask:#x?}, flags: {flags:#x?})"
733 );
734 reply.error(ENOSYS);
735 }
736
737 fn getlk(
739 &self,
740 _req: &Request<'_>,
741 ino: u64,
742 fh: u64,
743 lock_owner: u64,
744 start: u64,
745 end: u64,
746 typ: i32,
747 pid: u32,
748 reply: ReplyLock,
749 ) {
750 warn!(
751 "[Not Implemented] getlk(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner}, \
752 start: {start}, end: {end}, typ: {typ}, pid: {pid})"
753 );
754 reply.error(ENOSYS);
755 }
756
757 fn setlk(
765 &self,
766 _req: &Request<'_>,
767 ino: u64,
768 fh: u64,
769 lock_owner: u64,
770 start: u64,
771 end: u64,
772 typ: i32,
773 pid: u32,
774 sleep: bool,
775 reply: ReplyEmpty,
776 ) {
777 warn!(
778 "[Not Implemented] setlk(ino: {ino:#x?}, fh: {fh}, lock_owner: {lock_owner}, \
779 start: {start}, end: {end}, typ: {typ}, pid: {pid}, sleep: {sleep})"
780 );
781 reply.error(ENOSYS);
782 }
783
784 fn bmap(&self, _req: &Request<'_>, ino: u64, blocksize: u32, idx: u64, reply: ReplyBmap) {
788 warn!("[Not Implemented] bmap(ino: {ino:#x?}, blocksize: {blocksize}, idx: {idx})",);
789 reply.error(ENOSYS);
790 }
791
792 fn ioctl(
794 &self,
795 _req: &Request<'_>,
796 ino: u64,
797 fh: u64,
798 flags: u32,
799 cmd: u32,
800 in_data: &[u8],
801 out_size: u32,
802 reply: ReplyIoctl,
803 ) {
804 warn!(
805 "[Not Implemented] ioctl(ino: {ino:#x?}, fh: {fh}, flags: {flags}, \
806 cmd: {cmd}, in_data.len(): {}, out_size: {out_size})",
807 in_data.len()
808 );
809 reply.error(ENOSYS);
810 }
811
812 fn poll(
814 &self,
815 _req: &Request<'_>,
816 ino: u64,
817 fh: u64,
818 ph: PollHandle,
819 events: u32,
820 flags: u32,
821 reply: ReplyPoll,
822 ) {
823 warn!(
824 "[Not Implemented] poll(ino: {ino:#x?}, fh: {fh}, \
825 ph: {ph:?}, events: {events}, flags: {flags})"
826 );
827 reply.error(ENOSYS);
828 }
829
830 fn fallocate(
832 &self,
833 _req: &Request<'_>,
834 ino: u64,
835 fh: u64,
836 offset: i64,
837 length: i64,
838 mode: i32,
839 reply: ReplyEmpty,
840 ) {
841 warn!(
842 "[Not Implemented] fallocate(ino: {ino:#x?}, fh: {fh}, \
843 offset: {offset}, length: {length}, mode: {mode})"
844 );
845 reply.error(ENOSYS);
846 }
847
848 fn lseek(
850 &self,
851 _req: &Request<'_>,
852 ino: u64,
853 fh: u64,
854 offset: i64,
855 whence: i32,
856 reply: ReplyLseek,
857 ) {
858 warn!(
859 "[Not Implemented] lseek(ino: {ino:#x?}, fh: {fh}, \
860 offset: {offset}, whence: {whence})"
861 );
862 reply.error(ENOSYS);
863 }
864
865 fn copy_file_range(
867 &self,
868 _req: &Request<'_>,
869 ino_in: u64,
870 fh_in: u64,
871 offset_in: i64,
872 ino_out: u64,
873 fh_out: u64,
874 offset_out: i64,
875 len: u64,
876 flags: u32,
877 reply: ReplyWrite,
878 ) {
879 warn!(
880 "[Not Implemented] copy_file_range(ino_in: {ino_in:#x?}, fh_in: {fh_in}, \
881 offset_in: {offset_in}, ino_out: {ino_out:#x?}, fh_out: {fh_out}, \
882 offset_out: {offset_out}, len: {len}, flags: {flags})"
883 );
884 reply.error(ENOSYS);
885 }
886
887 #[cfg(target_os = "macos")]
890 fn setvolname(&self, _req: &Request<'_>, name: &OsStr, reply: ReplyEmpty) {
891 warn!("[Not Implemented] setvolname(name: {name:?})");
892 reply.error(ENOSYS);
893 }
894
895 #[cfg(target_os = "macos")]
897 fn exchange(
898 &self,
899 _req: &Request<'_>,
900 parent: u64,
901 name: &OsStr,
902 newparent: u64,
903 newname: &OsStr,
904 options: u64,
905 reply: ReplyEmpty,
906 ) {
907 warn!(
908 "[Not Implemented] exchange(parent: {parent:#x?}, name: {name:?}, \
909 newparent: {newparent:#x?}, newname: {newname:?}, options: {options})"
910 );
911 reply.error(ENOSYS);
912 }
913
914 #[cfg(target_os = "macos")]
917 fn getxtimes(&self, _req: &Request<'_>, ino: u64, reply: ReplyXTimes) {
918 warn!("[Not Implemented] getxtimes(ino: {ino:#x?})");
919 reply.error(ENOSYS);
920 }
921}
922
923#[deprecated(note = "use mount2() instead")]
928pub fn mount<FS: Filesystem, P: AsRef<Path>>(
929 filesystem: FS,
930 mountpoint: P,
931 options: &[&OsStr],
932) -> io::Result<()> {
933 let options = parse_options_from_args(options)?;
934 mount2(filesystem, mountpoint, options.as_ref())
935}
936
937pub fn mount2<FS: Filesystem, P: AsRef<Path>>(
942 filesystem: FS,
943 mountpoint: P,
944 options: &[MountOption],
945) -> io::Result<()> {
946 check_option_conflicts(options)?;
947 Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.run())
948}
949
950#[deprecated(note = "use spawn_mount2() instead")]
956pub fn spawn_mount<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef<Path>>(
957 filesystem: FS,
958 mountpoint: P,
959 options: &[&OsStr],
960) -> io::Result<BackgroundSession> {
961 let options: Option<Vec<_>> = options
962 .iter()
963 .map(|x| Some(MountOption::from_str(x.to_str()?)))
964 .collect();
965 let options = options.ok_or(ErrorKind::InvalidData)?;
966 Session::new(filesystem, mountpoint.as_ref(), options.as_ref()).and_then(|se| se.spawn())
967}
968
969pub fn spawn_mount2<'a, FS: Filesystem + Send + 'static + 'a, P: AsRef<Path>>(
977 filesystem: FS,
978 mountpoint: P,
979 options: &[MountOption],
980) -> io::Result<BackgroundSession> {
981 check_option_conflicts(options)?;
982 Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.spawn())
983}