1#![recursion_limit = "512"]
6
7use fuchsia_rcu::RcuReadScope;
8use linux_uapi::FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2;
9use starnix_core::mm::{MemoryAccessorExt, PAGE_SIZE};
10use starnix_core::mutable_state::Guard;
11use starnix_core::security;
12use starnix_core::task::waiter::WaiterOptions;
13use starnix_core::task::{CurrentTask, EventHandler, Kernel, WaitCanceler, WaitQueue, Waiter};
14use starnix_core::vfs::buffers::{
15 Buffer, InputBuffer, InputBufferExt as _, OutputBuffer, OutputBufferCallback,
16};
17use starnix_core::vfs::pseudo::dynamic_file::{DynamicFile, DynamicFileBuf, DynamicFileSource};
18use starnix_core::vfs::pseudo::simple_directory::SimpleDirectory;
19use starnix_core::vfs::pseudo::simple_file::SimpleFileNode;
20use starnix_core::vfs::pseudo::vec_directory::{VecDirectory, VecDirectoryEntry};
21use starnix_core::vfs::{
22 AppendLockWriteGuard, CacheMode, CheckAccessReason, DirEntry, DirEntryOps, DirectoryEntryType,
23 DirentSink, FallocMode, FdNumber, FileObject, FileObjectState, FileOps, FileSystem,
24 FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode, FsNodeFlags, FsNodeHandle,
25 FsNodeInfo, FsNodeOps, FsStr, FsString, NamespaceNode, PeekBufferSegmentsCallback, SeekTarget,
26 SymlinkTarget, ValueOrSize, WeakFileHandle, XattrOp, default_eof_offset, default_fcntl,
27 default_ioctl, default_seek, fileops_impl_nonseekable, fileops_impl_noop_sync, fs_args,
28 fs_node_impl_dir_readonly,
29};
30use starnix_lifecycle::AtomicCounter;
31use starnix_logging::{log_error, log_trace, log_warn, track_stub};
32use starnix_sync::{
33 AtomicMonotonicInstant, FileOpsCore, LockEqualOrBefore, Locked, Mutex, MutexGuard, RwLock,
34 RwLockReadGuard, RwLockWriteGuard, Unlocked,
35};
36use starnix_syscalls::{SyscallArg, SyscallResult};
37use starnix_types::time::{NANOS_PER_SECOND, duration_from_timespec, time_from_timespec};
38use starnix_types::vfs::default_statfs;
39use starnix_uapi::auth::FsCred;
40use starnix_uapi::device_id::DeviceId;
41use starnix_uapi::errors::{EINTR, EINVAL, ENOENT, ENOSYS, Errno};
42use starnix_uapi::file_mode::{Access, FileMode};
43use starnix_uapi::math::round_up_to_increment;
44use starnix_uapi::open_flags::OpenFlags;
45use starnix_uapi::vfs::FdEvents;
46use starnix_uapi::{
47 FUSE_SUPER_MAGIC, errno, errno_from_code, error, ino_t, mode, off_t, statfs, uapi,
48};
49use std::collections::hash_map::Entry;
50use std::collections::{HashMap, VecDeque};
51use std::ops::{Deref, DerefMut};
52use std::sync::atomic::{AtomicBool, Ordering};
53use std::sync::{Arc, Weak};
54use syncio::zxio_node_attr_has_t;
55use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
56
57const FUSE_ROOT_ID_U64: u64 = uapi::FUSE_ROOT_ID as u64;
58const CONFIGURATION_AVAILABLE_EVENT: u64 = std::u64::MAX;
59
60uapi::check_arch_independent_layout! {
61 fuse_access_in {}
62 fuse_attr {}
63 fuse_attr_out {}
64 fuse_create_in {}
65 fuse_dirent {}
66 fuse_entry_bpf_out {}
67 fuse_entry_out {}
68 fuse_flush_in {}
69 fuse_forget_in {}
70 fuse_getxattr_in {}
71 fuse_getxattr_out {}
72 fuse_in_header {}
73 fuse_init_in {}
74 fuse_init_out {}
75 fuse_interrupt_in {}
76 fuse_link_in {}
77 fuse_lseek_in {}
78 fuse_lseek_out {}
79 fuse_mkdir_in {}
80 fuse_mknod_in {}
81 fuse_opcode {}
82 fuse_open_in {}
83 fuse_open_out {}
84 fuse_out_header {}
85 fuse_poll_in {}
86 fuse_poll_out {}
87 fuse_read_in {}
88 fuse_release_in {}
89 fuse_rename2_in {}
90 fuse_setattr_in {}
91 fuse_setxattr_in {}
92 fuse_statfs_out {}
93 fuse_write_in {}
94 fuse_write_out {}
95}
96
97#[derive(Debug)]
98struct DevFuse {
99 connection: Arc<FuseConnection>,
100}
101
102pub fn open_fuse_device(
103 _locked: &mut Locked<FileOpsCore>,
104 current_task: &CurrentTask,
105 _id: DeviceId,
106 _node: &NamespaceNode,
107 _flags: OpenFlags,
108) -> Result<Box<dyn FileOps>, Errno> {
109 let connection = fuse_connections(current_task.kernel()).new_connection(current_task);
110 Ok(Box::new(DevFuse { connection }))
111}
112
113fn attr_valid_to_duration(
114 attr_valid: u64,
115 attr_valid_nsec: u32,
116) -> Result<zx::MonotonicDuration, Errno> {
117 duration_from_timespec(uapi::timespec {
118 tv_sec: i64::try_from(attr_valid).unwrap_or(std::i64::MAX),
119 tv_nsec: attr_valid_nsec.into(),
120 })
121}
122
123impl FileOps for DevFuse {
124 fileops_impl_nonseekable!();
125 fileops_impl_noop_sync!();
126
127 fn close(
128 self: Box<Self>,
129 _locked: &mut Locked<FileOpsCore>,
130 _file: &FileObjectState,
131 _current_task: &CurrentTask,
132 ) {
133 self.connection.lock().disconnect();
134 }
135
136 fn read(
137 &self,
138 locked: &mut Locked<FileOpsCore>,
139 file: &FileObject,
140 current_task: &CurrentTask,
141 offset: usize,
142 data: &mut dyn OutputBuffer,
143 ) -> Result<usize, Errno> {
144 debug_assert!(offset == 0);
145 file.blocking_op(locked, current_task, FdEvents::POLLIN, None, |_| {
146 self.connection.lock().read(data)
147 })
148 }
149
150 fn write(
151 &self,
152 _locked: &mut Locked<FileOpsCore>,
153 _file: &FileObject,
154 _current_task: &CurrentTask,
155 offset: usize,
156 data: &mut dyn InputBuffer,
157 ) -> Result<usize, Errno> {
158 debug_assert!(offset == 0);
159 self.connection.lock().write(data)
160 }
161
162 fn wait_async(
163 &self,
164 _locked: &mut Locked<FileOpsCore>,
165 _file: &FileObject,
166 _current_task: &CurrentTask,
167 waiter: &Waiter,
168 events: FdEvents,
169 handler: EventHandler,
170 ) -> Option<WaitCanceler> {
171 self.connection.lock().wait_async(waiter, events, handler)
172 }
173
174 fn query_events(
175 &self,
176 _locked: &mut Locked<FileOpsCore>,
177 _file: &FileObject,
178 _current_task: &CurrentTask,
179 ) -> Result<FdEvents, Errno> {
180 Ok(self.connection.lock().query_events())
181 }
182
183 fn ioctl(
184 &self,
185 locked: &mut Locked<Unlocked>,
186 file: &FileObject,
187 current_task: &CurrentTask,
188 request: u32,
189 arg: SyscallArg,
190 ) -> Result<SyscallResult, Errno> {
191 match request {
192 FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2 => {
193 let fd = current_task.read_object::<FdNumber>(arg.into())?;
194 let fd = current_task.get_file(fd)?;
195 let id = {
196 let mut connection = self.connection.lock();
197 let (mut id, _) = connection.last_passthrough_id.overflowing_add(1);
198 let mut entry = connection.registered_passthrough.entry(id);
199 while id == 0 || matches!(entry, Entry::Occupied(_)) {
200 let (new_id, _) = id.overflowing_add(1);
201 id = new_id;
202 entry = connection.registered_passthrough.entry(id);
203 }
204 entry.or_insert_with(|| Arc::downgrade(&fd));
205 connection.last_passthrough_id = id;
206 id
207 };
208 Ok(id.into())
209 }
210 _ => default_ioctl(file, locked, current_task, request, arg),
211 }
212 }
213}
214
215pub fn new_fuse_fs(
216 locked: &mut Locked<Unlocked>,
217 current_task: &CurrentTask,
218 options: FileSystemOptions,
219) -> Result<FileSystemHandle, Errno> {
220 let fd = fs_args::parse::<FdNumber>(
221 options.params.get(b"fd").ok_or_else(|| errno!(EINVAL))?.as_ref(),
222 )?;
223 let default_permissions = options.params.get(b"default_permissions").is_some().into();
224 let connection = current_task
225 .live()
226 .files
227 .get(fd)?
228 .downcast_file::<DevFuse>()
229 .ok_or_else(|| errno!(EINVAL))?
230 .connection
231 .clone();
232
233 let fs = FileSystem::new(
234 locked,
235 current_task.kernel(),
236 CacheMode::Cached(current_task.kernel().fs_cache_config()),
237 FuseFs { connection: connection.clone(), default_permissions },
238 options,
239 )?;
240 let fuse_node = FuseNode::new(connection.clone(), FUSE_ROOT_ID_U64, 0);
241 fuse_node.state.lock().nlookup += 1;
242
243 fs.create_root(FUSE_ROOT_ID_U64, fuse_node);
244
245 {
246 let mut state = connection.lock();
247 state.connect();
248 state.execute_operation(
249 locked,
250 current_task,
251 FuseNode::from_node(&fs.root().node),
252 FuseOperation::Init { fs: Arc::downgrade(&fs) },
253 )?;
254 }
255 Ok(fs)
256}
257
258fn fuse_connections(kernel: &Kernel) -> Arc<FuseConnections> {
259 kernel.expando.get::<FuseConnections>()
260}
261
262pub fn new_fusectl_fs(
263 locked: &mut Locked<Unlocked>,
264 current_task: &CurrentTask,
265 options: FileSystemOptions,
266) -> Result<FileSystemHandle, Errno> {
267 let fs =
268 FileSystem::new(locked, current_task.kernel(), CacheMode::Uncached, FuseCtlFs, options)?;
269
270 let root_ino = fs.allocate_ino();
271 fs.create_root_with_info(
272 root_ino,
273 FuseCtlConnectionsDirectory {},
274 FsNodeInfo::new(mode!(IFDIR, 0o755), FsCred::root()),
275 );
276
277 Ok(fs)
278}
279
280#[derive(Debug)]
281struct FuseFs {
282 connection: Arc<FuseConnection>,
283 default_permissions: AtomicBool,
284}
285
286impl FuseFs {
287 fn from_fs(fs: &FileSystem) -> &FuseFs {
293 fs.downcast_ops::<FuseFs>().expect("FUSE should only handle `FuseFs`s")
294 }
295}
296
297impl FileSystemOps for FuseFs {
298 fn rename(
299 &self,
300 locked: &mut Locked<FileOpsCore>,
301 _fs: &FileSystem,
302 current_task: &CurrentTask,
303 old_parent: &FsNodeHandle,
304 old_name: &FsStr,
305 new_parent: &FsNodeHandle,
306 new_name: &FsStr,
307 _renamed: &FsNodeHandle,
308 _replaced: Option<&FsNodeHandle>,
309 ) -> Result<(), Errno> {
310 self.connection.lock().execute_operation(
311 locked,
312 current_task,
313 FuseNode::from_node(&old_parent),
314 FuseOperation::Rename {
315 old_name: old_name.to_owned(),
316 new_dir: new_parent.node_key(),
317 new_name: new_name.to_owned(),
318 },
319 )?;
320 Ok(())
321 }
322
323 fn uses_external_node_ids(&self) -> bool {
324 true
325 }
326
327 fn statfs(
328 &self,
329 locked: &mut Locked<FileOpsCore>,
330 fs: &FileSystem,
331 current_task: &CurrentTask,
332 ) -> Result<statfs, Errno> {
333 let node = FuseNode::from_node(&fs.root().node);
334 let response = self.connection.lock().execute_operation(
335 locked,
336 current_task,
337 &node,
338 FuseOperation::Statfs,
339 )?;
340 let FuseResponse::Statfs(statfs_out) = response else {
341 return error!(EINVAL);
342 };
343 Ok(statfs {
344 f_type: FUSE_SUPER_MAGIC as i64,
345 f_blocks: statfs_out.st.blocks.try_into().map_err(|_| errno!(EINVAL))?,
346 f_bfree: statfs_out.st.bfree.try_into().map_err(|_| errno!(EINVAL))?,
347 f_bavail: statfs_out.st.bavail.try_into().map_err(|_| errno!(EINVAL))?,
348 f_files: statfs_out.st.files.try_into().map_err(|_| errno!(EINVAL))?,
349 f_ffree: statfs_out.st.ffree.try_into().map_err(|_| errno!(EINVAL))?,
350 f_bsize: statfs_out.st.bsize.try_into().map_err(|_| errno!(EINVAL))?,
351 f_namelen: statfs_out.st.namelen.try_into().map_err(|_| errno!(EINVAL))?,
352 f_frsize: statfs_out.st.frsize.try_into().map_err(|_| errno!(EINVAL))?,
353 ..statfs::default()
354 })
355 }
356 fn name(&self) -> &'static FsStr {
357 "fuse".into()
358 }
359 fn unmount(&self) {
360 self.connection.lock().disconnect();
361 }
362}
363
364#[derive(Debug, Default)]
365struct FuseConnections {
366 connections: Mutex<Vec<Weak<FuseConnection>>>,
367 next_identifier: AtomicCounter<u64>,
368}
369
370impl FuseConnections {
371 fn new_connection(&self, current_task: &CurrentTask) -> Arc<FuseConnection> {
372 let connection = Arc::new(FuseConnection {
373 id: self.next_identifier.next(),
374 creds: current_task.current_fscred(),
375 state: Default::default(),
376 });
377 self.connections.lock().push(Arc::downgrade(&connection));
378 connection
379 }
380
381 fn for_each<F>(&self, mut f: F)
382 where
383 F: FnMut(Arc<FuseConnection>),
384 {
385 self.connections.lock().retain(|connection| {
386 if let Some(connection) = connection.upgrade() {
387 f(connection);
388 true
389 } else {
390 false
391 }
392 });
393 }
394}
395
396struct FuseCtlFs;
397
398impl FileSystemOps for FuseCtlFs {
399 fn rename(
400 &self,
401 _locked: &mut Locked<FileOpsCore>,
402 _fs: &FileSystem,
403 _current_task: &CurrentTask,
404 _old_parent: &FsNodeHandle,
405 _old_name: &FsStr,
406 _new_parent: &FsNodeHandle,
407 _new_name: &FsStr,
408 _renamed: &FsNodeHandle,
409 _replaced: Option<&FsNodeHandle>,
410 ) -> Result<(), Errno> {
411 error!(ENOTSUP)
412 }
413
414 fn statfs(
415 &self,
416 _locked: &mut Locked<FileOpsCore>,
417 _fs: &FileSystem,
418 _current_task: &CurrentTask,
419 ) -> Result<statfs, Errno> {
420 const FUSE_CTL_MAGIC: u32 = 0x65735543;
422 Ok(default_statfs(FUSE_CTL_MAGIC))
423 }
424
425 fn name(&self) -> &'static FsStr {
426 "fusectl".into()
427 }
428}
429
430#[derive(Debug)]
431struct FuseCtlConnectionsDirectory;
432
433impl FsNodeOps for FuseCtlConnectionsDirectory {
434 fs_node_impl_dir_readonly!();
435
436 fn create_file_ops(
437 &self,
438 _locked: &mut Locked<FileOpsCore>,
439 _node: &FsNode,
440 current_task: &CurrentTask,
441 _flags: OpenFlags,
442 ) -> Result<Box<dyn FileOps>, Errno> {
443 let connnections = fuse_connections(current_task.kernel());
444 let mut entries = vec![];
445 connnections.for_each(|connection| {
446 entries.push(VecDirectoryEntry {
447 entry_type: DirectoryEntryType::DIR,
448 name: connection.id.to_string().into(),
449 inode: None,
450 });
451 });
452 Ok(VecDirectory::new_file(entries))
453 }
454
455 fn lookup(
456 &self,
457 _locked: &mut Locked<FileOpsCore>,
458 node: &FsNode,
459 current_task: &CurrentTask,
460 name: &FsStr,
461 ) -> Result<FsNodeHandle, Errno> {
462 let name = std::str::from_utf8(name).map_err(|_| errno!(ENOENT))?;
463 let id = name.parse::<u64>().map_err(|_| errno!(ENOENT))?;
464 let connnections = fuse_connections(current_task.kernel());
465 let mut connection = None;
466 connnections.for_each(|c| {
467 if c.id == id {
468 connection = Some(c);
469 }
470 });
471 let Some(connection) = connection else {
472 return error!(ENOENT);
473 };
474 let fs = node.fs();
475 let dir = SimpleDirectory::new();
476 dir.edit(&fs, |dir| {
477 dir.node(
478 "abort".into(),
479 fs.create_node_and_allocate_node_id(
480 AbortFile::new_node(connection.clone()),
481 FsNodeInfo::new(mode!(IFREG, 0o200), connection.creds),
482 ),
483 );
484 dir.node(
485 "waiting".into(),
486 fs.create_node_and_allocate_node_id(
487 WaitingFile::new_node(connection.clone()),
488 FsNodeInfo::new(mode!(IFREG, 0o400), connection.creds),
489 ),
490 );
491 });
492
493 let info = FsNodeInfo::new(mode!(IFDIR, 0o500), connection.creds);
494 Ok(fs.create_node_and_allocate_node_id(dir, info))
495 }
496}
497
498#[derive(Debug)]
499struct AbortFile {
500 connection: Arc<FuseConnection>,
501}
502
503impl AbortFile {
504 fn new_node(connection: Arc<FuseConnection>) -> impl FsNodeOps {
505 SimpleFileNode::new(move |_, _| Ok(Self { connection: connection.clone() }))
506 }
507}
508
509impl FileOps for AbortFile {
510 fileops_impl_nonseekable!();
511 fileops_impl_noop_sync!();
512
513 fn read(
514 &self,
515 _locked: &mut Locked<FileOpsCore>,
516 _file: &FileObject,
517 _current_task: &CurrentTask,
518 _offset: usize,
519 _data: &mut dyn OutputBuffer,
520 ) -> Result<usize, Errno> {
521 Ok(0)
522 }
523
524 fn write(
525 &self,
526 _locked: &mut Locked<FileOpsCore>,
527 _file: &FileObject,
528 _current_task: &CurrentTask,
529 _offset: usize,
530 data: &mut dyn InputBuffer,
531 ) -> Result<usize, Errno> {
532 let drained = data.drain();
533 if drained > 0 {
534 self.connection.lock().disconnect();
535 }
536 Ok(drained)
537 }
538}
539
540#[derive(Clone, Debug)]
541struct WaitingFile {
542 connection: Arc<FuseConnection>,
543}
544
545impl WaitingFile {
546 fn new_node(connection: Arc<FuseConnection>) -> impl FsNodeOps {
547 DynamicFile::new_node(Self { connection })
548 }
549}
550
551impl DynamicFileSource for WaitingFile {
552 fn generate(
553 &self,
554 _current_task: &CurrentTask,
555 sink: &mut DynamicFileBuf,
556 ) -> Result<(), Errno> {
557 let value = {
558 let state = self.connection.state.lock();
559 state.operations.len() + state.message_queue.len()
560 };
561 let value = format!("{value}\n");
562 sink.write(value.as_bytes());
563 Ok(())
564 }
565}
566
567#[derive(Debug, Default)]
568struct FuseNodeMutableState {
569 nlookup: u64,
570}
571
572#[derive(Debug)]
573struct FuseNode {
574 connection: Arc<FuseConnection>,
575
576 nodeid: u64,
583
584 generation: u64,
585 attributes_valid_until: AtomicMonotonicInstant,
586 state: Mutex<FuseNodeMutableState>,
587}
588
589impl FuseNode {
590 fn new(connection: Arc<FuseConnection>, nodeid: u64, generation: u64) -> Self {
591 Self {
592 connection,
593 nodeid,
594 generation,
595 attributes_valid_until: zx::MonotonicInstant::INFINITE_PAST.into(),
596 state: Default::default(),
597 }
598 }
599
600 fn from_node(node: &FsNode) -> &FuseNode {
606 node.downcast_ops::<FuseNode>().expect("FUSE should only handle `FuseNode`s")
607 }
608
609 fn default_check_access_with_valid_node_attributes(
610 &self,
611 locked: &mut Locked<FileOpsCore>,
612 node: &FsNode,
613 current_task: &CurrentTask,
614 permission_flags: security::PermissionFlags,
615 reason: CheckAccessReason,
616 info: &RwLock<FsNodeInfo>,
617 audit_context: security::Auditable<'_>,
618 ) -> Result<(), Errno> {
619 let info = self.refresh_expired_node_attributes(locked, current_task, info)?;
620 node.default_check_access_impl(current_task, permission_flags, reason, info, audit_context)
621 }
622
623 fn refresh_expired_node_attributes<'a>(
624 &self,
625 locked: &mut Locked<FileOpsCore>,
626 current_task: &CurrentTask,
627 info: &'a RwLock<FsNodeInfo>,
628 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
629 const VALID_UNTIL_LOAD_ORDERING: Ordering = Ordering::Relaxed;
632
633 let now = zx::MonotonicInstant::get();
634 if self.attributes_valid_until.load(VALID_UNTIL_LOAD_ORDERING) >= now {
635 let info = info.read();
636
637 if self.attributes_valid_until.load(VALID_UNTIL_LOAD_ORDERING) >= now {
649 return Ok(info);
650 }
651 }
652
653 self.fetch_and_refresh_info_impl(locked, current_task, info)
655 }
656
657 fn fetch_and_refresh_info_impl<'a>(
658 &self,
659 locked: &mut Locked<FileOpsCore>,
660 current_task: &CurrentTask,
661 info: &'a RwLock<FsNodeInfo>,
662 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
663 let response = self.connection.lock().execute_operation(
664 locked,
665 current_task,
666 self,
667 FuseOperation::GetAttr,
668 )?;
669 let uapi::fuse_attr_out { attr_valid, attr_valid_nsec, attr, .. } =
670 if let FuseResponse::Attr(attr) = response {
671 attr
672 } else {
673 return error!(EINVAL);
674 };
675 let mut info = info.write();
676 FuseNode::update_node_info_from_attr(
677 &mut info,
678 attr,
679 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
680 &self.attributes_valid_until,
681 )?;
682 Ok(RwLockWriteGuard::downgrade(info))
683 }
684
685 fn invalidate_attributes(&self) {
686 self.attributes_valid_until.store(zx::MonotonicInstant::INFINITE_PAST, Ordering::Relaxed);
687 }
688
689 fn update_node_info_from_attr(
690 info: &mut FsNodeInfo,
691 attributes: uapi::fuse_attr,
692 attr_valid_duration: zx::MonotonicDuration,
693 node_attributes_valid_until: &AtomicMonotonicInstant,
694 ) -> Result<(), Errno> {
695 info.mode = FileMode::from_bits(attributes.mode);
696 info.size = attributes.size.try_into().map_err(|_| errno!(EINVAL))?;
697 info.blocks = attributes.blocks.try_into().map_err(|_| errno!(EINVAL))?;
698 info.blksize = attributes.blksize.try_into().map_err(|_| errno!(EINVAL))?;
699 info.uid = attributes.uid;
700 info.gid = attributes.gid;
701 info.link_count = attributes.nlink.try_into().map_err(|_| errno!(EINVAL))?;
702 info.time_status_change = time_from_timespec(uapi::timespec {
703 tv_sec: attributes.ctime as i64,
704 tv_nsec: attributes.ctimensec as i64,
705 })?;
706 info.time_access = time_from_timespec(uapi::timespec {
707 tv_sec: attributes.atime as i64,
708 tv_nsec: attributes.atimensec as i64,
709 })?;
710 info.time_modify = time_from_timespec(uapi::timespec {
711 tv_sec: attributes.mtime as i64,
712 tv_nsec: attributes.mtimensec as i64,
713 })?;
714 info.rdev = DeviceId::from_bits(attributes.rdev as u64);
715
716 node_attributes_valid_until
717 .store(zx::MonotonicInstant::after(attr_valid_duration), Ordering::Relaxed);
718 Ok(())
719 }
720
721 fn fs_node_from_entry(
723 &self,
724 node: &FsNode,
725 name: &FsStr,
726 entry: &uapi::fuse_entry_out,
727 ) -> Result<FsNodeHandle, Errno> {
728 if entry.nodeid == 0 {
729 return error!(ENOENT);
730 }
731 let node = node.fs().get_and_validate_or_create_node(
732 entry.nodeid,
733 |node| {
734 let fuse_node = FuseNode::from_node(&node);
735 fuse_node.generation == entry.generation
736 },
737 || {
738 let fuse_node =
739 FuseNode::new(self.connection.clone(), entry.nodeid, entry.generation);
740 let mut info = FsNodeInfo::default();
741 FuseNode::update_node_info_from_attr(
742 &mut info,
743 entry.attr,
744 attr_valid_to_duration(entry.attr_valid, entry.attr_valid_nsec)?,
745 &fuse_node.attributes_valid_until,
746 )?;
747 Ok(FsNode::new_uncached(
748 entry.attr.ino,
749 fuse_node,
750 &node.fs(),
751 info,
752 FsNodeFlags::empty(),
753 ))
754 },
755 )?;
756 if !DirEntry::is_reserved_name(name) {
758 let fuse_node = FuseNode::from_node(&node);
759 fuse_node.state.lock().nlookup += 1;
760 }
761 Ok(node)
762 }
763}
764
765struct FuseFileObject {
766 connection: Arc<FuseConnection>,
767 passthrough_file: WeakFileHandle,
769 open_out: uapi::fuse_open_out,
771}
772
773impl FuseFileObject {
774 fn get_fuse_node(file: &FileObject) -> &FuseNode {
776 FuseNode::from_node(file.node())
777 }
778}
779
780impl FileOps for FuseFileObject {
781 fn close(
782 self: Box<Self>,
783 locked: &mut Locked<FileOpsCore>,
784 file: &FileObjectState,
785 current_task: &CurrentTask,
786 ) {
787 let node = FuseNode::from_node(file.node());
788 let is_dir = file.node().is_dir();
789 {
790 let mut connection = self.connection.lock();
791 if let Err(e) = connection.execute_operation(
792 locked,
793 current_task,
794 node,
795 if is_dir {
796 FuseOperation::ReleaseDir(self.open_out)
797 } else {
798 FuseOperation::Release(self.open_out)
799 },
800 ) {
801 if e.code != ENOSYS {
802 log_error!("Error when releasing fh: {e:?}");
803 }
804 }
805 connection.clear_released_passthrough_fds();
806 }
807 }
808
809 fn flush(
810 &self,
811 locked: &mut Locked<FileOpsCore>,
812 file: &FileObject,
813 current_task: &CurrentTask,
814 ) {
815 let node = Self::get_fuse_node(file);
816 if let Err(e) = self.connection.lock().execute_operation(
817 locked,
818 current_task,
819 node,
820 FuseOperation::Flush(self.open_out),
821 ) {
822 log_error!("Error when flushing fh: {e:?}");
823 }
824 }
825
826 fn is_seekable(&self) -> bool {
827 true
828 }
829
830 fn read(
831 &self,
832 locked: &mut Locked<FileOpsCore>,
833 file: &FileObject,
834 current_task: &CurrentTask,
835 offset: usize,
836 data: &mut dyn OutputBuffer,
837 ) -> Result<usize, Errno> {
838 if file.node().info().mode.is_dir() {
839 return error!(EISDIR);
840 }
841 if let Some(file_object) = self.passthrough_file.upgrade() {
842 return file_object.ops().read(locked, &file_object, current_task, offset, data);
843 }
844 let node = Self::get_fuse_node(file);
845 let response = self.connection.lock().execute_operation(
846 locked,
847 current_task,
848 node,
849 FuseOperation::Read(uapi::fuse_read_in {
850 fh: self.open_out.fh,
851 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
852 size: data.available().try_into().unwrap_or(u32::MAX),
853 read_flags: 0,
854 lock_owner: 0,
855 flags: 0,
856 padding: 0,
857 }),
858 )?;
859 let FuseResponse::Read(read_out) = response else {
860 return error!(EINVAL);
861 };
862 data.write(&read_out)
863 }
864
865 fn write(
866 &self,
867 locked: &mut Locked<FileOpsCore>,
868 file: &FileObject,
869 current_task: &CurrentTask,
870 offset: usize,
871 data: &mut dyn InputBuffer,
872 ) -> Result<usize, Errno> {
873 if file.node().info().mode.is_dir() {
874 return error!(EISDIR);
875 }
876 if let Some(file_object) = self.passthrough_file.upgrade() {
877 return file_object.ops().write(locked, &file_object, current_task, offset, data);
878 }
879 let node = Self::get_fuse_node(file);
880 let content = data.peek_all()?;
881 let response = self.connection.lock().execute_operation(
882 locked,
883 current_task,
884 node,
885 FuseOperation::Write {
886 write_in: uapi::fuse_write_in {
887 fh: self.open_out.fh,
888 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
889 size: content.len().try_into().map_err(|_| errno!(EINVAL))?,
890 write_flags: 0,
891 lock_owner: 0,
892 flags: 0,
893 padding: 0,
894 },
895 content,
896 },
897 )?;
898 let FuseResponse::Write(write_out) = response else {
899 return error!(EINVAL);
900 };
901 node.invalidate_attributes();
902
903 let written = write_out.size as usize;
904
905 data.advance(written)?;
906 Ok(written)
907 }
908
909 fn seek(
910 &self,
911 locked: &mut Locked<FileOpsCore>,
912 file: &FileObject,
913 current_task: &CurrentTask,
914 current_offset: off_t,
915 target: SeekTarget,
916 ) -> Result<off_t, Errno> {
917 if matches!(target, SeekTarget::Data(_) | SeekTarget::Hole(_)) {
919 let node = Self::get_fuse_node(file);
920 let response = self.connection.lock().execute_operation(
921 locked,
922 current_task,
923 node,
924 FuseOperation::Seek(uapi::fuse_lseek_in {
925 fh: self.open_out.fh,
926 offset: target.offset().try_into().map_err(|_| errno!(EINVAL))?,
927 whence: target.whence(),
928 padding: 0,
929 }),
930 );
931 match response {
932 Ok(response) => {
933 let FuseResponse::Seek(seek_out) = response else {
934 return error!(EINVAL);
935 };
936 return seek_out.offset.try_into().map_err(|_| errno!(EINVAL));
937 }
938 Err(errno) if errno == ENOSYS => {}
941 Err(errno) => return Err(errno),
942 };
943 }
944
945 default_seek(current_offset, target, || default_eof_offset(locked, file, current_task))
946 }
947
948 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
949 track_stub!(TODO("https://fxbug.dev/352359968"), "FUSE fsync()");
950 Ok(())
951 }
952
953 fn wait_async(
954 &self,
955 _locked: &mut Locked<FileOpsCore>,
956 _file: &FileObject,
957 _current_task: &CurrentTask,
958 _waiter: &Waiter,
959 _events: FdEvents,
960 _handler: EventHandler,
961 ) -> Option<WaitCanceler> {
962 None
963 }
964
965 fn query_events(
966 &self,
967 locked: &mut Locked<FileOpsCore>,
968 file: &FileObject,
969 current_task: &CurrentTask,
970 ) -> Result<FdEvents, Errno> {
971 let node = Self::get_fuse_node(file);
972 let response = self.connection.lock().execute_operation(
973 locked,
974 current_task,
975 node,
976 FuseOperation::Poll(uapi::fuse_poll_in {
977 fh: self.open_out.fh,
978 kh: 0,
979 flags: 0,
980 events: FdEvents::all().bits(),
981 }),
982 )?;
983 let FuseResponse::Poll(poll_out) = response else {
984 return error!(EINVAL);
985 };
986 FdEvents::from_bits(poll_out.revents).ok_or_else(|| errno!(EINVAL))
987 }
988
989 fn readdir(
990 &self,
991 locked: &mut Locked<FileOpsCore>,
992 file: &FileObject,
993 current_task: &CurrentTask,
994 sink: &mut dyn DirentSink,
995 ) -> Result<(), Errno> {
996 let mut state = self.connection.lock();
997 let configuration = state.get_configuration(locked, current_task)?;
998 let use_readdirplus = {
999 if configuration.flags.contains(FuseInitFlags::DO_READDIRPLUS) {
1000 if configuration.flags.contains(FuseInitFlags::READDIRPLUS_AUTO) {
1001 sink.offset() == 0
1002 } else {
1003 true
1004 }
1005 } else {
1006 false
1007 }
1008 };
1009 let user_capacity = if let Some(base_user_capacity) = sink.user_capacity() {
1012 if use_readdirplus {
1013 base_user_capacity * 3 / 2
1015 } else {
1016 base_user_capacity
1017 }
1018 } else {
1019 *PAGE_SIZE as usize
1020 };
1021 let node = Self::get_fuse_node(file);
1022 let response = state.execute_operation(
1023 locked,
1024 current_task,
1025 node,
1026 FuseOperation::Readdir {
1027 read_in: uapi::fuse_read_in {
1028 fh: self.open_out.fh,
1029 offset: sink.offset().try_into().map_err(|_| errno!(EINVAL))?,
1030 size: user_capacity.try_into().map_err(|_| errno!(EINVAL))?,
1031 read_flags: 0,
1032 lock_owner: 0,
1033 flags: 0,
1034 padding: 0,
1035 },
1036 use_readdirplus,
1037 },
1038 )?;
1039 std::mem::drop(state);
1040 let FuseResponse::Readdir(dirents) = response else {
1041 return error!(EINVAL);
1042 };
1043 let mut sink_result = Ok(());
1044 for (dirent, name, entry) in dirents {
1045 if let Some(entry) = entry {
1046 if entry.nodeid != 0 {
1048 if let Err(e) = node.fs_node_from_entry(file.node(), name.as_ref(), &entry) {
1049 log_error!("Unable to prefill entry: {e:?}");
1050 }
1051 }
1052 }
1053 if sink_result.is_ok() {
1054 sink_result = sink.add(
1055 dirent.ino,
1056 dirent.off.try_into().map_err(|_| errno!(EINVAL))?,
1057 DirectoryEntryType::from_bits(
1058 dirent.type_.try_into().map_err(|_| errno!(EINVAL))?,
1059 ),
1060 name.as_ref(),
1061 );
1062 }
1063 }
1064 sink_result
1065 }
1066
1067 fn ioctl(
1068 &self,
1069 locked: &mut Locked<Unlocked>,
1070 file: &FileObject,
1071 current_task: &CurrentTask,
1072 request: u32,
1073 arg: SyscallArg,
1074 ) -> Result<SyscallResult, Errno> {
1075 track_stub!(TODO("https://fxbug.dev/322875259"), "fuse ioctl");
1076 default_ioctl(file, locked, current_task, request, arg)
1077 }
1078
1079 fn fcntl(
1080 &self,
1081 _file: &FileObject,
1082 _current_task: &CurrentTask,
1083 cmd: u32,
1084 _arg: u64,
1085 ) -> Result<SyscallResult, Errno> {
1086 track_stub!(TODO("https://fxbug.dev/322875764"), "fuse fcntl");
1087 default_fcntl(cmd)
1088 }
1089}
1090
1091struct FuseDirEntry {
1092 valid_until: AtomicMonotonicInstant,
1093}
1094
1095impl Default for FuseDirEntry {
1096 fn default() -> Self {
1097 Self { valid_until: zx::MonotonicInstant::INFINITE_PAST.into() }
1098 }
1099}
1100
1101impl DirEntryOps for FuseDirEntry {
1102 fn revalidate(
1103 &self,
1104 locked: &mut Locked<FileOpsCore>,
1105 current_task: &CurrentTask,
1106 dir_entry: &DirEntry,
1107 ) -> Result<bool, Errno> {
1108 const VALID_UNTIL_ORDERING: Ordering = Ordering::Relaxed;
1111
1112 let now = zx::MonotonicInstant::get();
1113 if self.valid_until.load(VALID_UNTIL_ORDERING) >= now {
1114 return Ok(true);
1115 }
1116
1117 let node = FuseNode::from_node(&dir_entry.node);
1118 if node.nodeid == FUSE_ROOT_ID_U64 {
1119 return Ok(true);
1121 }
1122
1123 let (parent, name) = {
1126 let scope = RcuReadScope::new();
1127 let parent = dir_entry.parent().expect("non-root nodes always has a parent");
1128 let name = dir_entry.local_name(&scope).to_owned();
1129 (parent, name)
1130 };
1131 let parent = FuseNode::from_node(&parent.node);
1132 let FuseEntryOutExtended {
1133 arg:
1134 uapi::fuse_entry_out {
1135 nodeid,
1136 generation,
1137 entry_valid,
1138 entry_valid_nsec,
1139 attr,
1140 attr_valid,
1141 attr_valid_nsec,
1142 },
1143 ..
1144 } = match parent.connection.lock().execute_operation(
1145 locked,
1146 current_task,
1147 parent,
1148 FuseOperation::Lookup { name },
1149 ) {
1150 Ok(FuseResponse::Entry(entry)) => entry,
1151 Ok(_) => return error!(EINVAL),
1152 Err(errno) => {
1153 if errno == ENOENT {
1154 return Ok(false);
1156 } else {
1157 return Err(errno);
1158 };
1159 }
1160 };
1161
1162 if (nodeid != node.nodeid) || (generation != node.generation) {
1163 return Ok(false);
1167 }
1168
1169 dir_entry.node.update_info(|info| {
1170 FuseNode::update_node_info_from_attr(
1171 info,
1172 attr,
1173 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1174 &node.attributes_valid_until,
1175 )?;
1176
1177 self.valid_until.store(
1178 zx::MonotonicInstant::after(attr_valid_to_duration(entry_valid, entry_valid_nsec)?),
1179 VALID_UNTIL_ORDERING,
1180 );
1181
1182 Ok(true)
1183 })
1184 }
1185}
1186
1187const DEFAULT_PERMISSIONS_ATOMIC_ORDERING: Ordering = Ordering::Relaxed;
1189
1190impl FsNodeOps for FuseNode {
1191 fn check_access(
1192 &self,
1193 locked: &mut Locked<FileOpsCore>,
1194 node: &FsNode,
1195 current_task: &CurrentTask,
1196 permission_flags: security::PermissionFlags,
1197 info: &RwLock<FsNodeInfo>,
1198 reason: CheckAccessReason,
1199 audit_context: security::Auditable<'_>,
1200 ) -> Result<(), Errno> {
1201 if FuseFs::from_fs(&node.fs()).default_permissions.load(DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
1204 {
1205 return self.default_check_access_with_valid_node_attributes(
1206 locked,
1207 node,
1208 current_task,
1209 permission_flags,
1210 reason,
1211 info,
1212 audit_context,
1213 );
1214 }
1215
1216 match reason {
1217 CheckAccessReason::Access | CheckAccessReason::Chdir | CheckAccessReason::Chroot => {
1218 let response = self.connection.lock().execute_operation(
1223 locked,
1224 current_task,
1225 self,
1226 FuseOperation::Access {
1227 mask: (permission_flags.as_access() & Access::ACCESS_MASK).bits() as u32,
1228 },
1229 )?;
1230
1231 if let FuseResponse::Access(result) = response { result } else { error!(EINVAL) }
1232 }
1233 CheckAccessReason::Exec => self.default_check_access_with_valid_node_attributes(
1234 locked,
1235 node,
1236 current_task,
1237 permission_flags,
1238 reason,
1239 info,
1240 audit_context,
1241 ),
1242 CheckAccessReason::ChangeTimestamps { .. }
1243 | CheckAccessReason::InternalPermissionChecks => {
1244 Ok(())
1249 }
1250 }
1251 }
1252
1253 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1254 Box::new(FuseDirEntry::default())
1255 }
1256
1257 fn create_file_ops(
1258 &self,
1259 locked: &mut Locked<FileOpsCore>,
1260 node: &FsNode,
1261 current_task: &CurrentTask,
1262 flags: OpenFlags,
1263 ) -> Result<Box<dyn FileOps>, Errno> {
1264 let flags = flags & !(OpenFlags::CREAT | OpenFlags::EXCL);
1266 let mode = node.info().mode;
1267 let response = self.connection.lock().execute_operation(
1268 locked,
1269 current_task,
1270 self,
1271 FuseOperation::Open { flags, mode },
1272 )?;
1273 let FuseResponse::Open(open_out) = response else {
1274 return error!(EINVAL);
1275 };
1276 let passthrough_fh = unsafe { open_out.__bindgen_anon_1.passthrough_fh };
1279 let passthrough_file = if passthrough_fh != 0 {
1280 let mut connection = self.connection.lock();
1281 connection.registered_passthrough.remove(&passthrough_fh).unwrap_or_default()
1282 } else {
1283 Weak::new()
1284 };
1285 Ok(Box::new(FuseFileObject {
1286 connection: self.connection.clone(),
1287 passthrough_file,
1288 open_out,
1289 }))
1290 }
1291
1292 fn lookup(
1293 &self,
1294 locked: &mut Locked<FileOpsCore>,
1295 node: &FsNode,
1296 current_task: &CurrentTask,
1297 name: &FsStr,
1298 ) -> Result<FsNodeHandle, Errno> {
1299 let response = self.connection.lock().execute_operation(
1300 locked,
1301 current_task,
1302 self,
1303 FuseOperation::Lookup { name: name.to_owned() },
1304 )?;
1305 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1306 }
1307
1308 fn mknod(
1309 &self,
1310 locked: &mut Locked<FileOpsCore>,
1311 node: &FsNode,
1312 current_task: &CurrentTask,
1313 name: &FsStr,
1314 mode: FileMode,
1315 dev: DeviceId,
1316 _owner: FsCred,
1317 ) -> Result<FsNodeHandle, Errno> {
1318 let get_entry = |locked: &mut Locked<FileOpsCore>| {
1319 let umask = current_task.fs().umask().bits();
1320 let mut connection = self.connection.lock();
1321
1322 if dev == DeviceId::NONE && !connection.no_create {
1323 match connection.execute_operation(
1324 locked,
1325 current_task,
1326 self,
1327 FuseOperation::Create(
1328 uapi::fuse_create_in {
1329 flags: OpenFlags::CREAT.bits(),
1330 mode: mode.bits(),
1331 umask,
1332 open_flags: 0,
1333 },
1334 name.to_owned(),
1335 ),
1336 ) {
1337 Ok(response) => {
1338 let FuseResponse::Create(response) = response else {
1339 return error!(EINVAL);
1340 };
1341
1342 let fuse_node = FuseNode::new(
1343 self.connection.clone(),
1344 response.entry.nodeid,
1345 response.entry.generation,
1346 );
1347
1348 if let Err(e) = connection.execute_operation(
1354 locked,
1355 current_task,
1356 &fuse_node,
1357 FuseOperation::Release(response.open),
1358 ) {
1359 log_error!("Error when releasing fh: {e:?}");
1360 }
1361
1362 return Ok(response.entry);
1363 }
1364 Err(e) if e == ENOSYS => {
1365 connection.no_create = true;
1366 }
1368 Err(e) => return Err(e),
1369 }
1370 }
1371
1372 connection
1373 .execute_operation(
1374 locked,
1375 current_task,
1376 self,
1377 FuseOperation::Mknod {
1378 mknod_in: uapi::fuse_mknod_in {
1379 mode: mode.bits(),
1380 rdev: dev.bits() as u32,
1381 umask,
1382 padding: 0,
1383 },
1384 name: name.to_owned(),
1385 },
1386 )?
1387 .entry()
1388 .copied()
1389 .ok_or_else(|| errno!(EINVAL))
1390 };
1391
1392 let entry = get_entry(locked)?;
1393 self.fs_node_from_entry(node, name, &entry)
1394 }
1395
1396 fn mkdir(
1397 &self,
1398 locked: &mut Locked<FileOpsCore>,
1399 node: &FsNode,
1400 current_task: &CurrentTask,
1401 name: &FsStr,
1402 mode: FileMode,
1403 _owner: FsCred,
1404 ) -> Result<FsNodeHandle, Errno> {
1405 let response = self.connection.lock().execute_operation(
1406 locked,
1407 current_task,
1408 self,
1409 FuseOperation::Mkdir {
1410 mkdir_in: uapi::fuse_mkdir_in {
1411 mode: mode.bits(),
1412 umask: current_task.fs().umask().bits(),
1413 },
1414 name: name.to_owned(),
1415 },
1416 )?;
1417 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1418 }
1419
1420 fn create_symlink(
1421 &self,
1422 locked: &mut Locked<FileOpsCore>,
1423 node: &FsNode,
1424 current_task: &CurrentTask,
1425 name: &FsStr,
1426 target: &FsStr,
1427 _owner: FsCred,
1428 ) -> Result<FsNodeHandle, Errno> {
1429 let response = self.connection.lock().execute_operation(
1430 locked,
1431 current_task,
1432 self,
1433 FuseOperation::Symlink { target: target.to_owned(), name: name.to_owned() },
1434 )?;
1435 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1436 }
1437
1438 fn readlink(
1439 &self,
1440 locked: &mut Locked<FileOpsCore>,
1441 _node: &FsNode,
1442 current_task: &CurrentTask,
1443 ) -> Result<SymlinkTarget, Errno> {
1444 let response = self.connection.lock().execute_operation(
1445 locked,
1446 current_task,
1447 self,
1448 FuseOperation::Readlink,
1449 )?;
1450 let FuseResponse::Read(read_out) = response else {
1451 return error!(EINVAL);
1452 };
1453 Ok(SymlinkTarget::Path(read_out.into()))
1454 }
1455
1456 fn link(
1457 &self,
1458 locked: &mut Locked<FileOpsCore>,
1459 _node: &FsNode,
1460 current_task: &CurrentTask,
1461 name: &FsStr,
1462 child: &FsNodeHandle,
1463 ) -> Result<(), Errno> {
1464 let child_node = FuseNode::from_node(child);
1465 self.connection
1466 .lock()
1467 .execute_operation(
1468 locked,
1469 current_task,
1470 self,
1471 FuseOperation::Link {
1472 link_in: uapi::fuse_link_in { oldnodeid: child_node.nodeid },
1473 name: name.to_owned(),
1474 },
1475 )
1476 .map(|_| ())
1477 }
1478
1479 fn unlink(
1480 &self,
1481 locked: &mut Locked<FileOpsCore>,
1482 _node: &FsNode,
1483 current_task: &CurrentTask,
1484 name: &FsStr,
1485 child: &FsNodeHandle,
1486 ) -> Result<(), Errno> {
1487 let is_dir = child.is_dir();
1488 self.connection
1489 .lock()
1490 .execute_operation(
1491 locked,
1492 current_task,
1493 self,
1494 if is_dir {
1495 FuseOperation::Rmdir { name: name.to_owned() }
1496 } else {
1497 FuseOperation::Unlink { name: name.to_owned() }
1498 },
1499 )
1500 .map(|_| ())
1501 }
1502
1503 fn truncate(
1504 &self,
1505 locked: &mut Locked<FileOpsCore>,
1506 _guard: &AppendLockWriteGuard<'_>,
1507 node: &FsNode,
1508 current_task: &CurrentTask,
1509 length: u64,
1510 ) -> Result<(), Errno> {
1511 node.update_info(|info| {
1512 let attributes = uapi::fuse_setattr_in {
1514 size: length,
1515 valid: uapi::FATTR_SIZE,
1516 ..Default::default()
1517 };
1518
1519 let response = self.connection.lock().execute_operation(
1520 locked,
1521 current_task,
1522 self,
1523 FuseOperation::SetAttr(attributes),
1524 )?;
1525 let uapi::fuse_attr_out { attr_valid, attr_valid_nsec, attr, .. } =
1526 if let FuseResponse::Attr(attr) = response {
1527 attr
1528 } else {
1529 return error!(EINVAL);
1530 };
1531 FuseNode::update_node_info_from_attr(
1532 info,
1533 attr,
1534 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1535 &self.attributes_valid_until,
1536 )?;
1537 Ok(())
1538 })
1539 }
1540
1541 fn allocate(
1542 &self,
1543 _locked: &mut Locked<FileOpsCore>,
1544 _guard: &AppendLockWriteGuard<'_>,
1545 _node: &FsNode,
1546 _current_task: &CurrentTask,
1547 _mode: FallocMode,
1548 _offset: u64,
1549 _length: u64,
1550 ) -> Result<(), Errno> {
1551 track_stub!(TODO("https://fxbug.dev/322875414"), "FsNodeOps::allocate");
1552 error!(ENOTSUP)
1553 }
1554
1555 fn fetch_and_refresh_info<'a>(
1556 &self,
1557 locked: &mut Locked<FileOpsCore>,
1558 _node: &FsNode,
1559 current_task: &CurrentTask,
1560 info: &'a RwLock<FsNodeInfo>,
1561 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
1562 self.refresh_expired_node_attributes(locked, current_task, info)
1566 }
1567
1568 fn update_attributes(
1569 &self,
1570 locked: &mut Locked<FileOpsCore>,
1571 _node: &FsNode,
1572 current_task: &CurrentTask,
1573 info: &FsNodeInfo,
1574 has: zxio_node_attr_has_t,
1575 ) -> Result<(), Errno> {
1576 let mut valid = 0u32;
1577 if has.modification_time {
1579 valid |= uapi::FATTR_MTIME;
1580 }
1581 if has.access_time {
1582 valid |= uapi::FATTR_ATIME;
1583 }
1584 if has.mode {
1585 valid |= uapi::FATTR_MODE;
1586 }
1587 if has.uid {
1588 valid |= uapi::FATTR_UID;
1589 }
1590 if has.gid {
1591 valid |= uapi::FATTR_GID;
1592 }
1593
1594 let attributes = uapi::fuse_setattr_in {
1595 valid,
1596 atime: (info.time_access.into_nanos() / NANOS_PER_SECOND) as u64,
1597 mtime: (info.time_modify.into_nanos() / NANOS_PER_SECOND) as u64,
1598 ctime: (info.time_status_change.into_nanos() / NANOS_PER_SECOND) as u64,
1599 atimensec: (info.time_access.into_nanos() % NANOS_PER_SECOND) as u32,
1600 mtimensec: (info.time_modify.into_nanos() % NANOS_PER_SECOND) as u32,
1601 ctimensec: (info.time_status_change.into_nanos() % NANOS_PER_SECOND) as u32,
1602 mode: info.mode.bits(),
1603 uid: info.uid,
1604 gid: info.gid,
1605 ..Default::default()
1606 };
1607
1608 let response = self.connection.lock().execute_operation(
1609 locked,
1610 current_task,
1611 self,
1612 FuseOperation::SetAttr(attributes),
1613 )?;
1614 if let FuseResponse::Attr(_attr) = response { Ok(()) } else { error!(EINVAL) }
1615 }
1616
1617 fn get_xattr(
1618 &self,
1619 locked: &mut Locked<FileOpsCore>,
1620 _node: &FsNode,
1621 current_task: &CurrentTask,
1622 name: &FsStr,
1623 max_size: usize,
1624 ) -> Result<ValueOrSize<FsString>, Errno> {
1625 let response = self.connection.lock().execute_operation(
1626 locked,
1627 current_task,
1628 self,
1629 FuseOperation::GetXAttr {
1630 getxattr_in: uapi::fuse_getxattr_in {
1631 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1632 padding: 0,
1633 },
1634 name: name.to_owned(),
1635 },
1636 )?;
1637 if let FuseResponse::GetXAttr(result) = response { Ok(result) } else { error!(EINVAL) }
1638 }
1639
1640 fn set_xattr(
1641 &self,
1642 locked: &mut Locked<FileOpsCore>,
1643 _node: &FsNode,
1644 current_task: &CurrentTask,
1645 name: &FsStr,
1646 value: &FsStr,
1647 op: XattrOp,
1648 ) -> Result<(), Errno> {
1649 let mut state = self.connection.lock();
1650 let configuration = state.get_configuration(locked, current_task)?;
1651 state.execute_operation(
1652 locked,
1653 current_task,
1654 self,
1655 FuseOperation::SetXAttr {
1656 setxattr_in: uapi::fuse_setxattr_in {
1657 size: value.len().try_into().map_err(|_| errno!(EINVAL))?,
1658 flags: op.into_flags(),
1659 setxattr_flags: 0,
1660 padding: 0,
1661 },
1662 is_ext: configuration.flags.contains(FuseInitFlags::SETXATTR_EXT),
1663 name: name.to_owned(),
1664 value: value.to_owned(),
1665 },
1666 )?;
1667 Ok(())
1668 }
1669
1670 fn remove_xattr(
1671 &self,
1672 locked: &mut Locked<FileOpsCore>,
1673 _node: &FsNode,
1674 current_task: &CurrentTask,
1675 name: &FsStr,
1676 ) -> Result<(), Errno> {
1677 self.connection.lock().execute_operation(
1678 locked,
1679 current_task,
1680 self,
1681 FuseOperation::RemoveXAttr { name: name.to_owned() },
1682 )?;
1683 Ok(())
1684 }
1685
1686 fn list_xattrs(
1687 &self,
1688 locked: &mut Locked<FileOpsCore>,
1689 _node: &FsNode,
1690 current_task: &CurrentTask,
1691 max_size: usize,
1692 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
1693 let response = self.connection.lock().execute_operation(
1694 locked,
1695 current_task,
1696 self,
1697 FuseOperation::ListXAttr(uapi::fuse_getxattr_in {
1698 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1699 padding: 0,
1700 }),
1701 )?;
1702 if let FuseResponse::GetXAttr(result) = response {
1703 Ok(result.map(|s| {
1704 let mut result = s.split(|c| *c == 0).map(FsString::from).collect::<Vec<_>>();
1705 result.pop();
1708 result
1709 }))
1710 } else {
1711 error!(EINVAL)
1712 }
1713 }
1714
1715 fn forget(
1716 self: Box<Self>,
1717 locked: &mut Locked<FileOpsCore>,
1718 current_task: &CurrentTask,
1719 _info: FsNodeInfo,
1720 ) -> Result<(), Errno> {
1721 let nlookup = self.state.lock().nlookup;
1722 let mut state = self.connection.lock();
1723 if !state.is_connected() {
1724 return Ok(());
1725 }
1726 if nlookup > 0 {
1727 state.execute_operation(
1728 locked,
1729 current_task,
1730 self.as_ref(),
1731 FuseOperation::Forget(uapi::fuse_forget_in { nlookup }),
1732 )?;
1733 };
1734 Ok(())
1735 }
1736
1737 fn node_key(&self, _node: &FsNode) -> ino_t {
1738 self.nodeid
1739 }
1740}
1741
1742#[derive(Debug, Default)]
1744enum FuseConnectionState {
1745 #[default]
1746 Waiting,
1748 Connected,
1750 Disconnected,
1752}
1753
1754#[derive(Debug)]
1755struct FuseConnection {
1756 id: u64,
1758
1759 creds: FsCred,
1761
1762 state: Mutex<FuseMutableState>,
1764}
1765
1766struct FuseMutableStateGuard<'a>(Guard<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>);
1767
1768impl<'a> Deref for FuseMutableStateGuard<'a> {
1769 type Target = Guard<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>;
1770 fn deref(&self) -> &Self::Target {
1771 &self.0
1772 }
1773}
1774
1775impl<'a> DerefMut for FuseMutableStateGuard<'a> {
1776 fn deref_mut(&mut self) -> &mut Self::Target {
1777 &mut self.0
1778 }
1779}
1780
1781impl FuseConnection {
1782 fn lock<'a>(&'a self) -> FuseMutableStateGuard<'a> {
1783 FuseMutableStateGuard(Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::new(
1784 self,
1785 self.state.lock(),
1786 ))
1787 }
1788}
1789
1790#[derive(Clone, Copy, Debug)]
1791struct FuseConfiguration {
1792 flags: FuseInitFlags,
1793}
1794
1795impl TryFrom<uapi::fuse_init_out> for FuseConfiguration {
1796 type Error = Errno;
1797 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
1798 let flags = FuseInitFlags::try_from(init_out)?;
1799 Ok(Self { flags })
1800 }
1801}
1802
1803type OperationsState = HashMap<uapi::fuse_opcode, Result<FuseResponse, Errno>>;
1811
1812#[derive(Debug, Default)]
1813struct FuseMutableState {
1814 state: FuseConnectionState,
1816
1817 last_unique_id: u64,
1819
1820 configuration: Option<FuseConfiguration>,
1822
1823 operations: HashMap<u64, RunningOperation>,
1825
1826 message_queue: VecDeque<FuseKernelMessage>,
1830
1831 waiters: WaitQueue,
1833
1834 operations_state: OperationsState,
1836
1837 no_create: bool,
1839
1840 last_passthrough_id: u32,
1842
1843 registered_passthrough: HashMap<u32, WeakFileHandle>,
1846}
1847
1848impl<'a> FuseMutableStateGuard<'a> {
1849 fn wait_for_configuration<L, T>(
1850 &mut self,
1851 locked: &mut Locked<L>,
1852 current_task: &CurrentTask,
1853 f: impl Fn(&FuseConfiguration) -> T,
1854 ) -> Result<T, Errno>
1855 where
1856 L: LockEqualOrBefore<FileOpsCore>,
1857 {
1858 if let Some(configuration) = self.configuration.as_ref() {
1859 return Ok(f(configuration));
1860 }
1861 loop {
1862 if !self.is_connected() {
1863 return error!(ECONNABORTED);
1864 }
1865 let waiter = Waiter::new();
1866 self.waiters.wait_async_value(&waiter, CONFIGURATION_AVAILABLE_EVENT);
1867 if let Some(configuration) = self.configuration.as_ref() {
1868 return Ok(f(configuration));
1869 }
1870 Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::unlocked(self, || {
1871 waiter.wait(locked, current_task)
1872 })?;
1873 }
1874 }
1875
1876 fn get_configuration<L>(
1877 &mut self,
1878 locked: &mut Locked<L>,
1879 current_task: &CurrentTask,
1880 ) -> Result<FuseConfiguration, Errno>
1881 where
1882 L: LockEqualOrBefore<FileOpsCore>,
1883 {
1884 self.wait_for_configuration(locked, current_task, Clone::clone)
1885 }
1886
1887 fn wait_for_configuration_ready<L>(
1888 &mut self,
1889 locked: &mut Locked<L>,
1890 current_task: &CurrentTask,
1891 ) -> Result<(), Errno>
1892 where
1893 L: LockEqualOrBefore<FileOpsCore>,
1894 {
1895 self.wait_for_configuration(locked, current_task, |_| ())
1896 }
1897
1898 fn execute_operation<L>(
1904 &mut self,
1905 locked: &mut Locked<L>,
1906 current_task: &CurrentTask,
1907 node: &FuseNode,
1908 operation: FuseOperation,
1909 ) -> Result<FuseResponse, Errno>
1910 where
1911 L: LockEqualOrBefore<FileOpsCore>,
1912 {
1913 if !matches!(operation, FuseOperation::Init { .. }) {
1918 self.wait_for_configuration_ready(locked, current_task)?;
1919 }
1920
1921 if let Some(result) = self.operations_state.get(&operation.opcode()) {
1922 return result.clone();
1923 }
1924 if !operation.has_response() {
1925 self.queue_operation(current_task, node.nodeid, operation, None)?;
1926 return Ok(FuseResponse::None);
1927 }
1928 let waiter = Waiter::with_options(WaiterOptions::UNSAFE_CALLSTACK);
1929 let is_async = operation.is_async();
1930 let unique_id =
1931 self.queue_operation(current_task, node.nodeid, operation, Some(&waiter))?;
1932 if is_async {
1933 return Ok(FuseResponse::None);
1934 }
1935 let mut first_loop = true;
1936 loop {
1937 if !self.is_connected() {
1938 return error!(ECONNABORTED);
1939 }
1940 if let Some(response) = self.get_response(unique_id) {
1941 return response;
1942 }
1943 match Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::unlocked(
1944 self,
1945 || waiter.wait(locked, current_task),
1946 ) {
1947 Ok(()) => {}
1948 Err(e) if e == EINTR => {
1949 if first_loop {
1952 self.interrupt(current_task, node.nodeid, unique_id)?;
1953 first_loop = false;
1954 }
1955 }
1956 Err(e) => {
1957 log_error!("Unexpected error: {e:?}");
1958 return Err(e);
1959 }
1960 }
1961 }
1962 }
1963}
1964
1965impl FuseMutableState {
1966 fn wait_async(
1967 &self,
1968 waiter: &Waiter,
1969 events: FdEvents,
1970 handler: EventHandler,
1971 ) -> Option<WaitCanceler> {
1972 Some(self.waiters.wait_async_fd_events(waiter, events, handler))
1973 }
1974
1975 fn is_connected(&self) -> bool {
1976 matches!(self.state, FuseConnectionState::Connected)
1977 }
1978
1979 fn set_configuration(&mut self, configuration: FuseConfiguration) {
1980 debug_assert!(self.configuration.is_none());
1981 log_trace!("Fuse configuration: {configuration:?}");
1982 self.configuration = Some(configuration);
1983 self.waiters.notify_value(CONFIGURATION_AVAILABLE_EVENT);
1984 }
1985
1986 fn connect(&mut self) {
1987 debug_assert!(matches!(self.state, FuseConnectionState::Waiting));
1988 self.state = FuseConnectionState::Connected;
1989 }
1990
1991 fn disconnect(&mut self) {
1994 if matches!(self.state, FuseConnectionState::Disconnected) {
1995 return;
1996 }
1997 self.state = FuseConnectionState::Disconnected;
1998 self.message_queue.clear();
1999 self.operations.clear();
2000 self.waiters.notify_all();
2001 }
2002
2003 fn queue_operation(
2007 &mut self,
2008 current_task: &CurrentTask,
2009 nodeid: u64,
2010 operation: FuseOperation,
2011 waiter: Option<&Waiter>,
2012 ) -> Result<u64, Errno> {
2013 debug_assert!(waiter.is_some() == operation.has_response(), "{operation:?}");
2014 if !self.is_connected() {
2015 return error!(ECONNABORTED);
2016 }
2017 self.last_unique_id += 1;
2018 let message = FuseKernelMessage::new(self.last_unique_id, current_task, nodeid, operation)?;
2019 if let Some(waiter) = waiter {
2020 self.waiters.wait_async_value(waiter, self.last_unique_id);
2021 }
2022 if message.operation.has_response() {
2023 self.operations.insert(self.last_unique_id, message.operation.as_running().into());
2024 }
2025 self.message_queue.push_back(message);
2026 self.waiters.notify_fd_events(FdEvents::POLLIN);
2027 Ok(self.last_unique_id)
2028 }
2029
2030 fn interrupt(
2037 &mut self,
2038 current_task: &CurrentTask,
2039 nodeid: u64,
2040 unique_id: u64,
2041 ) -> Result<(), Errno> {
2042 debug_assert!(self.operations.contains_key(&unique_id));
2043
2044 let mut in_queue = false;
2045 self.message_queue.retain(|m| {
2046 if m.header.unique == unique_id {
2047 self.operations.remove(&unique_id);
2048 in_queue = true;
2049 false
2050 } else {
2051 true
2052 }
2053 });
2054 if in_queue {
2055 return error!(EINTR);
2057 }
2058 self.queue_operation(current_task, nodeid, FuseOperation::Interrupt { unique_id }, None)
2059 .map(|_| ())
2060 }
2061
2062 fn get_response(&mut self, unique_id: u64) -> Option<Result<FuseResponse, Errno>> {
2065 match self.operations.entry(unique_id) {
2066 Entry::Vacant(_) => Some(error!(EINVAL)),
2067 Entry::Occupied(mut entry) => {
2068 let result = entry.get_mut().response.take();
2069 if result.is_some() {
2070 entry.remove();
2071 }
2072 result
2073 }
2074 }
2075 }
2076
2077 fn query_events(&self) -> FdEvents {
2078 let mut events = FdEvents::POLLOUT;
2079 if !self.is_connected() || !self.message_queue.is_empty() {
2080 events |= FdEvents::POLLIN
2081 };
2082 if !self.is_connected() {
2083 events |= FdEvents::POLLERR;
2084 }
2085 events
2086 }
2087
2088 fn read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2089 match self.state {
2090 FuseConnectionState::Waiting => return error!(EPERM),
2091 FuseConnectionState::Disconnected => return error!(ENODEV),
2092 _ => {}
2093 }
2094 if let Some(message) = self.message_queue.pop_front() {
2095 message.serialize(data)
2096 } else {
2097 error!(EAGAIN)
2098 }
2099 }
2100
2101 fn write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
2102 match self.state {
2103 FuseConnectionState::Waiting => return error!(EPERM),
2104 FuseConnectionState::Disconnected => return error!(ENODEV),
2105 _ => {}
2106 }
2107 let header: uapi::fuse_out_header = data.read_to_object()?;
2108 let payload_size = (header.len as usize)
2109 .checked_sub(std::mem::size_of::<uapi::fuse_out_header>())
2110 .ok_or_else(|| errno!(EINVAL))?;
2111 if payload_size > data.available() {
2112 return error!(EINVAL);
2113 }
2114 if header.unique == 0 {
2115 track_stub!(TODO("https://fxbug.dev/322873416"), "Fuse notification from userspace");
2116 return error!(ENOTSUP);
2117 }
2118 self.waiters.notify_value(header.unique);
2119 let mut running_operation = match self.operations.entry(header.unique) {
2120 Entry::Occupied(e) => e,
2121 Entry::Vacant(_) => return error!(EINVAL),
2122 };
2123 let operation = &running_operation.get().operation;
2124 let is_async = operation.is_async();
2125 if header.error < 0 {
2126 log_trace!("Fuse: {operation:?} -> {header:?}");
2127 let code = i16::try_from(-header.error).unwrap_or_else(|_| EINVAL.error_code() as i16);
2128 let errno = errno_from_code!(code);
2129 let response = operation.handle_error(&mut self.operations_state, errno);
2130 if is_async {
2131 running_operation.remove();
2132 } else {
2133 running_operation.get_mut().response = Some(response);
2134 }
2135 } else {
2136 let buffer = data.read_to_vec_limited(payload_size)?;
2137 if buffer.len() != payload_size {
2138 return error!(EINVAL);
2139 }
2140 let response = operation.parse_response(buffer)?;
2141 log_trace!("Fuse: {operation:?} -> {response:?}");
2142 if is_async {
2143 let operation = running_operation.remove();
2144 self.handle_async(operation, response)?;
2145 } else {
2146 running_operation.get_mut().response = Some(Ok(response));
2147 }
2148 }
2149 Ok(data.bytes_read())
2150 }
2151
2152 fn handle_async(
2153 &mut self,
2154 operation: RunningOperation,
2155 response: FuseResponse,
2156 ) -> Result<(), Errno> {
2157 match (operation.operation, response) {
2158 (RunningOperationKind::Init { fs }, FuseResponse::Init(init_out)) => {
2159 let configuration = FuseConfiguration::try_from(init_out)?;
2160 if configuration.flags.contains(FuseInitFlags::POSIX_ACL) {
2161 if let Some(fs) = fs.upgrade() {
2165 FuseFs::from_fs(&fs)
2166 .default_permissions
2167 .store(true, DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
2168 } else {
2169 log_warn!("failed to upgrade FuseFs when handling FUSE_INIT response");
2170 return error!(ENOTCONN);
2171 }
2172 }
2173 self.set_configuration(configuration);
2174 Ok(())
2175 }
2176 operation => {
2177 panic!("Incompatible operation={operation:?}");
2179 }
2180 }
2181 }
2182
2183 fn clear_released_passthrough_fds(&mut self) {
2184 self.registered_passthrough.retain(|_, s| s.strong_count() > 0);
2185 }
2186}
2187
2188#[derive(Debug)]
2191struct RunningOperation {
2192 operation: RunningOperationKind,
2193 response: Option<Result<FuseResponse, Errno>>,
2194}
2195
2196impl From<RunningOperationKind> for RunningOperation {
2197 fn from(operation: RunningOperationKind) -> Self {
2198 Self { operation, response: None }
2199 }
2200}
2201
2202#[derive(Debug)]
2203struct FuseKernelMessage {
2204 header: uapi::fuse_in_header,
2205 operation: FuseOperation,
2206}
2207
2208impl FuseKernelMessage {
2209 fn new(
2210 unique: u64,
2211 current_task: &CurrentTask,
2212 nodeid: u64,
2213 operation: FuseOperation,
2214 ) -> Result<Self, Errno> {
2215 let current_creds = current_task.current_creds();
2216 Ok(Self {
2217 header: uapi::fuse_in_header {
2218 len: u32::try_from(std::mem::size_of::<uapi::fuse_in_header>() + operation.len())
2219 .map_err(|_| errno!(EINVAL))?,
2220 opcode: operation.opcode(),
2221 unique,
2222 nodeid,
2223 uid: current_creds.uid,
2224 gid: current_creds.gid,
2225 pid: current_task.get_tid() as u32,
2226 __bindgen_anon_1: Default::default(),
2227 },
2228 operation,
2229 })
2230 }
2231
2232 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2233 let size = data.write(self.header.as_bytes())?;
2234 Ok(size + self.operation.serialize(data)?)
2235 }
2236}
2237
2238bitflags::bitflags! {
2239 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2240 pub struct FuseInitFlags : u64 {
2241 const BIG_WRITES = uapi::FUSE_BIG_WRITES as u64;
2242 const DONT_MASK = uapi::FUSE_DONT_MASK as u64;
2243 const SPLICE_WRITE = uapi::FUSE_SPLICE_WRITE as u64;
2244 const SPLICE_MOVE = uapi::FUSE_SPLICE_MOVE as u64;
2245 const SPLICE_READ = uapi::FUSE_SPLICE_READ as u64;
2246 const DO_READDIRPLUS = uapi::FUSE_DO_READDIRPLUS as u64;
2247 const READDIRPLUS_AUTO = uapi::FUSE_READDIRPLUS_AUTO as u64;
2248 const SETXATTR_EXT = uapi::FUSE_SETXATTR_EXT as u64;
2249 const POSIX_ACL = uapi::FUSE_POSIX_ACL as u64;
2250 const PASSTHROUGH = uapi::FUSE_PASSTHROUGH as u64;
2251 const INIT_EXT = uapi::FUSE_INIT_EXT as u64;
2252 }
2253}
2254
2255impl TryFrom<uapi::fuse_init_out> for FuseInitFlags {
2256 type Error = Errno;
2257 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
2258 let flags = (init_out.flags as u64) | ((init_out.flags2 as u64) << 32);
2259 let unknown_flags = flags & !Self::all().bits();
2260 if unknown_flags != 0 {
2261 track_stub!(
2262 TODO("https://fxbug.dev/322875725"),
2263 "FUSE unknown init flags",
2264 unknown_flags
2265 );
2266 log_warn!("FUSE daemon requested unknown flags in init: {unknown_flags}");
2267 }
2268 Ok(Self::from_bits_truncate(flags))
2269 }
2270}
2271
2272impl FuseInitFlags {
2273 fn get_u32_components(&self) -> (u32, u32) {
2275 let flags = (self.bits() & (u32::max_value() as u64)) as u32;
2276 let flags2 = (self.bits() >> 32) as u32;
2277 (flags, flags2)
2278 }
2279}
2280
2281#[derive(Clone, Debug)]
2282enum RunningOperationKind {
2283 Access,
2284 Create,
2285 Flush,
2286 Forget,
2287 GetAttr,
2288 Init {
2289 fs: Weak<FileSystem>,
2294 },
2295 Interrupt,
2296 GetXAttr {
2297 size: u32,
2298 },
2299 ListXAttr {
2300 size: u32,
2301 },
2302 Lookup,
2303 Mkdir,
2304 Mknod,
2305 Link,
2306 Open {
2307 dir: bool,
2308 },
2309 Poll,
2310 Read,
2311 Readdir {
2312 use_readdirplus: bool,
2313 },
2314 Readlink,
2315 Release {
2316 dir: bool,
2317 },
2318 RemoveXAttr,
2319 Rename,
2320 Rmdir,
2321 Seek,
2322 SetAttr,
2323 SetXAttr,
2324 Statfs,
2325 Symlink,
2326 Unlink,
2327 Write,
2328}
2329
2330impl RunningOperationKind {
2331 fn is_async(&self) -> bool {
2332 matches!(self, Self::Init { .. })
2333 }
2334
2335 fn opcode(&self) -> u32 {
2336 match self {
2337 Self::Access => uapi::fuse_opcode_FUSE_ACCESS,
2338 Self::Create => uapi::fuse_opcode_FUSE_CREATE,
2339 Self::Flush => uapi::fuse_opcode_FUSE_FLUSH,
2340 Self::Forget => uapi::fuse_opcode_FUSE_FORGET,
2341 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2342 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2343 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2344 Self::Interrupt => uapi::fuse_opcode_FUSE_INTERRUPT,
2345 Self::ListXAttr { .. } => uapi::fuse_opcode_FUSE_LISTXATTR,
2346 Self::Lookup => uapi::fuse_opcode_FUSE_LOOKUP,
2347 Self::Mkdir => uapi::fuse_opcode_FUSE_MKDIR,
2348 Self::Mknod => uapi::fuse_opcode_FUSE_MKNOD,
2349 Self::Link => uapi::fuse_opcode_FUSE_LINK,
2350 Self::Open { dir } => {
2351 if *dir {
2352 uapi::fuse_opcode_FUSE_OPENDIR
2353 } else {
2354 uapi::fuse_opcode_FUSE_OPEN
2355 }
2356 }
2357 Self::Poll => uapi::fuse_opcode_FUSE_POLL,
2358 Self::Read => uapi::fuse_opcode_FUSE_READ,
2359 Self::Readdir { use_readdirplus } => {
2360 if *use_readdirplus {
2361 uapi::fuse_opcode_FUSE_READDIRPLUS
2362 } else {
2363 uapi::fuse_opcode_FUSE_READDIR
2364 }
2365 }
2366 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2367 Self::Release { dir } => {
2368 if *dir {
2369 uapi::fuse_opcode_FUSE_RELEASEDIR
2370 } else {
2371 uapi::fuse_opcode_FUSE_RELEASE
2372 }
2373 }
2374 Self::RemoveXAttr => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2375 Self::Rename => uapi::fuse_opcode_FUSE_RENAME2,
2376 Self::Rmdir => uapi::fuse_opcode_FUSE_RMDIR,
2377 Self::Seek => uapi::fuse_opcode_FUSE_LSEEK,
2378 Self::SetAttr => uapi::fuse_opcode_FUSE_SETATTR,
2379 Self::SetXAttr => uapi::fuse_opcode_FUSE_SETXATTR,
2380 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2381 Self::Symlink => uapi::fuse_opcode_FUSE_SYMLINK,
2382 Self::Unlink => uapi::fuse_opcode_FUSE_UNLINK,
2383 Self::Write => uapi::fuse_opcode_FUSE_WRITE,
2384 }
2385 }
2386
2387 fn to_response<T: FromBytes + IntoBytes + Immutable>(buffer: &[u8]) -> T {
2388 let mut result = T::new_zeroed();
2389 let length_to_copy = std::cmp::min(buffer.len(), std::mem::size_of::<T>());
2390 result.as_mut_bytes()[..length_to_copy].copy_from_slice(&buffer[..length_to_copy]);
2391 result
2392 }
2393
2394 fn parse_response(&self, buffer: Vec<u8>) -> Result<FuseResponse, Errno> {
2395 match self {
2396 Self::Access => Ok(FuseResponse::Access(Ok(()))),
2397 Self::Create { .. } => {
2398 Ok(FuseResponse::Create(Self::to_response::<CreateResponse>(&buffer)))
2399 }
2400 Self::GetAttr | Self::SetAttr => {
2401 Ok(FuseResponse::Attr(Self::to_response::<uapi::fuse_attr_out>(&buffer)))
2402 }
2403 Self::GetXAttr { size } | Self::ListXAttr { size } => {
2404 if *size == 0 {
2405 if buffer.len() < std::mem::size_of::<uapi::fuse_getxattr_out>() {
2406 return error!(EINVAL);
2407 }
2408 let getxattr_out = Self::to_response::<uapi::fuse_getxattr_out>(&buffer);
2409 Ok(FuseResponse::GetXAttr(ValueOrSize::Size(getxattr_out.size as usize)))
2410 } else {
2411 Ok(FuseResponse::GetXAttr(FsString::new(buffer).into()))
2412 }
2413 }
2414 Self::Init { .. } => {
2415 Ok(FuseResponse::Init(Self::to_response::<uapi::fuse_init_out>(&buffer)))
2416 }
2417 Self::Lookup | Self::Mkdir | Self::Mknod | Self::Link | Self::Symlink => {
2418 Ok(FuseResponse::Entry(Self::to_response::<FuseEntryOutExtended>(&buffer)))
2419 }
2420 Self::Open { .. } => {
2421 Ok(FuseResponse::Open(Self::to_response::<uapi::fuse_open_out>(&buffer)))
2422 }
2423 Self::Poll => Ok(FuseResponse::Poll(Self::to_response::<uapi::fuse_poll_out>(&buffer))),
2424 Self::Read | Self::Readlink => Ok(FuseResponse::Read(buffer)),
2425 Self::Readdir { use_readdirplus, .. } => {
2426 let mut result = vec![];
2427 let mut slice = &buffer[..];
2428 while !slice.is_empty() {
2429 let entry = if *use_readdirplus {
2431 if slice.len() < std::mem::size_of::<uapi::fuse_entry_out>() {
2432 return error!(EINVAL);
2433 }
2434 let entry = Self::to_response::<uapi::fuse_entry_out>(slice);
2435 slice = &slice[std::mem::size_of::<uapi::fuse_entry_out>()..];
2436 Some(entry)
2437 } else {
2438 None
2439 };
2440 if slice.len() < std::mem::size_of::<uapi::fuse_dirent>() {
2442 return error!(EINVAL);
2443 }
2444 let dirent = Self::to_response::<uapi::fuse_dirent>(slice);
2445 slice = &slice[std::mem::size_of::<uapi::fuse_dirent>()..];
2447 let namelen = dirent.namelen as usize;
2448 if slice.len() < namelen {
2449 return error!(EINVAL);
2450 }
2451 let name = FsString::from(&slice[..namelen]);
2452 result.push((dirent, name, entry));
2453 let skipped = round_up_to_increment(namelen, 8)?;
2454 if slice.len() < skipped {
2455 return error!(EINVAL);
2456 }
2457 slice = &slice[skipped..];
2458 }
2459 Ok(FuseResponse::Readdir(result))
2460 }
2461 Self::Flush
2462 | Self::Release { .. }
2463 | Self::RemoveXAttr
2464 | Self::Rename
2465 | Self::Rmdir
2466 | Self::SetXAttr
2467 | Self::Unlink => Ok(FuseResponse::None),
2468 Self::Statfs => {
2469 Ok(FuseResponse::Statfs(Self::to_response::<uapi::fuse_statfs_out>(&buffer)))
2470 }
2471 Self::Seek => {
2472 Ok(FuseResponse::Seek(Self::to_response::<uapi::fuse_lseek_out>(&buffer)))
2473 }
2474 Self::Write => {
2475 Ok(FuseResponse::Write(Self::to_response::<uapi::fuse_write_out>(&buffer)))
2476 }
2477 Self::Interrupt | Self::Forget => {
2478 panic!("Response for operation without one");
2479 }
2480 }
2481 }
2482
2483 fn handle_error(
2488 &self,
2489 state: &mut OperationsState,
2490 errno: Errno,
2491 ) -> Result<FuseResponse, Errno> {
2492 match self {
2493 Self::Access if errno == ENOSYS => {
2494 const UNIMPLEMENTED_ACCESS_RESPONSE: Result<FuseResponse, Errno> =
2499 Ok(FuseResponse::Access(Ok(())));
2500 state.insert(self.opcode(), UNIMPLEMENTED_ACCESS_RESPONSE);
2501 UNIMPLEMENTED_ACCESS_RESPONSE
2502 }
2503 Self::Flush if errno == ENOSYS => {
2504 state.insert(self.opcode(), Ok(FuseResponse::None));
2505 Ok(FuseResponse::None)
2506 }
2507 Self::Seek if errno == ENOSYS => {
2508 state.insert(self.opcode(), Err(errno.clone()));
2509 Err(errno)
2510 }
2511 Self::Poll if errno == ENOSYS => {
2512 let response = FuseResponse::Poll(uapi::fuse_poll_out {
2513 revents: (FdEvents::POLLIN | FdEvents::POLLOUT).bits(),
2514 padding: 0,
2515 });
2516 state.insert(self.opcode(), Ok(response.clone()));
2517 Ok(response)
2518 }
2519 _ => Err(errno),
2520 }
2521 }
2522}
2523
2524#[derive(Debug)]
2525enum FuseOperation {
2526 Access {
2527 mask: u32,
2528 },
2529 Create(uapi::fuse_create_in, FsString),
2530 Flush(uapi::fuse_open_out),
2531 Forget(uapi::fuse_forget_in),
2532 GetAttr,
2533 Init {
2534 fs: Weak<FileSystem>,
2539 },
2540 Interrupt {
2541 unique_id: u64,
2543 },
2544 GetXAttr {
2545 getxattr_in: uapi::fuse_getxattr_in,
2546 name: FsString,
2548 },
2549 ListXAttr(uapi::fuse_getxattr_in),
2550 Lookup {
2551 name: FsString,
2553 },
2554 Mkdir {
2555 mkdir_in: uapi::fuse_mkdir_in,
2556 name: FsString,
2558 },
2559 Mknod {
2560 mknod_in: uapi::fuse_mknod_in,
2561 name: FsString,
2563 },
2564 Link {
2565 link_in: uapi::fuse_link_in,
2566 name: FsString,
2568 },
2569 Open {
2570 flags: OpenFlags,
2571 mode: FileMode,
2572 },
2573 Poll(uapi::fuse_poll_in),
2574 Read(uapi::fuse_read_in),
2575 Readdir {
2576 read_in: uapi::fuse_read_in,
2577 use_readdirplus: bool,
2579 },
2580 Readlink,
2581 Release(uapi::fuse_open_out),
2582 ReleaseDir(uapi::fuse_open_out),
2583 RemoveXAttr {
2584 name: FsString,
2586 },
2587 Rename {
2588 old_name: FsString,
2589 new_dir: u64,
2590 new_name: FsString,
2591 },
2592 Rmdir {
2593 name: FsString,
2594 },
2595 Seek(uapi::fuse_lseek_in),
2596 SetAttr(uapi::fuse_setattr_in),
2597 SetXAttr {
2598 setxattr_in: uapi::fuse_setxattr_in,
2599 is_ext: bool,
2602 name: FsString,
2604 value: FsString,
2606 },
2607 Statfs,
2608 Symlink {
2609 target: FsString,
2611 name: FsString,
2613 },
2614 Unlink {
2615 name: FsString,
2617 },
2618 Write {
2619 write_in: uapi::fuse_write_in,
2620 content: Vec<u8>,
2622 },
2623}
2624
2625#[derive(Clone, Debug)]
2626enum FuseResponse {
2627 Access(Result<(), Errno>),
2628 Attr(uapi::fuse_attr_out),
2629 Create(CreateResponse),
2630 Entry(FuseEntryOutExtended),
2631 GetXAttr(ValueOrSize<FsString>),
2632 Init(uapi::fuse_init_out),
2633 Open(uapi::fuse_open_out),
2634 Poll(uapi::fuse_poll_out),
2635 Read(
2636 Vec<u8>,
2638 ),
2639 Seek(uapi::fuse_lseek_out),
2640 Readdir(Vec<(uapi::fuse_dirent, FsString, Option<uapi::fuse_entry_out>)>),
2641 Statfs(uapi::fuse_statfs_out),
2642 Write(uapi::fuse_write_out),
2643 None,
2644}
2645
2646impl FuseResponse {
2647 fn entry(&self) -> Option<&uapi::fuse_entry_out> {
2648 if let Self::Entry(entry) = self { Some(&entry.arg) } else { None }
2649 }
2650}
2651
2652#[repr(C)]
2653#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2654struct CreateResponse {
2655 entry: uapi::fuse_entry_out,
2656 open: uapi::fuse_open_out,
2657}
2658
2659static_assertions::const_assert_eq!(
2660 std::mem::offset_of!(CreateResponse, open),
2661 std::mem::size_of::<uapi::fuse_entry_out>()
2662);
2663
2664#[repr(C)]
2665#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2666struct FuseEntryOutExtended {
2667 arg: uapi::fuse_entry_out,
2668 bpf_arg: uapi::fuse_entry_bpf_out,
2669}
2670
2671static_assertions::const_assert_eq!(
2672 std::mem::offset_of!(FuseEntryOutExtended, bpf_arg),
2673 std::mem::size_of::<uapi::fuse_entry_out>()
2674);
2675
2676impl FuseOperation {
2677 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2678 match self {
2679 Self::Access { mask } => {
2680 let message = uapi::fuse_access_in { mask: *mask, padding: 0 };
2681 data.write_all(message.as_bytes())
2682 }
2683 Self::Create(create_in, name) => {
2684 Ok(data.write_all(create_in.as_bytes())? + Self::write_null_terminated(data, name)?)
2685 }
2686 Self::Flush(open_in) => {
2687 let message =
2688 uapi::fuse_flush_in { fh: open_in.fh, unused: 0, padding: 0, lock_owner: 0 };
2689 data.write_all(message.as_bytes())
2690 }
2691 Self::Forget(forget_in) => data.write_all(forget_in.as_bytes()),
2692 Self::GetAttr | Self::Readlink | Self::Statfs => Ok(0),
2693 Self::GetXAttr { getxattr_in, name } => {
2694 let mut len = data.write_all(getxattr_in.as_bytes())?;
2695 len += Self::write_null_terminated(data, name)?;
2696 Ok(len)
2697 }
2698 Self::Init { .. } => {
2699 let (flags, flags2) = FuseInitFlags::all().get_u32_components();
2700 let message = uapi::fuse_init_in {
2701 major: uapi::FUSE_KERNEL_VERSION,
2702 minor: uapi::FUSE_KERNEL_MINOR_VERSION,
2703 flags,
2704 flags2,
2705 ..Default::default()
2706 };
2707 data.write_all(message.as_bytes())
2708 }
2709 Self::Interrupt { unique_id } => {
2710 let message = uapi::fuse_interrupt_in { unique: *unique_id };
2711 data.write_all(message.as_bytes())
2712 }
2713 Self::ListXAttr(getxattr_in) => data.write_all(getxattr_in.as_bytes()),
2714 Self::Lookup { name } => Self::write_null_terminated(data, name),
2715 Self::Open { flags, .. } => {
2716 let message = uapi::fuse_open_in { flags: flags.bits(), open_flags: 0 };
2717 data.write_all(message.as_bytes())
2718 }
2719 Self::Poll(poll_in) => data.write_all(poll_in.as_bytes()),
2720 Self::Mkdir { mkdir_in, name } => {
2721 let mut len = data.write_all(mkdir_in.as_bytes())?;
2722 len += Self::write_null_terminated(data, name)?;
2723 Ok(len)
2724 }
2725 Self::Mknod { mknod_in, name } => {
2726 let mut len = data.write_all(mknod_in.as_bytes())?;
2727 len += Self::write_null_terminated(data, name)?;
2728 Ok(len)
2729 }
2730 Self::Link { link_in, name } => {
2731 let mut len = data.write_all(link_in.as_bytes())?;
2732 len += Self::write_null_terminated(data, name)?;
2733 Ok(len)
2734 }
2735 Self::Read(read_in) | Self::Readdir { read_in, .. } => {
2736 data.write_all(read_in.as_bytes())
2737 }
2738 Self::Release(open_out) | Self::ReleaseDir(open_out) => {
2739 let message = uapi::fuse_release_in {
2740 fh: open_out.fh,
2741 flags: 0,
2742 release_flags: 0,
2743 lock_owner: 0,
2744 };
2745 data.write_all(message.as_bytes())
2746 }
2747 Self::RemoveXAttr { name } => Self::write_null_terminated(data, name),
2748 Self::Rename { old_name, new_dir, new_name } => {
2749 Ok(data.write_all(
2750 uapi::fuse_rename2_in { newdir: *new_dir, flags: 0, padding: 0 }.as_bytes(),
2751 )? + Self::write_null_terminated(data, old_name)?
2752 + Self::write_null_terminated(data, new_name)?)
2753 }
2754 Self::Seek(seek_in) => data.write_all(seek_in.as_bytes()),
2755 Self::SetAttr(setattr_in) => data.write_all(setattr_in.as_bytes()),
2756 Self::SetXAttr { setxattr_in, is_ext, name, value } => {
2757 let header =
2758 if *is_ext { setxattr_in.as_bytes() } else { &setxattr_in.as_bytes()[..8] };
2759 let mut len = data.write_all(header)?;
2760 len += Self::write_null_terminated(data, name)?;
2761 len += data.write_all(value.as_bytes())?;
2762 Ok(len)
2763 }
2764 Self::Symlink { target, name } => {
2765 let mut len = Self::write_null_terminated(data, name)?;
2766 len += Self::write_null_terminated(data, target)?;
2767 Ok(len)
2768 }
2769 Self::Rmdir { name } | Self::Unlink { name } => Self::write_null_terminated(data, name),
2770 &Self::Write { mut write_in, ref content } => {
2771 let mut write_in_size = write_in.size as usize;
2772 assert!(write_in_size == content.len());
2773 if write_in_size + write_in.as_bytes().len() > data.available() {
2774 write_in_size = data.available() - write_in.as_bytes().len();
2775 write_in.size = write_in_size as u32;
2776 }
2777 let mut len = data.write_all(write_in.as_bytes())?;
2778 len += data.write_all(&content[..write_in_size])?;
2779 Ok(len)
2780 }
2781 }
2782 }
2783
2784 fn write_null_terminated(
2785 data: &mut dyn OutputBuffer,
2786 content: &Vec<u8>,
2787 ) -> Result<usize, Errno> {
2788 let mut len = data.write_all(content.as_bytes())?;
2789 len += data.write_all(&[0])?;
2790 Ok(len)
2791 }
2792
2793 fn opcode(&self) -> u32 {
2794 match self {
2795 Self::Access { .. } => uapi::fuse_opcode_FUSE_ACCESS,
2796 Self::Create { .. } => uapi::fuse_opcode_FUSE_CREATE,
2797 Self::Flush(_) => uapi::fuse_opcode_FUSE_FLUSH,
2798 Self::Forget(_) => uapi::fuse_opcode_FUSE_FORGET,
2799 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2800 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2801 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2802 Self::Interrupt { .. } => uapi::fuse_opcode_FUSE_INTERRUPT,
2803 Self::ListXAttr(_) => uapi::fuse_opcode_FUSE_LISTXATTR,
2804 Self::Lookup { .. } => uapi::fuse_opcode_FUSE_LOOKUP,
2805 Self::Mkdir { .. } => uapi::fuse_opcode_FUSE_MKDIR,
2806 Self::Mknod { .. } => uapi::fuse_opcode_FUSE_MKNOD,
2807 Self::Link { .. } => uapi::fuse_opcode_FUSE_LINK,
2808 Self::Open { flags, mode } => {
2809 if mode.is_dir() || flags.contains(OpenFlags::DIRECTORY) {
2810 uapi::fuse_opcode_FUSE_OPENDIR
2811 } else {
2812 uapi::fuse_opcode_FUSE_OPEN
2813 }
2814 }
2815 Self::Poll(_) => uapi::fuse_opcode_FUSE_POLL,
2816 Self::Read(_) => uapi::fuse_opcode_FUSE_READ,
2817 Self::Readdir { use_readdirplus, .. } => {
2818 if *use_readdirplus {
2819 uapi::fuse_opcode_FUSE_READDIRPLUS
2820 } else {
2821 uapi::fuse_opcode_FUSE_READDIR
2822 }
2823 }
2824 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2825 Self::Release(_) => uapi::fuse_opcode_FUSE_RELEASE,
2826 Self::ReleaseDir(_) => uapi::fuse_opcode_FUSE_RELEASEDIR,
2827 Self::RemoveXAttr { .. } => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2828 Self::Rename { .. } => uapi::fuse_opcode_FUSE_RENAME2,
2829 Self::Rmdir { .. } => uapi::fuse_opcode_FUSE_RMDIR,
2830 Self::Seek(_) => uapi::fuse_opcode_FUSE_LSEEK,
2831 Self::SetAttr(_) => uapi::fuse_opcode_FUSE_SETATTR,
2832 Self::SetXAttr { .. } => uapi::fuse_opcode_FUSE_SETXATTR,
2833 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2834 Self::Symlink { .. } => uapi::fuse_opcode_FUSE_SYMLINK,
2835 Self::Unlink { .. } => uapi::fuse_opcode_FUSE_UNLINK,
2836 Self::Write { .. } => uapi::fuse_opcode_FUSE_WRITE,
2837 }
2838 }
2839
2840 fn as_running(&self) -> RunningOperationKind {
2841 match self {
2842 Self::Access { .. } => RunningOperationKind::Access,
2843 Self::Create { .. } => RunningOperationKind::Create,
2844 Self::Flush(_) => RunningOperationKind::Flush,
2845 Self::Forget(_) => RunningOperationKind::Forget,
2846 Self::GetAttr => RunningOperationKind::GetAttr,
2847 Self::GetXAttr { getxattr_in, .. } => {
2848 RunningOperationKind::GetXAttr { size: getxattr_in.size }
2849 }
2850 Self::Init { fs } => RunningOperationKind::Init { fs: fs.clone() },
2851 Self::Interrupt { .. } => RunningOperationKind::Interrupt,
2852 Self::ListXAttr(getxattr_in) => {
2853 RunningOperationKind::ListXAttr { size: getxattr_in.size }
2854 }
2855 Self::Lookup { .. } => RunningOperationKind::Lookup,
2856 Self::Mkdir { .. } => RunningOperationKind::Mkdir,
2857 Self::Mknod { .. } => RunningOperationKind::Mknod,
2858 Self::Link { .. } => RunningOperationKind::Link,
2859 Self::Open { flags, mode } => RunningOperationKind::Open {
2860 dir: mode.is_dir() || flags.contains(OpenFlags::DIRECTORY),
2861 },
2862 Self::Poll(_) => RunningOperationKind::Poll,
2863 Self::Read(_) => RunningOperationKind::Read,
2864 Self::Readdir { use_readdirplus, .. } => {
2865 RunningOperationKind::Readdir { use_readdirplus: *use_readdirplus }
2866 }
2867 Self::Readlink => RunningOperationKind::Readlink,
2868 Self::Release(_) => RunningOperationKind::Release { dir: false },
2869 Self::ReleaseDir(_) => RunningOperationKind::Release { dir: true },
2870 Self::RemoveXAttr { .. } => RunningOperationKind::RemoveXAttr,
2871 Self::Rename { .. } => RunningOperationKind::Rename,
2872 Self::Rmdir { .. } => RunningOperationKind::Rmdir,
2873 Self::Seek(_) => RunningOperationKind::Seek,
2874 Self::SetAttr(_) => RunningOperationKind::SetAttr,
2875 Self::SetXAttr { .. } => RunningOperationKind::SetXAttr,
2876 Self::Statfs => RunningOperationKind::Statfs,
2877 Self::Symlink { .. } => RunningOperationKind::Symlink,
2878 Self::Unlink { .. } => RunningOperationKind::Unlink,
2879 Self::Write { .. } => RunningOperationKind::Write,
2880 }
2881 }
2882
2883 fn len(&self) -> usize {
2884 #[derive(Debug, Default)]
2885 struct CountingOutputBuffer {
2886 written: usize,
2887 }
2888
2889 impl Buffer for CountingOutputBuffer {
2890 fn segments_count(&self) -> Result<usize, Errno> {
2891 panic!("Should not be called");
2892 }
2893
2894 fn peek_each_segment(
2895 &mut self,
2896 _callback: &mut PeekBufferSegmentsCallback<'_>,
2897 ) -> Result<(), Errno> {
2898 panic!("Should not be called");
2899 }
2900 }
2901
2902 impl OutputBuffer for CountingOutputBuffer {
2903 fn available(&self) -> usize {
2904 usize::MAX
2905 }
2906
2907 fn bytes_written(&self) -> usize {
2908 self.written
2909 }
2910
2911 fn zero(&mut self) -> Result<usize, Errno> {
2912 panic!("Should not be called");
2913 }
2914
2915 fn write_each(
2916 &mut self,
2917 _callback: &mut OutputBufferCallback<'_>,
2918 ) -> Result<usize, Errno> {
2919 panic!("Should not be called.");
2920 }
2921
2922 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
2923 self.written += buffer.len();
2924 Ok(buffer.len())
2925 }
2926
2927 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
2928 panic!("Should not be called.");
2929 }
2930 }
2931
2932 let mut counting_output_buffer = CountingOutputBuffer::default();
2933 self.serialize(&mut counting_output_buffer).expect("Serialization should not fail");
2934 counting_output_buffer.written
2935 }
2936
2937 fn has_response(&self) -> bool {
2938 !matches!(self, Self::Interrupt { .. } | Self::Forget(_))
2939 }
2940
2941 fn is_async(&self) -> bool {
2942 matches!(self, Self::Init { .. })
2943 }
2944}