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, FsLockDepType, FsNode, FsNodeFlags,
25 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString, NamespaceNode,
26 PeekBufferSegmentsCallback, RenameContext, SeekTarget, SymlinkTarget, ValueOrSize,
27 WeakFileHandle, XattrOp, default_eof_offset, default_fcntl, default_seek,
28 fileops_impl_nonseekable, fileops_impl_noop_sync, fs_args, 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, DynamicLockDepRwLock, FileOpsCore, FuseConnectionStateLock,
34 FuseConnectionsLock, FuseNodeStateLock, LockDepGuard, LockDepMutex, LockDepReadGuard,
35 LockDepWriteGuard, LockEqualOrBefore, Locked, Unlocked,
36};
37use starnix_syscalls::{SyscallArg, SyscallResult};
38use starnix_types::time::{NANOS_PER_SECOND, duration_from_timespec, time_from_timespec};
39use starnix_types::vfs::default_statfs;
40use starnix_uapi::auth::FsCred;
41use starnix_uapi::device_id::DeviceId;
42use starnix_uapi::errors::{EINTR, EINVAL, ENOENT, ENOSYS, Errno};
43use starnix_uapi::file_mode::{Access, FileMode};
44use starnix_uapi::math::round_up_to_increment;
45use starnix_uapi::open_flags::OpenFlags;
46use starnix_uapi::vfs::FdEvents;
47use starnix_uapi::{
48 FUSE_SUPER_MAGIC, errno, errno_from_code, error, ino_t, mode, off_t, statfs, uapi,
49};
50use std::collections::hash_map::Entry;
51use std::collections::{HashMap, VecDeque};
52use std::ops::{Deref, DerefMut};
53use std::sync::atomic::{AtomicBool, Ordering};
54use std::sync::{Arc, Weak};
55use syncio::zxio_node_attr_has_t;
56use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
57
58const FUSE_ROOT_ID_U64: u64 = uapi::FUSE_ROOT_ID as u64;
59const CONFIGURATION_AVAILABLE_EVENT: u64 = std::u64::MAX;
60
61uapi::check_arch_independent_layout! {
62 fuse_access_in {}
63 fuse_attr {}
64 fuse_attr_out {}
65 fuse_create_in {}
66 fuse_dirent {}
67 fuse_entry_bpf_out {}
68 fuse_entry_out {}
69 fuse_flush_in {}
70 fuse_forget_in {}
71 fuse_getxattr_in {}
72 fuse_getxattr_out {}
73 fuse_in_header {}
74 fuse_init_in {}
75 fuse_init_out {}
76 fuse_interrupt_in {}
77 fuse_link_in {}
78 fuse_lseek_in {}
79 fuse_lseek_out {}
80 fuse_mkdir_in {}
81 fuse_mknod_in {}
82 fuse_opcode {}
83 fuse_open_in {}
84 fuse_open_out {}
85 fuse_out_header {}
86 fuse_poll_in {}
87 fuse_poll_out {}
88 fuse_read_in {}
89 fuse_release_in {}
90 fuse_rename2_in {}
91 fuse_setattr_in {}
92 fuse_setxattr_in {}
93 fuse_statfs_out {}
94 fuse_write_in {}
95 fuse_write_out {}
96}
97
98#[derive(Debug)]
99struct DevFuse {
100 connection: Arc<FuseConnection>,
101}
102
103pub fn open_fuse_device(
104 _locked: &mut Locked<FileOpsCore>,
105 current_task: &CurrentTask,
106 _id: DeviceId,
107 _node: &NamespaceNode,
108 _flags: OpenFlags,
109) -> Result<Box<dyn FileOps>, Errno> {
110 let connection = fuse_connections(current_task.kernel()).new_connection(current_task);
111 Ok(Box::new(DevFuse { connection }))
112}
113
114fn attr_valid_to_duration(
115 attr_valid: u64,
116 attr_valid_nsec: u32,
117) -> Result<zx::MonotonicDuration, Errno> {
118 duration_from_timespec(uapi::timespec {
119 tv_sec: i64::try_from(attr_valid).unwrap_or(std::i64::MAX),
120 tv_nsec: attr_valid_nsec.into(),
121 })
122}
123
124impl FileOps for DevFuse {
125 fileops_impl_nonseekable!();
126 fileops_impl_noop_sync!();
127
128 fn close(
129 self: Box<Self>,
130 _locked: &mut Locked<FileOpsCore>,
131 _file: &FileObjectState,
132 _current_task: &CurrentTask,
133 ) {
134 self.connection.lock().disconnect();
135 }
136
137 fn read(
138 &self,
139 locked: &mut Locked<FileOpsCore>,
140 file: &FileObject,
141 current_task: &CurrentTask,
142 offset: usize,
143 data: &mut dyn OutputBuffer,
144 ) -> Result<usize, Errno> {
145 debug_assert!(offset == 0);
146 file.blocking_op(locked, current_task, FdEvents::POLLIN, None, |_| {
147 self.connection.lock().read(data)
148 })
149 }
150
151 fn write(
152 &self,
153 _locked: &mut Locked<FileOpsCore>,
154 _file: &FileObject,
155 _current_task: &CurrentTask,
156 offset: usize,
157 data: &mut dyn InputBuffer,
158 ) -> Result<usize, Errno> {
159 debug_assert!(offset == 0);
160 self.connection.lock().write(data)
161 }
162
163 fn wait_async(
164 &self,
165 _locked: &mut Locked<FileOpsCore>,
166 _file: &FileObject,
167 _current_task: &CurrentTask,
168 waiter: &Waiter,
169 events: FdEvents,
170 handler: EventHandler,
171 ) -> Option<WaitCanceler> {
172 self.connection.lock().wait_async(waiter, events, handler)
173 }
174
175 fn query_events(
176 &self,
177 _locked: &mut Locked<FileOpsCore>,
178 _file: &FileObject,
179 _current_task: &CurrentTask,
180 ) -> Result<FdEvents, Errno> {
181 Ok(self.connection.lock().query_events())
182 }
183
184 fn ioctl(
185 &self,
186 _locked: &mut Locked<Unlocked>,
187 _file: &FileObject,
188 current_task: &CurrentTask,
189 request: u32,
190 arg: SyscallArg,
191 ) -> Result<SyscallResult, Errno> {
192 match request {
193 FUSE_DEV_IOC_PASSTHROUGH_OPEN_V2 => {
194 let fd = current_task.read_object::<FdNumber>(arg.into())?;
195 let fd = current_task.files().get(fd)?;
196 let id = {
197 let mut connection = self.connection.lock();
198 let (mut id, _) = connection.last_passthrough_id.overflowing_add(1);
199 let mut entry = connection.registered_passthrough.entry(id);
200 while id == 0 || matches!(entry, Entry::Occupied(_)) {
201 let (new_id, _) = id.overflowing_add(1);
202 id = new_id;
203 entry = connection.registered_passthrough.entry(id);
204 }
205 entry.or_insert_with(|| Arc::downgrade(&fd));
206 connection.last_passthrough_id = id;
207 id
208 };
209 Ok(id.into())
210 }
211 _ => error!(ENOTTY),
212 }
213 }
214}
215
216pub fn new_fuse_fs(
217 locked: &mut Locked<Unlocked>,
218 current_task: &CurrentTask,
219 options: FileSystemOptions,
220) -> Result<FileSystemHandle, Errno> {
221 let fd = fs_args::parse::<FdNumber>(
222 options.params.get(b"fd").ok_or_else(|| errno!(EINVAL))?.as_ref(),
223 )?;
224 let default_permissions = options.params.get(b"default_permissions").is_some().into();
225 let connection = current_task
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 fs_lockdep_type(&self) -> FsLockDepType {
299 FsLockDepType::Fuse
300 }
301
302 fn rename(
303 &self,
304 locked: &mut Locked<FileOpsCore>,
305 _fs: &FileSystem,
306 current_task: &CurrentTask,
307 context: &mut RenameContext<'_>,
308 old_name: &FsStr,
309 new_name: &FsStr,
310 ) -> Result<(), Errno> {
311 let old_parent = &context.old_parent().node;
312 let new_parent = &context.new_parent().node;
313 self.connection.lock().execute_operation(
314 locked,
315 current_task,
316 FuseNode::from_node(&old_parent),
317 FuseOperation::Rename {
318 old_name: old_name.to_owned(),
319 new_dir: new_parent.node_key(),
320 new_name: new_name.to_owned(),
321 },
322 )?;
323 Ok(())
324 }
325
326 fn uses_external_node_ids(&self) -> bool {
327 true
328 }
329
330 fn statfs(
331 &self,
332 locked: &mut Locked<FileOpsCore>,
333 fs: &FileSystem,
334 current_task: &CurrentTask,
335 ) -> Result<statfs, Errno> {
336 let node = FuseNode::from_node(&fs.root().node);
337 let response = self.connection.lock().execute_operation(
338 locked,
339 current_task,
340 &node,
341 FuseOperation::Statfs,
342 )?;
343 let FuseResponse::Statfs(statfs_out) = response else {
344 return error!(EINVAL);
345 };
346 Ok(statfs {
347 f_type: FUSE_SUPER_MAGIC as i64,
348 f_blocks: statfs_out.st.blocks.try_into().map_err(|_| errno!(EINVAL))?,
349 f_bfree: statfs_out.st.bfree.try_into().map_err(|_| errno!(EINVAL))?,
350 f_bavail: statfs_out.st.bavail.try_into().map_err(|_| errno!(EINVAL))?,
351 f_files: statfs_out.st.files.try_into().map_err(|_| errno!(EINVAL))?,
352 f_ffree: statfs_out.st.ffree.try_into().map_err(|_| errno!(EINVAL))?,
353 f_bsize: statfs_out.st.bsize.try_into().map_err(|_| errno!(EINVAL))?,
354 f_namelen: statfs_out.st.namelen.try_into().map_err(|_| errno!(EINVAL))?,
355 f_frsize: statfs_out.st.frsize.try_into().map_err(|_| errno!(EINVAL))?,
356 ..statfs::default()
357 })
358 }
359 fn name(&self) -> &'static FsStr {
360 "fuse".into()
361 }
362 fn unmount(&self) {
363 self.connection.lock().disconnect();
364 }
365}
366
367#[derive(Debug, Default)]
368struct FuseConnections {
369 connections: LockDepMutex<Vec<Weak<FuseConnection>>, FuseConnectionsLock>,
370 next_identifier: AtomicCounter<u64>,
371}
372
373impl FuseConnections {
374 fn new_connection(&self, current_task: &CurrentTask) -> Arc<FuseConnection> {
375 let connection = Arc::new(FuseConnection {
376 id: self.next_identifier.next(),
377 creds: current_task.current_fscred(),
378 state: Default::default(),
379 });
380 self.connections.lock().push(Arc::downgrade(&connection));
381 connection
382 }
383
384 fn for_each<F>(&self, mut f: F)
385 where
386 F: FnMut(Arc<FuseConnection>),
387 {
388 self.connections.lock().retain(|connection| {
389 if let Some(connection) = connection.upgrade() {
390 f(connection);
391 true
392 } else {
393 false
394 }
395 });
396 }
397}
398
399struct FuseCtlFs;
400
401impl FileSystemOps for FuseCtlFs {
402 fn rename(
403 &self,
404 _locked: &mut Locked<FileOpsCore>,
405 _fs: &FileSystem,
406 _current_task: &CurrentTask,
407 _context: &mut RenameContext<'_>,
408 _old_name: &FsStr,
409 _new_name: &FsStr,
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: LockDepMutex<FuseNodeMutableState, FuseNodeStateLock>,
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: &DynamicLockDepRwLock<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 DynamicLockDepRwLock<FsNodeInfo>,
628 ) -> Result<LockDepReadGuard<'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 DynamicLockDepRwLock<FsNodeInfo>,
662 ) -> Result<LockDepReadGuard<'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(LockDepWriteGuard::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
845 let file_size = file.node().info().size;
847 if offset >= file_size {
848 return Ok(0);
849 }
850 let target_size = std::cmp::min(data.available(), file_size - offset);
851 if target_size == 0 {
852 return Ok(0);
853 }
854
855 let node = Self::get_fuse_node(file);
856 let response = self.connection.lock().execute_operation(
857 locked,
858 current_task,
859 node,
860 FuseOperation::Read(uapi::fuse_read_in {
861 fh: self.open_out.fh,
862 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
863 size: target_size.try_into().unwrap_or(u32::MAX),
864 read_flags: 0,
865 lock_owner: 0,
866 flags: 0,
867 padding: 0,
868 }),
869 )?;
870 let FuseResponse::Read(read_out) = response else {
871 return error!(EINVAL);
872 };
873 data.write(&read_out)
874 }
875
876 fn write(
877 &self,
878 locked: &mut Locked<FileOpsCore>,
879 file: &FileObject,
880 current_task: &CurrentTask,
881 offset: usize,
882 data: &mut dyn InputBuffer,
883 ) -> Result<usize, Errno> {
884 if file.node().info().mode.is_dir() {
885 return error!(EISDIR);
886 }
887 if let Some(file_object) = self.passthrough_file.upgrade() {
888 return file_object.ops().write(locked, &file_object, current_task, offset, data);
889 }
890 let node = Self::get_fuse_node(file);
891 let content = data.peek_all()?;
892 let response = self.connection.lock().execute_operation(
893 locked,
894 current_task,
895 node,
896 FuseOperation::Write {
897 write_in: uapi::fuse_write_in {
898 fh: self.open_out.fh,
899 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
900 size: content.len().try_into().map_err(|_| errno!(EINVAL))?,
901 write_flags: 0,
902 lock_owner: 0,
903 flags: 0,
904 padding: 0,
905 },
906 content,
907 },
908 )?;
909 let FuseResponse::Write(write_out) = response else {
910 return error!(EINVAL);
911 };
912 node.invalidate_attributes();
913
914 let written = write_out.size as usize;
915
916 data.advance(written)?;
917 Ok(written)
918 }
919
920 fn seek(
921 &self,
922 locked: &mut Locked<FileOpsCore>,
923 file: &FileObject,
924 current_task: &CurrentTask,
925 current_offset: off_t,
926 target: SeekTarget,
927 ) -> Result<off_t, Errno> {
928 if matches!(target, SeekTarget::Data(_) | SeekTarget::Hole(_)) {
930 let node = Self::get_fuse_node(file);
931 let response = self.connection.lock().execute_operation(
932 locked,
933 current_task,
934 node,
935 FuseOperation::Seek(uapi::fuse_lseek_in {
936 fh: self.open_out.fh,
937 offset: target.offset().try_into().map_err(|_| errno!(EINVAL))?,
938 whence: target.whence(),
939 padding: 0,
940 }),
941 );
942 match response {
943 Ok(response) => {
944 let FuseResponse::Seek(seek_out) = response else {
945 return error!(EINVAL);
946 };
947 return seek_out.offset.try_into().map_err(|_| errno!(EINVAL));
948 }
949 Err(errno) if errno == ENOSYS => {}
952 Err(errno) => return Err(errno),
953 };
954 }
955
956 default_seek(current_offset, target, || default_eof_offset(locked, file, current_task))
957 }
958
959 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
960 track_stub!(TODO("https://fxbug.dev/352359968"), "FUSE fsync()");
961 Ok(())
962 }
963
964 fn wait_async(
965 &self,
966 _locked: &mut Locked<FileOpsCore>,
967 _file: &FileObject,
968 _current_task: &CurrentTask,
969 _waiter: &Waiter,
970 _events: FdEvents,
971 _handler: EventHandler,
972 ) -> Option<WaitCanceler> {
973 None
974 }
975
976 fn query_events(
977 &self,
978 locked: &mut Locked<FileOpsCore>,
979 file: &FileObject,
980 current_task: &CurrentTask,
981 ) -> Result<FdEvents, Errno> {
982 let node = Self::get_fuse_node(file);
983 let response = self.connection.lock().execute_operation(
984 locked,
985 current_task,
986 node,
987 FuseOperation::Poll(uapi::fuse_poll_in {
988 fh: self.open_out.fh,
989 kh: 0,
990 flags: 0,
991 events: FdEvents::all().bits(),
992 }),
993 )?;
994 let FuseResponse::Poll(poll_out) = response else {
995 return error!(EINVAL);
996 };
997 FdEvents::from_bits(poll_out.revents).ok_or_else(|| errno!(EINVAL))
998 }
999
1000 fn readdir(
1001 &self,
1002 locked: &mut Locked<FileOpsCore>,
1003 file: &FileObject,
1004 current_task: &CurrentTask,
1005 sink: &mut dyn DirentSink,
1006 ) -> Result<(), Errno> {
1007 let mut state = self.connection.lock();
1008 let configuration = state.get_configuration(locked, current_task)?;
1009 let use_readdirplus = {
1010 if configuration.flags.contains(FuseInitFlags::DO_READDIRPLUS) {
1011 if configuration.flags.contains(FuseInitFlags::READDIRPLUS_AUTO) {
1012 sink.offset() == 0
1013 } else {
1014 true
1015 }
1016 } else {
1017 false
1018 }
1019 };
1020 let user_capacity = if let Some(base_user_capacity) = sink.user_capacity() {
1023 if use_readdirplus {
1024 base_user_capacity * 3 / 2
1026 } else {
1027 base_user_capacity
1028 }
1029 } else {
1030 *PAGE_SIZE as usize
1031 };
1032 let node = Self::get_fuse_node(file);
1033 let response = state.execute_operation(
1034 locked,
1035 current_task,
1036 node,
1037 FuseOperation::Readdir {
1038 read_in: uapi::fuse_read_in {
1039 fh: self.open_out.fh,
1040 offset: sink.offset().try_into().map_err(|_| errno!(EINVAL))?,
1041 size: user_capacity.try_into().map_err(|_| errno!(EINVAL))?,
1042 read_flags: 0,
1043 lock_owner: 0,
1044 flags: 0,
1045 padding: 0,
1046 },
1047 use_readdirplus,
1048 },
1049 )?;
1050 std::mem::drop(state);
1051 let FuseResponse::Readdir(dirents) = response else {
1052 return error!(EINVAL);
1053 };
1054 let mut sink_result = Ok(());
1055 for (dirent, name, entry) in dirents {
1056 if let Some(entry) = entry {
1057 if entry.nodeid != 0 {
1059 if let Err(e) = node.fs_node_from_entry(file.node(), name.as_ref(), &entry) {
1060 log_error!("Unable to prefill entry: {e:?}");
1061 }
1062 }
1063 }
1064 if sink_result.is_ok() {
1065 sink_result = sink.add(
1066 dirent.ino,
1067 dirent.off.try_into().map_err(|_| errno!(EINVAL))?,
1068 DirectoryEntryType::from_bits(
1069 dirent.type_.try_into().map_err(|_| errno!(EINVAL))?,
1070 ),
1071 name.as_ref(),
1072 );
1073 }
1074 }
1075 sink_result
1076 }
1077
1078 fn ioctl(
1079 &self,
1080 _locked: &mut Locked<Unlocked>,
1081 _file: &FileObject,
1082 _current_task: &CurrentTask,
1083 _request: u32,
1084 _arg: SyscallArg,
1085 ) -> Result<SyscallResult, Errno> {
1086 track_stub!(TODO("https://fxbug.dev/322875259"), "fuse ioctl");
1087 error!(ENOTTY)
1088 }
1089
1090 fn fcntl(
1091 &self,
1092 _file: &FileObject,
1093 _current_task: &CurrentTask,
1094 cmd: u32,
1095 _arg: u64,
1096 ) -> Result<SyscallResult, Errno> {
1097 track_stub!(TODO("https://fxbug.dev/322875764"), "fuse fcntl");
1098 default_fcntl(cmd)
1099 }
1100}
1101
1102struct FuseDirEntry {
1103 valid_until: AtomicMonotonicInstant,
1104}
1105
1106impl Default for FuseDirEntry {
1107 fn default() -> Self {
1108 Self { valid_until: zx::MonotonicInstant::INFINITE_PAST.into() }
1109 }
1110}
1111
1112impl DirEntryOps for FuseDirEntry {
1113 fn revalidate(
1114 &self,
1115 locked: &mut Locked<FileOpsCore>,
1116 current_task: &CurrentTask,
1117 dir_entry: &DirEntry,
1118 ) -> Result<bool, Errno> {
1119 const VALID_UNTIL_ORDERING: Ordering = Ordering::Relaxed;
1122
1123 let now = zx::MonotonicInstant::get();
1124 if self.valid_until.load(VALID_UNTIL_ORDERING) >= now {
1125 return Ok(true);
1126 }
1127
1128 let node = FuseNode::from_node(&dir_entry.node);
1129 if node.nodeid == FUSE_ROOT_ID_U64 {
1130 return Ok(true);
1132 }
1133
1134 let (parent, name) = {
1137 let scope = RcuReadScope::new();
1138 let parent = dir_entry.parent().expect("non-root nodes always has a parent");
1139 let name = dir_entry.local_name(&scope).to_owned();
1140 (parent, name)
1141 };
1142 let parent = FuseNode::from_node(&parent.node);
1143 let FuseEntryOutExtended {
1144 arg:
1145 uapi::fuse_entry_out {
1146 nodeid,
1147 generation,
1148 entry_valid,
1149 entry_valid_nsec,
1150 attr,
1151 attr_valid,
1152 attr_valid_nsec,
1153 },
1154 ..
1155 } = match parent.connection.lock().execute_operation(
1156 locked,
1157 current_task,
1158 parent,
1159 FuseOperation::Lookup { name },
1160 ) {
1161 Ok(FuseResponse::Entry(entry)) => entry,
1162 Ok(_) => return error!(EINVAL),
1163 Err(errno) => {
1164 if errno == ENOENT {
1165 return Ok(false);
1167 } else {
1168 return Err(errno);
1169 };
1170 }
1171 };
1172
1173 if (nodeid != node.nodeid) || (generation != node.generation) {
1174 return Ok(false);
1178 }
1179
1180 dir_entry.node.update_info(|info| {
1181 FuseNode::update_node_info_from_attr(
1182 info,
1183 attr,
1184 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1185 &node.attributes_valid_until,
1186 )?;
1187
1188 self.valid_until.store(
1189 zx::MonotonicInstant::after(attr_valid_to_duration(entry_valid, entry_valid_nsec)?),
1190 VALID_UNTIL_ORDERING,
1191 );
1192
1193 Ok(true)
1194 })
1195 }
1196}
1197
1198const DEFAULT_PERMISSIONS_ATOMIC_ORDERING: Ordering = Ordering::Relaxed;
1200
1201impl FsNodeOps for FuseNode {
1202 fn check_access(
1203 &self,
1204 locked: &mut Locked<FileOpsCore>,
1205 node: &FsNode,
1206 current_task: &CurrentTask,
1207 permission_flags: security::PermissionFlags,
1208 info: &DynamicLockDepRwLock<FsNodeInfo>,
1209 reason: CheckAccessReason,
1210 audit_context: security::Auditable<'_>,
1211 ) -> Result<(), Errno> {
1212 if FuseFs::from_fs(&node.fs()).default_permissions.load(DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
1215 {
1216 return self.default_check_access_with_valid_node_attributes(
1217 locked,
1218 node,
1219 current_task,
1220 permission_flags,
1221 reason,
1222 info,
1223 audit_context,
1224 );
1225 }
1226
1227 match reason {
1228 CheckAccessReason::Access | CheckAccessReason::Chdir | CheckAccessReason::Chroot => {
1229 let response = self.connection.lock().execute_operation(
1234 locked,
1235 current_task,
1236 self,
1237 FuseOperation::Access {
1238 mask: (permission_flags.as_access() & Access::ACCESS_MASK).bits() as u32,
1239 },
1240 )?;
1241
1242 if let FuseResponse::Access(result) = response { result } else { error!(EINVAL) }
1243 }
1244 CheckAccessReason::Exec => self.default_check_access_with_valid_node_attributes(
1245 locked,
1246 node,
1247 current_task,
1248 permission_flags,
1249 reason,
1250 info,
1251 audit_context,
1252 ),
1253 CheckAccessReason::ChangeTimestamps { .. }
1254 | CheckAccessReason::InternalPermissionChecks => {
1255 Ok(())
1260 }
1261 }
1262 }
1263
1264 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1265 Box::new(FuseDirEntry::default())
1266 }
1267
1268 fn create_file_ops(
1269 &self,
1270 locked: &mut Locked<FileOpsCore>,
1271 node: &FsNode,
1272 current_task: &CurrentTask,
1273 flags: OpenFlags,
1274 ) -> Result<Box<dyn FileOps>, Errno> {
1275 let flags = flags & !(OpenFlags::CREAT | OpenFlags::EXCL);
1277 let mode = node.info().mode;
1278 let response = self.connection.lock().execute_operation(
1279 locked,
1280 current_task,
1281 self,
1282 FuseOperation::Open { flags, mode },
1283 )?;
1284 let FuseResponse::Open(open_out) = response else {
1285 return error!(EINVAL);
1286 };
1287 let passthrough_fh = unsafe { open_out.__bindgen_anon_1.passthrough_fh };
1290 let passthrough_file = if passthrough_fh != 0 {
1291 let mut connection = self.connection.lock();
1292 connection.registered_passthrough.remove(&passthrough_fh).unwrap_or_default()
1293 } else {
1294 Weak::new()
1295 };
1296 Ok(Box::new(FuseFileObject {
1297 connection: self.connection.clone(),
1298 passthrough_file,
1299 open_out,
1300 }))
1301 }
1302
1303 fn lookup(
1304 &self,
1305 locked: &mut Locked<FileOpsCore>,
1306 node: &FsNode,
1307 current_task: &CurrentTask,
1308 name: &FsStr,
1309 ) -> Result<FsNodeHandle, Errno> {
1310 let response = self.connection.lock().execute_operation(
1311 locked,
1312 current_task,
1313 self,
1314 FuseOperation::Lookup { name: name.to_owned() },
1315 )?;
1316 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1317 }
1318
1319 fn mknod(
1320 &self,
1321 locked: &mut Locked<FileOpsCore>,
1322 node: &FsNode,
1323 current_task: &CurrentTask,
1324 name: &FsStr,
1325 mode: FileMode,
1326 dev: DeviceId,
1327 _owner: FsCred,
1328 ) -> Result<FsNodeHandle, Errno> {
1329 let get_entry = |locked: &mut Locked<FileOpsCore>| {
1330 let umask = current_task.fs().umask().bits();
1331 let mut connection = self.connection.lock();
1332
1333 if dev == DeviceId::NONE && !connection.no_create {
1334 match connection.execute_operation(
1335 locked,
1336 current_task,
1337 self,
1338 FuseOperation::Create(
1339 uapi::fuse_create_in {
1340 flags: OpenFlags::CREAT.bits(),
1341 mode: mode.bits(),
1342 umask,
1343 open_flags: 0,
1344 },
1345 name.to_owned(),
1346 ),
1347 ) {
1348 Ok(response) => {
1349 let FuseResponse::Create(response) = response else {
1350 return error!(EINVAL);
1351 };
1352
1353 let fuse_node = FuseNode::new(
1354 self.connection.clone(),
1355 response.entry.nodeid,
1356 response.entry.generation,
1357 );
1358
1359 if let Err(e) = connection.execute_operation(
1365 locked,
1366 current_task,
1367 &fuse_node,
1368 FuseOperation::Release(response.open),
1369 ) {
1370 log_error!("Error when releasing fh: {e:?}");
1371 }
1372
1373 return Ok(response.entry);
1374 }
1375 Err(e) if e == ENOSYS => {
1376 connection.no_create = true;
1377 }
1379 Err(e) => return Err(e),
1380 }
1381 }
1382
1383 connection
1384 .execute_operation(
1385 locked,
1386 current_task,
1387 self,
1388 FuseOperation::Mknod {
1389 mknod_in: uapi::fuse_mknod_in {
1390 mode: mode.bits(),
1391 rdev: dev.bits() as u32,
1392 umask,
1393 padding: 0,
1394 },
1395 name: name.to_owned(),
1396 },
1397 )?
1398 .entry()
1399 .copied()
1400 .ok_or_else(|| errno!(EINVAL))
1401 };
1402
1403 let entry = get_entry(locked)?;
1404 self.fs_node_from_entry(node, name, &entry)
1405 }
1406
1407 fn mkdir(
1408 &self,
1409 locked: &mut Locked<FileOpsCore>,
1410 node: &FsNode,
1411 current_task: &CurrentTask,
1412 name: &FsStr,
1413 mode: FileMode,
1414 _owner: FsCred,
1415 ) -> Result<FsNodeHandle, Errno> {
1416 let response = self.connection.lock().execute_operation(
1417 locked,
1418 current_task,
1419 self,
1420 FuseOperation::Mkdir {
1421 mkdir_in: uapi::fuse_mkdir_in {
1422 mode: mode.bits(),
1423 umask: current_task.fs().umask().bits(),
1424 },
1425 name: name.to_owned(),
1426 },
1427 )?;
1428 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1429 }
1430
1431 fn create_symlink(
1432 &self,
1433 locked: &mut Locked<FileOpsCore>,
1434 node: &FsNode,
1435 current_task: &CurrentTask,
1436 name: &FsStr,
1437 target: &FsStr,
1438 _owner: FsCred,
1439 ) -> Result<FsNodeHandle, Errno> {
1440 let response = self.connection.lock().execute_operation(
1441 locked,
1442 current_task,
1443 self,
1444 FuseOperation::Symlink { target: target.to_owned(), name: name.to_owned() },
1445 )?;
1446 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1447 }
1448
1449 fn readlink(
1450 &self,
1451 locked: &mut Locked<FileOpsCore>,
1452 _node: &FsNode,
1453 current_task: &CurrentTask,
1454 ) -> Result<SymlinkTarget, Errno> {
1455 let response = self.connection.lock().execute_operation(
1456 locked,
1457 current_task,
1458 self,
1459 FuseOperation::Readlink,
1460 )?;
1461 let FuseResponse::Read(read_out) = response else {
1462 return error!(EINVAL);
1463 };
1464 Ok(SymlinkTarget::Path(read_out.into()))
1465 }
1466
1467 fn link(
1468 &self,
1469 locked: &mut Locked<FileOpsCore>,
1470 _node: &FsNode,
1471 current_task: &CurrentTask,
1472 name: &FsStr,
1473 child: &FsNodeHandle,
1474 ) -> Result<(), Errno> {
1475 let child_node = FuseNode::from_node(child);
1476 self.connection
1477 .lock()
1478 .execute_operation(
1479 locked,
1480 current_task,
1481 self,
1482 FuseOperation::Link {
1483 link_in: uapi::fuse_link_in { oldnodeid: child_node.nodeid },
1484 name: name.to_owned(),
1485 },
1486 )
1487 .map(|_| ())
1488 }
1489
1490 fn unlink(
1491 &self,
1492 locked: &mut Locked<FileOpsCore>,
1493 _node: &FsNode,
1494 current_task: &CurrentTask,
1495 name: &FsStr,
1496 child: &FsNodeHandle,
1497 ) -> Result<(), Errno> {
1498 let is_dir = child.is_dir();
1499 self.connection
1500 .lock()
1501 .execute_operation(
1502 locked,
1503 current_task,
1504 self,
1505 if is_dir {
1506 FuseOperation::Rmdir { name: name.to_owned() }
1507 } else {
1508 FuseOperation::Unlink { name: name.to_owned() }
1509 },
1510 )
1511 .map(|_| ())
1512 }
1513
1514 fn truncate(
1515 &self,
1516 locked: &mut Locked<FileOpsCore>,
1517 _guard: &AppendLockWriteGuard<'_>,
1518 node: &FsNode,
1519 current_task: &CurrentTask,
1520 length: u64,
1521 ) -> Result<(), Errno> {
1522 node.update_info(|info| {
1523 let attributes = uapi::fuse_setattr_in {
1525 size: length,
1526 valid: uapi::FATTR_SIZE,
1527 ..Default::default()
1528 };
1529
1530 let response = self.connection.lock().execute_operation(
1531 locked,
1532 current_task,
1533 self,
1534 FuseOperation::SetAttr(attributes),
1535 )?;
1536 let uapi::fuse_attr_out { attr_valid, attr_valid_nsec, attr, .. } =
1537 if let FuseResponse::Attr(attr) = response {
1538 attr
1539 } else {
1540 return error!(EINVAL);
1541 };
1542 FuseNode::update_node_info_from_attr(
1543 info,
1544 attr,
1545 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1546 &self.attributes_valid_until,
1547 )?;
1548 Ok(())
1549 })
1550 }
1551
1552 fn allocate(
1553 &self,
1554 _locked: &mut Locked<FileOpsCore>,
1555 _guard: &AppendLockWriteGuard<'_>,
1556 _node: &FsNode,
1557 _current_task: &CurrentTask,
1558 _mode: FallocMode,
1559 _offset: u64,
1560 _length: u64,
1561 ) -> Result<(), Errno> {
1562 track_stub!(TODO("https://fxbug.dev/322875414"), "FsNodeOps::allocate");
1563 error!(ENOTSUP)
1564 }
1565
1566 fn fetch_and_refresh_info<'a>(
1567 &self,
1568 locked: &mut Locked<FileOpsCore>,
1569 _node: &FsNode,
1570 current_task: &CurrentTask,
1571 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
1572 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
1573 self.refresh_expired_node_attributes(locked, current_task, info)
1577 }
1578
1579 fn update_attributes(
1580 &self,
1581 locked: &mut Locked<FileOpsCore>,
1582 _node: &FsNode,
1583 current_task: &CurrentTask,
1584 info: &FsNodeInfo,
1585 has: zxio_node_attr_has_t,
1586 ) -> Result<(), Errno> {
1587 let mut valid = 0u32;
1588 if has.modification_time {
1590 valid |= uapi::FATTR_MTIME;
1591 }
1592 if has.access_time {
1593 valid |= uapi::FATTR_ATIME;
1594 }
1595 if has.mode {
1596 valid |= uapi::FATTR_MODE;
1597 }
1598 if has.uid {
1599 valid |= uapi::FATTR_UID;
1600 }
1601 if has.gid {
1602 valid |= uapi::FATTR_GID;
1603 }
1604
1605 let attributes = uapi::fuse_setattr_in {
1606 valid,
1607 atime: (info.time_access.into_nanos() / NANOS_PER_SECOND) as u64,
1608 mtime: (info.time_modify.into_nanos() / NANOS_PER_SECOND) as u64,
1609 ctime: (info.time_status_change.into_nanos() / NANOS_PER_SECOND) as u64,
1610 atimensec: (info.time_access.into_nanos() % NANOS_PER_SECOND) as u32,
1611 mtimensec: (info.time_modify.into_nanos() % NANOS_PER_SECOND) as u32,
1612 ctimensec: (info.time_status_change.into_nanos() % NANOS_PER_SECOND) as u32,
1613 mode: info.mode.bits(),
1614 uid: info.uid,
1615 gid: info.gid,
1616 ..Default::default()
1617 };
1618
1619 let response = self.connection.lock().execute_operation(
1620 locked,
1621 current_task,
1622 self,
1623 FuseOperation::SetAttr(attributes),
1624 )?;
1625 if let FuseResponse::Attr(_attr) = response { Ok(()) } else { error!(EINVAL) }
1626 }
1627
1628 fn get_xattr(
1629 &self,
1630 locked: &mut Locked<FileOpsCore>,
1631 _node: &FsNode,
1632 current_task: &CurrentTask,
1633 name: &FsStr,
1634 max_size: usize,
1635 ) -> Result<ValueOrSize<FsString>, Errno> {
1636 let response = self.connection.lock().execute_operation(
1637 locked,
1638 current_task,
1639 self,
1640 FuseOperation::GetXAttr {
1641 getxattr_in: uapi::fuse_getxattr_in {
1642 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1643 padding: 0,
1644 },
1645 name: name.to_owned(),
1646 },
1647 )?;
1648 if let FuseResponse::GetXAttr(result) = response { Ok(result) } else { error!(EINVAL) }
1649 }
1650
1651 fn set_xattr(
1652 &self,
1653 locked: &mut Locked<FileOpsCore>,
1654 _node: &FsNode,
1655 current_task: &CurrentTask,
1656 name: &FsStr,
1657 value: &FsStr,
1658 op: XattrOp,
1659 ) -> Result<(), Errno> {
1660 let mut state = self.connection.lock();
1661 let configuration = state.get_configuration(locked, current_task)?;
1662 state.execute_operation(
1663 locked,
1664 current_task,
1665 self,
1666 FuseOperation::SetXAttr {
1667 setxattr_in: uapi::fuse_setxattr_in {
1668 size: value.len().try_into().map_err(|_| errno!(EINVAL))?,
1669 flags: op.into_flags(),
1670 setxattr_flags: 0,
1671 padding: 0,
1672 },
1673 is_ext: configuration.flags.contains(FuseInitFlags::SETXATTR_EXT),
1674 name: name.to_owned(),
1675 value: value.to_owned(),
1676 },
1677 )?;
1678 Ok(())
1679 }
1680
1681 fn remove_xattr(
1682 &self,
1683 locked: &mut Locked<FileOpsCore>,
1684 _node: &FsNode,
1685 current_task: &CurrentTask,
1686 name: &FsStr,
1687 ) -> Result<(), Errno> {
1688 self.connection.lock().execute_operation(
1689 locked,
1690 current_task,
1691 self,
1692 FuseOperation::RemoveXAttr { name: name.to_owned() },
1693 )?;
1694 Ok(())
1695 }
1696
1697 fn list_xattrs(
1698 &self,
1699 locked: &mut Locked<FileOpsCore>,
1700 _node: &FsNode,
1701 current_task: &CurrentTask,
1702 max_size: usize,
1703 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
1704 let response = self.connection.lock().execute_operation(
1705 locked,
1706 current_task,
1707 self,
1708 FuseOperation::ListXAttr(uapi::fuse_getxattr_in {
1709 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1710 padding: 0,
1711 }),
1712 )?;
1713 if let FuseResponse::GetXAttr(result) = response {
1714 Ok(result.map(|s| {
1715 let mut result = s.split(|c| *c == 0).map(FsString::from).collect::<Vec<_>>();
1716 result.pop();
1719 result
1720 }))
1721 } else {
1722 error!(EINVAL)
1723 }
1724 }
1725
1726 fn forget(
1727 self: Box<Self>,
1728 locked: &mut Locked<FileOpsCore>,
1729 current_task: &CurrentTask,
1730 _info: FsNodeInfo,
1731 ) -> Result<(), Errno> {
1732 let nlookup = self.state.lock().nlookup;
1733 let mut state = self.connection.lock();
1734 if !state.is_connected() {
1735 return Ok(());
1736 }
1737 if nlookup > 0 {
1738 state.execute_operation(
1739 locked,
1740 current_task,
1741 self.as_ref(),
1742 FuseOperation::Forget(uapi::fuse_forget_in { nlookup }),
1743 )?;
1744 };
1745 Ok(())
1746 }
1747
1748 fn node_key(&self, _node: &FsNode) -> ino_t {
1749 self.nodeid
1750 }
1751}
1752
1753#[derive(Debug, Default)]
1755enum FuseConnectionState {
1756 #[default]
1757 Waiting,
1759 Connected,
1761 Disconnected,
1763}
1764
1765#[derive(Debug)]
1766struct FuseConnection {
1767 id: u64,
1769
1770 creds: FsCred,
1772
1773 state: LockDepMutex<FuseMutableState, FuseConnectionStateLock>,
1775}
1776
1777struct FuseMutableStateGuard<'a>(Guard<'a, FuseConnection, LockDepGuard<'a, FuseMutableState>>);
1778
1779impl<'a> Deref for FuseMutableStateGuard<'a> {
1780 type Target = Guard<'a, FuseConnection, LockDepGuard<'a, FuseMutableState>>;
1781 fn deref(&self) -> &Self::Target {
1782 &self.0
1783 }
1784}
1785
1786impl<'a> DerefMut for FuseMutableStateGuard<'a> {
1787 fn deref_mut(&mut self) -> &mut Self::Target {
1788 &mut self.0
1789 }
1790}
1791
1792impl FuseConnection {
1793 fn lock<'a>(&'a self) -> FuseMutableStateGuard<'a> {
1794 FuseMutableStateGuard(Guard::<'a, FuseConnection, LockDepGuard<'a, FuseMutableState>>::new(
1795 self,
1796 self.state.lock(),
1797 ))
1798 }
1799}
1800
1801#[derive(Clone, Copy, Debug)]
1802struct FuseConfiguration {
1803 flags: FuseInitFlags,
1804}
1805
1806impl TryFrom<uapi::fuse_init_out> for FuseConfiguration {
1807 type Error = Errno;
1808 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
1809 let flags = FuseInitFlags::try_from(init_out)?;
1810 Ok(Self { flags })
1811 }
1812}
1813
1814type OperationsState = HashMap<uapi::fuse_opcode, Result<FuseResponse, Errno>>;
1822
1823#[derive(Debug, Default)]
1824struct FuseMutableState {
1825 state: FuseConnectionState,
1827
1828 last_unique_id: u64,
1830
1831 configuration: Option<FuseConfiguration>,
1833
1834 operations: HashMap<u64, RunningOperation>,
1836
1837 message_queue: VecDeque<FuseKernelMessage>,
1841
1842 waiters: WaitQueue,
1844
1845 operations_state: OperationsState,
1847
1848 no_create: bool,
1850
1851 last_passthrough_id: u32,
1853
1854 registered_passthrough: HashMap<u32, WeakFileHandle>,
1857}
1858
1859impl<'a> FuseMutableStateGuard<'a> {
1860 fn wait_for_configuration<L, T>(
1861 &mut self,
1862 locked: &mut Locked<L>,
1863 current_task: &CurrentTask,
1864 f: impl Fn(&FuseConfiguration) -> T,
1865 ) -> Result<T, Errno>
1866 where
1867 L: LockEqualOrBefore<FileOpsCore>,
1868 {
1869 if let Some(configuration) = self.configuration.as_ref() {
1870 return Ok(f(configuration));
1871 }
1872 loop {
1873 if !self.is_connected() {
1874 return error!(ECONNABORTED);
1875 }
1876 let waiter = Waiter::new();
1877 self.waiters.wait_async_value(&waiter, CONFIGURATION_AVAILABLE_EVENT);
1878 if let Some(configuration) = self.configuration.as_ref() {
1879 return Ok(f(configuration));
1880 }
1881 Guard::<'a, FuseConnection, LockDepGuard<'a, FuseMutableState>>::unlocked(
1882 self,
1883 || waiter.wait(locked, current_task),
1884 )?;
1885 }
1886 }
1887
1888 fn get_configuration<L>(
1889 &mut self,
1890 locked: &mut Locked<L>,
1891 current_task: &CurrentTask,
1892 ) -> Result<FuseConfiguration, Errno>
1893 where
1894 L: LockEqualOrBefore<FileOpsCore>,
1895 {
1896 self.wait_for_configuration(locked, current_task, Clone::clone)
1897 }
1898
1899 fn wait_for_configuration_ready<L>(
1900 &mut self,
1901 locked: &mut Locked<L>,
1902 current_task: &CurrentTask,
1903 ) -> Result<(), Errno>
1904 where
1905 L: LockEqualOrBefore<FileOpsCore>,
1906 {
1907 self.wait_for_configuration(locked, current_task, |_| ())
1908 }
1909
1910 fn execute_operation<L>(
1916 &mut self,
1917 locked: &mut Locked<L>,
1918 current_task: &CurrentTask,
1919 node: &FuseNode,
1920 operation: FuseOperation,
1921 ) -> Result<FuseResponse, Errno>
1922 where
1923 L: LockEqualOrBefore<FileOpsCore>,
1924 {
1925 if !matches!(operation, FuseOperation::Init { .. }) {
1930 self.wait_for_configuration_ready(locked, current_task)?;
1931 }
1932
1933 if let Some(result) = self.operations_state.get(&operation.opcode()) {
1934 return result.clone();
1935 }
1936 if !operation.has_response() {
1937 self.queue_operation(current_task, node.nodeid, operation, None)?;
1938 return Ok(FuseResponse::None);
1939 }
1940 let waiter = Waiter::with_options(WaiterOptions::UNSAFE_CALLSTACK);
1941 let is_async = operation.is_async();
1942 let unique_id =
1943 self.queue_operation(current_task, node.nodeid, operation, Some(&waiter))?;
1944 if is_async {
1945 return Ok(FuseResponse::None);
1946 }
1947 let mut first_loop = true;
1948 loop {
1949 if !self.is_connected() {
1950 return error!(ECONNABORTED);
1951 }
1952 if let Some(response) = self.get_response(unique_id) {
1953 return response;
1954 }
1955 match Guard::<'a, FuseConnection, LockDepGuard<'a, FuseMutableState>>::unlocked(
1956 self,
1957 || waiter.wait(locked, current_task),
1958 ) {
1959 Ok(()) => {}
1960 Err(e) if e == EINTR => {
1961 if first_loop {
1964 self.interrupt(current_task, node.nodeid, unique_id)?;
1965 first_loop = false;
1966 }
1967 }
1968 Err(e) => {
1969 log_error!("Unexpected error: {e:?}");
1970 return Err(e);
1971 }
1972 }
1973 }
1974 }
1975}
1976
1977impl FuseMutableState {
1978 fn wait_async(
1979 &self,
1980 waiter: &Waiter,
1981 events: FdEvents,
1982 handler: EventHandler,
1983 ) -> Option<WaitCanceler> {
1984 Some(self.waiters.wait_async_fd_events(waiter, events, handler))
1985 }
1986
1987 fn is_connected(&self) -> bool {
1988 matches!(self.state, FuseConnectionState::Connected)
1989 }
1990
1991 fn set_configuration(&mut self, configuration: FuseConfiguration) {
1992 debug_assert!(self.configuration.is_none());
1993 log_trace!("Fuse configuration: {configuration:?}");
1994 self.configuration = Some(configuration);
1995 self.waiters.notify_value(CONFIGURATION_AVAILABLE_EVENT);
1996 }
1997
1998 fn connect(&mut self) {
1999 debug_assert!(matches!(self.state, FuseConnectionState::Waiting));
2000 self.state = FuseConnectionState::Connected;
2001 }
2002
2003 fn disconnect(&mut self) {
2006 if matches!(self.state, FuseConnectionState::Disconnected) {
2007 return;
2008 }
2009 self.state = FuseConnectionState::Disconnected;
2010 self.message_queue.clear();
2011 self.operations.clear();
2012 self.waiters.notify_all();
2013 }
2014
2015 fn queue_operation(
2019 &mut self,
2020 current_task: &CurrentTask,
2021 nodeid: u64,
2022 operation: FuseOperation,
2023 waiter: Option<&Waiter>,
2024 ) -> Result<u64, Errno> {
2025 debug_assert!(waiter.is_some() == operation.has_response(), "{operation:?}");
2026 if !self.is_connected() {
2027 return error!(ECONNABORTED);
2028 }
2029 self.last_unique_id += 1;
2030 let message = FuseKernelMessage::new(self.last_unique_id, current_task, nodeid, operation)?;
2031 if let Some(waiter) = waiter {
2032 self.waiters.wait_async_value(waiter, self.last_unique_id);
2033 }
2034 if message.operation.has_response() {
2035 self.operations.insert(self.last_unique_id, message.operation.as_running().into());
2036 }
2037 self.message_queue.push_back(message);
2038 self.waiters.notify_fd_events(FdEvents::POLLIN);
2039 Ok(self.last_unique_id)
2040 }
2041
2042 fn interrupt(
2049 &mut self,
2050 current_task: &CurrentTask,
2051 nodeid: u64,
2052 unique_id: u64,
2053 ) -> Result<(), Errno> {
2054 debug_assert!(self.operations.contains_key(&unique_id));
2055
2056 let mut in_queue = false;
2057 self.message_queue.retain(|m| {
2058 if m.header.unique == unique_id {
2059 self.operations.remove(&unique_id);
2060 in_queue = true;
2061 false
2062 } else {
2063 true
2064 }
2065 });
2066 if in_queue {
2067 return error!(EINTR);
2069 }
2070 self.queue_operation(current_task, nodeid, FuseOperation::Interrupt { unique_id }, None)
2071 .map(|_| ())
2072 }
2073
2074 fn get_response(&mut self, unique_id: u64) -> Option<Result<FuseResponse, Errno>> {
2077 match self.operations.entry(unique_id) {
2078 Entry::Vacant(_) => Some(error!(EINVAL)),
2079 Entry::Occupied(mut entry) => {
2080 let result = entry.get_mut().response.take();
2081 if result.is_some() {
2082 entry.remove();
2083 }
2084 result
2085 }
2086 }
2087 }
2088
2089 fn query_events(&self) -> FdEvents {
2090 let mut events = FdEvents::POLLOUT;
2091 if !self.is_connected() || !self.message_queue.is_empty() {
2092 events |= FdEvents::POLLIN
2093 };
2094 if !self.is_connected() {
2095 events |= FdEvents::POLLERR;
2096 }
2097 events
2098 }
2099
2100 fn read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2101 match self.state {
2102 FuseConnectionState::Waiting => return error!(EPERM),
2103 FuseConnectionState::Disconnected => return error!(ENODEV),
2104 _ => {}
2105 }
2106 if let Some(message) = self.message_queue.pop_front() {
2107 message.serialize(data)
2108 } else {
2109 error!(EAGAIN)
2110 }
2111 }
2112
2113 fn write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
2114 match self.state {
2115 FuseConnectionState::Waiting => return error!(EPERM),
2116 FuseConnectionState::Disconnected => return error!(ENODEV),
2117 _ => {}
2118 }
2119 let header: uapi::fuse_out_header = data.read_to_object()?;
2120 let payload_size = (header.len as usize)
2121 .checked_sub(std::mem::size_of::<uapi::fuse_out_header>())
2122 .ok_or_else(|| errno!(EINVAL))?;
2123 if payload_size > data.available() {
2124 return error!(EINVAL);
2125 }
2126 if header.unique == 0 {
2127 track_stub!(TODO("https://fxbug.dev/322873416"), "Fuse notification from userspace");
2128 return error!(ENOTSUP);
2129 }
2130 self.waiters.notify_value(header.unique);
2131 let mut running_operation = match self.operations.entry(header.unique) {
2132 Entry::Occupied(e) => e,
2133 Entry::Vacant(_) => return error!(EINVAL),
2134 };
2135 let operation = &running_operation.get().operation;
2136 let is_async = operation.is_async();
2137 if header.error < 0 {
2138 log_trace!("Fuse: {operation:?} -> {header:?}");
2139 let code = i16::try_from(-header.error).unwrap_or_else(|_| EINVAL.error_code() as i16);
2140 let errno = errno_from_code!(code);
2141 let response = operation.handle_error(&mut self.operations_state, errno);
2142 if is_async {
2143 running_operation.remove();
2144 } else {
2145 running_operation.get_mut().response = Some(response);
2146 }
2147 } else {
2148 let buffer = data.read_to_vec_limited(payload_size)?;
2149 if buffer.len() != payload_size {
2150 return error!(EINVAL);
2151 }
2152 let response = operation.parse_response(buffer)?;
2153 log_trace!("Fuse: {operation:?} -> {response:?}");
2154 if is_async {
2155 let operation = running_operation.remove();
2156 self.handle_async(operation, response)?;
2157 } else {
2158 running_operation.get_mut().response = Some(Ok(response));
2159 }
2160 }
2161 Ok(data.bytes_read())
2162 }
2163
2164 fn handle_async(
2165 &mut self,
2166 operation: RunningOperation,
2167 response: FuseResponse,
2168 ) -> Result<(), Errno> {
2169 match (operation.operation, response) {
2170 (RunningOperationKind::Init { fs }, FuseResponse::Init(init_out)) => {
2171 let configuration = FuseConfiguration::try_from(init_out)?;
2172 if configuration.flags.contains(FuseInitFlags::POSIX_ACL) {
2173 if let Some(fs) = fs.upgrade() {
2177 FuseFs::from_fs(&fs)
2178 .default_permissions
2179 .store(true, DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
2180 } else {
2181 log_warn!("failed to upgrade FuseFs when handling FUSE_INIT response");
2182 return error!(ENOTCONN);
2183 }
2184 }
2185 self.set_configuration(configuration);
2186 Ok(())
2187 }
2188 operation => {
2189 panic!("Incompatible operation={operation:?}");
2191 }
2192 }
2193 }
2194
2195 fn clear_released_passthrough_fds(&mut self) {
2196 self.registered_passthrough.retain(|_, s| s.strong_count() > 0);
2197 }
2198}
2199
2200#[derive(Debug)]
2203struct RunningOperation {
2204 operation: RunningOperationKind,
2205 response: Option<Result<FuseResponse, Errno>>,
2206}
2207
2208impl From<RunningOperationKind> for RunningOperation {
2209 fn from(operation: RunningOperationKind) -> Self {
2210 Self { operation, response: None }
2211 }
2212}
2213
2214#[derive(Debug)]
2215struct FuseKernelMessage {
2216 header: uapi::fuse_in_header,
2217 operation: FuseOperation,
2218}
2219
2220impl FuseKernelMessage {
2221 fn new(
2222 unique: u64,
2223 current_task: &CurrentTask,
2224 nodeid: u64,
2225 operation: FuseOperation,
2226 ) -> Result<Self, Errno> {
2227 let current_creds = current_task.current_creds();
2228 Ok(Self {
2229 header: uapi::fuse_in_header {
2230 len: u32::try_from(std::mem::size_of::<uapi::fuse_in_header>() + operation.len())
2231 .map_err(|_| errno!(EINVAL))?,
2232 opcode: operation.opcode(),
2233 unique,
2234 nodeid,
2235 uid: current_creds.uid,
2236 gid: current_creds.gid,
2237 pid: current_task.get_tid() as u32,
2238 __bindgen_anon_1: Default::default(),
2239 },
2240 operation,
2241 })
2242 }
2243
2244 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2245 let size = data.write(self.header.as_bytes())?;
2246 Ok(size + self.operation.serialize(data)?)
2247 }
2248}
2249
2250bitflags::bitflags! {
2251 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2252 pub struct FuseInitFlags : u64 {
2253 const BIG_WRITES = uapi::FUSE_BIG_WRITES as u64;
2254 const DONT_MASK = uapi::FUSE_DONT_MASK as u64;
2255 const SPLICE_WRITE = uapi::FUSE_SPLICE_WRITE as u64;
2256 const SPLICE_MOVE = uapi::FUSE_SPLICE_MOVE as u64;
2257 const SPLICE_READ = uapi::FUSE_SPLICE_READ as u64;
2258 const DO_READDIRPLUS = uapi::FUSE_DO_READDIRPLUS as u64;
2259 const READDIRPLUS_AUTO = uapi::FUSE_READDIRPLUS_AUTO as u64;
2260 const SETXATTR_EXT = uapi::FUSE_SETXATTR_EXT as u64;
2261 const POSIX_ACL = uapi::FUSE_POSIX_ACL as u64;
2262 const PASSTHROUGH = uapi::FUSE_PASSTHROUGH as u64;
2263 const INIT_EXT = uapi::FUSE_INIT_EXT as u64;
2264 }
2265}
2266
2267impl TryFrom<uapi::fuse_init_out> for FuseInitFlags {
2268 type Error = Errno;
2269 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
2270 let flags = (init_out.flags as u64) | ((init_out.flags2 as u64) << 32);
2271 let unknown_flags = flags & !Self::all().bits();
2272 if unknown_flags != 0 {
2273 track_stub!(
2274 TODO("https://fxbug.dev/322875725"),
2275 "FUSE unknown init flags",
2276 unknown_flags
2277 );
2278 log_warn!("FUSE daemon requested unknown flags in init: {unknown_flags}");
2279 }
2280 Ok(Self::from_bits_truncate(flags))
2281 }
2282}
2283
2284impl FuseInitFlags {
2285 fn get_u32_components(&self) -> (u32, u32) {
2287 let flags = (self.bits() & (u32::max_value() as u64)) as u32;
2288 let flags2 = (self.bits() >> 32) as u32;
2289 (flags, flags2)
2290 }
2291}
2292
2293#[derive(Clone, Debug)]
2294enum RunningOperationKind {
2295 Access,
2296 Create,
2297 Flush,
2298 Forget,
2299 GetAttr,
2300 Init {
2301 fs: Weak<FileSystem>,
2306 },
2307 Interrupt,
2308 GetXAttr {
2309 size: u32,
2310 },
2311 ListXAttr {
2312 size: u32,
2313 },
2314 Lookup,
2315 Mkdir,
2316 Mknod,
2317 Link,
2318 Open {
2319 dir: bool,
2320 },
2321 Poll,
2322 Read,
2323 Readdir {
2324 use_readdirplus: bool,
2325 },
2326 Readlink,
2327 Release {
2328 dir: bool,
2329 },
2330 RemoveXAttr,
2331 Rename,
2332 Rmdir,
2333 Seek,
2334 SetAttr,
2335 SetXAttr,
2336 Statfs,
2337 Symlink,
2338 Unlink,
2339 Write,
2340}
2341
2342impl RunningOperationKind {
2343 fn is_async(&self) -> bool {
2344 matches!(self, Self::Init { .. })
2345 }
2346
2347 fn opcode(&self) -> u32 {
2348 match self {
2349 Self::Access => uapi::fuse_opcode_FUSE_ACCESS,
2350 Self::Create => uapi::fuse_opcode_FUSE_CREATE,
2351 Self::Flush => uapi::fuse_opcode_FUSE_FLUSH,
2352 Self::Forget => uapi::fuse_opcode_FUSE_FORGET,
2353 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2354 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2355 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2356 Self::Interrupt => uapi::fuse_opcode_FUSE_INTERRUPT,
2357 Self::ListXAttr { .. } => uapi::fuse_opcode_FUSE_LISTXATTR,
2358 Self::Lookup => uapi::fuse_opcode_FUSE_LOOKUP,
2359 Self::Mkdir => uapi::fuse_opcode_FUSE_MKDIR,
2360 Self::Mknod => uapi::fuse_opcode_FUSE_MKNOD,
2361 Self::Link => uapi::fuse_opcode_FUSE_LINK,
2362 Self::Open { dir } => {
2363 if *dir {
2364 uapi::fuse_opcode_FUSE_OPENDIR
2365 } else {
2366 uapi::fuse_opcode_FUSE_OPEN
2367 }
2368 }
2369 Self::Poll => uapi::fuse_opcode_FUSE_POLL,
2370 Self::Read => uapi::fuse_opcode_FUSE_READ,
2371 Self::Readdir { use_readdirplus } => {
2372 if *use_readdirplus {
2373 uapi::fuse_opcode_FUSE_READDIRPLUS
2374 } else {
2375 uapi::fuse_opcode_FUSE_READDIR
2376 }
2377 }
2378 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2379 Self::Release { dir } => {
2380 if *dir {
2381 uapi::fuse_opcode_FUSE_RELEASEDIR
2382 } else {
2383 uapi::fuse_opcode_FUSE_RELEASE
2384 }
2385 }
2386 Self::RemoveXAttr => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2387 Self::Rename => uapi::fuse_opcode_FUSE_RENAME2,
2388 Self::Rmdir => uapi::fuse_opcode_FUSE_RMDIR,
2389 Self::Seek => uapi::fuse_opcode_FUSE_LSEEK,
2390 Self::SetAttr => uapi::fuse_opcode_FUSE_SETATTR,
2391 Self::SetXAttr => uapi::fuse_opcode_FUSE_SETXATTR,
2392 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2393 Self::Symlink => uapi::fuse_opcode_FUSE_SYMLINK,
2394 Self::Unlink => uapi::fuse_opcode_FUSE_UNLINK,
2395 Self::Write => uapi::fuse_opcode_FUSE_WRITE,
2396 }
2397 }
2398
2399 fn to_response<T: FromBytes + IntoBytes + Immutable>(buffer: &[u8]) -> T {
2400 let mut result = T::new_zeroed();
2401 let length_to_copy = std::cmp::min(buffer.len(), std::mem::size_of::<T>());
2402 result.as_mut_bytes()[..length_to_copy].copy_from_slice(&buffer[..length_to_copy]);
2403 result
2404 }
2405
2406 fn parse_response(&self, buffer: Vec<u8>) -> Result<FuseResponse, Errno> {
2407 match self {
2408 Self::Access => Ok(FuseResponse::Access(Ok(()))),
2409 Self::Create { .. } => {
2410 Ok(FuseResponse::Create(Self::to_response::<CreateResponse>(&buffer)))
2411 }
2412 Self::GetAttr | Self::SetAttr => {
2413 Ok(FuseResponse::Attr(Self::to_response::<uapi::fuse_attr_out>(&buffer)))
2414 }
2415 Self::GetXAttr { size } | Self::ListXAttr { size } => {
2416 if *size == 0 {
2417 if buffer.len() < std::mem::size_of::<uapi::fuse_getxattr_out>() {
2418 return error!(EINVAL);
2419 }
2420 let getxattr_out = Self::to_response::<uapi::fuse_getxattr_out>(&buffer);
2421 Ok(FuseResponse::GetXAttr(ValueOrSize::Size(getxattr_out.size as usize)))
2422 } else {
2423 Ok(FuseResponse::GetXAttr(FsString::new(buffer).into()))
2424 }
2425 }
2426 Self::Init { .. } => {
2427 Ok(FuseResponse::Init(Self::to_response::<uapi::fuse_init_out>(&buffer)))
2428 }
2429 Self::Lookup | Self::Mkdir | Self::Mknod | Self::Link | Self::Symlink => {
2430 Ok(FuseResponse::Entry(Self::to_response::<FuseEntryOutExtended>(&buffer)))
2431 }
2432 Self::Open { .. } => {
2433 Ok(FuseResponse::Open(Self::to_response::<uapi::fuse_open_out>(&buffer)))
2434 }
2435 Self::Poll => Ok(FuseResponse::Poll(Self::to_response::<uapi::fuse_poll_out>(&buffer))),
2436 Self::Read | Self::Readlink => Ok(FuseResponse::Read(buffer)),
2437 Self::Readdir { use_readdirplus, .. } => {
2438 let mut result = vec![];
2439 let mut slice = &buffer[..];
2440 while !slice.is_empty() {
2441 let entry = if *use_readdirplus {
2443 if slice.len() < std::mem::size_of::<uapi::fuse_entry_out>() {
2444 return error!(EINVAL);
2445 }
2446 let entry = Self::to_response::<uapi::fuse_entry_out>(slice);
2447 slice = &slice[std::mem::size_of::<uapi::fuse_entry_out>()..];
2448 Some(entry)
2449 } else {
2450 None
2451 };
2452 if slice.len() < std::mem::size_of::<uapi::fuse_dirent>() {
2454 return error!(EINVAL);
2455 }
2456 let dirent = Self::to_response::<uapi::fuse_dirent>(slice);
2457 slice = &slice[std::mem::size_of::<uapi::fuse_dirent>()..];
2459 let namelen = dirent.namelen as usize;
2460 if slice.len() < namelen {
2461 return error!(EINVAL);
2462 }
2463 let name = FsString::from(&slice[..namelen]);
2464 result.push((dirent, name, entry));
2465 let skipped = round_up_to_increment(namelen, 8)?;
2466 if slice.len() < skipped {
2467 return error!(EINVAL);
2468 }
2469 slice = &slice[skipped..];
2470 }
2471 Ok(FuseResponse::Readdir(result))
2472 }
2473 Self::Flush
2474 | Self::Release { .. }
2475 | Self::RemoveXAttr
2476 | Self::Rename
2477 | Self::Rmdir
2478 | Self::SetXAttr
2479 | Self::Unlink => Ok(FuseResponse::None),
2480 Self::Statfs => {
2481 Ok(FuseResponse::Statfs(Self::to_response::<uapi::fuse_statfs_out>(&buffer)))
2482 }
2483 Self::Seek => {
2484 Ok(FuseResponse::Seek(Self::to_response::<uapi::fuse_lseek_out>(&buffer)))
2485 }
2486 Self::Write => {
2487 Ok(FuseResponse::Write(Self::to_response::<uapi::fuse_write_out>(&buffer)))
2488 }
2489 Self::Interrupt | Self::Forget => {
2490 panic!("Response for operation without one");
2491 }
2492 }
2493 }
2494
2495 fn handle_error(
2500 &self,
2501 state: &mut OperationsState,
2502 errno: Errno,
2503 ) -> Result<FuseResponse, Errno> {
2504 match self {
2505 Self::Access if errno == ENOSYS => {
2506 const UNIMPLEMENTED_ACCESS_RESPONSE: Result<FuseResponse, Errno> =
2511 Ok(FuseResponse::Access(Ok(())));
2512 state.insert(self.opcode(), UNIMPLEMENTED_ACCESS_RESPONSE);
2513 UNIMPLEMENTED_ACCESS_RESPONSE
2514 }
2515 Self::Flush if errno == ENOSYS => {
2516 state.insert(self.opcode(), Ok(FuseResponse::None));
2517 Ok(FuseResponse::None)
2518 }
2519 Self::Seek if errno == ENOSYS => {
2520 state.insert(self.opcode(), Err(errno.clone()));
2521 Err(errno)
2522 }
2523 Self::Poll if errno == ENOSYS => {
2524 let response = FuseResponse::Poll(uapi::fuse_poll_out {
2525 revents: (FdEvents::POLLIN | FdEvents::POLLOUT).bits(),
2526 padding: 0,
2527 });
2528 state.insert(self.opcode(), Ok(response.clone()));
2529 Ok(response)
2530 }
2531 _ => Err(errno),
2532 }
2533 }
2534}
2535
2536#[derive(Debug)]
2537enum FuseOperation {
2538 Access {
2539 mask: u32,
2540 },
2541 Create(uapi::fuse_create_in, FsString),
2542 Flush(uapi::fuse_open_out),
2543 Forget(uapi::fuse_forget_in),
2544 GetAttr,
2545 Init {
2546 fs: Weak<FileSystem>,
2551 },
2552 Interrupt {
2553 unique_id: u64,
2555 },
2556 GetXAttr {
2557 getxattr_in: uapi::fuse_getxattr_in,
2558 name: FsString,
2560 },
2561 ListXAttr(uapi::fuse_getxattr_in),
2562 Lookup {
2563 name: FsString,
2565 },
2566 Mkdir {
2567 mkdir_in: uapi::fuse_mkdir_in,
2568 name: FsString,
2570 },
2571 Mknod {
2572 mknod_in: uapi::fuse_mknod_in,
2573 name: FsString,
2575 },
2576 Link {
2577 link_in: uapi::fuse_link_in,
2578 name: FsString,
2580 },
2581 Open {
2582 flags: OpenFlags,
2583 mode: FileMode,
2584 },
2585 Poll(uapi::fuse_poll_in),
2586 Read(uapi::fuse_read_in),
2587 Readdir {
2588 read_in: uapi::fuse_read_in,
2589 use_readdirplus: bool,
2591 },
2592 Readlink,
2593 Release(uapi::fuse_open_out),
2594 ReleaseDir(uapi::fuse_open_out),
2595 RemoveXAttr {
2596 name: FsString,
2598 },
2599 Rename {
2600 old_name: FsString,
2601 new_dir: u64,
2602 new_name: FsString,
2603 },
2604 Rmdir {
2605 name: FsString,
2606 },
2607 Seek(uapi::fuse_lseek_in),
2608 SetAttr(uapi::fuse_setattr_in),
2609 SetXAttr {
2610 setxattr_in: uapi::fuse_setxattr_in,
2611 is_ext: bool,
2614 name: FsString,
2616 value: FsString,
2618 },
2619 Statfs,
2620 Symlink {
2621 target: FsString,
2623 name: FsString,
2625 },
2626 Unlink {
2627 name: FsString,
2629 },
2630 Write {
2631 write_in: uapi::fuse_write_in,
2632 content: Vec<u8>,
2634 },
2635}
2636
2637#[derive(Clone, Debug)]
2638enum FuseResponse {
2639 Access(Result<(), Errno>),
2640 Attr(uapi::fuse_attr_out),
2641 Create(CreateResponse),
2642 Entry(FuseEntryOutExtended),
2643 GetXAttr(ValueOrSize<FsString>),
2644 Init(uapi::fuse_init_out),
2645 Open(uapi::fuse_open_out),
2646 Poll(uapi::fuse_poll_out),
2647 Read(
2648 Vec<u8>,
2650 ),
2651 Seek(uapi::fuse_lseek_out),
2652 Readdir(Vec<(uapi::fuse_dirent, FsString, Option<uapi::fuse_entry_out>)>),
2653 Statfs(uapi::fuse_statfs_out),
2654 Write(uapi::fuse_write_out),
2655 None,
2656}
2657
2658impl FuseResponse {
2659 fn entry(&self) -> Option<&uapi::fuse_entry_out> {
2660 if let Self::Entry(entry) = self { Some(&entry.arg) } else { None }
2661 }
2662}
2663
2664#[repr(C)]
2665#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2666struct CreateResponse {
2667 entry: uapi::fuse_entry_out,
2668 open: uapi::fuse_open_out,
2669}
2670
2671static_assertions::const_assert_eq!(
2672 std::mem::offset_of!(CreateResponse, open),
2673 std::mem::size_of::<uapi::fuse_entry_out>()
2674);
2675
2676#[repr(C)]
2677#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2678struct FuseEntryOutExtended {
2679 arg: uapi::fuse_entry_out,
2680 bpf_arg: uapi::fuse_entry_bpf_out,
2681}
2682
2683static_assertions::const_assert_eq!(
2684 std::mem::offset_of!(FuseEntryOutExtended, bpf_arg),
2685 std::mem::size_of::<uapi::fuse_entry_out>()
2686);
2687
2688impl FuseOperation {
2689 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2690 match self {
2691 Self::Access { mask } => {
2692 let message = uapi::fuse_access_in { mask: *mask, padding: 0 };
2693 data.write_all(message.as_bytes())
2694 }
2695 Self::Create(create_in, name) => {
2696 Ok(data.write_all(create_in.as_bytes())? + Self::write_null_terminated(data, name)?)
2697 }
2698 Self::Flush(open_in) => {
2699 let message =
2700 uapi::fuse_flush_in { fh: open_in.fh, unused: 0, padding: 0, lock_owner: 0 };
2701 data.write_all(message.as_bytes())
2702 }
2703 Self::Forget(forget_in) => data.write_all(forget_in.as_bytes()),
2704 Self::GetAttr | Self::Readlink | Self::Statfs => Ok(0),
2705 Self::GetXAttr { getxattr_in, name } => {
2706 let mut len = data.write_all(getxattr_in.as_bytes())?;
2707 len += Self::write_null_terminated(data, name)?;
2708 Ok(len)
2709 }
2710 Self::Init { .. } => {
2711 let (flags, flags2) = FuseInitFlags::all().get_u32_components();
2712 let message = uapi::fuse_init_in {
2713 major: uapi::FUSE_KERNEL_VERSION,
2714 minor: uapi::FUSE_KERNEL_MINOR_VERSION,
2715 flags,
2716 flags2,
2717 ..Default::default()
2718 };
2719 data.write_all(message.as_bytes())
2720 }
2721 Self::Interrupt { unique_id } => {
2722 let message = uapi::fuse_interrupt_in { unique: *unique_id };
2723 data.write_all(message.as_bytes())
2724 }
2725 Self::ListXAttr(getxattr_in) => data.write_all(getxattr_in.as_bytes()),
2726 Self::Lookup { name } => Self::write_null_terminated(data, name),
2727 Self::Open { flags, .. } => {
2728 let message = uapi::fuse_open_in { flags: flags.bits(), open_flags: 0 };
2729 data.write_all(message.as_bytes())
2730 }
2731 Self::Poll(poll_in) => data.write_all(poll_in.as_bytes()),
2732 Self::Mkdir { mkdir_in, name } => {
2733 let mut len = data.write_all(mkdir_in.as_bytes())?;
2734 len += Self::write_null_terminated(data, name)?;
2735 Ok(len)
2736 }
2737 Self::Mknod { mknod_in, name } => {
2738 let mut len = data.write_all(mknod_in.as_bytes())?;
2739 len += Self::write_null_terminated(data, name)?;
2740 Ok(len)
2741 }
2742 Self::Link { link_in, name } => {
2743 let mut len = data.write_all(link_in.as_bytes())?;
2744 len += Self::write_null_terminated(data, name)?;
2745 Ok(len)
2746 }
2747 Self::Read(read_in) | Self::Readdir { read_in, .. } => {
2748 data.write_all(read_in.as_bytes())
2749 }
2750 Self::Release(open_out) | Self::ReleaseDir(open_out) => {
2751 let message = uapi::fuse_release_in {
2752 fh: open_out.fh,
2753 flags: 0,
2754 release_flags: 0,
2755 lock_owner: 0,
2756 };
2757 data.write_all(message.as_bytes())
2758 }
2759 Self::RemoveXAttr { name } => Self::write_null_terminated(data, name),
2760 Self::Rename { old_name, new_dir, new_name } => {
2761 Ok(data.write_all(
2762 uapi::fuse_rename2_in { newdir: *new_dir, flags: 0, padding: 0 }.as_bytes(),
2763 )? + Self::write_null_terminated(data, old_name)?
2764 + Self::write_null_terminated(data, new_name)?)
2765 }
2766 Self::Seek(seek_in) => data.write_all(seek_in.as_bytes()),
2767 Self::SetAttr(setattr_in) => data.write_all(setattr_in.as_bytes()),
2768 Self::SetXAttr { setxattr_in, is_ext, name, value } => {
2769 let header =
2770 if *is_ext { setxattr_in.as_bytes() } else { &setxattr_in.as_bytes()[..8] };
2771 let mut len = data.write_all(header)?;
2772 len += Self::write_null_terminated(data, name)?;
2773 len += data.write_all(value.as_bytes())?;
2774 Ok(len)
2775 }
2776 Self::Symlink { target, name } => {
2777 let mut len = Self::write_null_terminated(data, name)?;
2778 len += Self::write_null_terminated(data, target)?;
2779 Ok(len)
2780 }
2781 Self::Rmdir { name } | Self::Unlink { name } => Self::write_null_terminated(data, name),
2782 &Self::Write { mut write_in, ref content } => {
2783 let mut write_in_size = write_in.size as usize;
2784 assert!(write_in_size == content.len());
2785 if write_in_size + write_in.as_bytes().len() > data.available() {
2786 write_in_size = data.available() - write_in.as_bytes().len();
2787 write_in.size = write_in_size as u32;
2788 }
2789 let mut len = data.write_all(write_in.as_bytes())?;
2790 len += data.write_all(&content[..write_in_size])?;
2791 Ok(len)
2792 }
2793 }
2794 }
2795
2796 fn write_null_terminated(
2797 data: &mut dyn OutputBuffer,
2798 content: &Vec<u8>,
2799 ) -> Result<usize, Errno> {
2800 let mut len = data.write_all(content.as_bytes())?;
2801 len += data.write_all(&[0])?;
2802 Ok(len)
2803 }
2804
2805 fn opcode(&self) -> u32 {
2806 match self {
2807 Self::Access { .. } => uapi::fuse_opcode_FUSE_ACCESS,
2808 Self::Create { .. } => uapi::fuse_opcode_FUSE_CREATE,
2809 Self::Flush(_) => uapi::fuse_opcode_FUSE_FLUSH,
2810 Self::Forget(_) => uapi::fuse_opcode_FUSE_FORGET,
2811 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2812 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2813 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2814 Self::Interrupt { .. } => uapi::fuse_opcode_FUSE_INTERRUPT,
2815 Self::ListXAttr(_) => uapi::fuse_opcode_FUSE_LISTXATTR,
2816 Self::Lookup { .. } => uapi::fuse_opcode_FUSE_LOOKUP,
2817 Self::Mkdir { .. } => uapi::fuse_opcode_FUSE_MKDIR,
2818 Self::Mknod { .. } => uapi::fuse_opcode_FUSE_MKNOD,
2819 Self::Link { .. } => uapi::fuse_opcode_FUSE_LINK,
2820 Self::Open { flags, mode } => {
2821 if mode.is_dir() || flags.contains(OpenFlags::DIRECTORY) {
2822 uapi::fuse_opcode_FUSE_OPENDIR
2823 } else {
2824 uapi::fuse_opcode_FUSE_OPEN
2825 }
2826 }
2827 Self::Poll(_) => uapi::fuse_opcode_FUSE_POLL,
2828 Self::Read(_) => uapi::fuse_opcode_FUSE_READ,
2829 Self::Readdir { use_readdirplus, .. } => {
2830 if *use_readdirplus {
2831 uapi::fuse_opcode_FUSE_READDIRPLUS
2832 } else {
2833 uapi::fuse_opcode_FUSE_READDIR
2834 }
2835 }
2836 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2837 Self::Release(_) => uapi::fuse_opcode_FUSE_RELEASE,
2838 Self::ReleaseDir(_) => uapi::fuse_opcode_FUSE_RELEASEDIR,
2839 Self::RemoveXAttr { .. } => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2840 Self::Rename { .. } => uapi::fuse_opcode_FUSE_RENAME2,
2841 Self::Rmdir { .. } => uapi::fuse_opcode_FUSE_RMDIR,
2842 Self::Seek(_) => uapi::fuse_opcode_FUSE_LSEEK,
2843 Self::SetAttr(_) => uapi::fuse_opcode_FUSE_SETATTR,
2844 Self::SetXAttr { .. } => uapi::fuse_opcode_FUSE_SETXATTR,
2845 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2846 Self::Symlink { .. } => uapi::fuse_opcode_FUSE_SYMLINK,
2847 Self::Unlink { .. } => uapi::fuse_opcode_FUSE_UNLINK,
2848 Self::Write { .. } => uapi::fuse_opcode_FUSE_WRITE,
2849 }
2850 }
2851
2852 fn as_running(&self) -> RunningOperationKind {
2853 match self {
2854 Self::Access { .. } => RunningOperationKind::Access,
2855 Self::Create { .. } => RunningOperationKind::Create,
2856 Self::Flush(_) => RunningOperationKind::Flush,
2857 Self::Forget(_) => RunningOperationKind::Forget,
2858 Self::GetAttr => RunningOperationKind::GetAttr,
2859 Self::GetXAttr { getxattr_in, .. } => {
2860 RunningOperationKind::GetXAttr { size: getxattr_in.size }
2861 }
2862 Self::Init { fs } => RunningOperationKind::Init { fs: fs.clone() },
2863 Self::Interrupt { .. } => RunningOperationKind::Interrupt,
2864 Self::ListXAttr(getxattr_in) => {
2865 RunningOperationKind::ListXAttr { size: getxattr_in.size }
2866 }
2867 Self::Lookup { .. } => RunningOperationKind::Lookup,
2868 Self::Mkdir { .. } => RunningOperationKind::Mkdir,
2869 Self::Mknod { .. } => RunningOperationKind::Mknod,
2870 Self::Link { .. } => RunningOperationKind::Link,
2871 Self::Open { flags, mode } => RunningOperationKind::Open {
2872 dir: mode.is_dir() || flags.contains(OpenFlags::DIRECTORY),
2873 },
2874 Self::Poll(_) => RunningOperationKind::Poll,
2875 Self::Read(_) => RunningOperationKind::Read,
2876 Self::Readdir { use_readdirplus, .. } => {
2877 RunningOperationKind::Readdir { use_readdirplus: *use_readdirplus }
2878 }
2879 Self::Readlink => RunningOperationKind::Readlink,
2880 Self::Release(_) => RunningOperationKind::Release { dir: false },
2881 Self::ReleaseDir(_) => RunningOperationKind::Release { dir: true },
2882 Self::RemoveXAttr { .. } => RunningOperationKind::RemoveXAttr,
2883 Self::Rename { .. } => RunningOperationKind::Rename,
2884 Self::Rmdir { .. } => RunningOperationKind::Rmdir,
2885 Self::Seek(_) => RunningOperationKind::Seek,
2886 Self::SetAttr(_) => RunningOperationKind::SetAttr,
2887 Self::SetXAttr { .. } => RunningOperationKind::SetXAttr,
2888 Self::Statfs => RunningOperationKind::Statfs,
2889 Self::Symlink { .. } => RunningOperationKind::Symlink,
2890 Self::Unlink { .. } => RunningOperationKind::Unlink,
2891 Self::Write { .. } => RunningOperationKind::Write,
2892 }
2893 }
2894
2895 fn len(&self) -> usize {
2896 #[derive(Debug, Default)]
2897 struct CountingOutputBuffer {
2898 written: usize,
2899 }
2900
2901 impl Buffer for CountingOutputBuffer {
2902 fn segments_count(&self) -> Result<usize, Errno> {
2903 panic!("Should not be called");
2904 }
2905
2906 fn peek_each_segment(
2907 &mut self,
2908 _callback: &mut PeekBufferSegmentsCallback<'_>,
2909 ) -> Result<(), Errno> {
2910 panic!("Should not be called");
2911 }
2912 }
2913
2914 impl OutputBuffer for CountingOutputBuffer {
2915 fn available(&self) -> usize {
2916 usize::MAX
2917 }
2918
2919 fn bytes_written(&self) -> usize {
2920 self.written
2921 }
2922
2923 fn zero(&mut self) -> Result<usize, Errno> {
2924 panic!("Should not be called");
2925 }
2926
2927 fn write_each(
2928 &mut self,
2929 _callback: &mut OutputBufferCallback<'_>,
2930 ) -> Result<usize, Errno> {
2931 panic!("Should not be called.");
2932 }
2933
2934 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
2935 self.written += buffer.len();
2936 Ok(buffer.len())
2937 }
2938
2939 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
2940 panic!("Should not be called.");
2941 }
2942 }
2943
2944 let mut counting_output_buffer = CountingOutputBuffer::default();
2945 self.serialize(&mut counting_output_buffer).expect("Serialization should not fail");
2946 counting_output_buffer.written
2947 }
2948
2949 fn has_response(&self) -> bool {
2950 !matches!(self, Self::Interrupt { .. } | Self::Forget(_))
2951 }
2952
2953 fn is_async(&self) -> bool {
2954 matches!(self, Self::Init { .. })
2955 }
2956}