1use std::borrow::Cow;
9use std::fs::File;
10use std::io;
11use std::os::fd::AsFd;
12use std::os::fd::BorrowedFd;
13use std::os::fd::OwnedFd;
14use std::path::Path;
15use std::sync::Arc;
16use std::thread::JoinHandle;
17use std::thread::{self};
18
19use log::debug;
20use log::error;
21use log::info;
22use log::warn;
23use nix::unistd::Uid;
24use nix::unistd::geteuid;
25use parking_lot::Mutex;
26
27use crate::Errno;
28use crate::Filesystem;
29use crate::KernelConfig;
30use crate::MountOption;
31use crate::ReplyEmpty;
32use crate::Request;
33use crate::channel::Channel;
34use crate::channel::ChannelSender;
35use crate::dev_fuse::DevFuse;
36use crate::ll;
37use crate::ll::Operation;
38use crate::ll::ResponseErrno;
39use crate::ll::Version;
40use crate::ll::flags::init_flags::InitFlags;
41use crate::ll::fuse_abi as abi;
42use crate::mnt::Mount;
43use crate::mnt::mount_options::Config;
44use crate::notify::Notifier;
45use crate::read_buf::FuseReadBuf;
46use crate::reply::Reply;
47use crate::reply::ReplyRaw;
48use crate::reply::ReplySender;
49use crate::request::RequestWithSender;
50
51pub(crate) const MAX_WRITE_SIZE: usize = 16 * 1024 * 1024;
55
56#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
57pub enum SessionACL {
59 All,
61 RootAndOwner,
63 #[default]
65 Owner,
66}
67
68impl SessionACL {
69 #[allow(dead_code)]
73 pub(crate) fn to_mount_option(self) -> Option<&'static str> {
74 match self {
75 SessionACL::All | SessionACL::RootAndOwner => Some("allow_other"),
76 SessionACL::Owner => None,
77 }
78 }
79}
80
81#[derive(Debug)]
83pub(crate) struct FilesystemHolder<FS: Filesystem> {
84 pub(crate) fs: Option<FS>,
85}
86
87impl<FS: Filesystem> FilesystemHolder<FS> {
88 fn destroy(&mut self) {
89 if let Some(mut fs) = self.fs.take() {
90 fs.destroy();
91 }
92 }
93}
94
95impl<FS: Filesystem> Drop for FilesystemHolder<FS> {
96 fn drop(&mut self) {
97 self.destroy();
98 }
99}
100
101#[derive(Debug)]
102struct UmountOnDrop {
103 mount: Arc<Mutex<Option<Mount>>>,
104}
105
106impl UmountOnDrop {
107 fn umount(&self) -> io::Result<()> {
108 if let Some(mount) = self.mount.lock().take() {
109 mount.umount()?;
110 }
111 Ok(())
112 }
113}
114
115impl Drop for UmountOnDrop {
116 fn drop(&mut self) {
117 if let Err(e) = self.umount() {
118 warn!("Failed to umount filesystem: {}", e);
119 }
120 }
121}
122
123#[derive(Debug)]
125pub struct Session<FS: Filesystem> {
126 pub(crate) filesystem: FilesystemHolder<FS>,
128 pub(crate) ch: Channel,
130 mount: UmountOnDrop,
132 pub(crate) allowed: SessionACL,
135 pub(crate) session_owner: Uid,
137 pub(crate) proto_version: Option<Version>,
140 pub(crate) config: Config,
141}
142
143impl<FS: Filesystem> AsFd for Session<FS> {
144 fn as_fd(&self) -> BorrowedFd<'_> {
145 self.ch.as_fd()
146 }
147}
148
149impl<FS: Filesystem> Session<FS> {
150 pub fn new<P: AsRef<Path>>(
154 filesystem: FS,
155 mountpoint: P,
156 options: &Config,
157 ) -> io::Result<Session<FS>> {
158 let mountpoint = mountpoint.as_ref();
159 info!("Mounting {}", mountpoint.display());
160 if options.mount_options.contains(&MountOption::AutoUnmount)
163 && options.acl == SessionACL::Owner
164 {
165 return Err(io::Error::new(
166 io::ErrorKind::InvalidInput,
167 format!("auto_unmount requires acl != Owner, got: {:?}", options.acl),
168 ));
169 }
170 let (file, mount) = Mount::new(mountpoint, &options.mount_options, options.acl)?;
171
172 let ch = Channel::new(file);
173
174 let mut session = Session {
175 filesystem: FilesystemHolder {
176 fs: Some(filesystem),
177 },
178 ch,
179 mount: UmountOnDrop {
180 mount: Arc::new(Mutex::new(Some(mount))),
181 },
182 allowed: options.acl,
183 session_owner: geteuid(),
184 proto_version: None,
185 config: options.clone(),
186 };
187
188 session.handshake()?;
189
190 Ok(session)
191 }
192
193 pub fn from_fd(
196 filesystem: FS,
197 fd: OwnedFd,
198 acl: SessionACL,
199 config: Config,
200 ) -> io::Result<Self> {
201 let ch = Channel::new(Arc::new(DevFuse(File::from(fd))));
202 let mut session = Session {
203 filesystem: FilesystemHolder {
204 fs: Some(filesystem),
205 },
206 ch,
207 mount: UmountOnDrop {
208 mount: Arc::new(Mutex::new(None)),
209 },
210 allowed: acl,
211 session_owner: geteuid(),
212 proto_version: None,
213 config,
214 };
215
216 session.handshake()?;
217
218 Ok(session)
219 }
220
221 pub fn spawn(self) -> io::Result<BackgroundSession> {
224 let sender = self.ch.sender();
225 let mount = std::mem::take(&mut *self.mount.mount.lock());
227 let guard = thread::Builder::new()
228 .name("fuser-bg".to_string())
229 .spawn(move || self.run())?;
230 Ok(BackgroundSession {
231 guard,
232 sender,
233 mount,
234 })
235 }
236
237 pub(crate) fn run(self) -> io::Result<()> {
244 let Session {
245 filesystem,
246 ch,
247 mount: _do_not_umount_yet,
248 allowed,
249 session_owner,
250 proto_version: _,
251 config,
252 } = self;
253
254 let n_threads = config.n_threads.unwrap_or(1);
255
256 if !cfg!(target_os = "linux") && n_threads != 1 {
257 return Err(io::Error::other(
259 "n_threads != 1 is only supported on Linux",
260 ));
261 }
262
263 let Some(n_threads_minus_one) = n_threads.checked_sub(1) else {
264 return Err(io::Error::other("n_threads"));
265 };
266
267 let mut filesystem = Arc::new(filesystem);
268
269 let mut channels = Vec::with_capacity(n_threads);
270
271 for _ in 0..n_threads_minus_one {
272 if config.clone_fd {
273 #[cfg(target_os = "linux")]
274 {
275 channels.push(ch.clone_fd()?);
276 continue;
277 }
278 #[cfg(not(target_os = "linux"))]
279 {
280 return Err(io::Error::other("clone_fd is only supported on Linux"));
281 }
282 } else {
283 channels.push(ch.clone());
284 }
285 }
286 channels.push(ch);
287
288 let mut threads = Vec::with_capacity(n_threads);
289
290 for (i, ch) in channels.into_iter().enumerate() {
291 let thread_name = format!("fuser-{i}");
292 let event_loop = SessionEventLoop {
293 thread_name: thread_name.clone(),
294 filesystem: filesystem.clone(),
295 ch,
296 allowed,
297 session_owner,
298 };
299 threads.push(
300 thread::Builder::new()
301 .name(thread_name)
302 .spawn(move || event_loop.event_loop())?,
303 );
304 }
305
306 let mut reply: io::Result<()> = Ok(());
307 for thread in threads {
308 let res = match thread.join() {
309 Ok(res) => res,
310 Err(_) => {
311 return Err(io::Error::other("event loop thread panicked"));
312 }
313 };
314 if let Err(e) = res {
315 if reply.is_ok() {
316 reply = Err(e);
317 }
318 }
319 }
320
321 let Some(filesystem) = Arc::get_mut(&mut filesystem) else {
322 return Err(io::Error::other(
323 "BUG: must have one refcount for filesystem",
324 ));
325 };
326
327 filesystem.destroy();
328
329 reply
330 }
331
332 fn handshake(&mut self) -> io::Result<()> {
333 let mut buf = FuseReadBuf::new();
334 let buf = buf.as_mut();
335
336 loop {
337 let size = match self.ch.receive_retrying(buf) {
339 Ok(size) => size,
340 Err(nix::errno::Errno::ENODEV) => {
341 return Err(io::Error::new(
342 io::ErrorKind::NotConnected,
343 "FUSE device disconnected during handshake",
344 ));
345 }
346 Err(err) => return Err(err.into()),
347 };
348
349 let request = match ll::AnyRequest::try_from(&buf[..size]) {
351 Ok(request) => request,
352 Err(err) => {
353 error!("{err}");
354 return Err(io::Error::new(io::ErrorKind::InvalidData, err.to_string()));
355 }
356 };
357
358 let op = match request.operation() {
360 Ok(op) => op,
361 Err(_) => {
362 return Err(io::Error::new(
363 io::ErrorKind::InvalidData,
364 "Failed to parse FUSE operation",
365 ));
366 }
367 };
368
369 let init = match op {
370 ll::Operation::Init(init) => init,
371 _ => {
372 error!("Received non-init FUSE operation before init: {}", request);
373 <ReplyRaw as Reply>::new(
375 request.unique(),
376 ReplySender::Channel(self.ch.sender()),
377 )
378 .send_ll(&ResponseErrno(ll::Errno::EIO));
379 return Err(io::Error::new(
380 io::ErrorKind::InvalidData,
381 "Received non-init FUSE operation during handshake",
382 ));
383 }
384 };
385
386 let v = init.version();
387 if v.0 > abi::FUSE_KERNEL_VERSION {
388 debug!(
391 "INIT: Kernel version {} > our version {}, sending our version and waiting for next init",
392 v.0,
393 abi::FUSE_KERNEL_VERSION
394 );
395 let response = init.reply_version_only();
396 <ReplyRaw as Reply>::new(request.unique(), ReplySender::Channel(self.ch.sender()))
397 .send_ll(&response);
398 continue;
399 }
400
401 if v < Version(7, 6) {
403 error!("Unsupported FUSE ABI version {v}");
404 <ReplyRaw as Reply>::new(request.unique(), ReplySender::Channel(self.ch.sender()))
405 .send_ll(&ResponseErrno(ll::Errno::EPROTO));
406 return Err(io::Error::new(
407 io::ErrorKind::Unsupported,
408 format!("Unsupported FUSE ABI version {v}"),
409 ));
410 }
411
412 let mut config = KernelConfig::new(init.capabilities(), init.max_readahead(), v);
413
414 let Some(filesystem) = &mut self.filesystem.fs else {
416 return Err(io::Error::new(
417 io::ErrorKind::InvalidInput,
418 "Bug: filesystem must be initialized during handshake",
419 ));
420 };
421 let res = filesystem.init(Request::ref_cast(request.header()), &mut config);
422 if let Err(error) = res {
423 let errno = Errno::from_i32(error.raw_os_error().unwrap_or(0));
424 <ReplyRaw as Reply>::new(request.unique(), ReplySender::Channel(self.ch.sender()))
425 .send_ll(&ResponseErrno(errno));
426 return Err(error);
427 }
428
429 self.proto_version = Some(v);
431
432 for bit in 0..64 {
434 let bitflags = InitFlags::from_bits_retain(1 << bit);
435 if bitflags == InitFlags::FUSE_INIT_EXT {
436 continue;
437 }
438 let bitflag_is_known = InitFlags::all().contains(bitflags);
439 let kernel_supports = init.capabilities().contains(bitflags);
440 let we_requested = config.requested.contains(bitflags);
441 let name = if let Some((name, _)) = bitflags.iter_names().last() {
444 Cow::Borrowed(name)
445 } else {
446 Cow::Owned(format!("(1 << {bit})"))
447 };
448 if we_requested && kernel_supports {
449 debug!("capability {name} enabled")
450 } else if we_requested {
451 debug!("capability {name} not supported by kernel")
452 } else if kernel_supports {
453 debug!("capability {name} not requested by client")
454 } else if bitflag_is_known {
455 debug!("capability {name} not supported nor requested")
456 }
457 }
458
459 debug!(
461 "INIT response: ABI {}.{}, flags {:#x}, max readahead {}, max write {}",
462 abi::FUSE_KERNEL_VERSION,
463 abi::FUSE_KERNEL_MINOR_VERSION,
464 init.capabilities() & config.requested,
465 config.max_readahead,
466 config.max_write
467 );
468
469 let response = init.reply(&config);
470 <ReplyRaw as Reply>::new(request.unique(), ReplySender::Channel(self.ch.sender()))
471 .send_ll(&response);
472
473 return Ok(());
474 }
475 }
476
477 pub fn unmount(&mut self) -> io::Result<()> {
479 self.mount.umount()
480 }
481
482 pub fn unmount_callable(&mut self) -> SessionUnmounter {
484 SessionUnmounter {
485 mount: self.mount.mount.clone(),
486 }
487 }
488
489 pub fn notifier(&self) -> Notifier {
491 Notifier::new(self.ch.sender())
492 }
493}
494
495#[derive(Debug)]
496pub struct SessionUnmounter {
498 mount: Arc<Mutex<Option<Mount>>>,
499}
500
501impl SessionUnmounter {
502 pub fn unmount(&mut self) -> io::Result<()> {
504 if let Some(mount) = std::mem::take(&mut *self.mount.lock()) {
505 mount.umount()?;
506 }
507 Ok(())
508 }
509}
510
511pub(crate) struct SessionEventLoop<FS: Filesystem> {
512 pub(crate) thread_name: String,
514 pub(crate) ch: Channel,
515 pub(crate) filesystem: Arc<FilesystemHolder<FS>>,
516 pub(crate) allowed: SessionACL,
517 pub(crate) session_owner: Uid,
518}
519
520impl<FS: Filesystem> SessionEventLoop<FS> {
521 fn event_loop(&self) -> io::Result<()> {
522 let mut buf = FuseReadBuf::new();
525 let buf = buf.as_mut();
526 loop {
527 match self.ch.receive_retrying(buf) {
530 Ok(size) => match RequestWithSender::new(self.ch.sender(), &buf[..size]) {
531 Some(req) => {
533 if let Ok(Operation::Destroy(_)) = req.request.operation() {
534 req.reply::<ReplyEmpty>().ok();
535 return Ok(());
536 } else {
537 req.dispatch(self)
538 }
539 }
540 None => {
542 return Err(io::Error::new(
543 io::ErrorKind::InvalidData,
544 "Invalid request",
545 ));
546 }
547 },
548 Err(nix::errno::Errno::ENODEV) => return Ok(()),
549 Err(err) => return Err(err.into()),
550 }
551 }
552 }
553}
554
555#[derive(Debug)]
557pub struct BackgroundSession {
558 pub guard: JoinHandle<io::Result<()>>,
560 sender: ChannelSender,
562 mount: Option<Mount>,
564}
565
566impl BackgroundSession {
567 pub fn umount_and_join(mut self) -> io::Result<()> {
569 if let Some(mount) = self.mount.take() {
570 mount.umount()?;
571 }
572 self.join()
573 }
574
575 pub fn notifier(&self) -> Notifier {
577 Notifier::new(self.sender.clone())
578 }
579
580 pub fn join(self) -> io::Result<()> {
582 self.guard
583 .join()
584 .map_err(|_panic: Box<dyn std::any::Any + Send>| {
585 io::Error::new(
586 io::ErrorKind::Other,
587 "filesystem background thread panicked",
588 )
589 })?
590 }
591}