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