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 AppendLockGuard, CacheMode, CheckAccessReason, DirEntry, DirEntryOps, DirectoryEntryType,
23 DirentSink, FallocMode, FdNumber, FileObject, FileObjectState, FileOps, FileSystem,
24 FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo,
25 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::AtomicU64Counter;
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_type::DeviceType;
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: DeviceType,
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: AtomicU64Counter,
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 = DeviceType::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(entry.attr.ino, fuse_node, &node.fs(), info))
748 },
749 )?;
750 if !DirEntry::is_reserved_name(name) {
752 let fuse_node = FuseNode::from_node(&node);
753 fuse_node.state.lock().nlookup += 1;
754 }
755 Ok(node)
756 }
757}
758
759struct FuseFileObject {
760 connection: Arc<FuseConnection>,
761 passthrough_file: WeakFileHandle,
763 open_out: uapi::fuse_open_out,
765}
766
767impl FuseFileObject {
768 fn get_fuse_node(file: &FileObject) -> &FuseNode {
770 FuseNode::from_node(file.node())
771 }
772}
773
774impl FileOps for FuseFileObject {
775 fn close(
776 self: Box<Self>,
777 locked: &mut Locked<FileOpsCore>,
778 file: &FileObjectState,
779 current_task: &CurrentTask,
780 ) {
781 let node = FuseNode::from_node(file.node());
782 let is_dir = file.node().is_dir();
783 {
784 let mut connection = self.connection.lock();
785 if let Err(e) = connection.execute_operation(
786 locked,
787 current_task,
788 node,
789 if is_dir {
790 FuseOperation::ReleaseDir(self.open_out)
791 } else {
792 FuseOperation::Release(self.open_out)
793 },
794 ) {
795 if e.code != ENOSYS {
796 log_error!("Error when releasing fh: {e:?}");
797 }
798 }
799 connection.clear_released_passthrough_fds();
800 }
801 }
802
803 fn flush(
804 &self,
805 locked: &mut Locked<FileOpsCore>,
806 file: &FileObject,
807 current_task: &CurrentTask,
808 ) {
809 let node = Self::get_fuse_node(file);
810 if let Err(e) = self.connection.lock().execute_operation(
811 locked,
812 current_task,
813 node,
814 FuseOperation::Flush(self.open_out),
815 ) {
816 log_error!("Error when flushing fh: {e:?}");
817 }
818 }
819
820 fn is_seekable(&self) -> bool {
821 true
822 }
823
824 fn read(
825 &self,
826 locked: &mut Locked<FileOpsCore>,
827 file: &FileObject,
828 current_task: &CurrentTask,
829 offset: usize,
830 data: &mut dyn OutputBuffer,
831 ) -> Result<usize, Errno> {
832 if file.node().info().mode.is_dir() {
833 return error!(EISDIR);
834 }
835 if let Some(file_object) = self.passthrough_file.upgrade() {
836 return file_object.ops().read(locked, &file_object, current_task, offset, data);
837 }
838 let node = Self::get_fuse_node(file);
839 let response = self.connection.lock().execute_operation(
840 locked,
841 current_task,
842 node,
843 FuseOperation::Read(uapi::fuse_read_in {
844 fh: self.open_out.fh,
845 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
846 size: data.available().try_into().unwrap_or(u32::MAX),
847 read_flags: 0,
848 lock_owner: 0,
849 flags: 0,
850 padding: 0,
851 }),
852 )?;
853 let FuseResponse::Read(read_out) = response else {
854 return error!(EINVAL);
855 };
856 data.write(&read_out)
857 }
858
859 fn write(
860 &self,
861 locked: &mut Locked<FileOpsCore>,
862 file: &FileObject,
863 current_task: &CurrentTask,
864 offset: usize,
865 data: &mut dyn InputBuffer,
866 ) -> Result<usize, Errno> {
867 if file.node().info().mode.is_dir() {
868 return error!(EISDIR);
869 }
870 if let Some(file_object) = self.passthrough_file.upgrade() {
871 return file_object.ops().write(locked, &file_object, current_task, offset, data);
872 }
873 let node = Self::get_fuse_node(file);
874 let content = data.peek_all()?;
875 let response = self.connection.lock().execute_operation(
876 locked,
877 current_task,
878 node,
879 FuseOperation::Write {
880 write_in: uapi::fuse_write_in {
881 fh: self.open_out.fh,
882 offset: offset.try_into().map_err(|_| errno!(EINVAL))?,
883 size: content.len().try_into().map_err(|_| errno!(EINVAL))?,
884 write_flags: 0,
885 lock_owner: 0,
886 flags: 0,
887 padding: 0,
888 },
889 content,
890 },
891 )?;
892 let FuseResponse::Write(write_out) = response else {
893 return error!(EINVAL);
894 };
895 node.invalidate_attributes();
896
897 let written = write_out.size as usize;
898
899 data.advance(written)?;
900 Ok(written)
901 }
902
903 fn seek(
904 &self,
905 locked: &mut Locked<FileOpsCore>,
906 file: &FileObject,
907 current_task: &CurrentTask,
908 current_offset: off_t,
909 target: SeekTarget,
910 ) -> Result<off_t, Errno> {
911 if matches!(target, SeekTarget::Data(_) | SeekTarget::Hole(_)) {
913 let node = Self::get_fuse_node(file);
914 let response = self.connection.lock().execute_operation(
915 locked,
916 current_task,
917 node,
918 FuseOperation::Seek(uapi::fuse_lseek_in {
919 fh: self.open_out.fh,
920 offset: target.offset().try_into().map_err(|_| errno!(EINVAL))?,
921 whence: target.whence(),
922 padding: 0,
923 }),
924 );
925 match response {
926 Ok(response) => {
927 let FuseResponse::Seek(seek_out) = response else {
928 return error!(EINVAL);
929 };
930 return seek_out.offset.try_into().map_err(|_| errno!(EINVAL));
931 }
932 Err(errno) if errno == ENOSYS => {}
935 Err(errno) => return Err(errno),
936 };
937 }
938
939 default_seek(current_offset, target, || default_eof_offset(locked, file, current_task))
940 }
941
942 fn sync(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
943 track_stub!(TODO("https://fxbug.dev/352359968"), "FUSE fsync()");
944 Ok(())
945 }
946
947 fn wait_async(
948 &self,
949 _locked: &mut Locked<FileOpsCore>,
950 _file: &FileObject,
951 _current_task: &CurrentTask,
952 _waiter: &Waiter,
953 _events: FdEvents,
954 _handler: EventHandler,
955 ) -> Option<WaitCanceler> {
956 None
957 }
958
959 fn query_events(
960 &self,
961 locked: &mut Locked<FileOpsCore>,
962 file: &FileObject,
963 current_task: &CurrentTask,
964 ) -> Result<FdEvents, Errno> {
965 let node = Self::get_fuse_node(file);
966 let response = self.connection.lock().execute_operation(
967 locked,
968 current_task,
969 node,
970 FuseOperation::Poll(uapi::fuse_poll_in {
971 fh: self.open_out.fh,
972 kh: 0,
973 flags: 0,
974 events: FdEvents::all().bits(),
975 }),
976 )?;
977 let FuseResponse::Poll(poll_out) = response else {
978 return error!(EINVAL);
979 };
980 FdEvents::from_bits(poll_out.revents).ok_or_else(|| errno!(EINVAL))
981 }
982
983 fn readdir(
984 &self,
985 locked: &mut Locked<FileOpsCore>,
986 file: &FileObject,
987 current_task: &CurrentTask,
988 sink: &mut dyn DirentSink,
989 ) -> Result<(), Errno> {
990 let mut state = self.connection.lock();
991 let configuration = state.get_configuration(locked, current_task)?;
992 let use_readdirplus = {
993 if configuration.flags.contains(FuseInitFlags::DO_READDIRPLUS) {
994 if configuration.flags.contains(FuseInitFlags::READDIRPLUS_AUTO) {
995 sink.offset() == 0
996 } else {
997 true
998 }
999 } else {
1000 false
1001 }
1002 };
1003 let user_capacity = if let Some(base_user_capacity) = sink.user_capacity() {
1006 if use_readdirplus {
1007 base_user_capacity * 3 / 2
1009 } else {
1010 base_user_capacity
1011 }
1012 } else {
1013 *PAGE_SIZE as usize
1014 };
1015 let node = Self::get_fuse_node(file);
1016 let response = state.execute_operation(
1017 locked,
1018 current_task,
1019 node,
1020 FuseOperation::Readdir {
1021 read_in: uapi::fuse_read_in {
1022 fh: self.open_out.fh,
1023 offset: sink.offset().try_into().map_err(|_| errno!(EINVAL))?,
1024 size: user_capacity.try_into().map_err(|_| errno!(EINVAL))?,
1025 read_flags: 0,
1026 lock_owner: 0,
1027 flags: 0,
1028 padding: 0,
1029 },
1030 use_readdirplus,
1031 },
1032 )?;
1033 std::mem::drop(state);
1034 let FuseResponse::Readdir(dirents) = response else {
1035 return error!(EINVAL);
1036 };
1037 let mut sink_result = Ok(());
1038 for (dirent, name, entry) in dirents {
1039 if let Some(entry) = entry {
1040 if entry.nodeid != 0 {
1042 if let Err(e) = node.fs_node_from_entry(file.node(), name.as_ref(), &entry) {
1043 log_error!("Unable to prefill entry: {e:?}");
1044 }
1045 }
1046 }
1047 if sink_result.is_ok() {
1048 sink_result = sink.add(
1049 dirent.ino,
1050 dirent.off.try_into().map_err(|_| errno!(EINVAL))?,
1051 DirectoryEntryType::from_bits(
1052 dirent.type_.try_into().map_err(|_| errno!(EINVAL))?,
1053 ),
1054 name.as_ref(),
1055 );
1056 }
1057 }
1058 sink_result
1059 }
1060
1061 fn ioctl(
1062 &self,
1063 locked: &mut Locked<Unlocked>,
1064 file: &FileObject,
1065 current_task: &CurrentTask,
1066 request: u32,
1067 arg: SyscallArg,
1068 ) -> Result<SyscallResult, Errno> {
1069 track_stub!(TODO("https://fxbug.dev/322875259"), "fuse ioctl");
1070 default_ioctl(file, locked, current_task, request, arg)
1071 }
1072
1073 fn fcntl(
1074 &self,
1075 _file: &FileObject,
1076 _current_task: &CurrentTask,
1077 cmd: u32,
1078 _arg: u64,
1079 ) -> Result<SyscallResult, Errno> {
1080 track_stub!(TODO("https://fxbug.dev/322875764"), "fuse fcntl");
1081 default_fcntl(cmd)
1082 }
1083}
1084
1085struct FuseDirEntry {
1086 valid_until: AtomicMonotonicInstant,
1087}
1088
1089impl Default for FuseDirEntry {
1090 fn default() -> Self {
1091 Self { valid_until: zx::MonotonicInstant::INFINITE_PAST.into() }
1092 }
1093}
1094
1095impl DirEntryOps for FuseDirEntry {
1096 fn revalidate(
1097 &self,
1098 locked: &mut Locked<FileOpsCore>,
1099 current_task: &CurrentTask,
1100 dir_entry: &DirEntry,
1101 ) -> Result<bool, Errno> {
1102 const VALID_UNTIL_ORDERING: Ordering = Ordering::Relaxed;
1105
1106 let now = zx::MonotonicInstant::get();
1107 if self.valid_until.load(VALID_UNTIL_ORDERING) >= now {
1108 return Ok(true);
1109 }
1110
1111 let node = FuseNode::from_node(&dir_entry.node);
1112 if node.nodeid == FUSE_ROOT_ID_U64 {
1113 return Ok(true);
1115 }
1116
1117 let (parent, name) = {
1120 let scope = RcuReadScope::new();
1121 let parent = dir_entry.parent().expect("non-root nodes always has a parent");
1122 let name = dir_entry.local_name(&scope).to_owned();
1123 (parent, name)
1124 };
1125 let parent = FuseNode::from_node(&parent.node);
1126 let FuseEntryOutExtended {
1127 arg:
1128 uapi::fuse_entry_out {
1129 nodeid,
1130 generation,
1131 entry_valid,
1132 entry_valid_nsec,
1133 attr,
1134 attr_valid,
1135 attr_valid_nsec,
1136 },
1137 ..
1138 } = match parent.connection.lock().execute_operation(
1139 locked,
1140 current_task,
1141 parent,
1142 FuseOperation::Lookup { name },
1143 ) {
1144 Ok(FuseResponse::Entry(entry)) => entry,
1145 Ok(_) => return error!(EINVAL),
1146 Err(errno) => {
1147 if errno == ENOENT {
1148 return Ok(false);
1150 } else {
1151 return Err(errno);
1152 };
1153 }
1154 };
1155
1156 if (nodeid != node.nodeid) || (generation != node.generation) {
1157 return Ok(false);
1161 }
1162
1163 dir_entry.node.update_info(|info| {
1164 FuseNode::update_node_info_from_attr(
1165 info,
1166 attr,
1167 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1168 &node.attributes_valid_until,
1169 )?;
1170
1171 self.valid_until.store(
1172 zx::MonotonicInstant::after(attr_valid_to_duration(entry_valid, entry_valid_nsec)?),
1173 VALID_UNTIL_ORDERING,
1174 );
1175
1176 Ok(true)
1177 })
1178 }
1179}
1180
1181const DEFAULT_PERMISSIONS_ATOMIC_ORDERING: Ordering = Ordering::Relaxed;
1183
1184impl FsNodeOps for FuseNode {
1185 fn check_access(
1186 &self,
1187 locked: &mut Locked<FileOpsCore>,
1188 node: &FsNode,
1189 current_task: &CurrentTask,
1190 permission_flags: security::PermissionFlags,
1191 info: &RwLock<FsNodeInfo>,
1192 reason: CheckAccessReason,
1193 audit_context: security::Auditable<'_>,
1194 ) -> Result<(), Errno> {
1195 if FuseFs::from_fs(&node.fs()).default_permissions.load(DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
1198 {
1199 return self.default_check_access_with_valid_node_attributes(
1200 locked,
1201 node,
1202 current_task,
1203 permission_flags,
1204 reason,
1205 info,
1206 audit_context,
1207 );
1208 }
1209
1210 match reason {
1211 CheckAccessReason::Access | CheckAccessReason::Chdir | CheckAccessReason::Chroot => {
1212 let response = self.connection.lock().execute_operation(
1217 locked,
1218 current_task,
1219 self,
1220 FuseOperation::Access {
1221 mask: (permission_flags.as_access() & Access::ACCESS_MASK).bits() as u32,
1222 },
1223 )?;
1224
1225 if let FuseResponse::Access(result) = response { result } else { error!(EINVAL) }
1226 }
1227 CheckAccessReason::Exec => self.default_check_access_with_valid_node_attributes(
1228 locked,
1229 node,
1230 current_task,
1231 permission_flags,
1232 reason,
1233 info,
1234 audit_context,
1235 ),
1236 CheckAccessReason::ChangeTimestamps { .. }
1237 | CheckAccessReason::InternalPermissionChecks => {
1238 Ok(())
1243 }
1244 }
1245 }
1246
1247 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1248 Box::new(FuseDirEntry::default())
1249 }
1250
1251 fn create_file_ops(
1252 &self,
1253 locked: &mut Locked<FileOpsCore>,
1254 node: &FsNode,
1255 current_task: &CurrentTask,
1256 flags: OpenFlags,
1257 ) -> Result<Box<dyn FileOps>, Errno> {
1258 let flags = flags & !(OpenFlags::CREAT | OpenFlags::EXCL);
1260 let mode = node.info().mode;
1261 let response = self.connection.lock().execute_operation(
1262 locked,
1263 current_task,
1264 self,
1265 FuseOperation::Open { flags, mode },
1266 )?;
1267 let FuseResponse::Open(open_out) = response else {
1268 return error!(EINVAL);
1269 };
1270 let passthrough_fh = unsafe { open_out.__bindgen_anon_1.passthrough_fh };
1273 let passthrough_file = if passthrough_fh != 0 {
1274 let mut connection = self.connection.lock();
1275 connection.registered_passthrough.remove(&passthrough_fh).unwrap_or_default()
1276 } else {
1277 Weak::new()
1278 };
1279 Ok(Box::new(FuseFileObject {
1280 connection: self.connection.clone(),
1281 passthrough_file,
1282 open_out,
1283 }))
1284 }
1285
1286 fn lookup(
1287 &self,
1288 locked: &mut Locked<FileOpsCore>,
1289 node: &FsNode,
1290 current_task: &CurrentTask,
1291 name: &FsStr,
1292 ) -> Result<FsNodeHandle, Errno> {
1293 let response = self.connection.lock().execute_operation(
1294 locked,
1295 current_task,
1296 self,
1297 FuseOperation::Lookup { name: name.to_owned() },
1298 )?;
1299 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1300 }
1301
1302 fn mknod(
1303 &self,
1304 locked: &mut Locked<FileOpsCore>,
1305 node: &FsNode,
1306 current_task: &CurrentTask,
1307 name: &FsStr,
1308 mode: FileMode,
1309 dev: DeviceType,
1310 _owner: FsCred,
1311 ) -> Result<FsNodeHandle, Errno> {
1312 let get_entry = |locked: &mut Locked<FileOpsCore>| {
1313 let umask = current_task.fs().umask().bits();
1314 let mut connection = self.connection.lock();
1315
1316 if dev == DeviceType::NONE && !connection.no_create {
1317 match connection.execute_operation(
1318 locked,
1319 current_task,
1320 self,
1321 FuseOperation::Create(
1322 uapi::fuse_create_in {
1323 flags: OpenFlags::CREAT.bits(),
1324 mode: mode.bits(),
1325 umask,
1326 open_flags: 0,
1327 },
1328 name.to_owned(),
1329 ),
1330 ) {
1331 Ok(response) => {
1332 let FuseResponse::Create(response) = response else {
1333 return error!(EINVAL);
1334 };
1335
1336 let fuse_node = FuseNode::new(
1337 self.connection.clone(),
1338 response.entry.nodeid,
1339 response.entry.generation,
1340 );
1341
1342 if let Err(e) = connection.execute_operation(
1348 locked,
1349 current_task,
1350 &fuse_node,
1351 FuseOperation::Release(response.open),
1352 ) {
1353 log_error!("Error when releasing fh: {e:?}");
1354 }
1355
1356 return Ok(response.entry);
1357 }
1358 Err(e) if e == ENOSYS => {
1359 connection.no_create = true;
1360 }
1362 Err(e) => return Err(e),
1363 }
1364 }
1365
1366 connection
1367 .execute_operation(
1368 locked,
1369 current_task,
1370 self,
1371 FuseOperation::Mknod {
1372 mknod_in: uapi::fuse_mknod_in {
1373 mode: mode.bits(),
1374 rdev: dev.bits() as u32,
1375 umask,
1376 padding: 0,
1377 },
1378 name: name.to_owned(),
1379 },
1380 )?
1381 .entry()
1382 .copied()
1383 .ok_or_else(|| errno!(EINVAL))
1384 };
1385
1386 let entry = get_entry(locked)?;
1387 self.fs_node_from_entry(node, name, &entry)
1388 }
1389
1390 fn mkdir(
1391 &self,
1392 locked: &mut Locked<FileOpsCore>,
1393 node: &FsNode,
1394 current_task: &CurrentTask,
1395 name: &FsStr,
1396 mode: FileMode,
1397 _owner: FsCred,
1398 ) -> Result<FsNodeHandle, Errno> {
1399 let response = self.connection.lock().execute_operation(
1400 locked,
1401 current_task,
1402 self,
1403 FuseOperation::Mkdir {
1404 mkdir_in: uapi::fuse_mkdir_in {
1405 mode: mode.bits(),
1406 umask: current_task.fs().umask().bits(),
1407 },
1408 name: name.to_owned(),
1409 },
1410 )?;
1411 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1412 }
1413
1414 fn create_symlink(
1415 &self,
1416 locked: &mut Locked<FileOpsCore>,
1417 node: &FsNode,
1418 current_task: &CurrentTask,
1419 name: &FsStr,
1420 target: &FsStr,
1421 _owner: FsCred,
1422 ) -> Result<FsNodeHandle, Errno> {
1423 let response = self.connection.lock().execute_operation(
1424 locked,
1425 current_task,
1426 self,
1427 FuseOperation::Symlink { target: target.to_owned(), name: name.to_owned() },
1428 )?;
1429 self.fs_node_from_entry(node, name, response.entry().ok_or_else(|| errno!(EINVAL))?)
1430 }
1431
1432 fn readlink(
1433 &self,
1434 locked: &mut Locked<FileOpsCore>,
1435 _node: &FsNode,
1436 current_task: &CurrentTask,
1437 ) -> Result<SymlinkTarget, Errno> {
1438 let response = self.connection.lock().execute_operation(
1439 locked,
1440 current_task,
1441 self,
1442 FuseOperation::Readlink,
1443 )?;
1444 let FuseResponse::Read(read_out) = response else {
1445 return error!(EINVAL);
1446 };
1447 Ok(SymlinkTarget::Path(read_out.into()))
1448 }
1449
1450 fn link(
1451 &self,
1452 locked: &mut Locked<FileOpsCore>,
1453 _node: &FsNode,
1454 current_task: &CurrentTask,
1455 name: &FsStr,
1456 child: &FsNodeHandle,
1457 ) -> Result<(), Errno> {
1458 let child_node = FuseNode::from_node(child);
1459 self.connection
1460 .lock()
1461 .execute_operation(
1462 locked,
1463 current_task,
1464 self,
1465 FuseOperation::Link {
1466 link_in: uapi::fuse_link_in { oldnodeid: child_node.nodeid },
1467 name: name.to_owned(),
1468 },
1469 )
1470 .map(|_| ())
1471 }
1472
1473 fn unlink(
1474 &self,
1475 locked: &mut Locked<FileOpsCore>,
1476 _node: &FsNode,
1477 current_task: &CurrentTask,
1478 name: &FsStr,
1479 child: &FsNodeHandle,
1480 ) -> Result<(), Errno> {
1481 let is_dir = child.is_dir();
1482 self.connection
1483 .lock()
1484 .execute_operation(
1485 locked,
1486 current_task,
1487 self,
1488 if is_dir {
1489 FuseOperation::Rmdir { name: name.to_owned() }
1490 } else {
1491 FuseOperation::Unlink { name: name.to_owned() }
1492 },
1493 )
1494 .map(|_| ())
1495 }
1496
1497 fn truncate(
1498 &self,
1499 locked: &mut Locked<FileOpsCore>,
1500 _guard: &AppendLockGuard<'_>,
1501 node: &FsNode,
1502 current_task: &CurrentTask,
1503 length: u64,
1504 ) -> Result<(), Errno> {
1505 node.update_info(|info| {
1506 let attributes = uapi::fuse_setattr_in {
1508 size: length,
1509 valid: uapi::FATTR_SIZE,
1510 ..Default::default()
1511 };
1512
1513 let response = self.connection.lock().execute_operation(
1514 locked,
1515 current_task,
1516 self,
1517 FuseOperation::SetAttr(attributes),
1518 )?;
1519 let uapi::fuse_attr_out { attr_valid, attr_valid_nsec, attr, .. } =
1520 if let FuseResponse::Attr(attr) = response {
1521 attr
1522 } else {
1523 return error!(EINVAL);
1524 };
1525 FuseNode::update_node_info_from_attr(
1526 info,
1527 attr,
1528 attr_valid_to_duration(attr_valid, attr_valid_nsec)?,
1529 &self.attributes_valid_until,
1530 )?;
1531 Ok(())
1532 })
1533 }
1534
1535 fn allocate(
1536 &self,
1537 _locked: &mut Locked<FileOpsCore>,
1538 _guard: &AppendLockGuard<'_>,
1539 _node: &FsNode,
1540 _current_task: &CurrentTask,
1541 _mode: FallocMode,
1542 _offset: u64,
1543 _length: u64,
1544 ) -> Result<(), Errno> {
1545 track_stub!(TODO("https://fxbug.dev/322875414"), "FsNodeOps::allocate");
1546 error!(ENOTSUP)
1547 }
1548
1549 fn fetch_and_refresh_info<'a>(
1550 &self,
1551 locked: &mut Locked<FileOpsCore>,
1552 _node: &FsNode,
1553 current_task: &CurrentTask,
1554 info: &'a RwLock<FsNodeInfo>,
1555 ) -> Result<RwLockReadGuard<'a, FsNodeInfo>, Errno> {
1556 self.refresh_expired_node_attributes(locked, current_task, info)
1560 }
1561
1562 fn update_attributes(
1563 &self,
1564 locked: &mut Locked<FileOpsCore>,
1565 _node: &FsNode,
1566 current_task: &CurrentTask,
1567 info: &FsNodeInfo,
1568 has: zxio_node_attr_has_t,
1569 ) -> Result<(), Errno> {
1570 let mut valid = 0u32;
1571 if has.modification_time {
1573 valid |= uapi::FATTR_MTIME;
1574 }
1575 if has.access_time {
1576 valid |= uapi::FATTR_ATIME;
1577 }
1578 if has.mode {
1579 valid |= uapi::FATTR_MODE;
1580 }
1581 if has.uid {
1582 valid |= uapi::FATTR_UID;
1583 }
1584 if has.gid {
1585 valid |= uapi::FATTR_GID;
1586 }
1587
1588 let attributes = uapi::fuse_setattr_in {
1589 valid,
1590 atime: (info.time_access.into_nanos() / NANOS_PER_SECOND) as u64,
1591 mtime: (info.time_modify.into_nanos() / NANOS_PER_SECOND) as u64,
1592 ctime: (info.time_status_change.into_nanos() / NANOS_PER_SECOND) as u64,
1593 atimensec: (info.time_access.into_nanos() % NANOS_PER_SECOND) as u32,
1594 mtimensec: (info.time_modify.into_nanos() % NANOS_PER_SECOND) as u32,
1595 ctimensec: (info.time_status_change.into_nanos() % NANOS_PER_SECOND) as u32,
1596 mode: info.mode.bits(),
1597 uid: info.uid,
1598 gid: info.gid,
1599 ..Default::default()
1600 };
1601
1602 let response = self.connection.lock().execute_operation(
1603 locked,
1604 current_task,
1605 self,
1606 FuseOperation::SetAttr(attributes),
1607 )?;
1608 if let FuseResponse::Attr(_attr) = response { Ok(()) } else { error!(EINVAL) }
1609 }
1610
1611 fn get_xattr(
1612 &self,
1613 locked: &mut Locked<FileOpsCore>,
1614 _node: &FsNode,
1615 current_task: &CurrentTask,
1616 name: &FsStr,
1617 max_size: usize,
1618 ) -> Result<ValueOrSize<FsString>, Errno> {
1619 let response = self.connection.lock().execute_operation(
1620 locked,
1621 current_task,
1622 self,
1623 FuseOperation::GetXAttr {
1624 getxattr_in: uapi::fuse_getxattr_in {
1625 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1626 padding: 0,
1627 },
1628 name: name.to_owned(),
1629 },
1630 )?;
1631 if let FuseResponse::GetXAttr(result) = response { Ok(result) } else { error!(EINVAL) }
1632 }
1633
1634 fn set_xattr(
1635 &self,
1636 locked: &mut Locked<FileOpsCore>,
1637 _node: &FsNode,
1638 current_task: &CurrentTask,
1639 name: &FsStr,
1640 value: &FsStr,
1641 op: XattrOp,
1642 ) -> Result<(), Errno> {
1643 let mut state = self.connection.lock();
1644 let configuration = state.get_configuration(locked, current_task)?;
1645 state.execute_operation(
1646 locked,
1647 current_task,
1648 self,
1649 FuseOperation::SetXAttr {
1650 setxattr_in: uapi::fuse_setxattr_in {
1651 size: value.len().try_into().map_err(|_| errno!(EINVAL))?,
1652 flags: op.into_flags(),
1653 setxattr_flags: 0,
1654 padding: 0,
1655 },
1656 is_ext: configuration.flags.contains(FuseInitFlags::SETXATTR_EXT),
1657 name: name.to_owned(),
1658 value: value.to_owned(),
1659 },
1660 )?;
1661 Ok(())
1662 }
1663
1664 fn remove_xattr(
1665 &self,
1666 locked: &mut Locked<FileOpsCore>,
1667 _node: &FsNode,
1668 current_task: &CurrentTask,
1669 name: &FsStr,
1670 ) -> Result<(), Errno> {
1671 self.connection.lock().execute_operation(
1672 locked,
1673 current_task,
1674 self,
1675 FuseOperation::RemoveXAttr { name: name.to_owned() },
1676 )?;
1677 Ok(())
1678 }
1679
1680 fn list_xattrs(
1681 &self,
1682 locked: &mut Locked<FileOpsCore>,
1683 _node: &FsNode,
1684 current_task: &CurrentTask,
1685 max_size: usize,
1686 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
1687 let response = self.connection.lock().execute_operation(
1688 locked,
1689 current_task,
1690 self,
1691 FuseOperation::ListXAttr(uapi::fuse_getxattr_in {
1692 size: max_size.try_into().map_err(|_| errno!(EINVAL))?,
1693 padding: 0,
1694 }),
1695 )?;
1696 if let FuseResponse::GetXAttr(result) = response {
1697 Ok(result.map(|s| {
1698 let mut result = s.split(|c| *c == 0).map(FsString::from).collect::<Vec<_>>();
1699 result.pop();
1702 result
1703 }))
1704 } else {
1705 error!(EINVAL)
1706 }
1707 }
1708
1709 fn forget(
1710 self: Box<Self>,
1711 locked: &mut Locked<FileOpsCore>,
1712 current_task: &CurrentTask,
1713 _info: FsNodeInfo,
1714 ) -> Result<(), Errno> {
1715 let nlookup = self.state.lock().nlookup;
1716 let mut state = self.connection.lock();
1717 if !state.is_connected() {
1718 return Ok(());
1719 }
1720 if nlookup > 0 {
1721 state.execute_operation(
1722 locked,
1723 current_task,
1724 self.as_ref(),
1725 FuseOperation::Forget(uapi::fuse_forget_in { nlookup }),
1726 )?;
1727 };
1728 Ok(())
1729 }
1730
1731 fn node_key(&self, _node: &FsNode) -> ino_t {
1732 self.nodeid
1733 }
1734}
1735
1736#[derive(Debug, Default)]
1738enum FuseConnectionState {
1739 #[default]
1740 Waiting,
1742 Connected,
1744 Disconnected,
1746}
1747
1748#[derive(Debug)]
1749struct FuseConnection {
1750 id: u64,
1752
1753 creds: FsCred,
1755
1756 state: Mutex<FuseMutableState>,
1758}
1759
1760struct FuseMutableStateGuard<'a>(Guard<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>);
1761
1762impl<'a> Deref for FuseMutableStateGuard<'a> {
1763 type Target = Guard<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>;
1764 fn deref(&self) -> &Self::Target {
1765 &self.0
1766 }
1767}
1768
1769impl<'a> DerefMut for FuseMutableStateGuard<'a> {
1770 fn deref_mut(&mut self) -> &mut Self::Target {
1771 &mut self.0
1772 }
1773}
1774
1775impl FuseConnection {
1776 fn lock<'a>(&'a self) -> FuseMutableStateGuard<'a> {
1777 FuseMutableStateGuard(Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::new(
1778 self,
1779 self.state.lock(),
1780 ))
1781 }
1782}
1783
1784#[derive(Clone, Copy, Debug)]
1785struct FuseConfiguration {
1786 flags: FuseInitFlags,
1787}
1788
1789impl TryFrom<uapi::fuse_init_out> for FuseConfiguration {
1790 type Error = Errno;
1791 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
1792 let flags = FuseInitFlags::try_from(init_out)?;
1793 Ok(Self { flags })
1794 }
1795}
1796
1797type OperationsState = HashMap<uapi::fuse_opcode, Result<FuseResponse, Errno>>;
1805
1806#[derive(Debug, Default)]
1807struct FuseMutableState {
1808 state: FuseConnectionState,
1810
1811 last_unique_id: u64,
1813
1814 configuration: Option<FuseConfiguration>,
1816
1817 operations: HashMap<u64, RunningOperation>,
1819
1820 message_queue: VecDeque<FuseKernelMessage>,
1824
1825 waiters: WaitQueue,
1827
1828 operations_state: OperationsState,
1830
1831 no_create: bool,
1833
1834 last_passthrough_id: u32,
1836
1837 registered_passthrough: HashMap<u32, WeakFileHandle>,
1840}
1841
1842impl<'a> FuseMutableStateGuard<'a> {
1843 fn wait_for_configuration<L, T>(
1844 &mut self,
1845 locked: &mut Locked<L>,
1846 current_task: &CurrentTask,
1847 f: impl Fn(&FuseConfiguration) -> T,
1848 ) -> Result<T, Errno>
1849 where
1850 L: LockEqualOrBefore<FileOpsCore>,
1851 {
1852 if let Some(configuration) = self.configuration.as_ref() {
1853 return Ok(f(configuration));
1854 }
1855 loop {
1856 if !self.is_connected() {
1857 return error!(ECONNABORTED);
1858 }
1859 let waiter = Waiter::new();
1860 self.waiters.wait_async_value(&waiter, CONFIGURATION_AVAILABLE_EVENT);
1861 if let Some(configuration) = self.configuration.as_ref() {
1862 return Ok(f(configuration));
1863 }
1864 Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::unlocked(self, || {
1865 waiter.wait(locked, current_task)
1866 })?;
1867 }
1868 }
1869
1870 fn get_configuration<L>(
1871 &mut self,
1872 locked: &mut Locked<L>,
1873 current_task: &CurrentTask,
1874 ) -> Result<FuseConfiguration, Errno>
1875 where
1876 L: LockEqualOrBefore<FileOpsCore>,
1877 {
1878 self.wait_for_configuration(locked, current_task, Clone::clone)
1879 }
1880
1881 fn wait_for_configuration_ready<L>(
1882 &mut self,
1883 locked: &mut Locked<L>,
1884 current_task: &CurrentTask,
1885 ) -> Result<(), Errno>
1886 where
1887 L: LockEqualOrBefore<FileOpsCore>,
1888 {
1889 self.wait_for_configuration(locked, current_task, |_| ())
1890 }
1891
1892 fn execute_operation<L>(
1898 &mut self,
1899 locked: &mut Locked<L>,
1900 current_task: &CurrentTask,
1901 node: &FuseNode,
1902 operation: FuseOperation,
1903 ) -> Result<FuseResponse, Errno>
1904 where
1905 L: LockEqualOrBefore<FileOpsCore>,
1906 {
1907 if !matches!(operation, FuseOperation::Init { .. }) {
1912 self.wait_for_configuration_ready(locked, current_task)?;
1913 }
1914
1915 if let Some(result) = self.operations_state.get(&operation.opcode()) {
1916 return result.clone();
1917 }
1918 if !operation.has_response() {
1919 self.queue_operation(current_task, node.nodeid, operation, None)?;
1920 return Ok(FuseResponse::None);
1921 }
1922 let waiter = Waiter::with_options(WaiterOptions::UNSAFE_CALLSTACK);
1923 let is_async = operation.is_async();
1924 let unique_id =
1925 self.queue_operation(current_task, node.nodeid, operation, Some(&waiter))?;
1926 if is_async {
1927 return Ok(FuseResponse::None);
1928 }
1929 let mut first_loop = true;
1930 loop {
1931 if !self.is_connected() {
1932 return error!(ECONNABORTED);
1933 }
1934 if let Some(response) = self.get_response(unique_id) {
1935 return response;
1936 }
1937 match Guard::<'a, FuseConnection, MutexGuard<'a, FuseMutableState>>::unlocked(
1938 self,
1939 || waiter.wait(locked, current_task),
1940 ) {
1941 Ok(()) => {}
1942 Err(e) if e == EINTR => {
1943 if first_loop {
1946 self.interrupt(current_task, node.nodeid, unique_id)?;
1947 first_loop = false;
1948 }
1949 }
1950 Err(e) => {
1951 log_error!("Unexpected error: {e:?}");
1952 return Err(e);
1953 }
1954 }
1955 }
1956 }
1957}
1958
1959impl FuseMutableState {
1960 fn wait_async(
1961 &self,
1962 waiter: &Waiter,
1963 events: FdEvents,
1964 handler: EventHandler,
1965 ) -> Option<WaitCanceler> {
1966 Some(self.waiters.wait_async_fd_events(waiter, events, handler))
1967 }
1968
1969 fn is_connected(&self) -> bool {
1970 matches!(self.state, FuseConnectionState::Connected)
1971 }
1972
1973 fn set_configuration(&mut self, configuration: FuseConfiguration) {
1974 debug_assert!(self.configuration.is_none());
1975 log_trace!("Fuse configuration: {configuration:?}");
1976 self.configuration = Some(configuration);
1977 self.waiters.notify_value(CONFIGURATION_AVAILABLE_EVENT);
1978 }
1979
1980 fn connect(&mut self) {
1981 debug_assert!(matches!(self.state, FuseConnectionState::Waiting));
1982 self.state = FuseConnectionState::Connected;
1983 }
1984
1985 fn disconnect(&mut self) {
1988 if matches!(self.state, FuseConnectionState::Disconnected) {
1989 return;
1990 }
1991 self.state = FuseConnectionState::Disconnected;
1992 self.message_queue.clear();
1993 self.operations.clear();
1994 self.waiters.notify_all();
1995 }
1996
1997 fn queue_operation(
2001 &mut self,
2002 current_task: &CurrentTask,
2003 nodeid: u64,
2004 operation: FuseOperation,
2005 waiter: Option<&Waiter>,
2006 ) -> Result<u64, Errno> {
2007 debug_assert!(waiter.is_some() == operation.has_response(), "{operation:?}");
2008 if !self.is_connected() {
2009 return error!(ECONNABORTED);
2010 }
2011 self.last_unique_id += 1;
2012 let message = FuseKernelMessage::new(self.last_unique_id, current_task, nodeid, operation)?;
2013 if let Some(waiter) = waiter {
2014 self.waiters.wait_async_value(waiter, self.last_unique_id);
2015 }
2016 if message.operation.has_response() {
2017 self.operations.insert(self.last_unique_id, message.operation.as_running().into());
2018 }
2019 self.message_queue.push_back(message);
2020 self.waiters.notify_fd_events(FdEvents::POLLIN);
2021 Ok(self.last_unique_id)
2022 }
2023
2024 fn interrupt(
2031 &mut self,
2032 current_task: &CurrentTask,
2033 nodeid: u64,
2034 unique_id: u64,
2035 ) -> Result<(), Errno> {
2036 debug_assert!(self.operations.contains_key(&unique_id));
2037
2038 let mut in_queue = false;
2039 self.message_queue.retain(|m| {
2040 if m.header.unique == unique_id {
2041 self.operations.remove(&unique_id);
2042 in_queue = true;
2043 false
2044 } else {
2045 true
2046 }
2047 });
2048 if in_queue {
2049 return error!(EINTR);
2051 }
2052 self.queue_operation(current_task, nodeid, FuseOperation::Interrupt { unique_id }, None)
2053 .map(|_| ())
2054 }
2055
2056 fn get_response(&mut self, unique_id: u64) -> Option<Result<FuseResponse, Errno>> {
2059 match self.operations.entry(unique_id) {
2060 Entry::Vacant(_) => Some(error!(EINVAL)),
2061 Entry::Occupied(mut entry) => {
2062 let result = entry.get_mut().response.take();
2063 if result.is_some() {
2064 entry.remove();
2065 }
2066 result
2067 }
2068 }
2069 }
2070
2071 fn query_events(&self) -> FdEvents {
2072 let mut events = FdEvents::POLLOUT;
2073 if !self.is_connected() || !self.message_queue.is_empty() {
2074 events |= FdEvents::POLLIN
2075 };
2076 if !self.is_connected() {
2077 events |= FdEvents::POLLERR;
2078 }
2079 events
2080 }
2081
2082 fn read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2083 match self.state {
2084 FuseConnectionState::Waiting => return error!(EPERM),
2085 FuseConnectionState::Disconnected => return error!(ENODEV),
2086 _ => {}
2087 }
2088 if let Some(message) = self.message_queue.pop_front() {
2089 message.serialize(data)
2090 } else {
2091 error!(EAGAIN)
2092 }
2093 }
2094
2095 fn write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
2096 match self.state {
2097 FuseConnectionState::Waiting => return error!(EPERM),
2098 FuseConnectionState::Disconnected => return error!(ENODEV),
2099 _ => {}
2100 }
2101 let header: uapi::fuse_out_header = data.read_to_object()?;
2102 let payload_size = (header.len as usize)
2103 .checked_sub(std::mem::size_of::<uapi::fuse_out_header>())
2104 .ok_or_else(|| errno!(EINVAL))?;
2105 if payload_size > data.available() {
2106 return error!(EINVAL);
2107 }
2108 if header.unique == 0 {
2109 track_stub!(TODO("https://fxbug.dev/322873416"), "Fuse notification from userspace");
2110 return error!(ENOTSUP);
2111 }
2112 self.waiters.notify_value(header.unique);
2113 let mut running_operation = match self.operations.entry(header.unique) {
2114 Entry::Occupied(e) => e,
2115 Entry::Vacant(_) => return error!(EINVAL),
2116 };
2117 let operation = &running_operation.get().operation;
2118 let is_async = operation.is_async();
2119 if header.error < 0 {
2120 log_trace!("Fuse: {operation:?} -> {header:?}");
2121 let code = i16::try_from(-header.error).unwrap_or_else(|_| EINVAL.error_code() as i16);
2122 let errno = errno_from_code!(code);
2123 let response = operation.handle_error(&mut self.operations_state, errno);
2124 if is_async {
2125 running_operation.remove();
2126 } else {
2127 running_operation.get_mut().response = Some(response);
2128 }
2129 } else {
2130 let buffer = data.read_to_vec_limited(payload_size)?;
2131 if buffer.len() != payload_size {
2132 return error!(EINVAL);
2133 }
2134 let response = operation.parse_response(buffer)?;
2135 log_trace!("Fuse: {operation:?} -> {response:?}");
2136 if is_async {
2137 let operation = running_operation.remove();
2138 self.handle_async(operation, response)?;
2139 } else {
2140 running_operation.get_mut().response = Some(Ok(response));
2141 }
2142 }
2143 Ok(data.bytes_read())
2144 }
2145
2146 fn handle_async(
2147 &mut self,
2148 operation: RunningOperation,
2149 response: FuseResponse,
2150 ) -> Result<(), Errno> {
2151 match (operation.operation, response) {
2152 (RunningOperationKind::Init { fs }, FuseResponse::Init(init_out)) => {
2153 let configuration = FuseConfiguration::try_from(init_out)?;
2154 if configuration.flags.contains(FuseInitFlags::POSIX_ACL) {
2155 if let Some(fs) = fs.upgrade() {
2159 FuseFs::from_fs(&fs)
2160 .default_permissions
2161 .store(true, DEFAULT_PERMISSIONS_ATOMIC_ORDERING)
2162 } else {
2163 log_warn!("failed to upgrade FuseFs when handling FUSE_INIT response");
2164 return error!(ENOTCONN);
2165 }
2166 }
2167 self.set_configuration(configuration);
2168 Ok(())
2169 }
2170 operation => {
2171 panic!("Incompatible operation={operation:?}");
2173 }
2174 }
2175 }
2176
2177 fn clear_released_passthrough_fds(&mut self) {
2178 self.registered_passthrough.retain(|_, s| s.strong_count() > 0);
2179 }
2180}
2181
2182#[derive(Debug)]
2185struct RunningOperation {
2186 operation: RunningOperationKind,
2187 response: Option<Result<FuseResponse, Errno>>,
2188}
2189
2190impl From<RunningOperationKind> for RunningOperation {
2191 fn from(operation: RunningOperationKind) -> Self {
2192 Self { operation, response: None }
2193 }
2194}
2195
2196#[derive(Debug)]
2197struct FuseKernelMessage {
2198 header: uapi::fuse_in_header,
2199 operation: FuseOperation,
2200}
2201
2202impl FuseKernelMessage {
2203 fn new(
2204 unique: u64,
2205 current_task: &CurrentTask,
2206 nodeid: u64,
2207 operation: FuseOperation,
2208 ) -> Result<Self, Errno> {
2209 let current_creds = current_task.current_creds();
2210 Ok(Self {
2211 header: uapi::fuse_in_header {
2212 len: u32::try_from(std::mem::size_of::<uapi::fuse_in_header>() + operation.len())
2213 .map_err(|_| errno!(EINVAL))?,
2214 opcode: operation.opcode(),
2215 unique,
2216 nodeid,
2217 uid: current_creds.uid,
2218 gid: current_creds.gid,
2219 pid: current_task.get_tid() as u32,
2220 __bindgen_anon_1: Default::default(),
2221 },
2222 operation,
2223 })
2224 }
2225
2226 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2227 let size = data.write(self.header.as_bytes())?;
2228 Ok(size + self.operation.serialize(data)?)
2229 }
2230}
2231
2232bitflags::bitflags! {
2233 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2234 pub struct FuseInitFlags : u64 {
2235 const BIG_WRITES = uapi::FUSE_BIG_WRITES as u64;
2236 const DONT_MASK = uapi::FUSE_DONT_MASK as u64;
2237 const SPLICE_WRITE = uapi::FUSE_SPLICE_WRITE as u64;
2238 const SPLICE_MOVE = uapi::FUSE_SPLICE_MOVE as u64;
2239 const SPLICE_READ = uapi::FUSE_SPLICE_READ as u64;
2240 const DO_READDIRPLUS = uapi::FUSE_DO_READDIRPLUS as u64;
2241 const READDIRPLUS_AUTO = uapi::FUSE_READDIRPLUS_AUTO as u64;
2242 const SETXATTR_EXT = uapi::FUSE_SETXATTR_EXT as u64;
2243 const POSIX_ACL = uapi::FUSE_POSIX_ACL as u64;
2244 const PASSTHROUGH = uapi::FUSE_PASSTHROUGH as u64;
2245 const INIT_EXT = uapi::FUSE_INIT_EXT as u64;
2246 }
2247}
2248
2249impl TryFrom<uapi::fuse_init_out> for FuseInitFlags {
2250 type Error = Errno;
2251 fn try_from(init_out: uapi::fuse_init_out) -> Result<Self, Errno> {
2252 let flags = (init_out.flags as u64) | ((init_out.flags2 as u64) << 32);
2253 let unknown_flags = flags & !Self::all().bits();
2254 if unknown_flags != 0 {
2255 track_stub!(
2256 TODO("https://fxbug.dev/322875725"),
2257 "FUSE unknown init flags",
2258 unknown_flags
2259 );
2260 log_warn!("FUSE daemon requested unknown flags in init: {unknown_flags}");
2261 }
2262 Ok(Self::from_bits_truncate(flags))
2263 }
2264}
2265
2266impl FuseInitFlags {
2267 fn get_u32_components(&self) -> (u32, u32) {
2269 let flags = (self.bits() & (u32::max_value() as u64)) as u32;
2270 let flags2 = (self.bits() >> 32) as u32;
2271 (flags, flags2)
2272 }
2273}
2274
2275#[derive(Clone, Debug)]
2276enum RunningOperationKind {
2277 Access,
2278 Create,
2279 Flush,
2280 Forget,
2281 GetAttr,
2282 Init {
2283 fs: Weak<FileSystem>,
2288 },
2289 Interrupt,
2290 GetXAttr {
2291 size: u32,
2292 },
2293 ListXAttr {
2294 size: u32,
2295 },
2296 Lookup,
2297 Mkdir,
2298 Mknod,
2299 Link,
2300 Open {
2301 dir: bool,
2302 },
2303 Poll,
2304 Read,
2305 Readdir {
2306 use_readdirplus: bool,
2307 },
2308 Readlink,
2309 Release {
2310 dir: bool,
2311 },
2312 RemoveXAttr,
2313 Rename,
2314 Rmdir,
2315 Seek,
2316 SetAttr,
2317 SetXAttr,
2318 Statfs,
2319 Symlink,
2320 Unlink,
2321 Write,
2322}
2323
2324impl RunningOperationKind {
2325 fn is_async(&self) -> bool {
2326 matches!(self, Self::Init { .. })
2327 }
2328
2329 fn opcode(&self) -> u32 {
2330 match self {
2331 Self::Access => uapi::fuse_opcode_FUSE_ACCESS,
2332 Self::Create => uapi::fuse_opcode_FUSE_CREATE,
2333 Self::Flush => uapi::fuse_opcode_FUSE_FLUSH,
2334 Self::Forget => uapi::fuse_opcode_FUSE_FORGET,
2335 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2336 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2337 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2338 Self::Interrupt => uapi::fuse_opcode_FUSE_INTERRUPT,
2339 Self::ListXAttr { .. } => uapi::fuse_opcode_FUSE_LISTXATTR,
2340 Self::Lookup => uapi::fuse_opcode_FUSE_LOOKUP,
2341 Self::Mkdir => uapi::fuse_opcode_FUSE_MKDIR,
2342 Self::Mknod => uapi::fuse_opcode_FUSE_MKNOD,
2343 Self::Link => uapi::fuse_opcode_FUSE_LINK,
2344 Self::Open { dir } => {
2345 if *dir {
2346 uapi::fuse_opcode_FUSE_OPENDIR
2347 } else {
2348 uapi::fuse_opcode_FUSE_OPEN
2349 }
2350 }
2351 Self::Poll => uapi::fuse_opcode_FUSE_POLL,
2352 Self::Read => uapi::fuse_opcode_FUSE_READ,
2353 Self::Readdir { use_readdirplus } => {
2354 if *use_readdirplus {
2355 uapi::fuse_opcode_FUSE_READDIRPLUS
2356 } else {
2357 uapi::fuse_opcode_FUSE_READDIR
2358 }
2359 }
2360 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2361 Self::Release { dir } => {
2362 if *dir {
2363 uapi::fuse_opcode_FUSE_RELEASEDIR
2364 } else {
2365 uapi::fuse_opcode_FUSE_RELEASE
2366 }
2367 }
2368 Self::RemoveXAttr => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2369 Self::Rename => uapi::fuse_opcode_FUSE_RENAME2,
2370 Self::Rmdir => uapi::fuse_opcode_FUSE_RMDIR,
2371 Self::Seek => uapi::fuse_opcode_FUSE_LSEEK,
2372 Self::SetAttr => uapi::fuse_opcode_FUSE_SETATTR,
2373 Self::SetXAttr => uapi::fuse_opcode_FUSE_SETXATTR,
2374 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2375 Self::Symlink => uapi::fuse_opcode_FUSE_SYMLINK,
2376 Self::Unlink => uapi::fuse_opcode_FUSE_UNLINK,
2377 Self::Write => uapi::fuse_opcode_FUSE_WRITE,
2378 }
2379 }
2380
2381 fn to_response<T: FromBytes + IntoBytes + Immutable>(buffer: &[u8]) -> T {
2382 let mut result = T::new_zeroed();
2383 let length_to_copy = std::cmp::min(buffer.len(), std::mem::size_of::<T>());
2384 result.as_mut_bytes()[..length_to_copy].copy_from_slice(&buffer[..length_to_copy]);
2385 result
2386 }
2387
2388 fn parse_response(&self, buffer: Vec<u8>) -> Result<FuseResponse, Errno> {
2389 match self {
2390 Self::Access => Ok(FuseResponse::Access(Ok(()))),
2391 Self::Create { .. } => {
2392 Ok(FuseResponse::Create(Self::to_response::<CreateResponse>(&buffer)))
2393 }
2394 Self::GetAttr | Self::SetAttr => {
2395 Ok(FuseResponse::Attr(Self::to_response::<uapi::fuse_attr_out>(&buffer)))
2396 }
2397 Self::GetXAttr { size } | Self::ListXAttr { size } => {
2398 if *size == 0 {
2399 if buffer.len() < std::mem::size_of::<uapi::fuse_getxattr_out>() {
2400 return error!(EINVAL);
2401 }
2402 let getxattr_out = Self::to_response::<uapi::fuse_getxattr_out>(&buffer);
2403 Ok(FuseResponse::GetXAttr(ValueOrSize::Size(getxattr_out.size as usize)))
2404 } else {
2405 Ok(FuseResponse::GetXAttr(FsString::new(buffer).into()))
2406 }
2407 }
2408 Self::Init { .. } => {
2409 Ok(FuseResponse::Init(Self::to_response::<uapi::fuse_init_out>(&buffer)))
2410 }
2411 Self::Lookup | Self::Mkdir | Self::Mknod | Self::Link | Self::Symlink => {
2412 Ok(FuseResponse::Entry(Self::to_response::<FuseEntryOutExtended>(&buffer)))
2413 }
2414 Self::Open { .. } => {
2415 Ok(FuseResponse::Open(Self::to_response::<uapi::fuse_open_out>(&buffer)))
2416 }
2417 Self::Poll => Ok(FuseResponse::Poll(Self::to_response::<uapi::fuse_poll_out>(&buffer))),
2418 Self::Read | Self::Readlink => Ok(FuseResponse::Read(buffer)),
2419 Self::Readdir { use_readdirplus, .. } => {
2420 let mut result = vec![];
2421 let mut slice = &buffer[..];
2422 while !slice.is_empty() {
2423 let entry = if *use_readdirplus {
2425 if slice.len() < std::mem::size_of::<uapi::fuse_entry_out>() {
2426 return error!(EINVAL);
2427 }
2428 let entry = Self::to_response::<uapi::fuse_entry_out>(slice);
2429 slice = &slice[std::mem::size_of::<uapi::fuse_entry_out>()..];
2430 Some(entry)
2431 } else {
2432 None
2433 };
2434 if slice.len() < std::mem::size_of::<uapi::fuse_dirent>() {
2436 return error!(EINVAL);
2437 }
2438 let dirent = Self::to_response::<uapi::fuse_dirent>(slice);
2439 slice = &slice[std::mem::size_of::<uapi::fuse_dirent>()..];
2441 let namelen = dirent.namelen as usize;
2442 if slice.len() < namelen {
2443 return error!(EINVAL);
2444 }
2445 let name = FsString::from(&slice[..namelen]);
2446 result.push((dirent, name, entry));
2447 let skipped = round_up_to_increment(namelen, 8)?;
2448 if slice.len() < skipped {
2449 return error!(EINVAL);
2450 }
2451 slice = &slice[skipped..];
2452 }
2453 Ok(FuseResponse::Readdir(result))
2454 }
2455 Self::Flush
2456 | Self::Release { .. }
2457 | Self::RemoveXAttr
2458 | Self::Rename
2459 | Self::Rmdir
2460 | Self::SetXAttr
2461 | Self::Unlink => Ok(FuseResponse::None),
2462 Self::Statfs => {
2463 Ok(FuseResponse::Statfs(Self::to_response::<uapi::fuse_statfs_out>(&buffer)))
2464 }
2465 Self::Seek => {
2466 Ok(FuseResponse::Seek(Self::to_response::<uapi::fuse_lseek_out>(&buffer)))
2467 }
2468 Self::Write => {
2469 Ok(FuseResponse::Write(Self::to_response::<uapi::fuse_write_out>(&buffer)))
2470 }
2471 Self::Interrupt | Self::Forget => {
2472 panic!("Response for operation without one");
2473 }
2474 }
2475 }
2476
2477 fn handle_error(
2482 &self,
2483 state: &mut OperationsState,
2484 errno: Errno,
2485 ) -> Result<FuseResponse, Errno> {
2486 match self {
2487 Self::Access if errno == ENOSYS => {
2488 const UNIMPLEMENTED_ACCESS_RESPONSE: Result<FuseResponse, Errno> =
2493 Ok(FuseResponse::Access(Ok(())));
2494 state.insert(self.opcode(), UNIMPLEMENTED_ACCESS_RESPONSE);
2495 UNIMPLEMENTED_ACCESS_RESPONSE
2496 }
2497 Self::Flush if errno == ENOSYS => {
2498 state.insert(self.opcode(), Ok(FuseResponse::None));
2499 Ok(FuseResponse::None)
2500 }
2501 Self::Seek if errno == ENOSYS => {
2502 state.insert(self.opcode(), Err(errno.clone()));
2503 Err(errno)
2504 }
2505 Self::Poll if errno == ENOSYS => {
2506 let response = FuseResponse::Poll(uapi::fuse_poll_out {
2507 revents: (FdEvents::POLLIN | FdEvents::POLLOUT).bits(),
2508 padding: 0,
2509 });
2510 state.insert(self.opcode(), Ok(response.clone()));
2511 Ok(response)
2512 }
2513 _ => Err(errno),
2514 }
2515 }
2516}
2517
2518#[derive(Debug)]
2519enum FuseOperation {
2520 Access {
2521 mask: u32,
2522 },
2523 Create(uapi::fuse_create_in, FsString),
2524 Flush(uapi::fuse_open_out),
2525 Forget(uapi::fuse_forget_in),
2526 GetAttr,
2527 Init {
2528 fs: Weak<FileSystem>,
2533 },
2534 Interrupt {
2535 unique_id: u64,
2537 },
2538 GetXAttr {
2539 getxattr_in: uapi::fuse_getxattr_in,
2540 name: FsString,
2542 },
2543 ListXAttr(uapi::fuse_getxattr_in),
2544 Lookup {
2545 name: FsString,
2547 },
2548 Mkdir {
2549 mkdir_in: uapi::fuse_mkdir_in,
2550 name: FsString,
2552 },
2553 Mknod {
2554 mknod_in: uapi::fuse_mknod_in,
2555 name: FsString,
2557 },
2558 Link {
2559 link_in: uapi::fuse_link_in,
2560 name: FsString,
2562 },
2563 Open {
2564 flags: OpenFlags,
2565 mode: FileMode,
2566 },
2567 Poll(uapi::fuse_poll_in),
2568 Read(uapi::fuse_read_in),
2569 Readdir {
2570 read_in: uapi::fuse_read_in,
2571 use_readdirplus: bool,
2573 },
2574 Readlink,
2575 Release(uapi::fuse_open_out),
2576 ReleaseDir(uapi::fuse_open_out),
2577 RemoveXAttr {
2578 name: FsString,
2580 },
2581 Rename {
2582 old_name: FsString,
2583 new_dir: u64,
2584 new_name: FsString,
2585 },
2586 Rmdir {
2587 name: FsString,
2588 },
2589 Seek(uapi::fuse_lseek_in),
2590 SetAttr(uapi::fuse_setattr_in),
2591 SetXAttr {
2592 setxattr_in: uapi::fuse_setxattr_in,
2593 is_ext: bool,
2596 name: FsString,
2598 value: FsString,
2600 },
2601 Statfs,
2602 Symlink {
2603 target: FsString,
2605 name: FsString,
2607 },
2608 Unlink {
2609 name: FsString,
2611 },
2612 Write {
2613 write_in: uapi::fuse_write_in,
2614 content: Vec<u8>,
2616 },
2617}
2618
2619#[derive(Clone, Debug)]
2620enum FuseResponse {
2621 Access(Result<(), Errno>),
2622 Attr(uapi::fuse_attr_out),
2623 Create(CreateResponse),
2624 Entry(FuseEntryOutExtended),
2625 GetXAttr(ValueOrSize<FsString>),
2626 Init(uapi::fuse_init_out),
2627 Open(uapi::fuse_open_out),
2628 Poll(uapi::fuse_poll_out),
2629 Read(
2630 Vec<u8>,
2632 ),
2633 Seek(uapi::fuse_lseek_out),
2634 Readdir(Vec<(uapi::fuse_dirent, FsString, Option<uapi::fuse_entry_out>)>),
2635 Statfs(uapi::fuse_statfs_out),
2636 Write(uapi::fuse_write_out),
2637 None,
2638}
2639
2640impl FuseResponse {
2641 fn entry(&self) -> Option<&uapi::fuse_entry_out> {
2642 if let Self::Entry(entry) = self { Some(&entry.arg) } else { None }
2643 }
2644}
2645
2646#[repr(C)]
2647#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2648struct CreateResponse {
2649 entry: uapi::fuse_entry_out,
2650 open: uapi::fuse_open_out,
2651}
2652
2653static_assertions::const_assert_eq!(
2654 std::mem::offset_of!(CreateResponse, open),
2655 std::mem::size_of::<uapi::fuse_entry_out>()
2656);
2657
2658#[repr(C)]
2659#[derive(Clone, Debug, KnownLayout, FromBytes, IntoBytes, Immutable)]
2660struct FuseEntryOutExtended {
2661 arg: uapi::fuse_entry_out,
2662 bpf_arg: uapi::fuse_entry_bpf_out,
2663}
2664
2665static_assertions::const_assert_eq!(
2666 std::mem::offset_of!(FuseEntryOutExtended, bpf_arg),
2667 std::mem::size_of::<uapi::fuse_entry_out>()
2668);
2669
2670impl FuseOperation {
2671 fn serialize(&self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
2672 match self {
2673 Self::Access { mask } => {
2674 let message = uapi::fuse_access_in { mask: *mask, padding: 0 };
2675 data.write_all(message.as_bytes())
2676 }
2677 Self::Create(create_in, name) => {
2678 Ok(data.write_all(create_in.as_bytes())? + Self::write_null_terminated(data, name)?)
2679 }
2680 Self::Flush(open_in) => {
2681 let message =
2682 uapi::fuse_flush_in { fh: open_in.fh, unused: 0, padding: 0, lock_owner: 0 };
2683 data.write_all(message.as_bytes())
2684 }
2685 Self::Forget(forget_in) => data.write_all(forget_in.as_bytes()),
2686 Self::GetAttr | Self::Readlink | Self::Statfs => Ok(0),
2687 Self::GetXAttr { getxattr_in, name } => {
2688 let mut len = data.write_all(getxattr_in.as_bytes())?;
2689 len += Self::write_null_terminated(data, name)?;
2690 Ok(len)
2691 }
2692 Self::Init { .. } => {
2693 let (flags, flags2) = FuseInitFlags::all().get_u32_components();
2694 let message = uapi::fuse_init_in {
2695 major: uapi::FUSE_KERNEL_VERSION,
2696 minor: uapi::FUSE_KERNEL_MINOR_VERSION,
2697 flags,
2698 flags2,
2699 ..Default::default()
2700 };
2701 data.write_all(message.as_bytes())
2702 }
2703 Self::Interrupt { unique_id } => {
2704 let message = uapi::fuse_interrupt_in { unique: *unique_id };
2705 data.write_all(message.as_bytes())
2706 }
2707 Self::ListXAttr(getxattr_in) => data.write_all(getxattr_in.as_bytes()),
2708 Self::Lookup { name } => Self::write_null_terminated(data, name),
2709 Self::Open { flags, .. } => {
2710 let message = uapi::fuse_open_in { flags: flags.bits(), open_flags: 0 };
2711 data.write_all(message.as_bytes())
2712 }
2713 Self::Poll(poll_in) => data.write_all(poll_in.as_bytes()),
2714 Self::Mkdir { mkdir_in, name } => {
2715 let mut len = data.write_all(mkdir_in.as_bytes())?;
2716 len += Self::write_null_terminated(data, name)?;
2717 Ok(len)
2718 }
2719 Self::Mknod { mknod_in, name } => {
2720 let mut len = data.write_all(mknod_in.as_bytes())?;
2721 len += Self::write_null_terminated(data, name)?;
2722 Ok(len)
2723 }
2724 Self::Link { link_in, name } => {
2725 let mut len = data.write_all(link_in.as_bytes())?;
2726 len += Self::write_null_terminated(data, name)?;
2727 Ok(len)
2728 }
2729 Self::Read(read_in) | Self::Readdir { read_in, .. } => {
2730 data.write_all(read_in.as_bytes())
2731 }
2732 Self::Release(open_out) | Self::ReleaseDir(open_out) => {
2733 let message = uapi::fuse_release_in {
2734 fh: open_out.fh,
2735 flags: 0,
2736 release_flags: 0,
2737 lock_owner: 0,
2738 };
2739 data.write_all(message.as_bytes())
2740 }
2741 Self::RemoveXAttr { name } => Self::write_null_terminated(data, name),
2742 Self::Rename { old_name, new_dir, new_name } => {
2743 Ok(data.write_all(
2744 uapi::fuse_rename2_in { newdir: *new_dir, flags: 0, padding: 0 }.as_bytes(),
2745 )? + Self::write_null_terminated(data, old_name)?
2746 + Self::write_null_terminated(data, new_name)?)
2747 }
2748 Self::Seek(seek_in) => data.write_all(seek_in.as_bytes()),
2749 Self::SetAttr(setattr_in) => data.write_all(setattr_in.as_bytes()),
2750 Self::SetXAttr { setxattr_in, is_ext, name, value } => {
2751 let header =
2752 if *is_ext { setxattr_in.as_bytes() } else { &setxattr_in.as_bytes()[..8] };
2753 let mut len = data.write_all(header)?;
2754 len += Self::write_null_terminated(data, name)?;
2755 len += data.write_all(value.as_bytes())?;
2756 Ok(len)
2757 }
2758 Self::Symlink { target, name } => {
2759 let mut len = Self::write_null_terminated(data, name)?;
2760 len += Self::write_null_terminated(data, target)?;
2761 Ok(len)
2762 }
2763 Self::Rmdir { name } | Self::Unlink { name } => Self::write_null_terminated(data, name),
2764 &Self::Write { mut write_in, ref content } => {
2765 let mut write_in_size = write_in.size as usize;
2766 assert!(write_in_size == content.len());
2767 if write_in_size + write_in.as_bytes().len() > data.available() {
2768 write_in_size = data.available() - write_in.as_bytes().len();
2769 write_in.size = write_in_size as u32;
2770 }
2771 let mut len = data.write_all(write_in.as_bytes())?;
2772 len += data.write_all(&content[..write_in_size])?;
2773 Ok(len)
2774 }
2775 }
2776 }
2777
2778 fn write_null_terminated(
2779 data: &mut dyn OutputBuffer,
2780 content: &Vec<u8>,
2781 ) -> Result<usize, Errno> {
2782 let mut len = data.write_all(content.as_bytes())?;
2783 len += data.write_all(&[0])?;
2784 Ok(len)
2785 }
2786
2787 fn opcode(&self) -> u32 {
2788 match self {
2789 Self::Access { .. } => uapi::fuse_opcode_FUSE_ACCESS,
2790 Self::Create { .. } => uapi::fuse_opcode_FUSE_CREATE,
2791 Self::Flush(_) => uapi::fuse_opcode_FUSE_FLUSH,
2792 Self::Forget(_) => uapi::fuse_opcode_FUSE_FORGET,
2793 Self::GetAttr => uapi::fuse_opcode_FUSE_GETATTR,
2794 Self::GetXAttr { .. } => uapi::fuse_opcode_FUSE_GETXATTR,
2795 Self::Init { .. } => uapi::fuse_opcode_FUSE_INIT,
2796 Self::Interrupt { .. } => uapi::fuse_opcode_FUSE_INTERRUPT,
2797 Self::ListXAttr(_) => uapi::fuse_opcode_FUSE_LISTXATTR,
2798 Self::Lookup { .. } => uapi::fuse_opcode_FUSE_LOOKUP,
2799 Self::Mkdir { .. } => uapi::fuse_opcode_FUSE_MKDIR,
2800 Self::Mknod { .. } => uapi::fuse_opcode_FUSE_MKNOD,
2801 Self::Link { .. } => uapi::fuse_opcode_FUSE_LINK,
2802 Self::Open { flags, mode } => {
2803 if mode.is_dir() || flags.contains(OpenFlags::DIRECTORY) {
2804 uapi::fuse_opcode_FUSE_OPENDIR
2805 } else {
2806 uapi::fuse_opcode_FUSE_OPEN
2807 }
2808 }
2809 Self::Poll(_) => uapi::fuse_opcode_FUSE_POLL,
2810 Self::Read(_) => uapi::fuse_opcode_FUSE_READ,
2811 Self::Readdir { use_readdirplus, .. } => {
2812 if *use_readdirplus {
2813 uapi::fuse_opcode_FUSE_READDIRPLUS
2814 } else {
2815 uapi::fuse_opcode_FUSE_READDIR
2816 }
2817 }
2818 Self::Readlink => uapi::fuse_opcode_FUSE_READLINK,
2819 Self::Release(_) => uapi::fuse_opcode_FUSE_RELEASE,
2820 Self::ReleaseDir(_) => uapi::fuse_opcode_FUSE_RELEASEDIR,
2821 Self::RemoveXAttr { .. } => uapi::fuse_opcode_FUSE_REMOVEXATTR,
2822 Self::Rename { .. } => uapi::fuse_opcode_FUSE_RENAME2,
2823 Self::Rmdir { .. } => uapi::fuse_opcode_FUSE_RMDIR,
2824 Self::Seek(_) => uapi::fuse_opcode_FUSE_LSEEK,
2825 Self::SetAttr(_) => uapi::fuse_opcode_FUSE_SETATTR,
2826 Self::SetXAttr { .. } => uapi::fuse_opcode_FUSE_SETXATTR,
2827 Self::Statfs => uapi::fuse_opcode_FUSE_STATFS,
2828 Self::Symlink { .. } => uapi::fuse_opcode_FUSE_SYMLINK,
2829 Self::Unlink { .. } => uapi::fuse_opcode_FUSE_UNLINK,
2830 Self::Write { .. } => uapi::fuse_opcode_FUSE_WRITE,
2831 }
2832 }
2833
2834 fn as_running(&self) -> RunningOperationKind {
2835 match self {
2836 Self::Access { .. } => RunningOperationKind::Access,
2837 Self::Create { .. } => RunningOperationKind::Create,
2838 Self::Flush(_) => RunningOperationKind::Flush,
2839 Self::Forget(_) => RunningOperationKind::Forget,
2840 Self::GetAttr => RunningOperationKind::GetAttr,
2841 Self::GetXAttr { getxattr_in, .. } => {
2842 RunningOperationKind::GetXAttr { size: getxattr_in.size }
2843 }
2844 Self::Init { fs } => RunningOperationKind::Init { fs: fs.clone() },
2845 Self::Interrupt { .. } => RunningOperationKind::Interrupt,
2846 Self::ListXAttr(getxattr_in) => {
2847 RunningOperationKind::ListXAttr { size: getxattr_in.size }
2848 }
2849 Self::Lookup { .. } => RunningOperationKind::Lookup,
2850 Self::Mkdir { .. } => RunningOperationKind::Mkdir,
2851 Self::Mknod { .. } => RunningOperationKind::Mknod,
2852 Self::Link { .. } => RunningOperationKind::Link,
2853 Self::Open { flags, mode } => RunningOperationKind::Open {
2854 dir: mode.is_dir() || flags.contains(OpenFlags::DIRECTORY),
2855 },
2856 Self::Poll(_) => RunningOperationKind::Poll,
2857 Self::Read(_) => RunningOperationKind::Read,
2858 Self::Readdir { use_readdirplus, .. } => {
2859 RunningOperationKind::Readdir { use_readdirplus: *use_readdirplus }
2860 }
2861 Self::Readlink => RunningOperationKind::Readlink,
2862 Self::Release(_) => RunningOperationKind::Release { dir: false },
2863 Self::ReleaseDir(_) => RunningOperationKind::Release { dir: true },
2864 Self::RemoveXAttr { .. } => RunningOperationKind::RemoveXAttr,
2865 Self::Rename { .. } => RunningOperationKind::Rename,
2866 Self::Rmdir { .. } => RunningOperationKind::Rmdir,
2867 Self::Seek(_) => RunningOperationKind::Seek,
2868 Self::SetAttr(_) => RunningOperationKind::SetAttr,
2869 Self::SetXAttr { .. } => RunningOperationKind::SetXAttr,
2870 Self::Statfs => RunningOperationKind::Statfs,
2871 Self::Symlink { .. } => RunningOperationKind::Symlink,
2872 Self::Unlink { .. } => RunningOperationKind::Unlink,
2873 Self::Write { .. } => RunningOperationKind::Write,
2874 }
2875 }
2876
2877 fn len(&self) -> usize {
2878 #[derive(Debug, Default)]
2879 struct CountingOutputBuffer {
2880 written: usize,
2881 }
2882
2883 impl Buffer for CountingOutputBuffer {
2884 fn segments_count(&self) -> Result<usize, Errno> {
2885 panic!("Should not be called");
2886 }
2887
2888 fn peek_each_segment(
2889 &mut self,
2890 _callback: &mut PeekBufferSegmentsCallback<'_>,
2891 ) -> Result<(), Errno> {
2892 panic!("Should not be called");
2893 }
2894 }
2895
2896 impl OutputBuffer for CountingOutputBuffer {
2897 fn available(&self) -> usize {
2898 usize::MAX
2899 }
2900
2901 fn bytes_written(&self) -> usize {
2902 self.written
2903 }
2904
2905 fn zero(&mut self) -> Result<usize, Errno> {
2906 panic!("Should not be called");
2907 }
2908
2909 fn write_each(
2910 &mut self,
2911 _callback: &mut OutputBufferCallback<'_>,
2912 ) -> Result<usize, Errno> {
2913 panic!("Should not be called.");
2914 }
2915
2916 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
2917 self.written += buffer.len();
2918 Ok(buffer.len())
2919 }
2920
2921 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
2922 panic!("Should not be called.");
2923 }
2924 }
2925
2926 let mut counting_output_buffer = CountingOutputBuffer::default();
2927 self.serialize(&mut counting_output_buffer).expect("Serialization should not fail");
2928 counting_output_buffer.written
2929 }
2930
2931 fn has_response(&self) -> bool {
2932 !matches!(self, Self::Interrupt { .. } | Self::Forget(_))
2933 }
2934
2935 fn is_async(&self) -> bool {
2936 matches!(self, Self::Init { .. })
2937 }
2938}