1use crate::device::DeviceMode;
6use crate::mm::PAGE_SIZE;
7use crate::security::{self, Auditable, PermissionFlags};
8use crate::signals::{SignalInfo, send_standard_signal};
9use crate::task::{CurrentTask, CurrentTaskAndLocked, WaitQueue, Waiter, register_delayed_release};
10use crate::time::utc;
11use crate::vfs::fsverity::FsVerityState;
12use crate::vfs::pipe::{Pipe, PipeHandle};
13use crate::vfs::rw_queue::{RwQueue, RwQueueReadGuard, RwQueueWriteGuard};
14use crate::vfs::socket::SocketHandle;
15use crate::vfs::{
16 DefaultDirEntryOps, DirEntryOps, FileObject, FileObjectState, FileOps, FileSystem,
17 FileSystemHandle, FileWriteGuardState, FsLockDepType, FsStr, FsString, MAX_LFS_FILESIZE,
18 MountInfo, NamespaceNode, OPathOps, RecordLockCommand, RecordLockOwner, RecordLocks,
19 WeakFileHandle, checked_add_offset_and_length, inotify_hook,
20};
21use bitflags::bitflags;
22use fuchsia_runtime::UtcInstant;
23use linux_uapi::{XATTR_SECURITY_PREFIX, XATTR_SYSTEM_PREFIX, XATTR_TRUSTED_PREFIX};
24use once_cell::race::OnceBool;
25use smallvec::SmallVec;
26use starnix_crypt::EncryptionKeyId;
27use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
28use starnix_logging::{log_error, track_stub};
29use starnix_sync::{
30 BeforeFsNodeAppend, DynamicLockDepRwLock, FileOpsCore, FsNodeAppend, FsNodeFlockInfoLock,
31 FsNodeFsVerityLock, FsNodeInfoLevel, FsNodeInfoRecursiveLevel, FsNodeWriteGuardStateLock,
32 FuseFsNodeInfoLevel, LockDepMutex, LockDepReadGuard, LockEqualOrBefore, Locked, Unlocked,
33 allow_subclass,
34};
35use starnix_types::ownership::{Releasable, ReleaseGuard};
36use starnix_types::time::{NANOS_PER_SECOND, timespec_from_time};
37use starnix_uapi::as_any::AsAny;
38use starnix_uapi::auth::{
39 CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, CAP_MKNOD,
40 CAP_SYS_ADMIN, CAP_SYS_RESOURCE, Credentials, FsCred,
41};
42use starnix_uapi::device_id::DeviceId;
43use starnix_uapi::errors::{EACCES, ENOTSUP, EPERM, Errno};
44use starnix_uapi::file_mode::{Access, AccessCheck, FileMode};
45use starnix_uapi::inotify_mask::InotifyMask;
46use starnix_uapi::mount_flags::MountFlags;
47use starnix_uapi::open_flags::OpenFlags;
48use starnix_uapi::resource_limits::Resource;
49use starnix_uapi::seal_flags::SealFlags;
50use starnix_uapi::signals::SIGXFSZ;
51use starnix_uapi::{
52 FALLOC_FL_COLLAPSE_RANGE, FALLOC_FL_INSERT_RANGE, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE,
53 FALLOC_FL_UNSHARE_RANGE, FALLOC_FL_ZERO_RANGE, LOCK_EX, LOCK_NB, LOCK_SH, LOCK_UN,
54 STATX__RESERVED, STATX_ATIME, STATX_ATTR_VERITY, STATX_BASIC_STATS, STATX_BLOCKS, STATX_CTIME,
55 STATX_GID, STATX_INO, STATX_MTIME, STATX_NLINK, STATX_SIZE, STATX_UID, XATTR_USER_PREFIX,
56 errno, error, fsverity_descriptor, gid_t, ino_t, statx, statx_timestamp, timespec, uapi, uid_t,
57};
58use std::sync::atomic::Ordering;
59use std::sync::{Arc, OnceLock, Weak};
60use syncio::zxio_node_attr_has_t;
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum FsNodeLinkBehavior {
64 Allowed,
65 Disallowed,
66}
67
68impl Default for FsNodeLinkBehavior {
69 fn default() -> Self {
70 FsNodeLinkBehavior::Allowed
71 }
72}
73
74pub type AppendLockGuard<'a> = RwQueueReadGuard<'a, FsNodeAppend>;
75pub type AppendLockWriteGuard<'a> = RwQueueWriteGuard<'a, FsNodeAppend>;
76
77bitflags! {
78 pub struct FsNodeFlags: u8 {
79 const IS_PRIVATE = 1 << 0;
80 }
81}
82
83pub struct FsNode {
84 pub ino: ino_t,
86
87 pub flags: FsNodeFlags,
89
90 ops: Box<dyn FsNodeOps>,
95
96 fs: Weak<FileSystem>,
98
99 pub append_lock: RwQueue<FsNodeAppend>,
105
106 info: DynamicLockDepRwLock<FsNodeInfo>,
110
111 rare_data: OnceLock<Box<FsNodeRareData>>,
113
114 pub write_guard_state: LockDepMutex<FileWriteGuardState, FsNodeWriteGuardStateLock>,
116
117 pub fsverity: LockDepMutex<FsVerityState, FsNodeFsVerityLock>,
119
120 pub security_state: security::FsNodeState,
123}
124
125#[derive(Default)]
126struct FsNodeRareData {
127 fifo: OnceLock<PipeHandle>,
131
132 bound_socket: OnceLock<SocketHandle>,
134
135 flock_info: LockDepMutex<FlockInfo, FsNodeFlockInfoLock>,
139
140 record_locks: RecordLocks,
142
143 link_behavior: OnceLock<FsNodeLinkBehavior>,
147
148 watchers: inotify_hook::InotifyWatchers,
150}
151
152impl FsNodeRareData {
153 fn ensure_fifo(&self, current_task: &CurrentTask) -> &PipeHandle {
154 self.fifo.get_or_init(|| {
155 let default_pipe_capacity = (*PAGE_SIZE * 16) as usize;
156 let kernel = current_task.kernel();
157 let max_size = kernel.system_limits.pipe_max_size.load(Ordering::Relaxed);
158 let capacity = if default_pipe_capacity <= max_size
159 || security::is_task_capable_noaudit(current_task, CAP_SYS_RESOURCE)
160 {
161 default_pipe_capacity
162 } else {
163 max_size
164 };
165 Pipe::new(capacity)
166 })
167 }
168}
169
170pub enum FsNodeReleaserAction {}
171impl ReleaserAction<FsNode> for FsNodeReleaserAction {
172 fn release(fs_node: ReleaseGuard<FsNode>) {
173 register_delayed_release(fs_node);
174 }
175}
176pub type FsNodeReleaser = ObjectReleaser<FsNode, FsNodeReleaserAction>;
177pub type FsNodeHandle = Arc<FsNodeReleaser>;
178pub type WeakFsNodeHandle = Weak<FsNodeReleaser>;
179
180#[derive(Debug, Default, Clone, PartialEq)]
181pub struct FsNodeInfo {
182 pub mode: FileMode,
183 pub link_count: usize,
184 pub uid: uid_t,
185 pub gid: gid_t,
186 pub rdev: DeviceId,
187 pub size: usize,
188 pub blksize: usize,
189 pub blocks: usize,
190 pub time_status_change: UtcInstant,
191 pub time_access: UtcInstant,
192 pub time_modify: UtcInstant,
193 pub casefold: bool,
194
195 pub wrapping_key_id: Option<[u8; 16]>,
197
198 pub pending_time_access_update: bool,
203}
204
205impl FsNodeInfo {
206 pub fn new(mode: FileMode, owner: FsCred) -> Self {
207 let now = utc::utc_now();
208 Self {
209 mode,
210 link_count: if mode.is_dir() { 2 } else { 1 },
211 uid: owner.uid,
212 gid: owner.gid,
213 blksize: DEFAULT_BYTES_PER_BLOCK,
214 time_status_change: now,
215 time_access: now,
216 time_modify: now,
217 ..Default::default()
218 }
219 }
220
221 pub fn storage_size(&self) -> usize {
222 self.blksize.saturating_mul(self.blocks)
223 }
224
225 pub fn chmod(&mut self, mode: FileMode) {
226 self.mode = (self.mode & !FileMode::PERMISSIONS) | (mode & FileMode::PERMISSIONS);
227 }
228
229 pub fn chown(&mut self, owner: Option<uid_t>, group: Option<gid_t>) {
230 if let Some(owner) = owner {
231 self.uid = owner;
232 }
233 if let Some(group) = group {
234 self.gid = group;
235 }
236 if self.mode.is_reg() {
238 self.mode &= !FileMode::ISUID;
239 self.clear_sgid_bit();
240 }
241 }
242
243 fn clear_sgid_bit(&mut self) {
244 if self.mode.intersects(FileMode::IXGRP) {
247 self.mode &= !FileMode::ISGID;
248 }
249 }
250
251 fn clear_suid_and_sgid_bits(&mut self) {
252 self.mode &= !FileMode::ISUID;
253 self.clear_sgid_bit();
254 }
255
256 pub fn cred(&self) -> FsCred {
257 FsCred { uid: self.uid, gid: self.gid }
258 }
259
260 pub fn apply_suid_and_sgid(&self, creds: &mut Credentials) {
261 if self.mode.contains(FileMode::ISUID) {
262 creds.euid = self.uid;
263 }
264
265 if self.mode.contains(FileMode::ISGID | FileMode::IXGRP) {
273 creds.egid = self.gid;
274 }
275 }
276}
277
278#[derive(Default)]
279struct FlockInfo {
280 locked_exclusive: Option<bool>,
285 locking_handles: Vec<WeakFileHandle>,
287 wait_queue: WaitQueue,
289}
290
291impl FlockInfo {
292 pub fn retain<F>(&mut self, predicate: F)
295 where
296 F: Fn(&FileObject) -> bool,
297 {
298 if !self.locking_handles.is_empty() {
299 self.locking_handles
300 .retain(|w| if let Some(fh) = w.upgrade() { predicate(&fh) } else { false });
301 if self.locking_handles.is_empty() {
302 self.locked_exclusive = None;
303 self.wait_queue.notify_all();
304 }
305 }
306 }
307}
308
309pub const DEFAULT_BYTES_PER_BLOCK: usize = 512;
311
312pub struct FlockOperation {
313 operation: u32,
314}
315
316impl FlockOperation {
317 pub fn from_flags(operation: u32) -> Result<Self, Errno> {
318 if operation & !(LOCK_SH | LOCK_EX | LOCK_UN | LOCK_NB) != 0 {
319 return error!(EINVAL);
320 }
321 if [LOCK_SH, LOCK_EX, LOCK_UN].iter().filter(|&&o| operation & o == o).count() != 1 {
322 return error!(EINVAL);
323 }
324 Ok(Self { operation })
325 }
326
327 pub fn is_unlock(&self) -> bool {
328 self.operation & LOCK_UN > 0
329 }
330
331 pub fn is_lock_exclusive(&self) -> bool {
332 self.operation & LOCK_EX > 0
333 }
334
335 pub fn is_blocking(&self) -> bool {
336 self.operation & LOCK_NB == 0
337 }
338}
339
340impl FileObject {
341 pub fn flock(
345 &self,
346 locked: &mut Locked<Unlocked>,
347 current_task: &CurrentTask,
348 operation: FlockOperation,
349 ) -> Result<(), Errno> {
350 if self.flags().contains(OpenFlags::PATH) {
351 return error!(EBADF);
352 }
353 security::check_file_lock_access(current_task, self)?;
354 loop {
355 let mut flock_info = self.name.entry.node.ensure_rare_data().flock_info.lock();
356 if operation.is_unlock() {
357 flock_info.retain(|fh| !std::ptr::eq(fh, self));
358 return Ok(());
359 }
360 if flock_info.locked_exclusive.is_none() {
363 flock_info.locked_exclusive = Some(operation.is_lock_exclusive());
364 flock_info.locking_handles.push(self.weak_handle.clone());
365 return Ok(());
366 }
367
368 let file_lock_is_exclusive = flock_info.locked_exclusive == Some(true);
369 let fd_has_lock = flock_info
370 .locking_handles
371 .iter()
372 .find_map(|w| {
373 w.upgrade().and_then(|fh| {
374 if std::ptr::eq(&fh as &FileObject, self) { Some(()) } else { None }
375 })
376 })
377 .is_some();
378
379 if fd_has_lock {
381 if operation.is_lock_exclusive() == file_lock_is_exclusive {
382 return Ok(());
384 } else {
385 flock_info.retain(|fh| !std::ptr::eq(fh, self));
388 continue;
389 }
390 }
391
392 if !file_lock_is_exclusive && !operation.is_lock_exclusive() {
394 flock_info.locking_handles.push(self.weak_handle.clone());
396 return Ok(());
397 }
398
399 if !operation.is_blocking() {
401 return error!(EAGAIN);
402 }
403
404 let waiter = Waiter::new();
407 flock_info.wait_queue.wait_async(&waiter);
408 std::mem::drop(flock_info);
409 waiter.wait(locked, current_task)?;
410 }
411 }
412}
413
414mod inner_flags {
417 #![allow(clippy::bad_bit_mask)] use super::{bitflags, uapi};
421
422 bitflags! {
423 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
424 pub struct StatxFlags: u32 {
425 const AT_SYMLINK_NOFOLLOW = uapi::AT_SYMLINK_NOFOLLOW;
426 const AT_EMPTY_PATH = uapi::AT_EMPTY_PATH;
427 const AT_NO_AUTOMOUNT = uapi::AT_NO_AUTOMOUNT;
428 const AT_STATX_SYNC_AS_STAT = uapi::AT_STATX_SYNC_AS_STAT;
429 const AT_STATX_FORCE_SYNC = uapi::AT_STATX_FORCE_SYNC;
430 const AT_STATX_DONT_SYNC = uapi::AT_STATX_DONT_SYNC;
431 const STATX_ATTR_VERITY = uapi::STATX_ATTR_VERITY;
432 }
433 }
434}
435
436pub use inner_flags::StatxFlags;
437
438#[derive(Copy, Clone, Debug, PartialEq, Eq)]
439pub enum UnlinkKind {
440 Directory,
442
443 NonDirectory,
445}
446
447pub enum SymlinkTarget {
448 Path(FsString),
449 Node(NamespaceNode),
450}
451
452#[derive(Clone, Copy, PartialEq, Eq)]
453pub enum XattrOp {
454 Set,
456 Create,
458 Replace,
460}
461
462impl XattrOp {
463 pub fn into_flags(self) -> u32 {
464 match self {
465 Self::Set => 0,
466 Self::Create => uapi::XATTR_CREATE,
467 Self::Replace => uapi::XATTR_REPLACE,
468 }
469 }
470}
471
472#[derive(Clone, Debug, PartialEq)]
474pub enum ValueOrSize<T> {
475 Value(T),
476 Size(usize),
477}
478
479impl<T> ValueOrSize<T> {
480 pub fn map<F, U>(self, f: F) -> ValueOrSize<U>
481 where
482 F: FnOnce(T) -> U,
483 {
484 match self {
485 Self::Size(s) => ValueOrSize::Size(s),
486 Self::Value(v) => ValueOrSize::Value(f(v)),
487 }
488 }
489
490 #[cfg(test)]
491 pub fn unwrap(self) -> T {
492 match self {
493 Self::Size(_) => panic!("Unwrap ValueOrSize that is a Size"),
494 Self::Value(v) => v,
495 }
496 }
497}
498
499impl<T> From<T> for ValueOrSize<T> {
500 fn from(t: T) -> Self {
501 Self::Value(t)
502 }
503}
504
505#[derive(Copy, Clone, Eq, PartialEq, Debug)]
506pub enum FallocMode {
507 Allocate { keep_size: bool },
508 PunchHole,
509 Collapse,
510 Zero { keep_size: bool },
511 InsertRange,
512 UnshareRange,
513}
514
515impl FallocMode {
516 pub fn from_bits(mode: u32) -> Option<Self> {
517 if mode == 0 {
519 Some(Self::Allocate { keep_size: false })
520 } else if mode == FALLOC_FL_KEEP_SIZE {
521 Some(Self::Allocate { keep_size: true })
522 } else if mode == FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE {
523 Some(Self::PunchHole)
524 } else if mode == FALLOC_FL_COLLAPSE_RANGE {
525 Some(Self::Collapse)
526 } else if mode == FALLOC_FL_ZERO_RANGE {
527 Some(Self::Zero { keep_size: false })
528 } else if mode == FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE {
529 Some(Self::Zero { keep_size: true })
530 } else if mode == FALLOC_FL_INSERT_RANGE {
531 Some(Self::InsertRange)
532 } else if mode == FALLOC_FL_UNSHARE_RANGE {
533 Some(Self::UnshareRange)
534 } else {
535 None
536 }
537 }
538}
539
540#[derive(Debug, Copy, Clone, PartialEq)]
541pub enum CheckAccessReason {
542 Access,
543 Chdir,
544 Chroot,
545 Exec,
546 ChangeTimestamps { now: bool },
547 InternalPermissionChecks,
548}
549
550pub type LookupVec<T> = SmallVec<[T; 8]>;
551
552pub trait FsNodeOps: Send + Sync + AsAny + 'static {
553 fn check_access(
555 &self,
556 _locked: &mut Locked<FileOpsCore>,
557 node: &FsNode,
558 current_task: &CurrentTask,
559 access: security::PermissionFlags,
560 info: &DynamicLockDepRwLock<FsNodeInfo>,
561 reason: CheckAccessReason,
562 audit_context: security::Auditable<'_>,
563 ) -> Result<(), Errno> {
564 node.default_check_access_impl(current_task, access, reason, info.read(), audit_context)
565 }
566
567 fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
570 Box::new(DefaultDirEntryOps)
571 }
572
573 fn create_file_ops(
578 &self,
579 locked: &mut Locked<FileOpsCore>,
580 node: &FsNode,
581 _current_task: &CurrentTask,
582 flags: OpenFlags,
583 ) -> Result<Box<dyn FileOps>, Errno>;
584
585 fn lookup(
590 &self,
591 _locked: &mut Locked<FileOpsCore>,
592 _node: &FsNode,
593 _current_task: &CurrentTask,
594 name: &FsStr,
595 ) -> Result<FsNodeHandle, Errno> {
596 error!(ENOENT, format!("looking for {name}"))
599 }
600
601 fn has_lookup_pipelined(&self) -> bool {
603 false
604 }
605
606 fn lookup_pipelined(
610 &self,
611 _locked: &mut Locked<FileOpsCore>,
612 _node: &FsNode,
613 _current_task: &CurrentTask,
614 _names: &[&FsStr],
615 ) -> LookupVec<Result<FsNodeHandle, Errno>> {
616 panic!("has_lookup_pipelined should be false");
617 }
618
619 fn mknod(
627 &self,
628 locked: &mut Locked<FileOpsCore>,
629 _node: &FsNode,
630 _current_task: &CurrentTask,
631 _name: &FsStr,
632 _mode: FileMode,
633 _dev: DeviceId,
634 _owner: FsCred,
635 ) -> Result<FsNodeHandle, Errno>;
636
637 fn mkdir(
639 &self,
640 locked: &mut Locked<FileOpsCore>,
641 _node: &FsNode,
642 _current_task: &CurrentTask,
643 _name: &FsStr,
644 _mode: FileMode,
645 _owner: FsCred,
646 ) -> Result<FsNodeHandle, Errno>;
647
648 fn create_symlink(
650 &self,
651 locked: &mut Locked<FileOpsCore>,
652 _node: &FsNode,
653 _current_task: &CurrentTask,
654 _name: &FsStr,
655 _target: &FsStr,
656 _owner: FsCred,
657 ) -> Result<FsNodeHandle, Errno>;
658
659 fn create_tmpfile(
665 &self,
666 _node: &FsNode,
667 _current_task: &CurrentTask,
668 _mode: FileMode,
669 _owner: FsCred,
670 ) -> Result<FsNodeHandle, Errno> {
671 error!(EOPNOTSUPP)
672 }
673
674 fn readlink(
676 &self,
677 _locked: &mut Locked<FileOpsCore>,
678 _node: &FsNode,
679 _current_task: &CurrentTask,
680 ) -> Result<SymlinkTarget, Errno> {
681 error!(EINVAL)
682 }
683
684 fn link(
686 &self,
687 _locked: &mut Locked<FileOpsCore>,
688 _node: &FsNode,
689 _current_task: &CurrentTask,
690 _name: &FsStr,
691 _child: &FsNodeHandle,
692 ) -> Result<(), Errno> {
693 error!(EPERM)
694 }
695
696 fn unlink(
701 &self,
702 locked: &mut Locked<FileOpsCore>,
703 _node: &FsNode,
704 _current_task: &CurrentTask,
705 _name: &FsStr,
706 _child: &FsNodeHandle,
707 ) -> Result<(), Errno>;
708
709 fn append_lock_read<'a>(
712 &'a self,
713 locked: &'a mut Locked<BeforeFsNodeAppend>,
714 node: &'a FsNode,
715 current_task: &CurrentTask,
716 ) -> Result<(AppendLockGuard<'a>, &'a mut Locked<FsNodeAppend>), Errno> {
717 return node.append_lock.read_and(locked, current_task);
718 }
719
720 fn append_lock_write<'a>(
722 &'a self,
723 locked: &'a mut Locked<BeforeFsNodeAppend>,
724 node: &'a FsNode,
725 current_task: &CurrentTask,
726 ) -> Result<(AppendLockWriteGuard<'a>, &'a mut Locked<FsNodeAppend>), Errno> {
727 return node.append_lock.write_and(locked, current_task);
728 }
729
730 fn truncate(
732 &self,
733 _locked: &mut Locked<FileOpsCore>,
734 _guard: &AppendLockWriteGuard<'_>,
735 _node: &FsNode,
736 _current_task: &CurrentTask,
737 _length: u64,
738 ) -> Result<(), Errno> {
739 error!(EINVAL)
740 }
741
742 fn allocate(
744 &self,
745 _locked: &mut Locked<FileOpsCore>,
746 _guard: &AppendLockWriteGuard<'_>,
747 _node: &FsNode,
748 _current_task: &CurrentTask,
749 _mode: FallocMode,
750 _offset: u64,
751 _length: u64,
752 ) -> Result<(), Errno> {
753 error!(EINVAL)
754 }
755
756 fn initial_info(&self, _info: &mut FsNodeInfo) {}
761
762 fn fetch_and_refresh_info<'a>(
773 &self,
774 _locked: &mut Locked<FileOpsCore>,
775 _node: &FsNode,
776 _current_task: &CurrentTask,
777 info: &'a DynamicLockDepRwLock<FsNodeInfo>,
778 ) -> Result<LockDepReadGuard<'a, FsNodeInfo>, Errno> {
779 Ok(info.read())
780 }
781
782 fn sync(&self, _node: &FsNode, _current_task: &CurrentTask) -> Result<(), Errno> {
784 Ok(())
785 }
786
787 fn update_attributes(
789 &self,
790 _locked: &mut Locked<FileOpsCore>,
791 _node: &FsNode,
792 _current_task: &CurrentTask,
793 _info: &FsNodeInfo,
794 _has: zxio_node_attr_has_t,
795 ) -> Result<(), Errno> {
796 Ok(())
797 }
798
799 fn get_xattr(
805 &self,
806 _locked: &mut Locked<FileOpsCore>,
807 _node: &FsNode,
808 _current_task: &CurrentTask,
809 _name: &FsStr,
810 _max_size: usize,
811 ) -> Result<ValueOrSize<FsString>, Errno> {
812 error!(ENOTSUP)
813 }
814
815 fn set_xattr(
817 &self,
818 _locked: &mut Locked<FileOpsCore>,
819 _node: &FsNode,
820 _current_task: &CurrentTask,
821 _name: &FsStr,
822 _value: &FsStr,
823 _op: XattrOp,
824 ) -> Result<(), Errno> {
825 error!(ENOTSUP)
826 }
827
828 fn remove_xattr(
829 &self,
830 _locked: &mut Locked<FileOpsCore>,
831 _node: &FsNode,
832 _current_task: &CurrentTask,
833 _name: &FsStr,
834 ) -> Result<(), Errno> {
835 error!(ENOTSUP)
836 }
837
838 fn list_xattrs(
842 &self,
843 _locked: &mut Locked<FileOpsCore>,
844 _node: &FsNode,
845 _current_task: &CurrentTask,
846 _max_size: usize,
847 ) -> Result<ValueOrSize<Vec<FsString>>, Errno> {
848 error!(ENOTSUP)
849 }
850
851 fn forget(
853 self: Box<Self>,
854 _locked: &mut Locked<FileOpsCore>,
855 _current_task: &CurrentTask,
856 _info: FsNodeInfo,
857 ) -> Result<(), Errno> {
858 Ok(())
859 }
860
861 fn enable_fsverity(
869 &self,
870 _locked: &mut Locked<FileOpsCore>,
871 _node: &FsNode,
872 _current_task: &CurrentTask,
873 _descriptor: &fsverity_descriptor,
874 ) -> Result<(), Errno> {
875 error!(ENOTSUP)
876 }
877
878 fn get_fsverity_descriptor(&self, _log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
880 error!(ENOTSUP)
881 }
882
883 fn node_key(&self, node: &FsNode) -> ino_t {
888 node.ino
889 }
890
891 fn get_size(
893 &self,
894 locked: &mut Locked<FileOpsCore>,
895 node: &FsNode,
896 current_task: &CurrentTask,
897 ) -> Result<usize, Errno> {
898 let info = node.fetch_and_refresh_info(locked, current_task)?;
899 Ok(info.size.try_into().map_err(|_| errno!(EINVAL))?)
900 }
901}
902
903impl<T> From<T> for Box<dyn FsNodeOps>
904where
905 T: FsNodeOps,
906{
907 fn from(ops: T) -> Box<dyn FsNodeOps> {
908 Box::new(ops)
909 }
910}
911
912#[macro_export]
915macro_rules! fs_node_impl_symlink {
916 () => {
917 $crate::vfs::fs_node_impl_not_dir!();
918
919 fn create_file_ops(
920 &self,
921 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
922 node: &$crate::vfs::FsNode,
923 _current_task: &CurrentTask,
924 _flags: starnix_uapi::open_flags::OpenFlags,
925 ) -> Result<Box<dyn $crate::vfs::FileOps>, starnix_uapi::errors::Errno> {
926 assert!(node.is_lnk());
927 unreachable!("Symlink nodes cannot be opened.");
928 }
929 };
930}
931
932#[macro_export]
933macro_rules! fs_node_impl_dir_readonly {
934 () => {
935 fn check_access(
936 &self,
937 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
938 node: &$crate::vfs::FsNode,
939 current_task: &$crate::task::CurrentTask,
940 permission_flags: $crate::security::PermissionFlags,
941 info: &starnix_sync::DynamicLockDepRwLock<$crate::vfs::FsNodeInfo>,
942 reason: $crate::vfs::CheckAccessReason,
943 audit_context: $crate::security::Auditable<'_>,
944 ) -> Result<(), starnix_uapi::errors::Errno> {
945 let access = permission_flags.as_access();
946 if access.contains(starnix_uapi::file_mode::Access::WRITE) {
947 return starnix_uapi::error!(
948 EROFS,
949 format!("check_access failed: read-only directory")
950 );
951 }
952 node.default_check_access_impl(
953 current_task,
954 permission_flags,
955 reason,
956 info.read(),
957 audit_context,
958 )
959 }
960
961 fn mkdir(
962 &self,
963 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
964 _node: &$crate::vfs::FsNode,
965 _current_task: &$crate::task::CurrentTask,
966 name: &$crate::vfs::FsStr,
967 _mode: starnix_uapi::file_mode::FileMode,
968 _owner: starnix_uapi::auth::FsCred,
969 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
970 starnix_uapi::error!(EROFS, format!("mkdir failed: {:?}", name))
971 }
972
973 fn mknod(
974 &self,
975 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
976 _node: &$crate::vfs::FsNode,
977 _current_task: &$crate::task::CurrentTask,
978 name: &$crate::vfs::FsStr,
979 _mode: starnix_uapi::file_mode::FileMode,
980 _dev: starnix_uapi::device_id::DeviceId,
981 _owner: starnix_uapi::auth::FsCred,
982 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
983 starnix_uapi::error!(EROFS, format!("mknod failed: {:?}", name))
984 }
985
986 fn create_symlink(
987 &self,
988 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
989 _node: &$crate::vfs::FsNode,
990 _current_task: &$crate::task::CurrentTask,
991 name: &$crate::vfs::FsStr,
992 _target: &$crate::vfs::FsStr,
993 _owner: starnix_uapi::auth::FsCred,
994 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
995 starnix_uapi::error!(EROFS, format!("symlink failed: {:?}", name))
996 }
997
998 fn link(
999 &self,
1000 _locked: &mut Locked<FileOpsCore>,
1001 _node: &$crate::vfs::FsNode,
1002 _current_task: &$crate::task::CurrentTask,
1003 name: &$crate::vfs::FsStr,
1004 _child: &$crate::vfs::FsNodeHandle,
1005 ) -> Result<(), starnix_uapi::errors::Errno> {
1006 starnix_uapi::error!(EROFS, format!("link failed: {:?}", name))
1007 }
1008
1009 fn unlink(
1010 &self,
1011 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1012 _node: &$crate::vfs::FsNode,
1013 _current_task: &$crate::task::CurrentTask,
1014 name: &$crate::vfs::FsStr,
1015 _child: &$crate::vfs::FsNodeHandle,
1016 ) -> Result<(), starnix_uapi::errors::Errno> {
1017 starnix_uapi::error!(EROFS, format!("unlink failed: {:?}", name))
1018 }
1019 };
1020}
1021
1022pub trait XattrStorage {
1027 fn get_xattr(&self, locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<FsString, Errno>;
1029
1030 fn set_xattr(
1032 &self,
1033 locked: &mut Locked<FileOpsCore>,
1034 name: &FsStr,
1035 value: &FsStr,
1036 op: XattrOp,
1037 ) -> Result<(), Errno>;
1038
1039 fn remove_xattr(&self, locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<(), Errno>;
1041
1042 fn list_xattrs(&self, locked: &mut Locked<FileOpsCore>) -> Result<Vec<FsString>, Errno>;
1044}
1045
1046#[macro_export]
1068macro_rules! fs_node_impl_xattr_delegate {
1069 ($self:ident, $delegate:expr) => {
1070 fn get_xattr(
1071 &$self,
1072 locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1073 _node: &FsNode,
1074 _current_task: &CurrentTask,
1075 name: &$crate::vfs::FsStr,
1076 _size: usize,
1077 ) -> Result<$crate::vfs::ValueOrSize<$crate::vfs::FsString>, starnix_uapi::errors::Errno> {
1078 Ok($delegate.get_xattr(locked, name)?.into())
1079 }
1080
1081 fn set_xattr(
1082 &$self,
1083 locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1084 _node: &FsNode,
1085 _current_task: &CurrentTask,
1086 name: &$crate::vfs::FsStr,
1087 value: &$crate::vfs::FsStr,
1088 op: $crate::vfs::XattrOp,
1089 ) -> Result<(), starnix_uapi::errors::Errno> {
1090 $delegate.set_xattr(locked, name, value, op)
1091 }
1092
1093 fn remove_xattr(
1094 &$self,
1095 locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1096 _node: &FsNode,
1097 _current_task: &CurrentTask,
1098 name: &$crate::vfs::FsStr,
1099 ) -> Result<(), starnix_uapi::errors::Errno> {
1100 $delegate.remove_xattr(locked, name)
1101 }
1102
1103 fn list_xattrs(
1104 &$self,
1105 locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1106 _node: &FsNode,
1107 _current_task: &CurrentTask,
1108 _size: usize,
1109 ) -> Result<$crate::vfs::ValueOrSize<Vec<$crate::vfs::FsString>>, starnix_uapi::errors::Errno> {
1110 Ok($delegate.list_xattrs(locked)?.into())
1111 }
1112 };
1113}
1114
1115#[macro_export]
1117macro_rules! fs_node_impl_not_dir {
1118 () => {
1119 fn lookup(
1120 &self,
1121 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1122 _node: &$crate::vfs::FsNode,
1123 _current_task: &$crate::task::CurrentTask,
1124 _name: &$crate::vfs::FsStr,
1125 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
1126 starnix_uapi::error!(ENOTDIR)
1127 }
1128
1129 fn mknod(
1130 &self,
1131 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1132 _node: &$crate::vfs::FsNode,
1133 _current_task: &$crate::task::CurrentTask,
1134 _name: &$crate::vfs::FsStr,
1135 _mode: starnix_uapi::file_mode::FileMode,
1136 _dev: starnix_uapi::device_id::DeviceId,
1137 _owner: starnix_uapi::auth::FsCred,
1138 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
1139 starnix_uapi::error!(ENOTDIR)
1140 }
1141
1142 fn mkdir(
1143 &self,
1144 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1145 _node: &$crate::vfs::FsNode,
1146 _current_task: &$crate::task::CurrentTask,
1147 _name: &$crate::vfs::FsStr,
1148 _mode: starnix_uapi::file_mode::FileMode,
1149 _owner: starnix_uapi::auth::FsCred,
1150 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
1151 starnix_uapi::error!(ENOTDIR)
1152 }
1153
1154 fn create_symlink(
1155 &self,
1156 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1157 _node: &$crate::vfs::FsNode,
1158 _current_task: &$crate::task::CurrentTask,
1159 _name: &$crate::vfs::FsStr,
1160 _target: &$crate::vfs::FsStr,
1161 _owner: starnix_uapi::auth::FsCred,
1162 ) -> Result<$crate::vfs::FsNodeHandle, starnix_uapi::errors::Errno> {
1163 starnix_uapi::error!(ENOTDIR)
1164 }
1165
1166 fn unlink(
1167 &self,
1168 _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
1169 _node: &$crate::vfs::FsNode,
1170 _current_task: &$crate::task::CurrentTask,
1171 _name: &$crate::vfs::FsStr,
1172 _child: &$crate::vfs::FsNodeHandle,
1173 ) -> Result<(), starnix_uapi::errors::Errno> {
1174 starnix_uapi::error!(ENOTDIR)
1175 }
1176 };
1177}
1178
1179#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1180pub enum TimeUpdateType {
1181 Now,
1182 Omit,
1183 Time(UtcInstant),
1184}
1185
1186pub use fs_node_impl_dir_readonly;
1188pub use fs_node_impl_not_dir;
1189pub use fs_node_impl_symlink;
1190pub use fs_node_impl_xattr_delegate;
1191
1192pub struct SpecialNode;
1193
1194impl FsNodeOps for SpecialNode {
1195 fs_node_impl_not_dir!();
1196
1197 fn create_file_ops(
1198 &self,
1199 _locked: &mut Locked<FileOpsCore>,
1200 _node: &FsNode,
1201 _current_task: &CurrentTask,
1202 _flags: OpenFlags,
1203 ) -> Result<Box<dyn FileOps>, Errno> {
1204 unreachable!("Special nodes cannot be opened.");
1205 }
1206}
1207
1208impl FsNode {
1209 pub fn is_private(&self) -> bool {
1212 self.flags.contains(FsNodeFlags::IS_PRIVATE)
1213 }
1214
1215 pub fn new_uncached(
1220 ino: ino_t,
1221 ops: impl Into<Box<dyn FsNodeOps>>,
1222 fs: &FileSystemHandle,
1223 info: FsNodeInfo,
1224 flags: FsNodeFlags,
1225 ) -> FsNodeHandle {
1226 let ops = ops.into();
1227 FsNodeHandle::new(Self::new_internal(ino, ops, Arc::downgrade(fs), info, flags).into())
1228 }
1229
1230 fn new_internal(
1231 ino: ino_t,
1232 ops: Box<dyn FsNodeOps>,
1233 fs: Weak<FileSystem>,
1234 info: FsNodeInfo,
1235 flags: FsNodeFlags,
1236 ) -> Self {
1237 let mut info = info;
1239 ops.initial_info(&mut info);
1240
1241 let fs_lockdep_type =
1242 fs.upgrade().map(|fs| fs.fs_lockdep_type()).unwrap_or(FsLockDepType::Normal);
1243 let info_lock = match fs_lockdep_type {
1244 FsLockDepType::Normal => DynamicLockDepRwLock::new::<FsNodeInfoLevel>(info),
1245 FsLockDepType::Fuse => DynamicLockDepRwLock::new::<FuseFsNodeInfoLevel>(info),
1246 FsLockDepType::Recursive => DynamicLockDepRwLock::new::<FsNodeInfoRecursiveLevel>(info),
1247 };
1248
1249 #[allow(clippy::let_and_return)]
1251 {
1252 let result = Self {
1253 ino,
1254 flags,
1255 ops,
1256 fs,
1257 info: info_lock,
1258 append_lock: Default::default(),
1259 rare_data: Default::default(),
1260 write_guard_state: Default::default(),
1261 fsverity: Default::default(),
1262 security_state: Default::default(),
1263 };
1264 #[cfg(any(test, debug_assertions))]
1265 {
1266 #[allow(
1267 clippy::undocumented_unsafe_blocks,
1268 reason = "Force documented unsafe blocks in Starnix"
1269 )]
1270 let locked = unsafe { Unlocked::new() };
1271 let _l1 = result.append_lock.read_for_lock_ordering(locked);
1272 let _l2 = result.info.read();
1273 let _l3 = result.write_guard_state.lock();
1274 let _l4 = result.fsverity.lock();
1275 }
1276 result
1277 }
1278 }
1279
1280 pub fn fs(&self) -> FileSystemHandle {
1281 self.fs.upgrade().expect("FileSystem did not live long enough")
1282 }
1283
1284 pub fn ops(&self) -> &dyn FsNodeOps {
1285 self.ops.as_ref()
1286 }
1287
1288 pub fn fail_if_locked(
1292 &self,
1293 _current_task: &CurrentTask,
1294 node_info: &FsNodeInfo,
1295 ) -> Result<(), Errno> {
1296 if let Some(wrapping_key_id) = node_info.wrapping_key_id {
1297 let crypt_service = self.fs().crypt_service().ok_or_else(|| errno!(ENOKEY))?;
1298 if !crypt_service.contains_key(EncryptionKeyId::from(wrapping_key_id)) {
1299 return error!(ENOKEY);
1300 }
1301 }
1302 Ok(())
1303 }
1304
1305 pub fn downcast_ops<T>(&self) -> Option<&T>
1307 where
1308 T: 'static,
1309 {
1310 self.ops().as_any().downcast_ref::<T>()
1311 }
1312
1313 pub fn on_file_closed(&self, file: &FileObjectState) {
1314 if let Some(rare_data) = self.rare_data.get() {
1315 let mut flock_info = rare_data.flock_info.lock();
1316 flock_info.retain(|_| true);
1319 }
1320 self.record_lock_release(RecordLockOwner::FileObject(file.id));
1321 }
1322
1323 pub fn record_lock(
1324 &self,
1325 locked: &mut Locked<Unlocked>,
1326 current_task: &CurrentTask,
1327 file: &FileObject,
1328 cmd: RecordLockCommand,
1329 flock: uapi::flock,
1330 ) -> Result<Option<uapi::flock>, Errno> {
1331 self.ensure_rare_data().record_locks.lock(locked, current_task, file, cmd, flock)
1332 }
1333
1334 pub fn record_lock_release(&self, owner: RecordLockOwner) {
1336 if let Some(rare_data) = self.rare_data.get() {
1337 rare_data.record_locks.release_locks(owner);
1338 }
1339 }
1340
1341 pub fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
1342 self.ops().create_dir_entry_ops()
1343 }
1344
1345 pub fn create_file_ops<L>(
1346 &self,
1347 locked: &mut Locked<L>,
1348 current_task: &CurrentTask,
1349 flags: OpenFlags,
1350 ) -> Result<Box<dyn FileOps>, Errno>
1351 where
1352 L: LockEqualOrBefore<FileOpsCore>,
1353 {
1354 let locked = locked.cast_locked::<FileOpsCore>();
1355 self.ops().create_file_ops(locked, self, current_task, flags)
1356 }
1357
1358 pub fn open(
1359 &self,
1360 locked: &mut Locked<Unlocked>,
1361 current_task: &CurrentTask,
1362 namespace_node: &NamespaceNode,
1363 flags: OpenFlags,
1364 access_check: AccessCheck,
1365 ) -> Result<Box<dyn FileOps>, Errno> {
1366 if flags.contains(OpenFlags::PATH) {
1369 return Ok(Box::new(OPathOps::new()));
1370 }
1371
1372 let access = access_check.resolve(flags);
1373 if access.is_nontrivial() {
1374 if flags.contains(OpenFlags::NOATIME) {
1375 self.check_o_noatime_allowed(current_task)?;
1376 }
1377
1378 let mut permission_flags = PermissionFlags::from(access);
1382
1383 if flags.contains(OpenFlags::APPEND) {
1386 permission_flags |= security::PermissionFlags::APPEND;
1387 }
1388
1389 self.check_access(
1390 locked,
1391 current_task,
1392 &namespace_node.mount,
1393 permission_flags,
1394 CheckAccessReason::InternalPermissionChecks,
1395 namespace_node,
1396 )?;
1397 }
1398
1399 let (mode, rdev) = {
1400 let info = self.info();
1403 (info.mode, info.rdev)
1404 };
1405
1406 match mode & FileMode::IFMT {
1407 FileMode::IFCHR => {
1408 if namespace_node.mount.flags().contains(MountFlags::NODEV) {
1409 return error!(EACCES);
1410 }
1411 current_task.kernel().open_device(
1412 locked,
1413 current_task,
1414 namespace_node,
1415 flags,
1416 rdev,
1417 DeviceMode::Char,
1418 )
1419 }
1420 FileMode::IFBLK => {
1421 if namespace_node.mount.flags().contains(MountFlags::NODEV) {
1422 return error!(EACCES);
1423 }
1424 current_task.kernel().open_device(
1425 locked,
1426 current_task,
1427 namespace_node,
1428 flags,
1429 rdev,
1430 DeviceMode::Block,
1431 )
1432 }
1433 FileMode::IFIFO => Pipe::open(locked, current_task, self.fifo(current_task), flags),
1434 FileMode::IFSOCK => error!(ENXIO),
1436 _ => self.create_file_ops(locked, current_task, flags),
1437 }
1438 }
1439
1440 pub fn lookup<L>(
1441 &self,
1442 locked: &mut Locked<L>,
1443 current_task: &CurrentTask,
1444 mount: &MountInfo,
1445 name: &FsStr,
1446 ) -> Result<FsNodeHandle, Errno>
1447 where
1448 L: LockEqualOrBefore<FileOpsCore>,
1449 {
1450 self.check_access(
1451 locked,
1452 current_task,
1453 mount,
1454 Access::EXEC,
1455 CheckAccessReason::InternalPermissionChecks,
1456 &[Auditable::Name(name), std::panic::Location::caller().into()],
1457 )?;
1458 let locked = locked.cast_locked::<FileOpsCore>();
1459 self.ops().lookup(locked, self, current_task, name)
1460 }
1461
1462 pub fn create_node<L>(
1463 &self,
1464 locked: &mut Locked<L>,
1465 current_task: &CurrentTask,
1466 mount: &MountInfo,
1467 name: &FsStr,
1468 mut mode: FileMode,
1469 dev: DeviceId,
1470 mut owner: FsCred,
1471 ) -> Result<FsNodeHandle, Errno>
1472 where
1473 L: LockEqualOrBefore<FileOpsCore>,
1474 {
1475 assert!(
1476 !matches!(mode.fmt(), FileMode::EMPTY | FileMode::IFLNK),
1477 "create_node with missing or symlink node type"
1478 );
1479
1480 self.check_access(
1481 locked,
1482 current_task,
1483 mount,
1484 Access::WRITE,
1485 CheckAccessReason::InternalPermissionChecks,
1486 security::Auditable::Name(name),
1487 )?;
1488
1489 if mode.is_dir() {
1490 security::check_fs_node_mkdir_access(current_task, self, mode, name)?;
1494 } else {
1495 match mode.fmt() {
1504 FileMode::IFREG | FileMode::IFIFO | FileMode::IFSOCK => (),
1505 FileMode::IFCHR if dev == DeviceId::NONE => (),
1506 _ => security::check_task_capable(current_task, CAP_MKNOD)?,
1507 }
1508
1509 if mode.is_reg() {
1510 security::check_fs_node_create_access(current_task, self, mode, name)?;
1511 } else {
1512 security::check_fs_node_mknod_access(current_task, self, mode, name, dev)?;
1513 }
1514 }
1515
1516 self.update_metadata_for_child(current_task, &mut mode, &mut owner);
1518
1519 let locked = locked.cast_locked::<FileOpsCore>();
1521 let new_node = if mode.is_dir() {
1522 self.ops().mkdir(locked, self, current_task, name, mode, owner)?
1523 } else {
1524 self.ops().mknod(locked, self, current_task, name, mode, dev, owner)?
1525 };
1526
1527 self.init_new_node_security_on_create(locked, current_task, &new_node, name)?;
1529
1530 Ok(new_node)
1531 }
1532
1533 pub fn create_symlink<L>(
1534 &self,
1535 locked: &mut Locked<L>,
1536 current_task: &CurrentTask,
1537 mount: &MountInfo,
1538 name: &FsStr,
1539 target: &FsStr,
1540 owner: FsCred,
1541 ) -> Result<FsNodeHandle, Errno>
1542 where
1543 L: LockEqualOrBefore<FileOpsCore>,
1544 {
1545 self.check_access(
1546 locked,
1547 current_task,
1548 mount,
1549 Access::WRITE,
1550 CheckAccessReason::InternalPermissionChecks,
1551 security::Auditable::Name(name),
1552 )?;
1553 security::check_fs_node_symlink_access(current_task, self, name, target)?;
1554
1555 let locked = locked.cast_locked::<FileOpsCore>();
1556 let new_node =
1557 self.ops().create_symlink(locked, self, current_task, name, target, owner)?;
1558
1559 self.init_new_node_security_on_create(locked, current_task, &new_node, name)?;
1560
1561 Ok(new_node)
1562 }
1563
1564 fn init_new_node_security_on_create<L>(
1569 &self,
1570 locked: &mut Locked<L>,
1571 current_task: &CurrentTask,
1572 new_node: &FsNode,
1573 name: &FsStr,
1574 ) -> Result<(), Errno>
1575 where
1576 L: LockEqualOrBefore<FileOpsCore>,
1577 {
1578 let locked = locked.cast_locked::<FileOpsCore>();
1579 security::fs_node_init_on_create(current_task, &new_node, self, name)?
1580 .map(|xattr| {
1581 match new_node.ops().set_xattr(
1582 locked,
1583 &new_node,
1584 current_task,
1585 xattr.name,
1586 xattr.value.as_slice().into(),
1587 XattrOp::Create,
1588 ) {
1589 Err(e) => {
1590 if e.code == ENOTSUP {
1591 Ok(())
1594 } else {
1595 Err(e)
1596 }
1597 }
1598 result => result,
1599 }
1600 })
1601 .unwrap_or_else(|| Ok(()))
1602 }
1603
1604 pub fn create_tmpfile<L>(
1605 &self,
1606 locked: &mut Locked<L>,
1607 current_task: &CurrentTask,
1608 mount: &MountInfo,
1609 mut mode: FileMode,
1610 mut owner: FsCred,
1611 link_behavior: FsNodeLinkBehavior,
1612 ) -> Result<FsNodeHandle, Errno>
1613 where
1614 L: LockEqualOrBefore<FileOpsCore>,
1615 {
1616 self.check_access(
1617 locked,
1618 current_task,
1619 mount,
1620 Access::WRITE,
1621 CheckAccessReason::InternalPermissionChecks,
1622 security::Auditable::Location(std::panic::Location::caller()),
1623 )?;
1624 self.update_metadata_for_child(current_task, &mut mode, &mut owner);
1625 let node = self.ops().create_tmpfile(self, current_task, mode, owner)?;
1626 self.init_new_node_security_on_create(locked, current_task, &node, "".into())?;
1627 if link_behavior == FsNodeLinkBehavior::Disallowed {
1628 node.ensure_rare_data().link_behavior.set(link_behavior).unwrap();
1629 }
1630 Ok(node)
1631 }
1632
1633 pub fn readlink<L>(
1636 &self,
1637 locked: &mut Locked<L>,
1638 current_task: &CurrentTask,
1639 ) -> Result<SymlinkTarget, Errno>
1640 where
1641 L: LockEqualOrBefore<FileOpsCore>,
1642 {
1643 security::check_fs_node_read_link_access(current_task, self)?;
1645 self.ops().readlink(locked.cast_locked::<FileOpsCore>(), self, current_task)
1646 }
1647
1648 pub fn link<L>(
1649 &self,
1650 locked: &mut Locked<L>,
1651 current_task: &CurrentTask,
1652 mount: &MountInfo,
1653 name: &FsStr,
1654 child: &FsNodeHandle,
1655 ) -> Result<FsNodeHandle, Errno>
1656 where
1657 L: LockEqualOrBefore<FileOpsCore>,
1658 {
1659 self.check_access(
1660 locked,
1661 current_task,
1662 mount,
1663 Access::WRITE,
1664 CheckAccessReason::InternalPermissionChecks,
1665 security::Auditable::Location(std::panic::Location::caller()),
1666 )?;
1667
1668 if child.is_dir() {
1669 return error!(EPERM);
1670 }
1671
1672 if let Some(child_rare_data) = child.rare_data.get() {
1673 if matches!(child_rare_data.link_behavior.get(), Some(FsNodeLinkBehavior::Disallowed)) {
1674 return error!(ENOENT);
1675 }
1676 }
1677
1678 let (child_uid, mode) = {
1685 let info = child.info();
1686 (info.uid, info.mode)
1687 };
1688 if child_uid != current_task.current_creds().fsuid
1692 && !security::is_task_capable_noaudit(current_task, CAP_FOWNER)
1693 {
1694 child
1697 .check_access(
1698 locked,
1699 current_task,
1700 mount,
1701 Access::READ | Access::WRITE,
1702 CheckAccessReason::InternalPermissionChecks,
1703 security::Auditable::Name(name),
1704 )
1705 .map_err(|e| {
1706 if e == EACCES { errno!(EPERM) } else { e }
1709 })?;
1710 if mode.contains(FileMode::ISGID | FileMode::IXGRP) {
1713 return error!(EPERM);
1714 };
1715 if mode.contains(FileMode::ISUID) {
1716 return error!(EPERM);
1717 };
1718 if !mode.contains(FileMode::IFREG) {
1719 return error!(EPERM);
1720 };
1721 }
1722
1723 security::check_fs_node_link_access(current_task, self, child)?;
1724
1725 let locked = locked.cast_locked::<FileOpsCore>();
1726 self.ops().link(locked, self, current_task, name, child)?;
1727 Ok(child.clone())
1728 }
1729
1730 pub fn unlink<L>(
1731 &self,
1732 locked: &mut Locked<L>,
1733 current_task: &CurrentTask,
1734 mount: &MountInfo,
1735 name: &FsStr,
1736 child: &FsNodeHandle,
1737 ) -> Result<(), Errno>
1738 where
1739 L: LockEqualOrBefore<FileOpsCore>,
1740 {
1741 self.check_access(
1743 locked,
1744 current_task,
1745 mount,
1746 Access::EXEC | Access::WRITE,
1747 CheckAccessReason::InternalPermissionChecks,
1748 security::Auditable::Name(name),
1749 )?;
1750 {
1751 let parent_info = self.info();
1752 let _token = allow_subclass();
1756 self.check_sticky_bit(current_task, child, &parent_info)?;
1757 }
1758 if child.is_dir() {
1759 security::check_fs_node_rmdir_access(current_task, self, child, name)?;
1760 } else {
1761 security::check_fs_node_unlink_access(current_task, self, child, name)?;
1762 }
1763 let locked = locked.cast_locked::<FileOpsCore>();
1764 self.ops().unlink(locked, self, current_task, name, child)?;
1765 self.update_ctime_mtime();
1766 Ok(())
1767 }
1768
1769 pub fn truncate<L>(
1770 &self,
1771 locked: &mut Locked<L>,
1772 current_task: &CurrentTask,
1773 mount: &MountInfo,
1774 length: u64,
1775 ) -> Result<(), Errno>
1776 where
1777 L: LockEqualOrBefore<BeforeFsNodeAppend>,
1778 {
1779 let mut locked = locked.cast_locked::<BeforeFsNodeAppend>();
1780 if self.is_dir() {
1781 return error!(EISDIR);
1782 }
1783 self.check_access(
1784 &mut locked,
1785 current_task,
1786 mount,
1787 Access::WRITE,
1788 CheckAccessReason::InternalPermissionChecks,
1789 security::Auditable::Location(std::panic::Location::caller()),
1790 )?;
1791
1792 let (guard, locked) = self.ops().append_lock_write(&mut locked, self, current_task)?;
1793 self.truncate_locked(locked, &guard, current_task, length)
1794 }
1795
1796 pub fn ftruncate<L>(
1799 &self,
1800 locked: &mut Locked<L>,
1801 current_task: &CurrentTask,
1802 length: u64,
1803 ) -> Result<(), Errno>
1804 where
1805 L: LockEqualOrBefore<BeforeFsNodeAppend>,
1806 {
1807 let locked = locked.cast_locked::<BeforeFsNodeAppend>();
1808
1809 if self.is_dir() {
1810 return error!(EINVAL);
1815 }
1816
1817 let (guard, locked) = self.ops().append_lock_write(locked, self, current_task)?;
1833 self.truncate_locked(locked, &guard, current_task, length)
1834 }
1835
1836 pub fn truncate_locked<L>(
1838 &self,
1839 locked: &mut Locked<L>,
1840 guard: &AppendLockWriteGuard<'_>,
1841 current_task: &CurrentTask,
1842 length: u64,
1843 ) -> Result<(), Errno>
1844 where
1845 L: LockEqualOrBefore<FileOpsCore>,
1846 {
1847 let locked = locked.cast_locked::<FileOpsCore>();
1848 if length > MAX_LFS_FILESIZE as u64 {
1849 return error!(EINVAL);
1850 }
1851 if length > current_task.thread_group().get_rlimit(locked, Resource::FSIZE) {
1852 send_standard_signal(locked, current_task, SignalInfo::kernel(SIGXFSZ));
1853 return error!(EFBIG);
1854 }
1855 self.clear_suid_and_sgid_bits(locked, current_task)?;
1856
1857 self.ops().truncate(locked, guard, self, current_task, length)?;
1858 self.update_ctime_mtime();
1859 Ok(())
1860 }
1861
1862 pub fn fallocate<L>(
1865 &self,
1866 locked: &mut Locked<L>,
1867 current_task: &CurrentTask,
1868 mode: FallocMode,
1869 offset: u64,
1870 length: u64,
1871 ) -> Result<(), Errno>
1872 where
1873 L: LockEqualOrBefore<BeforeFsNodeAppend>,
1874 {
1875 let mut locked = locked.cast_locked::<BeforeFsNodeAppend>();
1876 let (guard, locked) = self.ops().append_lock_write(&mut locked, self, current_task)?;
1877 self.fallocate_locked(locked, &guard, current_task, mode, offset, length)
1878 }
1879
1880 pub fn fallocate_locked<L>(
1881 &self,
1882 locked: &mut Locked<L>,
1883 guard: &AppendLockWriteGuard<'_>,
1884 current_task: &CurrentTask,
1885 mode: FallocMode,
1886 offset: u64,
1887 length: u64,
1888 ) -> Result<(), Errno>
1889 where
1890 L: LockEqualOrBefore<FileOpsCore>,
1891 {
1892 let locked = locked.cast_locked::<FileOpsCore>();
1893 let allocate_size = checked_add_offset_and_length(offset as usize, length as usize)
1894 .map_err(|_| errno!(EFBIG))? as u64;
1895 if allocate_size > current_task.thread_group().get_rlimit(locked, Resource::FSIZE) {
1896 send_standard_signal(locked, current_task, SignalInfo::kernel(SIGXFSZ));
1897 return error!(EFBIG);
1898 }
1899
1900 self.clear_suid_and_sgid_bits(locked, current_task)?;
1901
1902 self.ops().allocate(locked, guard, self, current_task, mode, offset, length)?;
1903 self.update_ctime_mtime();
1904 Ok(())
1905 }
1906
1907 fn update_metadata_for_child(
1908 &self,
1909 current_task: &CurrentTask,
1910 mode: &mut FileMode,
1911 owner: &mut FsCred,
1912 ) {
1913 {
1916 let self_info = self.info();
1917 if self_info.mode.contains(FileMode::ISGID) {
1918 owner.gid = self_info.gid;
1919 if mode.is_dir() {
1920 *mode |= FileMode::ISGID;
1921 }
1922 }
1923 }
1924
1925 if !mode.is_dir() {
1926 let current_creds = current_task.current_creds();
1935 if owner.gid != current_creds.fsgid
1936 && !current_creds.is_in_group(owner.gid)
1937 && !security::is_task_capable_noaudit(current_task, CAP_FOWNER)
1938 {
1939 *mode &= !FileMode::ISGID;
1940 }
1941 }
1942 }
1943
1944 pub fn check_o_noatime_allowed(&self, current_task: &CurrentTask) -> Result<(), Errno> {
1946 if current_task.current_creds().fsuid != self.info().uid {
1961 security::check_task_capable(current_task, CAP_FOWNER)?;
1962 }
1963 Ok(())
1964 }
1965
1966 pub fn default_check_access_impl(
1967 &self,
1968 current_task: &CurrentTask,
1969 permission_flags: security::PermissionFlags,
1970 reason: CheckAccessReason,
1971 info: LockDepReadGuard<'_, FsNodeInfo>,
1972 audit_context: Auditable<'_>,
1973 ) -> Result<(), Errno> {
1974 let (node_uid, node_gid, mode) = (info.uid, info.gid, info.mode);
1975 std::mem::drop(info);
1976 if let CheckAccessReason::ChangeTimestamps { now } = reason {
1977 if current_task.current_creds().fsuid == node_uid {
1982 return Ok(());
1983 }
1984 if now {
1985 if security::is_task_capable_noaudit(current_task, CAP_FOWNER) {
1986 return Ok(());
1987 }
1988 } else {
1989 security::check_task_capable(current_task, CAP_FOWNER)?;
1990 return Ok(());
1991 }
1992 }
1993 check_access(self, current_task, permission_flags, node_uid, node_gid, mode)?;
1994 security::fs_node_permission(current_task, self, permission_flags, audit_context)
1995 }
1996
1997 pub fn check_access<'a, L>(
2001 &self,
2002 locked: &mut Locked<L>,
2003 current_task: &CurrentTask,
2004 mount: &MountInfo,
2005 access: impl Into<security::PermissionFlags>,
2006 reason: CheckAccessReason,
2007 audit_context: impl Into<security::Auditable<'a>>,
2008 ) -> Result<(), Errno>
2009 where
2010 L: LockEqualOrBefore<FileOpsCore>,
2011 {
2012 let mut permission_flags = access.into();
2013 if permission_flags.contains(security::PermissionFlags::WRITE)
2014 && !self.info().mode.is_special()
2015 {
2016 mount.check_readonly_filesystem()?;
2017 }
2018 if permission_flags.contains(security::PermissionFlags::EXEC) && !self.is_dir() {
2019 mount.check_noexec_filesystem()?;
2020 }
2021 if reason == CheckAccessReason::Access {
2022 permission_flags |= PermissionFlags::ACCESS;
2023 }
2024 self.ops().check_access(
2025 locked.cast_locked::<FileOpsCore>(),
2026 self,
2027 current_task,
2028 permission_flags,
2029 &self.info,
2030 reason,
2031 audit_context.into(),
2032 )
2033 }
2034
2035 pub fn check_sticky_bit(
2039 &self,
2040 current_task: &CurrentTask,
2041 child: &FsNodeHandle,
2042 self_info: &FsNodeInfo,
2043 ) -> Result<(), Errno> {
2044 if self_info.mode.contains(FileMode::ISVTX)
2045 && child.info().uid != current_task.current_creds().fsuid
2046 {
2047 security::check_task_capable(current_task, CAP_FOWNER)?;
2048 }
2049 Ok(())
2050 }
2051
2052 pub fn fifo(&self, current_task: &CurrentTask) -> &PipeHandle {
2053 assert!(self.is_fifo());
2054 self.ensure_rare_data().ensure_fifo(current_task)
2055 }
2056
2057 pub fn bound_socket(&self) -> Option<&SocketHandle> {
2059 if let Some(rare_data) = self.rare_data.get() { rare_data.bound_socket.get() } else { None }
2060 }
2061
2062 pub fn set_bound_socket(&self, socket: SocketHandle) {
2066 assert!(self.ensure_rare_data().bound_socket.set(socket).is_ok());
2067 }
2068
2069 pub fn update_attributes<L, F>(
2070 &self,
2071 locked: &mut Locked<L>,
2072 current_task: &CurrentTask,
2073 mutator: F,
2074 ) -> Result<(), Errno>
2075 where
2076 L: LockEqualOrBefore<FileOpsCore>,
2077 F: FnOnce(&mut FsNodeInfo) -> Result<(), Errno>,
2078 {
2079 let mut info = self.info.write();
2080 let mut new_info = info.clone();
2081 mutator(&mut new_info)?;
2082
2083 let new_access = new_info.mode.user_access()
2084 | new_info.mode.group_access()
2085 | new_info.mode.other_access();
2086
2087 if new_access.intersects(Access::EXEC) {
2088 let write_guard_state = self.write_guard_state.lock();
2089 if let Ok(seals) = write_guard_state.get_seals() {
2090 if seals.contains(SealFlags::NO_EXEC) {
2091 return error!(EPERM);
2092 }
2093 }
2094 }
2095
2096 assert_eq!(info.time_status_change, new_info.time_status_change);
2098 if *info == new_info {
2099 return Ok(());
2100 }
2101 new_info.time_status_change = utc::utc_now();
2102
2103 let mut has = zxio_node_attr_has_t { ..Default::default() };
2104 has.modification_time = info.time_modify != new_info.time_modify;
2105 has.access_time = info.time_access != new_info.time_access;
2106 has.mode = info.mode != new_info.mode;
2107 has.uid = info.uid != new_info.uid;
2108 has.gid = info.gid != new_info.gid;
2109 has.rdev = info.rdev != new_info.rdev;
2110 has.casefold = info.casefold != new_info.casefold;
2111 has.wrapping_key_id = info.wrapping_key_id != new_info.wrapping_key_id;
2112
2113 security::check_fs_node_setattr_access(current_task, &self, &has)?;
2114
2115 if has.modification_time
2117 || has.access_time
2118 || has.mode
2119 || has.uid
2120 || has.gid
2121 || has.rdev
2122 || has.casefold
2123 || has.wrapping_key_id
2124 {
2125 let locked = locked.cast_locked::<FileOpsCore>();
2126 self.ops().update_attributes(locked, self, current_task, &new_info, has)?;
2127 }
2128
2129 *info = new_info;
2130 Ok(())
2131 }
2132
2133 pub fn chmod<L>(
2137 &self,
2138 locked: &mut Locked<L>,
2139 current_task: &CurrentTask,
2140 mount: &MountInfo,
2141 mut mode: FileMode,
2142 ) -> Result<(), Errno>
2143 where
2144 L: LockEqualOrBefore<FileOpsCore>,
2145 {
2146 mount.check_readonly_filesystem()?;
2147 self.update_attributes(locked, current_task, |info| {
2148 let current_creds = current_task.current_creds();
2149 if info.uid != current_creds.euid {
2150 security::check_task_capable(current_task, CAP_FOWNER)?;
2151 } else if info.gid != current_creds.egid
2152 && !current_creds.is_in_group(info.gid)
2153 && mode.intersects(FileMode::ISGID)
2154 && !security::is_task_capable_noaudit(current_task, CAP_FOWNER)
2155 {
2156 mode &= !FileMode::ISGID;
2157 }
2158 info.chmod(mode);
2159 Ok(())
2160 })
2161 }
2162
2163 pub fn chown<L>(
2165 &self,
2166 locked: &mut Locked<L>,
2167 current_task: &CurrentTask,
2168 mount: &MountInfo,
2169 owner: Option<uid_t>,
2170 group: Option<gid_t>,
2171 ) -> Result<(), Errno>
2172 where
2173 L: LockEqualOrBefore<FileOpsCore>,
2174 {
2175 mount.check_readonly_filesystem()?;
2176 self.update_attributes(locked, current_task, |info| {
2177 if security::is_task_capable_noaudit(current_task, CAP_CHOWN) {
2178 info.chown(owner, group);
2179 return Ok(());
2180 }
2181
2182 if let Some(uid) = owner {
2184 if info.uid != uid {
2185 return error!(EPERM);
2186 }
2187 }
2188
2189 let (euid, is_in_group) = {
2190 let current_creds = current_task.current_creds();
2191 (current_creds.euid, group.map(|gid| current_creds.is_in_group(gid)))
2192 };
2193
2194 if info.uid == euid {
2196 if let Some(is_in_group) = is_in_group {
2198 if !is_in_group {
2199 return error!(EPERM);
2200 }
2201 }
2202 info.chown(None, group);
2203 return Ok(());
2204 }
2205
2206 if owner.is_some() || group.is_some() {
2208 return error!(EPERM);
2209 }
2210
2211 if info.mode.is_reg()
2214 && (info.mode.contains(FileMode::ISUID)
2215 || info.mode.contains(FileMode::ISGID | FileMode::IXGRP))
2216 {
2217 return error!(EPERM);
2218 }
2219
2220 info.chown(None, None);
2221 Ok(())
2222 })
2223 }
2224
2225 pub unsafe fn force_chown(&self, creds: FsCred) {
2236 self.update_info(|info| {
2237 info.chown(Some(creds.uid), Some(creds.gid));
2238 });
2239 }
2240
2241 pub fn is_reg(&self) -> bool {
2243 self.info().mode.is_reg()
2244 }
2245
2246 pub fn is_dir(&self) -> bool {
2248 self.info().mode.is_dir()
2249 }
2250
2251 pub fn is_sock(&self) -> bool {
2253 self.info().mode.is_sock()
2254 }
2255
2256 pub fn is_fifo(&self) -> bool {
2258 self.info().mode.is_fifo()
2259 }
2260
2261 pub fn is_lnk(&self) -> bool {
2263 self.info().mode.is_lnk()
2264 }
2265
2266 pub fn dev(&self) -> DeviceId {
2267 self.fs().dev_id
2268 }
2269
2270 pub fn stat<L>(
2271 &self,
2272 locked: &mut Locked<L>,
2273 current_task: &CurrentTask,
2274 ) -> Result<uapi::stat, Errno>
2275 where
2276 L: LockEqualOrBefore<FileOpsCore>,
2277 {
2278 security::check_fs_node_getattr_access(current_task, self)?;
2279
2280 let info = self.fetch_and_refresh_info(locked, current_task)?;
2281
2282 let time_to_kernel_timespec_pair = |t| {
2283 let timespec { tv_sec, tv_nsec } = timespec_from_time(t);
2284 let time = tv_sec.try_into().map_err(|_| errno!(EINVAL))?;
2285 let time_nsec = tv_nsec.try_into().map_err(|_| errno!(EINVAL))?;
2286 Ok((time, time_nsec))
2287 };
2288
2289 let (st_atime, st_atime_nsec) = time_to_kernel_timespec_pair(info.time_access)?;
2290 let (st_mtime, st_mtime_nsec) = time_to_kernel_timespec_pair(info.time_modify)?;
2291 let (st_ctime, st_ctime_nsec) = time_to_kernel_timespec_pair(info.time_status_change)?;
2292
2293 Ok(uapi::stat {
2294 st_dev: self.dev().bits(),
2295 st_ino: self.ino,
2296 st_nlink: info.link_count.try_into().map_err(|_| errno!(EINVAL))?,
2297 st_mode: info.mode.bits(),
2298 st_uid: info.uid,
2299 st_gid: info.gid,
2300 st_rdev: info.rdev.bits(),
2301 st_size: info.size.try_into().map_err(|_| errno!(EINVAL))?,
2302 st_blksize: info.blksize.try_into().map_err(|_| errno!(EINVAL))?,
2303 st_blocks: info.blocks.try_into().map_err(|_| errno!(EINVAL))?,
2304 st_atime,
2305 st_atime_nsec,
2306 st_mtime,
2307 st_mtime_nsec,
2308 st_ctime,
2309 st_ctime_nsec,
2310 ..Default::default()
2311 })
2312 }
2313
2314 pub fn get_size<L>(
2321 &self,
2322 locked: &mut Locked<L>,
2323 current_task: &CurrentTask,
2324 ) -> Result<usize, Errno>
2325 where
2326 L: LockEqualOrBefore<FileOpsCore>,
2327 {
2328 self.ops().get_size(locked.cast_locked::<FileOpsCore>(), self, current_task)
2329 }
2330
2331 fn statx_timestamp_from_time(time: UtcInstant) -> statx_timestamp {
2332 let nanos = time.into_nanos();
2333 statx_timestamp {
2334 tv_sec: nanos / NANOS_PER_SECOND,
2335 tv_nsec: (nanos % NANOS_PER_SECOND) as u32,
2336 ..Default::default()
2337 }
2338 }
2339
2340 pub fn statx<L>(
2341 &self,
2342 locked: &mut Locked<L>,
2343 current_task: &CurrentTask,
2344 flags: StatxFlags,
2345 mask: u32,
2346 ) -> Result<statx, Errno>
2347 where
2348 L: LockEqualOrBefore<FileOpsCore>,
2349 {
2350 security::check_fs_node_getattr_access(current_task, self)?;
2351
2352 let info = if flags.contains(StatxFlags::AT_STATX_DONT_SYNC) {
2354 self.info()
2355 } else {
2356 self.fetch_and_refresh_info(locked, current_task)?
2357 };
2358 if mask & STATX__RESERVED == STATX__RESERVED {
2359 return error!(EINVAL);
2360 }
2361
2362 track_stub!(TODO("https://fxbug.dev/302594110"), "statx attributes");
2363 let stx_mnt_id = 0;
2364 let mut stx_attributes = 0;
2365 let stx_attributes_mask = STATX_ATTR_VERITY as u64;
2366
2367 if matches!(*self.fsverity.lock(), FsVerityState::FsVerity) {
2368 stx_attributes |= STATX_ATTR_VERITY as u64;
2369 }
2370
2371 Ok(statx {
2372 stx_mask: STATX_NLINK
2373 | STATX_UID
2374 | STATX_GID
2375 | STATX_ATIME
2376 | STATX_MTIME
2377 | STATX_CTIME
2378 | STATX_INO
2379 | STATX_SIZE
2380 | STATX_BLOCKS
2381 | STATX_BASIC_STATS,
2382 stx_blksize: info.blksize.try_into().map_err(|_| errno!(EINVAL))?,
2383 stx_attributes,
2384 stx_nlink: info.link_count.try_into().map_err(|_| errno!(EINVAL))?,
2385 stx_uid: info.uid,
2386 stx_gid: info.gid,
2387 stx_mode: info.mode.bits().try_into().map_err(|_| errno!(EINVAL))?,
2388 stx_ino: self.ino,
2389 stx_size: info.size.try_into().map_err(|_| errno!(EINVAL))?,
2390 stx_blocks: info.blocks.try_into().map_err(|_| errno!(EINVAL))?,
2391 stx_attributes_mask,
2392 stx_ctime: Self::statx_timestamp_from_time(info.time_status_change),
2393 stx_mtime: Self::statx_timestamp_from_time(info.time_modify),
2394 stx_atime: Self::statx_timestamp_from_time(info.time_access),
2395
2396 stx_rdev_major: info.rdev.major(),
2397 stx_rdev_minor: info.rdev.minor(),
2398
2399 stx_dev_major: self.fs().dev_id.major(),
2400 stx_dev_minor: self.fs().dev_id.minor(),
2401 stx_mnt_id,
2402 ..Default::default()
2403 })
2404 }
2405
2406 fn check_xattr_access<L>(
2409 &self,
2410 locked: &mut Locked<L>,
2411 current_task: &CurrentTask,
2412 mount: &MountInfo,
2413 name: &FsStr,
2414 access: Access,
2415 ) -> Result<(), Errno>
2416 where
2417 L: LockEqualOrBefore<FileOpsCore>,
2418 {
2419 assert!(access == Access::READ || access == Access::WRITE);
2420
2421 let enodata_if_read =
2422 |e: Errno| if access == Access::READ && e.code == EPERM { errno!(ENODATA) } else { e };
2423
2424 if name.starts_with(XATTR_USER_PREFIX.to_bytes()) {
2427 {
2428 let info = self.info();
2429 if !info.mode.is_reg() && !info.mode.is_dir() {
2430 return Err(enodata_if_read(errno!(EPERM)));
2431 }
2432 }
2433
2434 self.check_access(
2438 locked,
2439 current_task,
2440 mount,
2441 access,
2442 CheckAccessReason::InternalPermissionChecks,
2443 security::Auditable::Name(name),
2444 )?;
2445 } else if name.starts_with(XATTR_TRUSTED_PREFIX.to_bytes()) {
2446 security::check_task_capable(current_task, CAP_SYS_ADMIN).map_err(enodata_if_read)?;
2448 } else if name.starts_with(XATTR_SYSTEM_PREFIX.to_bytes()) {
2449 security::check_task_capable(current_task, CAP_SYS_ADMIN).map_err(enodata_if_read)?;
2453 } else if name.starts_with(XATTR_SECURITY_PREFIX.to_bytes()) {
2454 if access == Access::WRITE {
2455 if !security::fs_node_xattr_skipcap(current_task, name) {
2457 security::check_task_capable(current_task, CAP_SYS_ADMIN)
2458 .map_err(enodata_if_read)?;
2459 }
2460 }
2461 } else {
2462 panic!("Unknown extended attribute prefix: {}", name);
2463 }
2464 Ok(())
2465 }
2466
2467 pub fn get_xattr<L>(
2468 &self,
2469 locked: &mut Locked<L>,
2470 current_task: &CurrentTask,
2471 mount: &MountInfo,
2472 name: &FsStr,
2473 max_size: usize,
2474 ) -> Result<ValueOrSize<FsString>, Errno>
2475 where
2476 L: LockEqualOrBefore<FileOpsCore>,
2477 {
2478 self.check_xattr_access(locked, current_task, mount, name, Access::READ)?;
2480
2481 security::check_fs_node_getxattr_access(current_task, self, name)?;
2483
2484 if name.starts_with(XATTR_SECURITY_PREFIX.to_bytes()) {
2485 security::fs_node_getsecurity(locked, current_task, self, name, max_size)
2488 } else {
2489 self.ops().get_xattr(
2491 locked.cast_locked::<FileOpsCore>(),
2492 self,
2493 current_task,
2494 name,
2495 max_size,
2496 )
2497 }
2498 }
2499
2500 pub fn set_xattr<L>(
2501 &self,
2502 locked: &mut Locked<L>,
2503 current_task: &CurrentTask,
2504 mount: &MountInfo,
2505 name: &FsStr,
2506 value: &FsStr,
2507 op: XattrOp,
2508 ) -> Result<(), Errno>
2509 where
2510 L: LockEqualOrBefore<FileOpsCore>,
2511 {
2512 self.check_xattr_access(locked, current_task, mount, name, Access::WRITE)?;
2514
2515 security::check_fs_node_setxattr_access(current_task, self, name, value, op)?;
2517
2518 if name.starts_with(XATTR_SECURITY_PREFIX.to_bytes()) {
2519 security::fs_node_setsecurity(locked, current_task, self, name, value, op)
2522 } else {
2523 self.ops().set_xattr(
2525 locked.cast_locked::<FileOpsCore>(),
2526 self,
2527 current_task,
2528 name,
2529 value,
2530 op,
2531 )
2532 }
2533 }
2534
2535 pub fn remove_xattr<L>(
2536 &self,
2537 locked: &mut Locked<L>,
2538 current_task: &CurrentTask,
2539 mount: &MountInfo,
2540 name: &FsStr,
2541 ) -> Result<(), Errno>
2542 where
2543 L: LockEqualOrBefore<FileOpsCore>,
2544 {
2545 self.check_xattr_access(locked, current_task, mount, name, Access::WRITE)?;
2547
2548 security::check_fs_node_removexattr_access(current_task, self, name)?;
2550 self.ops().remove_xattr(locked.cast_locked::<FileOpsCore>(), self, current_task, name)
2551 }
2552
2553 pub fn list_xattrs<L>(
2554 &self,
2555 locked: &mut Locked<L>,
2556 current_task: &CurrentTask,
2557 max_size: usize,
2558 ) -> Result<ValueOrSize<Vec<FsString>>, Errno>
2559 where
2560 L: LockEqualOrBefore<FileOpsCore>,
2561 {
2562 security::check_fs_node_listxattr_access(current_task, self)?;
2563 Ok(self
2564 .ops()
2565 .list_xattrs(locked.cast_locked::<FileOpsCore>(), self, current_task, max_size)?
2566 .map(|mut v| {
2567 if !security::is_task_capable_noaudit(current_task, CAP_SYS_ADMIN) {
2572 v.retain(|name| !name.starts_with(XATTR_TRUSTED_PREFIX.to_bytes()));
2573 }
2574 v
2575 }))
2576 }
2577
2578 pub fn info(&self) -> LockDepReadGuard<'_, FsNodeInfo> {
2580 self.info.read()
2581 }
2582
2583 pub(super) fn info_lock(&self) -> &DynamicLockDepRwLock<FsNodeInfo> {
2588 &self.info
2589 }
2590
2591 pub fn fetch_and_refresh_info<L>(
2593 &self,
2594 locked: &mut Locked<L>,
2595 current_task: &CurrentTask,
2596 ) -> Result<LockDepReadGuard<'_, FsNodeInfo>, Errno>
2597 where
2598 L: LockEqualOrBefore<FileOpsCore>,
2599 {
2600 self.ops().fetch_and_refresh_info(
2601 locked.cast_locked::<FileOpsCore>(),
2602 self,
2603 current_task,
2604 &self.info,
2605 )
2606 }
2607
2608 pub fn update_info<F, T>(&self, mutator: F) -> T
2609 where
2610 F: FnOnce(&mut FsNodeInfo) -> T,
2611 {
2612 let mut info = self.info.write();
2613 mutator(&mut info)
2614 }
2615
2616 pub fn clear_suid_and_sgid_bits<L>(
2618 &self,
2619 locked: &mut Locked<L>,
2620 current_task: &CurrentTask,
2621 ) -> Result<(), Errno>
2622 where
2623 L: LockEqualOrBefore<FileOpsCore>,
2624 {
2625 if !security::is_task_capable_noaudit(current_task, CAP_FSETID) {
2626 self.update_attributes(locked, current_task, |info| {
2627 info.clear_suid_and_sgid_bits();
2628 Ok(())
2629 })?;
2630 }
2631 Ok(())
2632 }
2633
2634 pub fn update_ctime_mtime(&self) {
2636 if self.fs().manages_timestamps() {
2637 return;
2638 }
2639 self.update_info(|info| {
2640 let now = utc::utc_now();
2641 info.time_status_change = now;
2642 info.time_modify = now;
2643 });
2644 }
2645
2646 pub fn update_ctime(&self) {
2648 if self.fs().manages_timestamps() {
2649 return;
2650 }
2651 self.update_info(|info| {
2652 let now = utc::utc_now();
2653 info.time_status_change = now;
2654 });
2655 }
2656
2657 pub fn update_atime_mtime<L>(
2660 &self,
2661 locked: &mut Locked<L>,
2662 current_task: &CurrentTask,
2663 mount: &MountInfo,
2664 atime: TimeUpdateType,
2665 mtime: TimeUpdateType,
2666 ) -> Result<(), Errno>
2667 where
2668 L: LockEqualOrBefore<FileOpsCore>,
2669 {
2670 mount.check_readonly_filesystem()?;
2672
2673 let now = matches!((atime, mtime), (TimeUpdateType::Now, TimeUpdateType::Now));
2674 self.check_access(
2675 locked,
2676 current_task,
2677 mount,
2678 Access::WRITE,
2679 CheckAccessReason::ChangeTimestamps { now },
2680 security::Auditable::Location(std::panic::Location::caller()),
2681 )?;
2682
2683 if !matches!((atime, mtime), (TimeUpdateType::Omit, TimeUpdateType::Omit)) {
2684 self.update_attributes(locked, current_task, |info| {
2688 let now = utc::utc_now();
2689 let get_time = |time: TimeUpdateType| match time {
2690 TimeUpdateType::Now => Some(now),
2691 TimeUpdateType::Time(t) => Some(t),
2692 TimeUpdateType::Omit => None,
2693 };
2694 if let Some(time) = get_time(atime) {
2695 info.time_access = time;
2696 }
2697 if let Some(time) = get_time(mtime) {
2698 info.time_modify = time;
2699 }
2700 Ok(())
2701 })?;
2702 }
2703 Ok(())
2704 }
2705
2706 pub fn node_key(&self) -> ino_t {
2711 self.ops().node_key(self)
2712 }
2713
2714 fn ensure_rare_data(&self) -> &FsNodeRareData {
2715 self.rare_data.get_or_init(|| Box::new(FsNodeRareData::default()))
2716 }
2717
2718 pub fn ensure_watchers(&self) -> &inotify_hook::InotifyWatchers {
2723 &self.ensure_rare_data().watchers
2724 }
2725
2726 pub fn notify(
2728 &self,
2729 event_mask: InotifyMask,
2730 cookie: u32,
2731 name: &FsStr,
2732 mode: FileMode,
2733 is_dead: bool,
2734 ) {
2735 if let Some(rare_data) = self.rare_data.get() {
2736 let kernel = self.fs().kernel.upgrade().expect("kernel is dead");
2737 if let Some(hook) = kernel.expando.peek::<Arc<dyn inotify_hook::NotifyHook>>() {
2738 hook.notify(&rare_data.watchers, event_mask, cookie, name, mode, is_dead);
2739 }
2740 }
2741 }
2742
2743 pub fn enable_fsverity<L>(
2745 &self,
2746 locked: &mut Locked<L>,
2747 current_task: &CurrentTask,
2748 descriptor: &fsverity_descriptor,
2749 ) -> Result<(), Errno>
2750 where
2751 L: LockEqualOrBefore<FileOpsCore>,
2752 {
2753 let locked = locked.cast_locked::<FileOpsCore>();
2754 self.ops().enable_fsverity(locked, self, current_task, descriptor)
2755 }
2756}
2757
2758impl std::fmt::Debug for FsNode {
2759 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2760 f.debug_struct("FsNode")
2761 .field("fs", &self.fs().name())
2762 .field("info", &*self.info())
2763 .field("ops_ty", &self.ops().type_name())
2764 .finish()
2765 }
2766}
2767
2768impl Releasable for FsNode {
2769 type Context<'a> = CurrentTaskAndLocked<'a>;
2770
2771 fn release<'a>(self, context: CurrentTaskAndLocked<'a>) {
2772 let (locked, current_task) = context;
2773 if let Some(fs) = self.fs.upgrade() {
2774 fs.remove_node(&self);
2775 }
2776 if let Err(err) = self.ops.forget(
2777 locked.cast_locked::<FileOpsCore>(),
2778 current_task,
2779 self.info.into_inner(),
2780 ) {
2781 log_error!("Error on FsNodeOps::forget: {err:?}");
2782 }
2783 }
2784}
2785
2786fn check_access(
2787 fs_node: &FsNode,
2788 current_task: &CurrentTask,
2789 permission_flags: security::PermissionFlags,
2790 node_uid: uid_t,
2791 node_gid: gid_t,
2792 mode: FileMode,
2793) -> Result<(), Errno> {
2794 let (fsuid, is_in_group) = {
2796 let current_creds = current_task.current_creds();
2797 (current_creds.fsuid, current_creds.is_in_group(node_gid))
2798 };
2799 let granted = if fsuid == node_uid {
2800 mode.user_access()
2801 } else if is_in_group {
2802 mode.group_access()
2803 } else {
2804 mode.other_access()
2805 };
2806
2807 let access = permission_flags.as_access();
2808 if granted.contains(access) {
2809 return Ok(());
2810 }
2811
2812 let mut requested = access & !granted;
2815
2816 let have_dont_audit = OnceBool::new();
2819 let has_capability = move |current_task, capability| {
2820 let dont_audit = have_dont_audit.get_or_init(|| {
2821 permission_flags.contains(PermissionFlags::ACCESS)
2822 && security::has_dontaudit_access(current_task, fs_node)
2823 });
2824 if dont_audit {
2825 security::is_task_capable_noaudit(current_task, capability)
2826 } else {
2827 security::check_task_capable(current_task, capability).is_ok()
2828 }
2829 };
2830
2831 let dac_read_search_access =
2833 if mode.is_dir() { Access::READ | Access::EXEC } else { Access::READ };
2834 if dac_read_search_access.intersects(requested)
2835 && has_capability(current_task, CAP_DAC_READ_SEARCH)
2836 {
2837 requested.remove(dac_read_search_access);
2838 }
2839 if requested.is_empty() {
2840 return Ok(());
2841 }
2842
2843 let mut dac_override_access = Access::READ | Access::WRITE;
2845 dac_override_access |= if mode.is_dir() {
2846 Access::EXEC
2847 } else {
2848 (mode.user_access() | mode.group_access() | mode.other_access()) & Access::EXEC
2850 };
2851 if dac_override_access.intersects(requested) && has_capability(current_task, CAP_DAC_OVERRIDE) {
2852 requested.remove(dac_override_access);
2853 }
2854 if requested.is_empty() {
2855 return Ok(());
2856 }
2857
2858 return error!(EACCES);
2859}
2860
2861#[cfg(test)]
2862mod tests {
2863 use super::*;
2864 use crate::device::mem::mem_device_init;
2865 use crate::testing::*;
2866 use crate::vfs::buffers::VecOutputBuffer;
2867 use starnix_uapi::auth::Credentials;
2868 use starnix_uapi::file_mode::mode;
2869
2870 #[::fuchsia::test]
2871 async fn open_device_file() {
2872 spawn_kernel_and_run(async |locked, current_task| {
2873 mem_device_init(locked, current_task.kernel()).expect("mem_device_init");
2874
2875 current_task
2878 .fs()
2879 .root()
2880 .create_node(
2881 locked,
2882 ¤t_task,
2883 "zero".into(),
2884 mode!(IFCHR, 0o666),
2885 DeviceId::ZERO,
2886 )
2887 .expect("create_node");
2888
2889 const CONTENT_LEN: usize = 10;
2890 let mut buffer = VecOutputBuffer::new(CONTENT_LEN);
2891
2892 let device_file = current_task
2894 .open_file(locked, "zero".into(), OpenFlags::RDONLY)
2895 .expect("open device file");
2896 device_file.read(locked, ¤t_task, &mut buffer).expect("read from zero");
2897
2898 assert_eq!(&[0; CONTENT_LEN], buffer.data());
2900 })
2901 .await;
2902 }
2903
2904 #[::fuchsia::test]
2905 async fn node_info_is_reflected_in_stat() {
2906 spawn_kernel_and_run(async |locked, current_task| {
2907 let node = ¤t_task
2909 .fs()
2910 .root()
2911 .create_node(locked, ¤t_task, "zero".into(), FileMode::IFCHR, DeviceId::ZERO)
2912 .expect("create_node")
2913 .entry
2914 .node;
2915 node.update_info(|info| {
2916 info.mode = FileMode::IFSOCK;
2917 info.size = 1;
2918 info.blocks = 2;
2919 info.blksize = 4;
2920 info.uid = 9;
2921 info.gid = 10;
2922 info.link_count = 11;
2923 info.time_status_change = UtcInstant::from_nanos(1);
2924 info.time_access = UtcInstant::from_nanos(2);
2925 info.time_modify = UtcInstant::from_nanos(3);
2926 info.rdev = DeviceId::new(13, 13);
2927 });
2928 let stat = node.stat(locked, ¤t_task).expect("stat");
2929
2930 assert_eq!(stat.st_mode, FileMode::IFSOCK.bits());
2931 assert_eq!(stat.st_size, 1);
2932 assert_eq!(stat.st_blksize, 4);
2933 assert_eq!(stat.st_blocks, 2);
2934 assert_eq!(stat.st_uid, 9);
2935 assert_eq!(stat.st_gid, 10);
2936 assert_eq!(stat.st_nlink, 11);
2937 assert_eq!(stat.st_ctime, 0);
2938 assert_eq!(stat.st_ctime_nsec, 1);
2939 assert_eq!(stat.st_atime, 0);
2940 assert_eq!(stat.st_atime_nsec, 2);
2941 assert_eq!(stat.st_mtime, 0);
2942 assert_eq!(stat.st_mtime_nsec, 3);
2943 assert_eq!(stat.st_rdev, DeviceId::new(13, 13).bits());
2944 })
2945 .await;
2946 }
2947
2948 #[::fuchsia::test]
2949 fn test_flock_operation() {
2950 assert!(FlockOperation::from_flags(0).is_err());
2951 assert!(FlockOperation::from_flags(u32::MAX).is_err());
2952
2953 let operation1 = FlockOperation::from_flags(LOCK_SH).expect("from_flags");
2954 assert!(!operation1.is_unlock());
2955 assert!(!operation1.is_lock_exclusive());
2956 assert!(operation1.is_blocking());
2957
2958 let operation2 = FlockOperation::from_flags(LOCK_EX | LOCK_NB).expect("from_flags");
2959 assert!(!operation2.is_unlock());
2960 assert!(operation2.is_lock_exclusive());
2961 assert!(!operation2.is_blocking());
2962
2963 let operation3 = FlockOperation::from_flags(LOCK_UN).expect("from_flags");
2964 assert!(operation3.is_unlock());
2965 assert!(!operation3.is_lock_exclusive());
2966 assert!(operation3.is_blocking());
2967 }
2968
2969 #[::fuchsia::test]
2970 async fn test_check_access() {
2971 spawn_kernel_and_run(async |locked, current_task| {
2972 let mut creds = Credentials::with_ids(1, 2);
2973 creds.groups = vec![3, 4];
2974 current_task.set_creds(creds);
2975
2976 let node = ¤t_task
2978 .fs()
2979 .root()
2980 .create_node(locked, ¤t_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
2981 .expect("create_node")
2982 .entry
2983 .node;
2984 let check_access = |locked: &mut Locked<Unlocked>,
2985 uid: uid_t,
2986 gid: gid_t,
2987 perm: u32,
2988 access: Access| {
2989 node.update_info(|info| {
2990 info.mode = mode!(IFREG, perm);
2991 info.uid = uid;
2992 info.gid = gid;
2993 });
2994 node.check_access(
2995 locked,
2996 ¤t_task,
2997 &MountInfo::detached(),
2998 access,
2999 CheckAccessReason::InternalPermissionChecks,
3000 security::Auditable::Location(std::panic::Location::caller()),
3001 )
3002 };
3003
3004 assert_eq!(check_access(locked, 0, 0, 0o700, Access::EXEC), error!(EACCES));
3005 assert_eq!(check_access(locked, 0, 0, 0o700, Access::READ), error!(EACCES));
3006 assert_eq!(check_access(locked, 0, 0, 0o700, Access::WRITE), error!(EACCES));
3007
3008 assert_eq!(check_access(locked, 0, 0, 0o070, Access::EXEC), error!(EACCES));
3009 assert_eq!(check_access(locked, 0, 0, 0o070, Access::READ), error!(EACCES));
3010 assert_eq!(check_access(locked, 0, 0, 0o070, Access::WRITE), error!(EACCES));
3011
3012 assert_eq!(check_access(locked, 0, 0, 0o007, Access::EXEC), Ok(()));
3013 assert_eq!(check_access(locked, 0, 0, 0o007, Access::READ), Ok(()));
3014 assert_eq!(check_access(locked, 0, 0, 0o007, Access::WRITE), Ok(()));
3015
3016 assert_eq!(check_access(locked, 1, 0, 0o700, Access::EXEC), Ok(()));
3017 assert_eq!(check_access(locked, 1, 0, 0o700, Access::READ), Ok(()));
3018 assert_eq!(check_access(locked, 1, 0, 0o700, Access::WRITE), Ok(()));
3019
3020 assert_eq!(check_access(locked, 1, 0, 0o100, Access::EXEC), Ok(()));
3021 assert_eq!(check_access(locked, 1, 0, 0o100, Access::READ), error!(EACCES));
3022 assert_eq!(check_access(locked, 1, 0, 0o100, Access::WRITE), error!(EACCES));
3023
3024 assert_eq!(check_access(locked, 1, 0, 0o200, Access::EXEC), error!(EACCES));
3025 assert_eq!(check_access(locked, 1, 0, 0o200, Access::READ), error!(EACCES));
3026 assert_eq!(check_access(locked, 1, 0, 0o200, Access::WRITE), Ok(()));
3027
3028 assert_eq!(check_access(locked, 1, 0, 0o400, Access::EXEC), error!(EACCES));
3029 assert_eq!(check_access(locked, 1, 0, 0o400, Access::READ), Ok(()));
3030 assert_eq!(check_access(locked, 1, 0, 0o400, Access::WRITE), error!(EACCES));
3031
3032 assert_eq!(check_access(locked, 0, 2, 0o700, Access::EXEC), error!(EACCES));
3033 assert_eq!(check_access(locked, 0, 2, 0o700, Access::READ), error!(EACCES));
3034 assert_eq!(check_access(locked, 0, 2, 0o700, Access::WRITE), error!(EACCES));
3035
3036 assert_eq!(check_access(locked, 0, 2, 0o070, Access::EXEC), Ok(()));
3037 assert_eq!(check_access(locked, 0, 2, 0o070, Access::READ), Ok(()));
3038 assert_eq!(check_access(locked, 0, 2, 0o070, Access::WRITE), Ok(()));
3039
3040 assert_eq!(check_access(locked, 0, 3, 0o070, Access::EXEC), Ok(()));
3041 assert_eq!(check_access(locked, 0, 3, 0o070, Access::READ), Ok(()));
3042 assert_eq!(check_access(locked, 0, 3, 0o070, Access::WRITE), Ok(()));
3043 })
3044 .await;
3045 }
3046
3047 #[::fuchsia::test]
3048 async fn set_security_xattr_fails_without_security_module_or_root() {
3049 spawn_kernel_and_run(async |locked, current_task| {
3050 let mut creds = Credentials::with_ids(1, 2);
3051 creds.groups = vec![3, 4];
3052 current_task.set_creds(creds);
3053
3054 let node = ¤t_task
3056 .fs()
3057 .root()
3058 .create_node(locked, ¤t_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3059 .expect("create_node")
3060 .entry
3061 .node;
3062
3063 node.update_info(|info| info.mode = mode!(IFREG, 0o777));
3065
3066 assert_eq!(
3069 node.set_xattr(
3070 locked,
3071 ¤t_task,
3072 &MountInfo::detached(),
3073 "security.name".into(),
3074 "security_label".into(),
3075 XattrOp::Create,
3076 ),
3077 error!(EPERM)
3078 );
3079 })
3080 .await;
3081 }
3082
3083 #[::fuchsia::test]
3084 async fn set_non_user_xattr_fails_without_security_module_or_root() {
3085 spawn_kernel_and_run(async |locked, current_task| {
3086 let mut creds = Credentials::with_ids(1, 2);
3087 creds.groups = vec![3, 4];
3088 current_task.set_creds(creds);
3089
3090 let node = ¤t_task
3092 .fs()
3093 .root()
3094 .create_node(locked, ¤t_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3095 .expect("create_node")
3096 .entry
3097 .node;
3098
3099 node.update_info(|info| info.mode = mode!(IFREG, 0o777));
3101
3102 assert_eq!(
3105 node.set_xattr(
3106 locked,
3107 ¤t_task,
3108 &MountInfo::detached(),
3109 "trusted.name".into(),
3110 "some data".into(),
3111 XattrOp::Create,
3112 ),
3113 error!(EPERM)
3114 );
3115 })
3116 .await;
3117 }
3118
3119 #[::fuchsia::test]
3120 async fn get_security_xattr_succeeds_without_read_access() {
3121 spawn_kernel_and_run(async |locked, current_task| {
3122 let mut creds = Credentials::with_ids(1, 2);
3123 creds.groups = vec![3, 4];
3124 current_task.set_creds(creds);
3125
3126 let node = ¤t_task
3128 .fs()
3129 .root()
3130 .create_node(locked, ¤t_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3131 .expect("create_node")
3132 .entry
3133 .node;
3134
3135 node.update_info(|info| info.mode = mode!(IFREG, 0o100));
3137 current_task.set_creds(Credentials::with_ids(0, 0));
3138
3139 assert_eq!(
3141 node.set_xattr(
3142 locked,
3143 ¤t_task,
3144 &MountInfo::detached(),
3145 "security.name".into(),
3146 "security_label".into(),
3147 XattrOp::Create,
3148 ),
3149 Ok(())
3150 );
3151
3152 current_task.set_creds(Credentials::with_ids(1, 1));
3154
3155 assert_eq!(
3157 node.get_xattr(
3158 locked,
3159 ¤t_task,
3160 &MountInfo::detached(),
3161 "security.name".into(),
3162 4096
3163 ),
3164 Ok(ValueOrSize::Value("security_label".into()))
3165 );
3166 })
3167 .await;
3168 }
3169}