Skip to main content

starnix_core/vfs/
fs_node.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    /// The inode number for this FsNode.
85    pub ino: ino_t,
86
87    /// Flags for this node.
88    pub flags: FsNodeFlags,
89
90    /// The FsNodeOps for this FsNode.
91    ///
92    /// The FsNodeOps are implemented by the individual file systems to provide
93    /// specific behaviors for this FsNode.
94    ops: Box<dyn FsNodeOps>,
95
96    /// The FileSystem that owns this FsNode's tree.
97    fs: Weak<FileSystem>,
98
99    /// A RwLock to synchronize append operations for this node.
100    ///
101    /// FileObjects writing with O_APPEND should grab a write() lock on this
102    /// field to ensure they operate sequentially. FileObjects writing without
103    /// O_APPEND should grab read() lock so that they can operate in parallel.
104    pub append_lock: RwQueue<FsNodeAppend>,
105
106    /// Mutable information about this node.
107    ///
108    /// This data is used to populate the uapi::stat structure.
109    info: DynamicLockDepRwLock<FsNodeInfo>,
110
111    /// Data associated with an FsNode that is rarely needed.
112    rare_data: OnceLock<Box<FsNodeRareData>>,
113
114    /// Tracks lock state for this file.
115    pub write_guard_state: LockDepMutex<FileWriteGuardState, FsNodeWriteGuardStateLock>,
116
117    /// Cached FsVerity state associated with this node.
118    pub fsverity: LockDepMutex<FsVerityState, FsNodeFsVerityLock>,
119
120    /// The security state associated with this node. Must always be acquired last
121    /// relative to other `FsNode` locks.
122    pub security_state: security::FsNodeState,
123}
124
125#[derive(Default)]
126struct FsNodeRareData {
127    /// The pipe located at this node, if any.
128    ///
129    /// Used if, and only if, the node has a mode of FileMode::IFIFO.
130    fifo: OnceLock<PipeHandle>,
131
132    /// The UNIX domain socket bound to this node, if any.
133    bound_socket: OnceLock<SocketHandle>,
134
135    /// Information about the locking information on this node.
136    ///
137    /// No other lock on this object may be taken while this lock is held.
138    flock_info: LockDepMutex<FlockInfo, FsNodeFlockInfoLock>,
139
140    /// Records locks associated with this node.
141    record_locks: RecordLocks,
142
143    /// Whether this node can be linked into a directory.
144    ///
145    /// Only set for nodes created with `O_TMPFILE`.
146    link_behavior: OnceLock<FsNodeLinkBehavior>,
147
148    /// Inotify watchers on this node. See inotify(7).
149    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    // If this node is fscrypt encrypted, stores the id of the user wrapping key used to encrypt it.
196    pub wrapping_key_id: Option<[u8; 16]>,
197
198    // Used to indicate to filesystems that manage timestamps that an access has occurred and to
199    // update the node's atime.
200    // This only impacts accesses within Starnix. Most Fuchsia programs are not expected to maintain
201    // access times. If the file handle is transferred out of Starnix, there may be inconsistencies.
202    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        // Clear the setuid and setgid bits if the file is executable and a regular file.
237        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 the group execute bit is not set, the setgid bit actually indicates mandatory
245        // locking and should not be cleared.
246        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        // See <https://man7.org/linux/man-pages/man7/inode.7.html>:
266        //
267        //   For an executable file, the set-group-ID bit causes the
268        //   effective group ID of a process that executes the file to change
269        //   as described in execve(2).  For a file that does not have the
270        //   group execution bit (S_IXGRP) set, the set-group-ID bit indicates
271        //   mandatory file/record locking.
272        if self.mode.contains(FileMode::ISGID | FileMode::IXGRP) {
273            creds.egid = self.gid;
274        }
275    }
276}
277
278#[derive(Default)]
279struct FlockInfo {
280    /// Whether the node is currently locked. The meaning of the different values are:
281    /// - `None`: The node is not locked.
282    /// - `Some(false)`: The node is locked non exclusively.
283    /// - `Some(true)`: The node is locked exclusively.
284    locked_exclusive: Option<bool>,
285    /// The FileObject that hold the lock.
286    locking_handles: Vec<WeakFileHandle>,
287    /// The queue to notify process waiting on the lock.
288    wait_queue: WaitQueue,
289}
290
291impl FlockInfo {
292    /// Removes all file handle not holding `predicate` from the list of object holding the lock. If
293    /// this empties the list, unlocks the node and notifies all waiting processes.
294    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
309/// `st_blksize` is measured in units of 512 bytes.
310pub 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    /// Advisory locking.
342    ///
343    /// See flock(2).
344    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            // Operation is a locking operation.
361            // 1. File is not locked
362            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            // 2. File is locked, but fd already have a lock
380            if fd_has_lock {
381                if operation.is_lock_exclusive() == file_lock_is_exclusive {
382                    // Correct lock is already held, return.
383                    return Ok(());
384                } else {
385                    // Incorrect lock is held. Release the lock and loop back to try to reacquire
386                    // it. flock doesn't guarantee atomic lock type switching.
387                    flock_info.retain(|fh| !std::ptr::eq(fh, self));
388                    continue;
389                }
390            }
391
392            // 3. File is locked, and fd doesn't have a lock.
393            if !file_lock_is_exclusive && !operation.is_lock_exclusive() {
394                // The lock is not exclusive, let's grab it.
395                flock_info.locking_handles.push(self.weak_handle.clone());
396                return Ok(());
397            }
398
399            // 4. The operation cannot be done at this time.
400            if !operation.is_blocking() {
401                return error!(EAGAIN);
402            }
403
404            // Register a waiter to be notified when the lock is released. Release the lock on
405            // FlockInfo, and wait.
406            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
414// The inner mod is required because bitflags cannot pass the attribute through to the single
415// variant, and attributes cannot be applied to macro invocations.
416mod inner_flags {
417    // Part of the code for the AT_STATX_SYNC_AS_STAT case that's produced by the macro triggers the
418    // lint, but as a whole, the produced code is still correct.
419    #![allow(clippy::bad_bit_mask)] // TODO(b/303500202) Remove once addressed in bitflags.
420    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    /// Unlink a directory.
441    Directory,
442
443    /// Unlink a non-directory.
444    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 the value of the extended attribute regardless of whether it exists.
455    Set,
456    /// Create a new extended attribute. Fail if it already exists.
457    Create,
458    /// Replace the value of the extended attribute. Fail if it doesn't exist.
459    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/// Returns a value, or the size required to contains it.
473#[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        // `fallocate()` allows only the following values for `mode`.
518        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    /// Delegate the access check to the node.
554    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    /// Build the [`DirEntryOps`] for a new [`DirEntry`] that will be associated
568    /// to this node.
569    fn create_dir_entry_ops(&self) -> Box<dyn DirEntryOps> {
570        Box::new(DefaultDirEntryOps)
571    }
572
573    /// Build the `FileOps` for the file associated to this node.
574    ///
575    /// The returned FileOps will be used to create a FileObject, which might
576    /// be assigned an FdNumber.
577    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    /// Find an existing child node and populate the child parameter. Return the node.
586    ///
587    /// The child parameter is an empty node. Operations other than initialize may panic before
588    /// initialize is called.
589    fn lookup(
590        &self,
591        _locked: &mut Locked<FileOpsCore>,
592        _node: &FsNode,
593        _current_task: &CurrentTask,
594        name: &FsStr,
595    ) -> Result<FsNodeHandle, Errno> {
596        // The default implementation here is suitable for filesystems that have permanent entries;
597        // entries that already exist will get found in the cache and shouldn't get this far.
598        error!(ENOENT, format!("looking for {name}"))
599    }
600
601    /// Returns whether this node supports pipelined lookups.
602    fn has_lookup_pipelined(&self) -> bool {
603        false
604    }
605
606    /// Find multiple children nodes in sequence.
607    ///
608    /// This can be used to pipeline lookups in filesystems that support it.
609    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    /// Create and return the given child node.
620    ///
621    /// The mode field of the FsNodeInfo indicates what kind of child to
622    /// create.
623    ///
624    /// This function is never called with FileMode::IFDIR. The mkdir function
625    /// is used to create directories instead.
626    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    /// Create and return the given child node as a subdirectory.
638    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    /// Creates a symlink with the given `target` path.
649    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    /// Creates an anonymous file.
660    ///
661    /// The FileMode::IFMT of the FileMode is always FileMode::IFREG.
662    ///
663    /// Used by O_TMPFILE.
664    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    /// Reads the symlink from this node.
675    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    /// Create a hard link with the given name to the given child.
685    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    /// Remove the child with the given name, if the child exists.
697    ///
698    /// The UnlinkKind parameter indicates whether the caller intends to unlink
699    /// a directory or a non-directory child.
700    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    /// Acquire the necessary append lock for the operations that depend on them.
710    /// Should be done before calling `allocate` or `truncate` to avoid lock ordering issues.
711    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    /// Acquire the necessary append lock for operations that need exclusive access (e.g., write append).
721    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    /// Change the length of the file.
731    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    /// Manipulate allocated disk space for the file.
743    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    /// Update the supplied info with initial state (e.g. size) for the node.
757    ///
758    /// FsNode calls this method when created, to allow the FsNodeOps to
759    /// set appropriate initial values in the FsNodeInfo.
760    fn initial_info(&self, _info: &mut FsNodeInfo) {}
761
762    /// Update node.info as needed.
763    ///
764    /// FsNode calls this method before converting the FsNodeInfo struct into
765    /// the uapi::stat struct to give the file system a chance to update this data
766    /// before it is used by clients.
767    ///
768    /// File systems that keep the FsNodeInfo up-to-date do not need to
769    /// override this function.
770    ///
771    /// Return a read guard for the updated information.
772    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    /// Syncs cached data to persistent storage.
783    fn sync(&self, _node: &FsNode, _current_task: &CurrentTask) -> Result<(), Errno> {
784        Ok(())
785    }
786
787    /// Update node attributes persistently.
788    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    /// Get an extended attribute on the node.
800    ///
801    /// An implementation can systematically return a value. Otherwise, if `max_size` is 0, it can
802    /// instead return the size of the attribute, and can return an ERANGE error if max_size is not
803    /// 0, and lesser than the required size.
804    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    /// Set an extended attribute on the node.
816    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    /// An implementation can systematically return a value. Otherwise, if `max_size` is 0, it can
839    /// instead return the size of the 0 separated string needed to represent the value, and can
840    /// return an ERANGE error if max_size is not 0, and lesser than the required size.
841    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    /// Called when the FsNode is freed by the Kernel.
852    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    ////////////////////
862    // FS-Verity operations
863
864    /// Marks that FS-Verity is being built. Writes fsverity descriptor and merkle tree, the latter
865    /// computed by the filesystem.
866    /// This should ensure there are no writable file handles. Returns EEXIST if the file was
867    /// already fsverity-enabled. Returns EBUSY if this ioctl was already running on this file.
868    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    /// Read fsverity descriptor, if the node is fsverity-enabled. Else returns ENODATA.
879    fn get_fsverity_descriptor(&self, _log_blocksize: u8) -> Result<fsverity_descriptor, Errno> {
880        error!(ENOTSUP)
881    }
882
883    /// The key used to identify this node in the file system's node cache.
884    ///
885    /// For many file systems, this will be the same as the inode number. However, some file
886    /// systems, such as FUSE, sometimes use different `node_key` and inode numbers.
887    fn node_key(&self, node: &FsNode) -> ino_t {
888        node.ino
889    }
890
891    /// Returns the size of the file.
892    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/// Implements [`FsNodeOps`] methods in a way that makes sense for symlinks.
913/// You must implement [`FsNodeOps::readlink`].
914#[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
1022/// Trait that objects can implement if they need to handle extended attribute storage. Allows
1023/// delegating extended attribute operations in [`FsNodeOps`] to another object.
1024///
1025/// See [`fs_node_impl_xattr_delegate`] for usage details.
1026pub trait XattrStorage {
1027    /// Delegate for [`FsNodeOps::get_xattr`].
1028    fn get_xattr(&self, locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<FsString, Errno>;
1029
1030    /// Delegate for [`FsNodeOps::set_xattr`].
1031    fn set_xattr(
1032        &self,
1033        locked: &mut Locked<FileOpsCore>,
1034        name: &FsStr,
1035        value: &FsStr,
1036        op: XattrOp,
1037    ) -> Result<(), Errno>;
1038
1039    /// Delegate for [`FsNodeOps::remove_xattr`].
1040    fn remove_xattr(&self, locked: &mut Locked<FileOpsCore>, name: &FsStr) -> Result<(), Errno>;
1041
1042    /// Delegate for [`FsNodeOps::list_xattrs`].
1043    fn list_xattrs(&self, locked: &mut Locked<FileOpsCore>) -> Result<Vec<FsString>, Errno>;
1044}
1045
1046/// Implements extended attribute ops for [`FsNodeOps`] by delegating to another object which
1047/// implements the [`XattrStorage`] trait or a similar interface. For example:
1048///
1049/// ```
1050/// struct Xattrs {}
1051///
1052/// impl XattrStorage for Xattrs {
1053///     // implement XattrStorage
1054/// }
1055///
1056/// struct Node {
1057///     xattrs: Xattrs
1058/// }
1059///
1060/// impl FsNodeOps for Node {
1061///     // Delegate extended attribute ops in FsNodeOps to self.xattrs
1062///     fs_node_impl_xattr_delegate!(self, self.xattrs);
1063///
1064///     // add other FsNodeOps impls here
1065/// }
1066/// ```
1067#[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/// Stubs out [`FsNodeOps`] methods that only apply to directories.
1116#[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
1186// Public re-export of macros allows them to be used like regular rust items.
1187pub 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    /// Returns true if the `fs_node` is private to the `Kernel`/`FileSystem`, in which
1210    /// case both MAC and DAC checks should be skipped.
1211    pub fn is_private(&self) -> bool {
1212        self.flags.contains(FsNodeFlags::IS_PRIVATE)
1213    }
1214
1215    /// Create a node without inserting it into the FileSystem node cache.
1216    ///
1217    /// This is usually not what you want!
1218    /// Only use if you're also using get_or_create_node, like ext4.
1219    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        // Allow the FsNodeOps to populate initial info.
1238        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        // The linter will fail in non test mode as it will not see the lock check.
1250        #[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    /// Returns an error if this node is encrypted and locked. Does not require
1289    /// fetch_and_refresh_info because FS_IOC_SET_ENCRYPTION_POLICY updates info and once a node is
1290    /// encrypted, it remains encrypted forever.
1291    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    /// Returns the `FsNode`'s `FsNodeOps` as a `&T`, or `None` if the downcast fails.
1306    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            // This function will drop the flock from `file` because the `WeakFileHandle` for
1317            // `file` will no longer upgrade to an `FileHandle`.
1318            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    /// Release all record locks acquired by the given owner.
1335    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 O_PATH is set, there is no need to create a real FileOps because
1367        // most file operations are disabled.
1368        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            // `flags` doesn't contain any information about the EXEC permission. Instead the syscalls
1379            // used to execute a file (`sys_execve` and `sys_execveat`) call `open()` with the EXEC
1380            // permission request in `access`.
1381            let mut permission_flags = PermissionFlags::from(access);
1382
1383            // The `APPEND` flag exists only in `flags`, to modify the behaviour of
1384            // `PermissionFlags::WRITE`
1385            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            // Don't hold the info lock while calling into open_device or self.ops().
1401            // TODO: The mode and rdev are immutable and shouldn't require a lock to read.
1402            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            // UNIX domain sockets can't be opened.
1435            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            // Even though the man page for mknod(2) says that mknod "cannot be used to create
1491            // directories" in starnix the mkdir syscall (`sys_mkdirat`) ends up calling
1492            // create_node.
1493            security::check_fs_node_mkdir_access(current_task, self, mode, name)?;
1494        } else {
1495            // https://man7.org/linux/man-pages/man2/mknod.2.html says on error EPERM:
1496            //
1497            //   mode requested creation of something other than a regular
1498            //   file, FIFO (named pipe), or UNIX domain socket, and the
1499            //   caller is not privileged (Linux: does not have the
1500            //   CAP_MKNOD capability); also returned if the filesystem
1501            //   containing pathname does not support the type of node
1502            //   requested.
1503            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        // Propagate sticky bit(s) from parent directory to the child.
1517        self.update_metadata_for_child(current_task, &mut mode, &mut owner);
1518
1519        // Delegate to the `ops` implementation to actually create the node.
1520        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        // Allow the LSM to apply a security label to the new node.
1528        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    /// Requests that the LSM initialise a security label for the `new_node`, and optionally provide
1565    /// an extended attribute to write to the file to persist it.  If no LSM is enabled, no extended
1566    /// attribute returned, or if the filesystem does not support extended attributes, then the call
1567    /// returns success. All other failure modes return an `Errno` that should be early-returned.
1568    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                            // This should only occur if a task has an "fscreate" context set, and
1592                            // creates a new file in a filesystem that does not support xattrs.
1593                            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    // This method does not attempt to update the atime of the node.
1634    // Use `NamespaceNode::readlink` which checks the mount flags and updates the atime accordingly.
1635    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        // TODO: 378864856 - Is there a permission check here other than security checks?
1644        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        // Check that `current_task` has permission to create the hard link.
1679        //
1680        // See description of /proc/sys/fs/protected_hardlinks in
1681        // https://man7.org/linux/man-pages/man5/proc.5.html for details of the security
1682        // vulnerabilities.
1683        //
1684        let (child_uid, mode) = {
1685            let info = child.info();
1686            (info.uid, info.mode)
1687        };
1688        // Check that the the filesystem UID of the calling process (`current_task`) is the same as
1689        // the UID of the existing file. The check can be bypassed if the calling process has
1690        // `CAP_FOWNER` capability.
1691        if child_uid != current_task.current_creds().fsuid
1692            && !security::is_task_capable_noaudit(current_task, CAP_FOWNER)
1693        {
1694            // If current_task is not the user of the existing file, it needs to have read and write
1695            // access to the existing file.
1696            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                    // `check_access(..)` returns EACCES when the access rights doesn't match - change
1707                    // it to EPERM to match Linux standards.
1708                    if e == EACCES { errno!(EPERM) } else { e }
1709                })?;
1710            // There are also security issues that may arise when users link to setuid, setgid, or
1711            // special files.
1712            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        // The user must be able to search and write to the directory.
1742        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            // Safe because we acquire the parent directory lock first, and then the child lock
1753            // inside check_sticky_bit. This parent -> child acquisition follows the
1754            // hierarchical lock ordering.
1755            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    /// Avoid calling this method directly. You probably want to call `FileObject::ftruncate()`
1797    /// which will also perform all file-descriptor based verifications.
1798    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            // When truncating a file descriptor, if the descriptor references a directory,
1811            // return EINVAL. This is different from the truncate() syscall which returns EISDIR.
1812            //
1813            // See https://man7.org/linux/man-pages/man2/ftruncate.2.html#ERRORS
1814            return error!(EINVAL);
1815        }
1816
1817        // For ftruncate, we do not need to check that the file node is writable.
1818        //
1819        // The file object that calls this method must verify that the file was opened
1820        // with write permissions.
1821        //
1822        // This matters because a file could be opened with O_CREAT + O_RDWR + 0444 mode.
1823        // The file descriptor returned from such an operation can be truncated, even
1824        // though the file was created with a read-only mode.
1825        //
1826        // See https://man7.org/linux/man-pages/man2/ftruncate.2.html#DESCRIPTION
1827        // which says:
1828        //
1829        // "With ftruncate(), the file must be open for writing; with truncate(),
1830        // the file must be writable."
1831
1832        let (guard, locked) = self.ops().append_lock_write(locked, self, current_task)?;
1833        self.truncate_locked(locked, &guard, current_task, length)
1834    }
1835
1836    // Called by `truncate` and `ftruncate` above.
1837    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    /// Avoid calling this method directly. You probably want to call `FileObject::fallocate()`
1863    /// which will also perform additional verifications.
1864    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        // The setgid bit on a directory causes the gid to be inherited by new children and the
1914        // setgid bit to be inherited by new child directories. See SetgidDirTest in gvisor.
1915        {
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            // https://man7.org/linux/man-pages/man7/inode.7.html says:
1927            //
1928            //   For an executable file, the set-group-ID bit causes the
1929            //   effective group ID of a process that executes the file to change
1930            //   as described in execve(2).
1931            //
1932            // We need to check whether the current task has permission to create such a file.
1933            // See a similar check in `FsNode::chmod`.
1934            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    /// Checks if O_NOATIME is allowed,
1945    pub fn check_o_noatime_allowed(&self, current_task: &CurrentTask) -> Result<(), Errno> {
1946        // Per open(2),
1947        //
1948        //   O_NOATIME (since Linux 2.6.8)
1949        //      ...
1950        //
1951        //      This flag can be employed only if one of the following
1952        //      conditions is true:
1953        //
1954        //      *  The effective UID of the process matches the owner UID
1955        //         of the file.
1956        //
1957        //      *  The calling process has the CAP_FOWNER capability in
1958        //         its user namespace and the owner UID of the file has a
1959        //         mapping in the namespace.
1960        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            // To set the timestamps to the current time the caller must either have write access to
1978            // the file, be the file owner, or hold the CAP_DAC_OVERRIDE or CAP_FOWNER capability.
1979            // To set the timestamps to other values the caller must either be the file owner or hold
1980            // the CAP_FOWNER capability.
1981            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    /// Check whether the node can be accessed in the current context with the specified access
1998    /// flags (read, write, or exec). Accounts for capabilities and whether the current user is the
1999    /// owner or is in the file's group.
2000    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    /// Check whether the stick bit, `S_ISVTX`, forbids the `current_task` from removing the given
2036    /// `child`. If this node has `S_ISVTX`, then either the child must be owned by the `fsuid` of
2037    /// `current_task` or `current_task` must have `CAP_FOWNER`.
2038    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    /// Returns the UNIX domain socket bound to this node, if any.
2058    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    /// Register the provided socket as the UNIX domain socket bound to this node.
2063    ///
2064    /// It is a fatal error to call this method again if it has already been called on this node.
2065    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        // `mutator`s should not update the attribute change time, which is managed by this API.
2097        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        // Call `update_attributes(..)` to persist the changes for the following fields.
2116        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    /// Set the permissions on this FsNode to the given values.
2134    ///
2135    /// Does not change the IFMT of the node.
2136    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    /// Sets the owner and/or group on this FsNode.
2164    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            // Nobody can change the owner.
2183            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            // The owner can change the group.
2195            if info.uid == euid {
2196                // To a group that it belongs.
2197                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            // Any other user can call chown(file, -1, -1)
2207            if owner.is_some() || group.is_some() {
2208                return error!(EPERM);
2209            }
2210
2211            // But not on set-user-ID or set-group-ID files.
2212            // If we were to chown them, they would drop the set-ID bit.
2213            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    /// Forcefully change the owner and group of this node.
2226    ///
2227    /// # Safety
2228    ///
2229    /// This function skips all the security checks and just updates the owner and group. Also, does
2230    /// not check if the filesystem is read-only and does not update the attribute change time.
2231    ///
2232    /// This function is used to set the owner and group of /proc/pid to the credentials of the
2233    /// current task. Please consider carefully whether you want to use this function for another
2234    /// purpose.
2235    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    /// Whether this node is a regular file.
2242    pub fn is_reg(&self) -> bool {
2243        self.info().mode.is_reg()
2244    }
2245
2246    /// Whether this node is a directory.
2247    pub fn is_dir(&self) -> bool {
2248        self.info().mode.is_dir()
2249    }
2250
2251    /// Whether this node is a socket.
2252    pub fn is_sock(&self) -> bool {
2253        self.info().mode.is_sock()
2254    }
2255
2256    /// Whether this node is a FIFO.
2257    pub fn is_fifo(&self) -> bool {
2258        self.info().mode.is_fifo()
2259    }
2260
2261    /// Whether this node is a symbolic link.
2262    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    /// Returns the current size of the file.  This is inherently racy, so any caller that
2315    /// might want to use the value returned should hold their own locks if necessary.  For
2316    /// example, if using the value here to implement append (which is the case at the time
2317    /// of writing this comment), locks must be held to prevent the file size being changed
2318    /// concurrently.
2319    // TODO(https://fxbug.dev/454730248): This is probably the wrong way to implement O_APPEND.
2320    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        // Ignore mask for now and fill in all of the fields.
2353        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    /// Checks whether `current_task` has capabilities required for the specified `access` to the
2407    /// extended attribute `name`.
2408    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        // man xattr(7) describes the different access checks applied to each extended attribute
2425        // namespace.
2426        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            // TODO: https://fxbug.dev/460734830 - Perform capability check(s) if file has sticky
2435            // bit set.
2436
2437            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            // Trusted extended attributes require `CAP_SYS_ADMIN` to read or write.
2447            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            // System extended attributes have attribute-specific access policy.
2450            // TODO: https://fxbug.dev/460734830 -  Revise how system extended attributes are
2451            // access-controlled.
2452            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                // Writes require `CAP_SYS_ADMIN`, unless the LSM owning `name` specifies to skip.
2456                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        // Perform discretionary capability & access checks appropriate to the xattr prefix.
2479        self.check_xattr_access(locked, current_task, mount, name, Access::READ)?;
2480
2481        // LSM access checks must be performed after discretionary checks.
2482        security::check_fs_node_getxattr_access(current_task, self, name)?;
2483
2484        if name.starts_with(XATTR_SECURITY_PREFIX.to_bytes()) {
2485            // If the attribute is in the security.* domain then allow the LSM to handle the
2486            // request, or to delegate to `FsNodeOps::get_xattr()`.
2487            security::fs_node_getsecurity(locked, current_task, self, name, max_size)
2488        } else {
2489            // If the attribute is outside security.*, delegate the read to the `FsNodeOps`.
2490            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        // Perform discretionary capability & access checks appropriate to the xattr prefix.
2513        self.check_xattr_access(locked, current_task, mount, name, Access::WRITE)?;
2514
2515        // LSM access checks must be performed after discretionary checks.
2516        security::check_fs_node_setxattr_access(current_task, self, name, value, op)?;
2517
2518        if name.starts_with(XATTR_SECURITY_PREFIX.to_bytes()) {
2519            // If the attribute is in the security.* domain then allow the LSM to handle the
2520            // request, or to delegate to `FsNodeOps::set_xattr()`.
2521            security::fs_node_setsecurity(locked, current_task, self, name, value, op)
2522        } else {
2523            // If the attribute is outside security.*, delegate the read to the `FsNodeOps`.
2524            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        // Perform discretionary capability & access checks appropriate to the xattr prefix.
2546        self.check_xattr_access(locked, current_task, mount, name, Access::WRITE)?;
2547
2548        // LSM access checks must be performed after discretionary checks.
2549        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                // Extended attributes may be listed even if the caller would not be able to read
2568                // (or modify) the attribute's value.
2569                // trusted.* attributes are only accessible with CAP_SYS_ADMIN and are omitted by
2570                // `listxattr()` unless the caller holds CAP_SYS_ADMIN.
2571                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    /// Returns current `FsNodeInfo`.
2579    pub fn info(&self) -> LockDepReadGuard<'_, FsNodeInfo> {
2580        self.info.read()
2581    }
2582
2583    /// Returns a reference to the `info` lock itself.
2584    ///
2585    /// This should ONLY be used by `RenameGuard` to perform ordered write locking on independent
2586    /// nodes.
2587    pub(super) fn info_lock(&self) -> &DynamicLockDepRwLock<FsNodeInfo> {
2588        &self.info
2589    }
2590
2591    /// Refreshes the `FsNodeInfo` if necessary and returns a read guard.
2592    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    /// Clear the SUID and SGID bits unless the `current_task` has `CAP_FSETID`
2617    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    /// Update the ctime and mtime of a file to now.
2635    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    /// Update the ctime of a file to now.
2647    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    /// Update the atime and mtime if the `current_task` has write access, is the file owner, or
2658    /// holds either the CAP_DAC_OVERRIDE or CAP_FOWNER capability.
2659    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        // If the filesystem is read-only, this always fail.
2671        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            // This function is called by `utimes(..)` which will update the access and
2685            // modification time. We need to call `update_attributes()` to update the mtime of
2686            // filesystems that manages file timestamps.
2687            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    /// The key used to identify this node in the file system's node cache.
2707    ///
2708    /// For many file systems, this will be the same as the inode number. However, some file
2709    /// systems, such as FUSE, sometimes use different `node_key` and inode numbers.
2710    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    /// Returns the set of watchers for this node.
2719    ///
2720    /// Only call this function if you require this node to actually store a list of watchers. If
2721    /// you just wish to notify any watchers that might exist, please use `notify` instead.
2722    pub fn ensure_watchers(&self) -> &inotify_hook::InotifyWatchers {
2723        &self.ensure_rare_data().watchers
2724    }
2725
2726    /// Notify the watchers of the given event.
2727    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    /// Calls through to the filesystem to enable fs-verity on this file.
2744    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    // Determine which of the access bits apply to the `current_task`.
2795    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    // Callers with CAP_DAC_READ_SEARCH override can read files & directories, and traverse
2813    // directories to which they lack permission.
2814    let mut requested = access & !granted;
2815
2816    // If this check was triggered by `access()`, or a variant, then check for a `dontaudit`
2817    // statement for the `audit_access` permission for this caller & file.
2818    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    // CAP_DAC_READ_SEARCH allows bypass of read checks, and directory traverse (eXecute) checks.
2832    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    // CAP_DAC_OVERRIDE allows bypass of all checks (though see the comment for file-execute).
2844    let mut dac_override_access = Access::READ | Access::WRITE;
2845    dac_override_access |= if mode.is_dir() {
2846        Access::EXEC
2847    } else {
2848        // File execute access checks may not be bypassed unless at least one executable bit is set.
2849        (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            // Create a device file that points to the `zero` device (which is automatically
2876            // registered in the kernel).
2877            current_task
2878                .fs()
2879                .root()
2880                .create_node(
2881                    locked,
2882                    &current_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            // Read from the zero device.
2893            let device_file = current_task
2894                .open_file(locked, "zero".into(), OpenFlags::RDONLY)
2895                .expect("open device file");
2896            device_file.read(locked, &current_task, &mut buffer).expect("read from zero");
2897
2898            // Assert the contents.
2899            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            // Create a node.
2908            let node = &current_task
2909                .fs()
2910                .root()
2911                .create_node(locked, &current_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, &current_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            // Create a node.
2977            let node = &current_task
2978                .fs()
2979                .root()
2980                .create_node(locked, &current_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                    &current_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            // Create a node.
3055            let node = &current_task
3056                .fs()
3057                .root()
3058                .create_node(locked, &current_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3059                .expect("create_node")
3060                .entry
3061                .node;
3062
3063            // Give read-write-execute access.
3064            node.update_info(|info| info.mode = mode!(IFREG, 0o777));
3065
3066            // Without a security module, and without CAP_SYS_ADMIN capabilities, setting the xattr
3067            // should fail.
3068            assert_eq!(
3069                node.set_xattr(
3070                    locked,
3071                    &current_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            // Create a node.
3091            let node = &current_task
3092                .fs()
3093                .root()
3094                .create_node(locked, &current_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3095                .expect("create_node")
3096                .entry
3097                .node;
3098
3099            // Give read-write-execute access.
3100            node.update_info(|info| info.mode = mode!(IFREG, 0o777));
3101
3102            // Without a security module, and without CAP_SYS_ADMIN capabilities, setting the xattr
3103            // should fail.
3104            assert_eq!(
3105                node.set_xattr(
3106                    locked,
3107                    &current_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            // Create a node.
3127            let node = &current_task
3128                .fs()
3129                .root()
3130                .create_node(locked, &current_task, "foo".into(), FileMode::IFREG, DeviceId::NONE)
3131                .expect("create_node")
3132                .entry
3133                .node;
3134
3135            // Only give read access to the root and give root access to the current task.
3136            node.update_info(|info| info.mode = mode!(IFREG, 0o100));
3137            current_task.set_creds(Credentials::with_ids(0, 0));
3138
3139            // Setting the label should succeed even without write access to the file.
3140            assert_eq!(
3141                node.set_xattr(
3142                    locked,
3143                    &current_task,
3144                    &MountInfo::detached(),
3145                    "security.name".into(),
3146                    "security_label".into(),
3147                    XattrOp::Create,
3148                ),
3149                Ok(())
3150            );
3151
3152            // Remove root access from the current task.
3153            current_task.set_creds(Credentials::with_ids(1, 1));
3154
3155            // Getting the label should succeed even without read access to the file.
3156            assert_eq!(
3157                node.get_xattr(
3158                    locked,
3159                    &current_task,
3160                    &MountInfo::detached(),
3161                    "security.name".into(),
3162                    4096
3163                ),
3164                Ok(ValueOrSize::Value("security_label".into()))
3165            );
3166        })
3167        .await;
3168    }
3169}