Skip to main content

starnix_core/vfs/
file_object.rs

1// Cmpyright 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::mm::memory::MemoryObject;
6use crate::mm::{DesiredAddress, MappingName, MappingOptions, MemoryAccessorExt, ProtectionFlags};
7use crate::power::OnWakeOps;
8use crate::security;
9use crate::task::{
10    CurrentTask, CurrentTaskAndLocked, EventHandler, ThreadGroupKey, WaitCallback, WaitCanceler,
11    Waiter, register_delayed_release,
12};
13use crate::vfs::buffers::{InputBuffer, OutputBuffer};
14use crate::vfs::file_server::serve_file;
15use crate::vfs::fsverity::{
16    FsVerityState, {self},
17};
18use crate::vfs::{
19    ActiveNamespaceNode, DirentSink, EpollFileObject, EpollKey, FallocMode, FdTableId,
20    FileSystemHandle, FileWriteGuardMode, FsNodeHandle, FsString, NamespaceNode, RecordLockCommand,
21    RecordLockOwner,
22};
23use starnix_crypt::EncryptionKeyId;
24use starnix_lifecycle::{ObjectReleaser, ReleaserAction};
25use starnix_rcu::RcuAtomic;
26use starnix_types::ownership::ReleaseGuard;
27use starnix_uapi::mount_flags::MountFlags;
28use starnix_uapi::user_address::ArchSpecific;
29
30use fidl::endpoints::ProtocolMarker as _;
31use linux_uapi::{FSCRYPT_MODE_AES_256_CTS, FSCRYPT_MODE_AES_256_XTS};
32use starnix_logging::{CATEGORY_STARNIX_MM, impossible_error, log_error, track_stub};
33use starnix_sync::{
34    BeforeFsNodeAppend, FileAsyncOwnerLock, FileEpollFilesLock, FileLeaseLock, FileObjectOffset,
35    FileOpsCore, LockBefore, LockDepMutex, LockEqualOrBefore, Locked, Unlocked,
36};
37use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
38use starnix_types::math::round_up_to_system_page_size;
39use starnix_types::ownership::Releasable;
40use starnix_uapi::arc_key::WeakKey;
41use starnix_uapi::as_any::AsAny;
42use starnix_uapi::auth::{CAP_FOWNER, CAP_SYS_RAWIO};
43use starnix_uapi::errors::{EAGAIN, ETIMEDOUT, Errno};
44use starnix_uapi::file_lease::FileLeaseType;
45use starnix_uapi::file_mode::Access;
46use starnix_uapi::inotify_mask::InotifyMask;
47use starnix_uapi::open_flags::{AtomicOpenFlags, OpenFlags};
48use starnix_uapi::seal_flags::SealFlags;
49use starnix_uapi::user_address::{UserAddress, UserRef};
50use starnix_uapi::vfs::FdEvents;
51use starnix_uapi::{
52    FIBMAP, FIGETBSZ, FIONBIO, FIONREAD, FIOQSIZE, FS_CASEFOLD_FL, FS_IOC_ADD_ENCRYPTION_KEY,
53    FS_IOC_ENABLE_VERITY, FS_IOC_FSGETXATTR, FS_IOC_FSSETXATTR, FS_IOC_MEASURE_VERITY,
54    FS_IOC_READ_VERITY_METADATA, FS_IOC_REMOVE_ENCRYPTION_KEY, FS_IOC_SET_ENCRYPTION_POLICY,
55    FS_VERITY_FL, FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER, FSCRYPT_POLICY_V2, SEEK_CUR, SEEK_DATA,
56    SEEK_END, SEEK_HOLE, SEEK_SET, errno, error, fscrypt_add_key_arg, fscrypt_identifier, fsxattr,
57    off_t, pid_t, uapi,
58};
59use std::collections::HashMap;
60use std::fmt;
61use std::ops::Deref;
62use std::sync::atomic::Ordering;
63use std::sync::{Arc, Weak};
64
65pub const MAX_LFS_FILESIZE: usize = 0x7fff_ffff_ffff_ffff;
66
67pub fn checked_add_offset_and_length(offset: usize, length: usize) -> Result<usize, Errno> {
68    let end = offset.checked_add(length).ok_or_else(|| errno!(EINVAL))?;
69    if end > MAX_LFS_FILESIZE {
70        return error!(EINVAL);
71    }
72    Ok(end)
73}
74
75#[derive(Debug)]
76pub enum SeekTarget {
77    /// Seek to the given offset relative to the start of the file.
78    Set(off_t),
79    /// Seek to the given offset relative to the current position.
80    Cur(off_t),
81    /// Seek to the given offset relative to the end of the file.
82    End(off_t),
83    /// Seek for the first data after the given offset,
84    Data(off_t),
85    /// Seek for the first hole after the given offset,
86    Hole(off_t),
87}
88
89impl SeekTarget {
90    pub fn from_raw(whence: u32, offset: off_t) -> Result<SeekTarget, Errno> {
91        match whence {
92            SEEK_SET => Ok(SeekTarget::Set(offset)),
93            SEEK_CUR => Ok(SeekTarget::Cur(offset)),
94            SEEK_END => Ok(SeekTarget::End(offset)),
95            SEEK_DATA => Ok(SeekTarget::Data(offset)),
96            SEEK_HOLE => Ok(SeekTarget::Hole(offset)),
97            _ => error!(EINVAL),
98        }
99    }
100
101    pub fn whence(&self) -> u32 {
102        match self {
103            Self::Set(_) => SEEK_SET,
104            Self::Cur(_) => SEEK_CUR,
105            Self::End(_) => SEEK_END,
106            Self::Data(_) => SEEK_DATA,
107            Self::Hole(_) => SEEK_HOLE,
108        }
109    }
110
111    pub fn offset(&self) -> off_t {
112        match self {
113            Self::Set(off)
114            | Self::Cur(off)
115            | Self::End(off)
116            | Self::Data(off)
117            | Self::Hole(off) => *off,
118        }
119    }
120}
121
122/// Corresponds to struct file_operations in Linux, plus any filesystem-specific data.
123pub trait FileOps: Send + Sync + AsAny + 'static {
124    /// Called when the FileObject is opened/created
125    fn open(
126        &self,
127        _locked: &mut Locked<FileOpsCore>,
128        _file: &FileObject,
129        _current_task: &CurrentTask,
130    ) -> Result<(), Errno> {
131        Ok(())
132    }
133
134    /// Called when the FileObject is destroyed.
135    fn close(
136        self: Box<Self>,
137        _locked: &mut Locked<FileOpsCore>,
138        _file: &FileObjectState,
139        _current_task: &CurrentTask,
140    ) {
141    }
142
143    /// Called every time close() is called on this file, even if the file is not ready to be
144    /// released.
145    fn flush(
146        &self,
147        _locked: &mut Locked<FileOpsCore>,
148        _file: &FileObject,
149        _current_task: &CurrentTask,
150    ) {
151    }
152
153    /// Returns whether the file has meaningful seek offsets. Returning `false` is only
154    /// optimization and will makes `FileObject` never hold the offset lock when calling `read` and
155    /// `write`.
156    fn has_persistent_offsets(&self) -> bool {
157        self.is_seekable()
158    }
159
160    /// Returns whether the file is seekable.
161    fn is_seekable(&self) -> bool;
162
163    /// Returns true if `write()` operations on the file will update the seek offset.
164    fn writes_update_seek_offset(&self) -> bool {
165        self.has_persistent_offsets()
166    }
167
168    /// Read from the file at an offset. If the file does not have persistent offsets (either
169    /// directly, or because it is not seekable), offset will be 0 and can be ignored.
170    /// Returns the number of bytes read.
171    fn read(
172        &self,
173        locked: &mut Locked<FileOpsCore>,
174        file: &FileObject,
175        current_task: &CurrentTask,
176        offset: usize,
177        data: &mut dyn OutputBuffer,
178    ) -> Result<usize, Errno>;
179
180    /// Write to the file with an offset. If the file does not have persistent offsets (either
181    /// directly, or because it is not seekable), offset will be 0 and can be ignored.
182    /// Returns the number of bytes written.
183    fn write(
184        &self,
185        locked: &mut Locked<FileOpsCore>,
186        file: &FileObject,
187        current_task: &CurrentTask,
188        offset: usize,
189        data: &mut dyn InputBuffer,
190    ) -> Result<usize, Errno>;
191
192    /// Adjust the `current_offset` if the file is seekable.
193    fn seek(
194        &self,
195        locked: &mut Locked<FileOpsCore>,
196        file: &FileObject,
197        current_task: &CurrentTask,
198        current_offset: off_t,
199        target: SeekTarget,
200    ) -> Result<off_t, Errno>;
201
202    /// Syncs cached state associated with the file descriptor to persistent storage.
203    ///
204    /// The method blocks until the synchronization is complete.
205    fn sync(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
206        file.node().ops().sync(file.node(), current_task)
207    }
208
209    /// Syncs cached data, and only enough metadata to retrieve said data, to persistent storage.
210    ///
211    /// The method blocks until the synchronization is complete.
212    fn data_sync(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
213        // TODO(https://fxbug.dev/297305634) make a default macro once data can be done separately
214        self.sync(file, current_task)
215    }
216
217    /// Returns a VMO representing this file. At least the requested protection flags must
218    /// be set on the VMO. Reading or writing the VMO must read or write the file. If this is not
219    /// possible given the requested protection, an error must be returned.
220    /// The `length` is a hint for the desired size of the VMO. The returned VMO may be larger or
221    /// smaller than the requested length.
222    /// This method is typically called by [`Self::mmap`].
223    fn get_memory(
224        &self,
225        _locked: &mut Locked<FileOpsCore>,
226        _file: &FileObject,
227        _current_task: &CurrentTask,
228        _length: Option<usize>,
229        _prot: ProtectionFlags,
230    ) -> Result<Arc<MemoryObject>, Errno> {
231        error!(ENODEV)
232    }
233
234    /// Responds to an mmap call. The default implementation calls [`Self::get_memory`] to get a VMO
235    /// and then maps it with [`crate::mm::MemoryManager::map`].
236    /// Only implement this trait method if your file needs to control mapping, or record where
237    /// a VMO gets mapped.
238    fn mmap(
239        &self,
240        locked: &mut Locked<FileOpsCore>,
241        file: &FileObject,
242        current_task: &CurrentTask,
243        addr: DesiredAddress,
244        memory_offset: u64,
245        length: usize,
246        prot_flags: ProtectionFlags,
247        options: MappingOptions,
248        filename: NamespaceNode,
249    ) -> Result<UserAddress, Errno> {
250        default_mmap(
251            locked,
252            file,
253            current_task,
254            addr,
255            memory_offset,
256            length,
257            prot_flags,
258            options,
259            filename,
260        )
261    }
262
263    /// Respond to a `getdents` or `getdents64` calls.
264    ///
265    /// The `file.offset` lock will be held while entering this method. The implementation must look
266    /// at `sink.offset()` to read the current offset into the file.
267    fn readdir(
268        &self,
269        _locked: &mut Locked<FileOpsCore>,
270        _file: &FileObject,
271        _current_task: &CurrentTask,
272        _sink: &mut dyn DirentSink,
273    ) -> Result<(), Errno> {
274        error!(ENOTDIR)
275    }
276
277    /// Establish a one-shot, edge-triggered, asynchronous wait for the given FdEvents for the
278    /// given file and task. Returns `None` if this file does not support blocking waits.
279    ///
280    /// Active events are not considered. This is similar to the semantics of the
281    /// ZX_WAIT_ASYNC_EDGE flag on zx_wait_async. To avoid missing events, the caller must call
282    /// query_events after calling this.
283    ///
284    /// If your file does not support blocking waits, leave this as the default implementation.
285    fn wait_async(
286        &self,
287        _locked: &mut Locked<FileOpsCore>,
288        _file: &FileObject,
289        _current_task: &CurrentTask,
290        _waiter: &Waiter,
291        _events: FdEvents,
292        _handler: EventHandler,
293    ) -> Option<WaitCanceler> {
294        None
295    }
296
297    /// The events currently active on this file.
298    ///
299    /// If this function returns `POLLIN` or `POLLOUT`, then FileObject will
300    /// add `POLLRDNORM` and `POLLWRNORM`, respective, which are equivalent in
301    /// the Linux UAPI.
302    ///
303    /// See https://linux.die.net/man/2/poll
304    fn query_events(
305        &self,
306        _locked: &mut Locked<FileOpsCore>,
307        _file: &FileObject,
308        _current_task: &CurrentTask,
309    ) -> Result<FdEvents, Errno> {
310        Ok(FdEvents::POLLIN | FdEvents::POLLOUT)
311    }
312
313    fn ioctl(
314        &self,
315        _locked: &mut Locked<Unlocked>,
316        _file: &FileObject,
317        _current_task: &CurrentTask,
318        _request: u32,
319        _arg: SyscallArg,
320    ) -> Result<SyscallResult, Errno> {
321        error!(ENOTTY)
322    }
323
324    fn fcntl(
325        &self,
326        _file: &FileObject,
327        _current_task: &CurrentTask,
328        cmd: u32,
329        _arg: u64,
330    ) -> Result<SyscallResult, Errno> {
331        default_fcntl(cmd)
332    }
333
334    /// Return a handle that allows access to this file descritor through the zxio protocols.
335    ///
336    /// If None is returned, the file will act as if it was a fd to `/dev/null`.
337    fn to_handle(
338        &self,
339        file: &FileObject,
340        current_task: &CurrentTask,
341    ) -> Result<Option<zx::NullableHandle>, Errno> {
342        serve_file(current_task, file, current_task.current_creds().clone())
343            .map(|c| Some(c.0.into_channel().into()))
344    }
345
346    // Return a vector of handles. This is used in situations where there is more than one handle
347    // associated with this file descriptor.
348    //
349    // In Fuchsia, there is an expectation that there is a 1:1 mapping between a file descriptor and
350    // a handle. In general, we do not want to violate that rule. This function is intended to used
351    // in very limited circumstances (compatibility with Linux and Binder), where we need to violate
352    // rule.
353    //
354    // Specifically, we are using this to implement SyncFiles correctly, where a single SyncFile can
355    // represent multiple SyncPoints. Each SyncPoint contains a zx::Counter.
356    //
357    // If you chose to implement this function, to_handle() should return an error. You must also be
358    // aware that if these handles are passed to Fuchsia over Binder, they will be represented as
359    // single file descriptor, and you should use the composite_fd library to manage that file
360    // descriptor.
361    fn get_handles(
362        &self,
363        _file: &FileObject,
364        _current_task: &CurrentTask,
365    ) -> Result<Vec<zx::NullableHandle>, Errno> {
366        error!(ENOTSUP)
367    }
368
369    /// Returns the associated pid_t.
370    ///
371    /// Used by pidfd and `/proc/<pid>`. Unlikely to be used by other files.
372    fn as_thread_group_key(&self, _file: &FileObject) -> Result<ThreadGroupKey, Errno> {
373        error!(EBADF)
374    }
375
376    fn readahead(
377        &self,
378        _file: &FileObject,
379        _current_task: &CurrentTask,
380        _offset: usize,
381        _length: usize,
382    ) -> Result<(), Errno> {
383        error!(EINVAL)
384    }
385
386    /// Extra information that is included in the /proc/<pid>/fdfino/<fd> entry.
387    fn extra_fdinfo(
388        &self,
389        _locked: &mut Locked<FileOpsCore>,
390        _file: &FileHandle,
391        _current_task: &CurrentTask,
392    ) -> Option<FsString> {
393        None
394    }
395}
396
397/// Marker trait for implementation of FileOps that do not need to implement `close` and can
398/// then pass a wrapper object as the `FileOps` implementation.
399pub trait CloseFreeSafe {}
400impl<T: FileOps + CloseFreeSafe, P: Deref<Target = T> + Send + Sync + 'static> FileOps for P {
401    fn close(
402        self: Box<Self>,
403        _locked: &mut Locked<FileOpsCore>,
404        _file: &FileObjectState,
405        _current_task: &CurrentTask,
406    ) {
407        // This method cannot be delegated. T being `CloseFreeSafe` this is fine.
408    }
409
410    fn flush(
411        &self,
412        locked: &mut Locked<FileOpsCore>,
413        file: &FileObject,
414        current_task: &CurrentTask,
415    ) {
416        self.deref().flush(locked, file, current_task)
417    }
418
419    fn has_persistent_offsets(&self) -> bool {
420        self.deref().has_persistent_offsets()
421    }
422
423    fn writes_update_seek_offset(&self) -> bool {
424        self.deref().writes_update_seek_offset()
425    }
426
427    fn is_seekable(&self) -> bool {
428        self.deref().is_seekable()
429    }
430
431    fn read(
432        &self,
433        locked: &mut Locked<FileOpsCore>,
434        file: &FileObject,
435        current_task: &CurrentTask,
436        offset: usize,
437        data: &mut dyn OutputBuffer,
438    ) -> Result<usize, Errno> {
439        self.deref().read(locked, file, current_task, offset, data)
440    }
441
442    fn write(
443        &self,
444        locked: &mut Locked<FileOpsCore>,
445        file: &FileObject,
446        current_task: &CurrentTask,
447        offset: usize,
448        data: &mut dyn InputBuffer,
449    ) -> Result<usize, Errno> {
450        self.deref().write(locked, file, current_task, offset, data)
451    }
452
453    fn seek(
454        &self,
455        locked: &mut Locked<FileOpsCore>,
456        file: &FileObject,
457        current_task: &CurrentTask,
458        current_offset: off_t,
459        target: SeekTarget,
460    ) -> Result<off_t, Errno> {
461        self.deref().seek(locked, file, current_task, current_offset, target)
462    }
463
464    fn sync(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
465        self.deref().sync(file, current_task)
466    }
467
468    fn data_sync(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
469        self.deref().data_sync(file, current_task)
470    }
471
472    fn get_memory(
473        &self,
474        locked: &mut Locked<FileOpsCore>,
475        file: &FileObject,
476        current_task: &CurrentTask,
477        length: Option<usize>,
478        prot: ProtectionFlags,
479    ) -> Result<Arc<MemoryObject>, Errno> {
480        self.deref().get_memory(locked, file, current_task, length, prot)
481    }
482
483    fn mmap(
484        &self,
485        locked: &mut Locked<FileOpsCore>,
486        file: &FileObject,
487        current_task: &CurrentTask,
488        addr: DesiredAddress,
489        memory_offset: u64,
490        length: usize,
491        prot_flags: ProtectionFlags,
492        options: MappingOptions,
493        filename: NamespaceNode,
494    ) -> Result<UserAddress, Errno> {
495        self.deref().mmap(
496            locked,
497            file,
498            current_task,
499            addr,
500            memory_offset,
501            length,
502            prot_flags,
503            options,
504            filename,
505        )
506    }
507
508    fn readdir(
509        &self,
510        locked: &mut Locked<FileOpsCore>,
511        file: &FileObject,
512        current_task: &CurrentTask,
513        sink: &mut dyn DirentSink,
514    ) -> Result<(), Errno> {
515        self.deref().readdir(locked, file, current_task, sink)
516    }
517
518    fn wait_async(
519        &self,
520        locked: &mut Locked<FileOpsCore>,
521        file: &FileObject,
522        current_task: &CurrentTask,
523        waiter: &Waiter,
524        events: FdEvents,
525        handler: EventHandler,
526    ) -> Option<WaitCanceler> {
527        self.deref().wait_async(locked, file, current_task, waiter, events, handler)
528    }
529
530    fn query_events(
531        &self,
532        locked: &mut Locked<FileOpsCore>,
533        file: &FileObject,
534        current_task: &CurrentTask,
535    ) -> Result<FdEvents, Errno> {
536        self.deref().query_events(locked, file, current_task)
537    }
538
539    fn ioctl(
540        &self,
541        locked: &mut Locked<Unlocked>,
542        file: &FileObject,
543        current_task: &CurrentTask,
544        request: u32,
545        arg: SyscallArg,
546    ) -> Result<SyscallResult, Errno> {
547        self.deref().ioctl(locked, file, current_task, request, arg)
548    }
549
550    fn fcntl(
551        &self,
552        file: &FileObject,
553        current_task: &CurrentTask,
554        cmd: u32,
555        arg: u64,
556    ) -> Result<SyscallResult, Errno> {
557        self.deref().fcntl(file, current_task, cmd, arg)
558    }
559
560    fn to_handle(
561        &self,
562        file: &FileObject,
563        current_task: &CurrentTask,
564    ) -> Result<Option<zx::NullableHandle>, Errno> {
565        self.deref().to_handle(file, current_task)
566    }
567
568    fn get_handles(
569        &self,
570        file: &FileObject,
571        current_task: &CurrentTask,
572    ) -> Result<Vec<zx::NullableHandle>, Errno> {
573        self.deref().get_handles(file, current_task)
574    }
575
576    fn as_thread_group_key(&self, file: &FileObject) -> Result<ThreadGroupKey, Errno> {
577        self.deref().as_thread_group_key(file)
578    }
579
580    fn readahead(
581        &self,
582        file: &FileObject,
583        current_task: &CurrentTask,
584        offset: usize,
585        length: usize,
586    ) -> Result<(), Errno> {
587        self.deref().readahead(file, current_task, offset, length)
588    }
589
590    fn extra_fdinfo(
591        &self,
592        locked: &mut Locked<FileOpsCore>,
593        file: &FileHandle,
594        current_task: &CurrentTask,
595    ) -> Option<FsString> {
596        self.deref().extra_fdinfo(locked, file, current_task)
597    }
598}
599
600pub fn default_eof_offset<L>(
601    locked: &mut Locked<L>,
602    file: &FileObject,
603    current_task: &CurrentTask,
604) -> Result<off_t, Errno>
605where
606    L: LockEqualOrBefore<FileOpsCore>,
607{
608    Ok(file.node().get_size(locked, current_task)? as off_t)
609}
610
611/// Implement the seek method for a file. The computation from the end of the file must be provided
612/// through a callback.
613///
614/// Errors if the calculated offset is invalid.
615///
616/// - `current_offset`: The current position
617/// - `target`: The location to seek to.
618/// - `compute_end`: Compute the new offset from the end. Return an error if the operation is not
619///    supported.
620pub fn default_seek<F>(
621    current_offset: off_t,
622    target: SeekTarget,
623    compute_end: F,
624) -> Result<off_t, Errno>
625where
626    F: FnOnce() -> Result<off_t, Errno>,
627{
628    let new_offset = match target {
629        SeekTarget::Set(offset) => Some(offset),
630        SeekTarget::Cur(offset) => current_offset.checked_add(offset),
631        SeekTarget::End(offset) => compute_end()?.checked_add(offset),
632        SeekTarget::Data(offset) => {
633            let eof = compute_end().unwrap_or(off_t::MAX);
634            if offset >= eof {
635                return error!(ENXIO);
636            }
637            Some(offset)
638        }
639        SeekTarget::Hole(offset) => {
640            let eof = compute_end()?;
641            if offset >= eof {
642                return error!(ENXIO);
643            }
644            Some(eof)
645        }
646    }
647    .ok_or_else(|| errno!(EINVAL))?;
648
649    if new_offset < 0 {
650        return error!(EINVAL);
651    }
652
653    Ok(new_offset)
654}
655
656/// Implement the seek method for a file without an upper bound on the resulting offset.
657///
658/// This is useful for files without a defined size.
659///
660/// Errors if the calculated offset is invalid.
661///
662/// - `current_offset`: The current position
663/// - `target`: The location to seek to.
664pub fn unbounded_seek(current_offset: off_t, target: SeekTarget) -> Result<off_t, Errno> {
665    default_seek(current_offset, target, || Ok(MAX_LFS_FILESIZE as off_t))
666}
667
668#[macro_export]
669macro_rules! fileops_impl_delegate_read_write_and_seek {
670    ($self:ident, $delegate:expr) => {
671        fn is_seekable(&self) -> bool {
672            true
673        }
674
675        fn read(
676            &$self,
677            locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
678            file: &FileObject,
679            current_task: &$crate::task::CurrentTask,
680            offset: usize,
681            data: &mut dyn $crate::vfs::buffers::OutputBuffer,
682        ) -> Result<usize, starnix_uapi::errors::Errno> {
683            $delegate.read(locked, file, current_task, offset, data)
684        }
685
686        fn write(
687            &$self,
688            locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
689            file: &FileObject,
690            current_task: &$crate::task::CurrentTask,
691            offset: usize,
692            data: &mut dyn $crate::vfs::buffers::InputBuffer,
693        ) -> Result<usize, starnix_uapi::errors::Errno> {
694            $delegate.write(locked, file, current_task, offset, data)
695        }
696
697        fn seek(
698            &$self,
699        locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
700            file: &FileObject,
701            current_task: &$crate::task::CurrentTask,
702            current_offset: starnix_uapi::off_t,
703            target: $crate::vfs::SeekTarget,
704        ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
705            $delegate.seek(locked, file, current_task, current_offset, target)
706        }
707    };
708}
709
710/// Implements [`FileOps::seek`] in a way that makes sense for seekable files.
711#[macro_export]
712macro_rules! fileops_impl_seekable {
713    () => {
714        fn is_seekable(&self) -> bool {
715            true
716        }
717
718        fn seek(
719            &self,
720            locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
721            file: &$crate::vfs::FileObject,
722            current_task: &$crate::task::CurrentTask,
723            current_offset: starnix_uapi::off_t,
724            target: $crate::vfs::SeekTarget,
725        ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
726            $crate::vfs::default_seek(current_offset, target, || {
727                $crate::vfs::default_eof_offset(locked, file, current_task)
728            })
729        }
730    };
731}
732
733/// Implements [`FileOps`] methods in a way that makes sense for non-seekable files.
734#[macro_export]
735macro_rules! fileops_impl_nonseekable {
736    () => {
737        fn is_seekable(&self) -> bool {
738            false
739        }
740
741        fn seek(
742            &self,
743            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
744            _file: &$crate::vfs::FileObject,
745            _current_task: &$crate::task::CurrentTask,
746            _current_offset: starnix_uapi::off_t,
747            _target: $crate::vfs::SeekTarget,
748        ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
749            starnix_uapi::error!(ESPIPE)
750        }
751    };
752}
753
754/// Implements [`FileOps::seek`] methods in a way that makes sense for files that ignore
755/// seeking operations and always read/write at offset 0.
756#[macro_export]
757macro_rules! fileops_impl_seekless {
758    () => {
759        fn has_persistent_offsets(&self) -> bool {
760            false
761        }
762
763        fn is_seekable(&self) -> bool {
764            true
765        }
766
767        fn seek(
768            &self,
769            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
770            _file: &$crate::vfs::FileObject,
771            _current_task: &$crate::task::CurrentTask,
772            _current_offset: starnix_uapi::off_t,
773            _target: $crate::vfs::SeekTarget,
774        ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
775            Ok(0)
776        }
777    };
778}
779
780#[macro_export]
781macro_rules! fileops_impl_dataless {
782    () => {
783        fn write(
784            &self,
785            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
786            _file: &$crate::vfs::FileObject,
787            _current_task: &$crate::task::CurrentTask,
788            _offset: usize,
789            _data: &mut dyn $crate::vfs::buffers::InputBuffer,
790        ) -> Result<usize, starnix_uapi::errors::Errno> {
791            starnix_uapi::error!(EINVAL)
792        }
793
794        fn read(
795            &self,
796            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
797            _file: &$crate::vfs::FileObject,
798            _current_task: &$crate::task::CurrentTask,
799            _offset: usize,
800            _data: &mut dyn $crate::vfs::buffers::OutputBuffer,
801        ) -> Result<usize, starnix_uapi::errors::Errno> {
802            starnix_uapi::error!(EINVAL)
803        }
804    };
805}
806
807/// Implements [`FileOps`] methods in a way that makes sense for directories. You must implement
808/// [`FileOps::seek`] and [`FileOps::readdir`].
809#[macro_export]
810macro_rules! fileops_impl_directory {
811    () => {
812        fn is_seekable(&self) -> bool {
813            true
814        }
815
816        fn read(
817            &self,
818            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
819            _file: &$crate::vfs::FileObject,
820            _current_task: &$crate::task::CurrentTask,
821            _offset: usize,
822            _data: &mut dyn $crate::vfs::buffers::OutputBuffer,
823        ) -> Result<usize, starnix_uapi::errors::Errno> {
824            starnix_uapi::error!(EISDIR)
825        }
826
827        fn write(
828            &self,
829            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
830            _file: &$crate::vfs::FileObject,
831            _current_task: &$crate::task::CurrentTask,
832            _offset: usize,
833            _data: &mut dyn $crate::vfs::buffers::InputBuffer,
834        ) -> Result<usize, starnix_uapi::errors::Errno> {
835            starnix_uapi::error!(EISDIR)
836        }
837    };
838}
839
840#[macro_export]
841macro_rules! fileops_impl_unbounded_seek {
842    () => {
843        fn seek(
844            &self,
845            _locked: &mut starnix_sync::Locked<starnix_sync::FileOpsCore>,
846            _file: &$crate::vfs::FileObject,
847            _current_task: &$crate::task::CurrentTask,
848            current_offset: starnix_uapi::off_t,
849            target: $crate::vfs::SeekTarget,
850        ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
851            $crate::vfs::unbounded_seek(current_offset, target)
852        }
853    };
854}
855
856#[macro_export]
857macro_rules! fileops_impl_noop_sync {
858    () => {
859        fn sync(
860            &self,
861            file: &$crate::vfs::FileObject,
862            _current_task: &$crate::task::CurrentTask,
863        ) -> Result<(), starnix_uapi::errors::Errno> {
864            if !file.node().is_reg() && !file.node().is_dir() {
865                return starnix_uapi::error!(EINVAL);
866            }
867            Ok(())
868        }
869    };
870}
871
872// Public re-export of macros allows them to be used like regular rust items.
873
874pub use fileops_impl_dataless;
875pub use fileops_impl_delegate_read_write_and_seek;
876pub use fileops_impl_directory;
877pub use fileops_impl_nonseekable;
878pub use fileops_impl_noop_sync;
879pub use fileops_impl_seekable;
880pub use fileops_impl_seekless;
881pub use fileops_impl_unbounded_seek;
882pub const AES256_KEY_SIZE: usize = 32;
883
884pub fn canonicalize_ioctl_request(current_task: &CurrentTask, request: u32) -> u32 {
885    if current_task.is_arch32() {
886        match request {
887            uapi::arch32::FS_IOC_GETFLAGS => uapi::FS_IOC_GETFLAGS,
888            uapi::arch32::FS_IOC_SETFLAGS => uapi::FS_IOC_SETFLAGS,
889            _ => request,
890        }
891    } else {
892        request
893    }
894}
895
896/// Universal VFS ioctl dispatcher for [`FileObject`].
897///
898/// Handles generic file system ioctls (such as non-blocking mode toggles, block size queries,
899/// and file cloning) at the VFS layer without delegating to underlying device drivers.
900///
901/// Returns `Some(result)` if the ioctl command is handled by the VFS layer, or `None` if the
902/// command is unhandled and should be dispatched to [`FileOps::ioctl`].
903pub fn default_vfs_ioctl(
904    file: &FileObject,
905    locked: &mut Locked<Unlocked>,
906    current_task: &CurrentTask,
907    request: u32,
908    arg: SyscallArg,
909) -> Result<Option<SyscallResult>, Errno> {
910    match canonicalize_ioctl_request(current_task, request) {
911        FIGETBSZ if file.node().is_reg() || file.node().is_dir() => {
912            let blocksize = file.node().stat(locked, current_task)?.st_blksize;
913            current_task.write_object(arg.into(), &blocksize)?;
914            Ok(Some(SUCCESS))
915        }
916        FIONBIO => {
917            let arg_ref = UserAddress::from(arg).into();
918            let arg: i32 = current_task.read_object(arg_ref)?;
919            let val = if arg == 0 {
920                // Clear the NONBLOCK flag
921                OpenFlags::empty()
922            } else {
923                // Set the NONBLOCK flag
924                OpenFlags::NONBLOCK
925            };
926            file.update_file_flags(val, OpenFlags::NONBLOCK);
927            Ok(Some(SUCCESS))
928        }
929        FIOQSIZE if file.node().is_reg() || file.node().is_dir() => {
930            let size = file.node().stat(locked, current_task)?.st_size;
931            current_task.write_object(arg.into(), &size)?;
932            Ok(Some(SUCCESS))
933        }
934        FIONREAD if file.node().is_reg() => {
935            track_stub!(TODO("https://fxbug.dev/322874897"), "FIONREAD");
936            let size = file
937                .node()
938                .fetch_and_refresh_info(locked, current_task)
939                .map_err(|_| errno!(EINVAL))?
940                .size;
941            let offset = usize::try_from(file.offset.read()).map_err(|_| errno!(EINVAL))?;
942            let remaining =
943                if size < offset { 0 } else { i32::try_from(size - offset).unwrap_or(i32::MAX) };
944            current_task.write_object(arg.into(), &remaining)?;
945            Ok(Some(SUCCESS))
946        }
947        FS_IOC_FSGETXATTR => {
948            track_stub!(TODO("https://fxbug.dev/322875209"), "FS_IOC_FSGETXATTR");
949            let arg = UserAddress::from(arg).into();
950            current_task.write_object(arg, &fsxattr::default())?;
951            Ok(Some(SUCCESS))
952        }
953        FS_IOC_FSSETXATTR => {
954            track_stub!(TODO("https://fxbug.dev/322875271"), "FS_IOC_FSSETXATTR");
955            let arg = UserAddress::from(arg).into();
956            let _: fsxattr = current_task.read_object(arg)?;
957            Ok(Some(SUCCESS))
958        }
959        uapi::FS_IOC_GETFLAGS => {
960            track_stub!(TODO("https://fxbug.dev/322874935"), "FS_IOC_GETFLAGS");
961            let arg = UserRef::<u32>::from(arg);
962            let mut flags: u32 = 0;
963            if matches!(*file.node().fsverity.lock(), FsVerityState::FsVerity) {
964                flags |= FS_VERITY_FL;
965            }
966            if file.node().info().casefold {
967                flags |= FS_CASEFOLD_FL;
968            }
969            current_task.write_object(arg, &flags)?;
970            Ok(Some(SUCCESS))
971        }
972        uapi::FS_IOC_SETFLAGS => {
973            track_stub!(TODO("https://fxbug.dev/322875367"), "FS_IOC_SETFLAGS");
974            let arg = UserRef::<u32>::from(arg);
975            let flags: u32 = current_task.read_object(arg)?;
976            file.node().update_attributes(locked, current_task, |info| {
977                info.casefold = flags & FS_CASEFOLD_FL != 0;
978                Ok(())
979            })?;
980            Ok(Some(SUCCESS))
981        }
982        FS_IOC_ENABLE_VERITY => {
983            fsverity::ioctl::enable(locked, current_task, UserAddress::from(arg).into(), file)
984                .map(Some)
985        }
986        FS_IOC_MEASURE_VERITY => {
987            fsverity::ioctl::measure(locked, current_task, UserAddress::from(arg).into(), file)
988                .map(Some)
989        }
990        FS_IOC_READ_VERITY_METADATA => {
991            fsverity::ioctl::read_metadata(current_task, UserAddress::from(arg).into(), file)
992                .map(Some)
993        }
994        FS_IOC_ADD_ENCRYPTION_KEY => {
995            let fscrypt_add_key_ref = UserRef::<fscrypt_add_key_arg>::from(arg);
996            let key_ref_addr = fscrypt_add_key_ref.next()?.addr();
997            let mut fscrypt_add_key_arg = current_task.read_object(fscrypt_add_key_ref.clone())?;
998            if fscrypt_add_key_arg.key_id != 0 {
999                track_stub!(TODO("https://fxbug.dev/375649227"), "non-zero key ids");
1000                return error!(ENOTSUP);
1001            }
1002            if fscrypt_add_key_arg.key_spec.type_ != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER {
1003                track_stub!(TODO("https://fxbug.dev/375648306"), "fscrypt descriptor type");
1004                return error!(ENOTSUP);
1005            }
1006            let key = current_task
1007                .read_memory_to_vec(key_ref_addr, fscrypt_add_key_arg.raw_size as usize)?;
1008            let user_id = current_task.current_creds().uid;
1009
1010            let crypt_service = file.node().fs().crypt_service().ok_or_else(|| errno!(ENOTSUP))?;
1011            let key_identifier = crypt_service.add_wrapping_key(&key, user_id)?;
1012            fscrypt_add_key_arg.key_spec.u.identifier =
1013                fscrypt_identifier { value: key_identifier, ..Default::default() };
1014            current_task.write_object(fscrypt_add_key_ref, &fscrypt_add_key_arg)?;
1015            Ok(Some(SUCCESS))
1016        }
1017        FS_IOC_SET_ENCRYPTION_POLICY => {
1018            let fscrypt_policy_ref = UserRef::<uapi::fscrypt_policy_v2>::from(arg);
1019            let policy = current_task.read_object(fscrypt_policy_ref)?;
1020            if policy.version as u32 != FSCRYPT_POLICY_V2 {
1021                track_stub!(TODO("https://fxbug.dev/375649656"), "fscrypt policy v1");
1022                return error!(ENOTSUP);
1023            }
1024            if policy.flags != 0 {
1025                track_stub!(
1026                    TODO("https://fxbug.dev/375700939"),
1027                    "fscrypt policy flags",
1028                    policy.flags
1029                );
1030            }
1031            if policy.contents_encryption_mode as u32 != FSCRYPT_MODE_AES_256_XTS {
1032                track_stub!(
1033                    TODO("https://fxbug.dev/375684057"),
1034                    "fscrypt encryption modes",
1035                    policy.contents_encryption_mode
1036                );
1037            }
1038            if policy.filenames_encryption_mode as u32 != FSCRYPT_MODE_AES_256_CTS {
1039                track_stub!(
1040                    TODO("https://fxbug.dev/375684057"),
1041                    "fscrypt encryption modes",
1042                    policy.filenames_encryption_mode
1043                );
1044            }
1045            let user_id = current_task.current_creds().uid;
1046            if user_id != file.node().info().uid {
1047                security::check_task_capable(current_task, CAP_FOWNER)
1048                    .map_err(|_| errno!(EACCES))?;
1049            }
1050
1051            let crypt_service = file.node().fs().crypt_service().ok_or_else(|| errno!(ENOTSUP))?;
1052            if let Some(users) =
1053                crypt_service.get_users_for_key(EncryptionKeyId::from(policy.master_key_identifier))
1054            {
1055                if !users.contains(&user_id) {
1056                    return error!(ENOKEY);
1057                }
1058            } else {
1059                track_stub!(
1060                    TODO("https://fxbug.dev/375067633"),
1061                    "users with CAP_FOWNER can set encryption policies with unadded keys"
1062                );
1063                return error!(ENOKEY);
1064            }
1065
1066            let attributes = file.node().fetch_and_refresh_info(locked, current_task)?;
1067            if let Some(wrapping_key_id) = &attributes.wrapping_key_id {
1068                if wrapping_key_id != &policy.master_key_identifier {
1069                    return error!(EEXIST);
1070                }
1071            } else {
1072                // Don't deadlock! update_attributes will also lock the attributes.
1073                std::mem::drop(attributes);
1074                file.node().update_attributes(locked, current_task, |info| {
1075                    info.wrapping_key_id = Some(policy.master_key_identifier);
1076                    Ok(())
1077                })?;
1078            }
1079            Ok(Some(SUCCESS))
1080        }
1081        FS_IOC_REMOVE_ENCRYPTION_KEY => {
1082            let fscrypt_remove_key_arg_ref = UserRef::<uapi::fscrypt_remove_key_arg>::from(arg);
1083            let fscrypt_remove_key_arg = current_task.read_object(fscrypt_remove_key_arg_ref)?;
1084            if fscrypt_remove_key_arg.key_spec.type_ != FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER {
1085                track_stub!(TODO("https://fxbug.dev/375648306"), "fscrypt descriptor type");
1086                return error!(ENOTSUP);
1087            }
1088            let crypt_service = file.node().fs().crypt_service().ok_or_else(|| errno!(ENOTSUP))?;
1089            let user_id = current_task.current_creds().uid;
1090            #[allow(
1091                clippy::undocumented_unsafe_blocks,
1092                reason = "Force documented unsafe blocks in Starnix"
1093            )]
1094            let identifier = unsafe { fscrypt_remove_key_arg.key_spec.u.identifier.value };
1095            crypt_service.forget_wrapping_key(identifier, user_id)?;
1096            Ok(Some(SUCCESS))
1097        }
1098        linux_uapi::FICLONE | linux_uapi::FICLONERANGE | linux_uapi::FIDEDUPERANGE => {
1099            error!(EOPNOTSUPP)
1100        }
1101        _ => Ok(None),
1102    }
1103}
1104
1105pub fn default_fcntl(cmd: u32) -> Result<SyscallResult, Errno> {
1106    track_stub!(TODO("https://fxbug.dev/322875704"), "default fcntl", cmd);
1107    error!(EINVAL)
1108}
1109
1110pub fn default_mmap(
1111    locked: &mut Locked<FileOpsCore>,
1112    file: &FileObject,
1113    current_task: &CurrentTask,
1114    addr: DesiredAddress,
1115    memory_offset: u64,
1116    length: usize,
1117    prot_flags: ProtectionFlags,
1118    options: MappingOptions,
1119    filename: NamespaceNode,
1120) -> Result<UserAddress, Errno> {
1121    fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "FileOpsDefaultMmap");
1122    let min_memory_size = (memory_offset as usize)
1123        .checked_add(round_up_to_system_page_size(length)?)
1124        .ok_or_else(|| errno!(EINVAL))?;
1125    let mut memory = if options.contains(MappingOptions::SHARED) {
1126        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "GetSharedVmo");
1127        file.ops.get_memory(locked, file, current_task, Some(min_memory_size), prot_flags)?
1128    } else {
1129        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "GetPrivateVmo");
1130        // TODO(tbodt): Use PRIVATE_CLONE to have the filesystem server do the clone for us.
1131        let base_prot_flags = (prot_flags | ProtectionFlags::READ) - ProtectionFlags::WRITE;
1132        let memory = file.ops.get_memory(
1133            locked,
1134            file,
1135            current_task,
1136            Some(min_memory_size),
1137            base_prot_flags,
1138        )?;
1139        let mut clone_flags = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
1140        if !prot_flags.contains(ProtectionFlags::WRITE) {
1141            clone_flags |= zx::VmoChildOptions::NO_WRITE;
1142        }
1143        fuchsia_trace::duration!(CATEGORY_STARNIX_MM, "CreatePrivateChildVmo");
1144        Arc::new(memory.create_child(clone_flags, 0, memory.get_size()).map_err(impossible_error)?)
1145    };
1146
1147    // Write guard is necessary only for shared mappings. Note that this doesn't depend on
1148    // `prot_flags` since these can be changed later with `mprotect()`.
1149    let file_write_guard = if options.contains(MappingOptions::SHARED) && file.can_write() {
1150        let node = &file.name.entry.node;
1151        let state = node.write_guard_state.lock();
1152
1153        // `F_SEAL_FUTURE_WRITE` should allow `mmap(PROT_READ)`, but block
1154        // `mprotect(PROT_WRITE)`. This is different from `F_SEAL_WRITE`, which blocks
1155        // `mmap(PROT_READ)`. To handle this case correctly remove `WRITE` right from the
1156        // VMO handle to ensure `mprotect(PROT_WRITE)` fails.
1157        let seals = state.get_seals().unwrap_or(SealFlags::empty());
1158        if seals.contains(SealFlags::FUTURE_WRITE)
1159            && !seals.contains(SealFlags::WRITE)
1160            && !prot_flags.contains(ProtectionFlags::WRITE)
1161        {
1162            let mut new_rights = zx::Rights::VMO_DEFAULT - zx::Rights::WRITE;
1163            if prot_flags.contains(ProtectionFlags::EXEC) {
1164                new_rights |= zx::Rights::EXECUTE;
1165            }
1166            memory = Arc::new(memory.duplicate_handle(new_rights).map_err(impossible_error)?);
1167
1168            None
1169        } else {
1170            Some(FileWriteGuardMode::WriteMapping)
1171        }
1172    } else {
1173        None
1174    };
1175
1176    current_task.mm()?.map_memory(
1177        addr,
1178        memory,
1179        memory_offset,
1180        length,
1181        prot_flags,
1182        file.max_access_for_memory_mapping(),
1183        options,
1184        MappingName::File(filename.into_mapping(file_write_guard)?),
1185    )
1186}
1187
1188pub struct OPathOps {}
1189
1190impl OPathOps {
1191    pub fn new() -> OPathOps {
1192        OPathOps {}
1193    }
1194}
1195
1196impl FileOps for OPathOps {
1197    fileops_impl_noop_sync!();
1198
1199    fn has_persistent_offsets(&self) -> bool {
1200        false
1201    }
1202    fn is_seekable(&self) -> bool {
1203        true
1204    }
1205    fn read(
1206        &self,
1207        _locked: &mut Locked<FileOpsCore>,
1208        _file: &FileObject,
1209        _current_task: &CurrentTask,
1210        _offset: usize,
1211        _data: &mut dyn OutputBuffer,
1212    ) -> Result<usize, Errno> {
1213        error!(EBADF)
1214    }
1215    fn write(
1216        &self,
1217        _locked: &mut Locked<FileOpsCore>,
1218        _file: &FileObject,
1219        _current_task: &CurrentTask,
1220        _offset: usize,
1221        _data: &mut dyn InputBuffer,
1222    ) -> Result<usize, Errno> {
1223        error!(EBADF)
1224    }
1225    fn seek(
1226        &self,
1227        _locked: &mut Locked<FileOpsCore>,
1228        _file: &FileObject,
1229        _current_task: &CurrentTask,
1230        _current_offset: off_t,
1231        _target: SeekTarget,
1232    ) -> Result<off_t, Errno> {
1233        error!(EBADF)
1234    }
1235    fn get_memory(
1236        &self,
1237        _locked: &mut Locked<FileOpsCore>,
1238        _file: &FileObject,
1239        _current_task: &CurrentTask,
1240        _length: Option<usize>,
1241        _prot: ProtectionFlags,
1242    ) -> Result<Arc<MemoryObject>, Errno> {
1243        error!(EBADF)
1244    }
1245    fn readdir(
1246        &self,
1247        _locked: &mut Locked<FileOpsCore>,
1248        _file: &FileObject,
1249        _current_task: &CurrentTask,
1250        _sink: &mut dyn DirentSink,
1251    ) -> Result<(), Errno> {
1252        error!(EBADF)
1253    }
1254
1255    fn ioctl(
1256        &self,
1257        _locked: &mut Locked<Unlocked>,
1258        _file: &FileObject,
1259        _current_task: &CurrentTask,
1260        _request: u32,
1261        _arg: SyscallArg,
1262    ) -> Result<SyscallResult, Errno> {
1263        error!(EBADF)
1264    }
1265}
1266
1267pub struct ProxyFileOps(pub FileHandle);
1268
1269impl FileOps for ProxyFileOps {
1270    // `close` is not delegated because the last reference to a `ProxyFileOps` is not
1271    // necessarily the last reference of the proxied file. If this is the case, the
1272    // releaser will handle it.
1273    // These don't take &FileObject making it too hard to handle them properly in the macro
1274    fn has_persistent_offsets(&self) -> bool {
1275        self.0.ops().has_persistent_offsets()
1276    }
1277    fn writes_update_seek_offset(&self) -> bool {
1278        self.0.ops().writes_update_seek_offset()
1279    }
1280    fn is_seekable(&self) -> bool {
1281        self.0.ops().is_seekable()
1282    }
1283    // These take &mut Locked<L> as a second argument
1284    fn flush(
1285        &self,
1286        locked: &mut Locked<FileOpsCore>,
1287        _file: &FileObject,
1288        current_task: &CurrentTask,
1289    ) {
1290        self.0.ops().flush(locked, &self.0, current_task);
1291    }
1292    fn wait_async(
1293        &self,
1294        locked: &mut Locked<FileOpsCore>,
1295        _file: &FileObject,
1296        current_task: &CurrentTask,
1297        waiter: &Waiter,
1298        events: FdEvents,
1299        handler: EventHandler,
1300    ) -> Option<WaitCanceler> {
1301        self.0.ops().wait_async(locked, &self.0, current_task, waiter, events, handler)
1302    }
1303    fn query_events(
1304        &self,
1305        locked: &mut Locked<FileOpsCore>,
1306        _file: &FileObject,
1307        current_task: &CurrentTask,
1308    ) -> Result<FdEvents, Errno> {
1309        self.0.ops().query_events(locked, &self.0, current_task)
1310    }
1311    fn read(
1312        &self,
1313        locked: &mut Locked<FileOpsCore>,
1314        _file: &FileObject,
1315        current_task: &CurrentTask,
1316        offset: usize,
1317        data: &mut dyn OutputBuffer,
1318    ) -> Result<usize, Errno> {
1319        self.0.ops().read(locked, &self.0, current_task, offset, data)
1320    }
1321    fn write(
1322        &self,
1323        locked: &mut Locked<FileOpsCore>,
1324        _file: &FileObject,
1325        current_task: &CurrentTask,
1326        offset: usize,
1327        data: &mut dyn InputBuffer,
1328    ) -> Result<usize, Errno> {
1329        self.0.ops().write(locked, &self.0, current_task, offset, data)
1330    }
1331    fn ioctl(
1332        &self,
1333        locked: &mut Locked<Unlocked>,
1334        _file: &FileObject,
1335        current_task: &CurrentTask,
1336        request: u32,
1337        arg: SyscallArg,
1338    ) -> Result<SyscallResult, Errno> {
1339        self.0.ops().ioctl(locked, &self.0, current_task, request, arg)
1340    }
1341    fn fcntl(
1342        &self,
1343        _file: &FileObject,
1344        current_task: &CurrentTask,
1345        cmd: u32,
1346        arg: u64,
1347    ) -> Result<SyscallResult, Errno> {
1348        self.0.ops().fcntl(&self.0, current_task, cmd, arg)
1349    }
1350    fn readdir(
1351        &self,
1352        locked: &mut Locked<FileOpsCore>,
1353        _file: &FileObject,
1354        current_task: &CurrentTask,
1355        sink: &mut dyn DirentSink,
1356    ) -> Result<(), Errno> {
1357        self.0.ops().readdir(locked, &self.0, current_task, sink)
1358    }
1359    fn sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1360        self.0.ops().sync(&self.0, current_task)
1361    }
1362    fn data_sync(&self, _file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
1363        self.0.ops().sync(&self.0, current_task)
1364    }
1365    fn get_memory(
1366        &self,
1367        locked: &mut Locked<FileOpsCore>,
1368        _file: &FileObject,
1369        current_task: &CurrentTask,
1370        length: Option<usize>,
1371        prot: ProtectionFlags,
1372    ) -> Result<Arc<MemoryObject>, Errno> {
1373        self.0.ops.get_memory(locked, &self.0, current_task, length, prot)
1374    }
1375    fn mmap(
1376        &self,
1377        locked: &mut Locked<FileOpsCore>,
1378        _file: &FileObject,
1379        current_task: &CurrentTask,
1380        addr: DesiredAddress,
1381        memory_offset: u64,
1382        length: usize,
1383        prot_flags: ProtectionFlags,
1384        options: MappingOptions,
1385        filename: NamespaceNode,
1386    ) -> Result<UserAddress, Errno> {
1387        self.0.ops.mmap(
1388            locked,
1389            &self.0,
1390            current_task,
1391            addr,
1392            memory_offset,
1393            length,
1394            prot_flags,
1395            options,
1396            filename,
1397        )
1398    }
1399    fn seek(
1400        &self,
1401        locked: &mut Locked<FileOpsCore>,
1402        _file: &FileObject,
1403        current_task: &CurrentTask,
1404        offset: off_t,
1405        target: SeekTarget,
1406    ) -> Result<off_t, Errno> {
1407        self.0.ops.seek(locked, &self.0, current_task, offset, target)
1408    }
1409}
1410
1411#[derive(Debug, Default, Copy, Clone)]
1412pub enum FileAsyncOwner {
1413    #[default]
1414    Unowned,
1415    Thread(pid_t),
1416    Process(pid_t),
1417    ProcessGroup(pid_t),
1418}
1419
1420impl FileAsyncOwner {
1421    pub fn validate(self, current_task: &CurrentTask) -> Result<(), Errno> {
1422        match self {
1423            FileAsyncOwner::Unowned => (),
1424            FileAsyncOwner::Thread(id) | FileAsyncOwner::Process(id) => {
1425                if id != 0 {
1426                    current_task.get_task(id)?;
1427                }
1428            }
1429            FileAsyncOwner::ProcessGroup(pgid) => {
1430                if pgid != 0 {
1431                    current_task
1432                        .kernel()
1433                        .pids
1434                        .read()
1435                        .get_process_group(pgid)
1436                        .ok_or_else(|| errno!(ESRCH))?;
1437                }
1438            }
1439        }
1440        Ok(())
1441    }
1442}
1443
1444#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1445pub struct FileObjectId(u64);
1446
1447impl FileObjectId {
1448    pub fn as_epoll_key(&self) -> EpollKey {
1449        self.0 as EpollKey
1450    }
1451}
1452
1453/// A session with a file object.
1454///
1455/// Each time a client calls open(), we create a new FileObject from the
1456/// underlying FsNode that receives the open(). This object contains the state
1457/// that is specific to this sessions whereas the underlying FsNode contains
1458/// the state that is shared between all the sessions.
1459pub struct FileObject {
1460    ops: Box<dyn FileOps>,
1461    state: FileObjectState,
1462}
1463
1464impl std::ops::Deref for FileObject {
1465    type Target = FileObjectState;
1466    fn deref(&self) -> &Self::Target {
1467        &self.state
1468    }
1469}
1470
1471pub struct FileObjectState {
1472    /// Weak reference to the `FileHandle` of this `FileObject`. This allows to retrieve the
1473    /// `FileHandle` from a `FileObject`.
1474    pub weak_handle: WeakFileHandle,
1475
1476    /// A unique identifier for this file object.
1477    pub id: FileObjectId,
1478
1479    /// The NamespaceNode associated with this FileObject.
1480    ///
1481    /// Represents the name the process used to open this file.
1482    pub name: ActiveNamespaceNode,
1483
1484    pub fs: FileSystemHandle,
1485
1486    pub offset: RcuAtomic<off_t, FileObjectOffset>,
1487
1488    flags: AtomicOpenFlags,
1489
1490    async_owner: LockDepMutex<FileAsyncOwner, FileAsyncOwnerLock>,
1491
1492    /// A set of epoll file descriptor numbers that tracks which `EpollFileObject`s add this
1493    /// `FileObject` as the control file.
1494    epoll_files: LockDepMutex<HashMap<FileHandleKey, WeakFileHandle>, FileEpollFilesLock>,
1495
1496    /// See fcntl F_SETLEASE and F_GETLEASE.
1497    lease: LockDepMutex<FileLeaseType, FileLeaseLock>,
1498
1499    // This extra reference to the FsNode should not be needed, but it is needed to make
1500    // Inotify.ExcludeUnlinkInodeEvents pass.
1501    _mysterious_node: Option<FsNodeHandle>,
1502
1503    /// Opaque security state associated this file object.
1504    pub security_state: security::FileObjectState,
1505}
1506
1507pub enum FileObjectReleaserAction {}
1508impl ReleaserAction<FileObject> for FileObjectReleaserAction {
1509    fn release(file_object: ReleaseGuard<FileObject>) {
1510        register_delayed_release(file_object);
1511    }
1512}
1513pub type FileReleaser = ObjectReleaser<FileObject, FileObjectReleaserAction>;
1514pub type FileHandle = Arc<FileReleaser>;
1515pub type WeakFileHandle = Weak<FileReleaser>;
1516pub type FileHandleKey = WeakKey<FileReleaser>;
1517
1518impl FileObjectState {
1519    /// The FsNode from which this FileObject was created.
1520    pub fn node(&self) -> &FsNodeHandle {
1521        &self.name.entry.node
1522    }
1523
1524    pub fn flags(&self) -> OpenFlags {
1525        self.flags.load(Ordering::Relaxed)
1526    }
1527
1528    pub fn can_read(&self) -> bool {
1529        self.flags.load(Ordering::Relaxed).can_read()
1530    }
1531
1532    pub fn can_write(&self) -> bool {
1533        self.flags.load(Ordering::Relaxed).can_write()
1534    }
1535
1536    /// Returns false if the file is not allowed to be executed.
1537    pub fn can_exec(&self) -> bool {
1538        let mounted_no_exec = self.name.to_passive().mount.flags().contains(MountFlags::NOEXEC);
1539        let no_exec_seal = self
1540            .node()
1541            .write_guard_state
1542            .lock()
1543            .get_seals()
1544            .map(|seals| seals.contains(SealFlags::NO_EXEC))
1545            .unwrap_or(false);
1546        !(mounted_no_exec || no_exec_seal)
1547    }
1548
1549    // Notifies watchers on the current node and its parent about an event.
1550    pub fn notify(&self, event_mask: InotifyMask) {
1551        self.name.notify(event_mask)
1552    }
1553}
1554
1555impl FileObject {
1556    /// Create a FileObject that is not mounted in a namespace.
1557    ///
1558    /// In particular, this will create a new unrooted entries. This should not be used on
1559    /// file system with persistent entries, as the created entry will be out of sync with the one
1560    /// from the file system.
1561    ///
1562    /// The returned FileObject does not have a name.
1563    pub fn new_anonymous<L>(
1564        locked: &mut Locked<L>,
1565        current_task: &CurrentTask,
1566        ops: Box<dyn FileOps>,
1567        node: FsNodeHandle,
1568        flags: OpenFlags,
1569    ) -> FileHandle
1570    where
1571        L: LockEqualOrBefore<FileOpsCore>,
1572    {
1573        assert!(!node.fs().has_permanent_entries());
1574        Self::new(
1575            locked,
1576            current_task,
1577            ops,
1578            NamespaceNode::new_anonymous_unrooted(current_task, node),
1579            flags,
1580        )
1581        .expect("Failed to create anonymous FileObject")
1582    }
1583
1584    /// Create a FileObject with an associated NamespaceNode.
1585    ///
1586    /// This function is not typically called directly. Instead, consider
1587    /// calling NamespaceNode::open.
1588    pub fn new<L>(
1589        locked: &mut Locked<L>,
1590        current_task: &CurrentTask,
1591        ops: Box<dyn FileOps>,
1592        name: NamespaceNode,
1593        flags: OpenFlags,
1594    ) -> Result<FileHandle, Errno>
1595    where
1596        L: LockEqualOrBefore<FileOpsCore>,
1597    {
1598        let _mysterious_node = if flags.can_write() {
1599            name.entry.node.write_guard_state.lock().acquire(FileWriteGuardMode::WriteFile)?;
1600            Some(name.entry.node.clone())
1601        } else {
1602            None
1603        };
1604        let fs = name.entry.node.fs();
1605        let id = FileObjectId(current_task.kernel.next_file_object_id.next());
1606        let security_state = security::file_alloc_security(current_task);
1607        let file = FileHandle::new_cyclic(|weak_handle| {
1608            Self {
1609                ops,
1610                state: FileObjectState {
1611                    weak_handle: weak_handle.clone(),
1612                    id,
1613                    name: name.into_active(),
1614                    fs,
1615                    offset: RcuAtomic::new(0),
1616                    flags: AtomicOpenFlags::new(flags - OpenFlags::CREAT),
1617                    async_owner: Default::default(),
1618                    epoll_files: Default::default(),
1619                    lease: Default::default(),
1620                    _mysterious_node,
1621                    security_state,
1622                },
1623            }
1624            .into()
1625        });
1626        file.notify(InotifyMask::OPEN);
1627
1628        file.ops().open(locked.cast_locked::<FileOpsCore>(), &file, current_task)?;
1629        Ok(file)
1630    }
1631
1632    pub fn max_access_for_memory_mapping(&self) -> Access {
1633        let mut access = Access::EXIST;
1634        if self.can_exec() {
1635            access |= Access::EXEC;
1636        }
1637        let flags = self.flags.load(Ordering::Relaxed);
1638        if flags.can_read() {
1639            access |= Access::READ;
1640        }
1641        if flags.can_write() {
1642            access |= Access::WRITE;
1643        }
1644        access
1645    }
1646
1647    pub fn ops(&self) -> &dyn FileOps {
1648        self.ops.as_ref()
1649    }
1650
1651    pub fn ops_type_name(&self) -> &'static str {
1652        self.ops().type_name()
1653    }
1654
1655    pub fn is_non_blocking(&self) -> bool {
1656        self.flags().contains(OpenFlags::NONBLOCK)
1657    }
1658
1659    /// Common implementation for blocking operations.
1660    ///
1661    /// This function is used to implement the blocking operations for file objects. FileOps
1662    /// implementations should call this function to handle the blocking logic.
1663    ///
1664    /// The `op` parameter is a function that implements the non-blocking version of the operation.
1665    /// The function is called once without registering a waiter in case no wait is needed. If the
1666    /// operation returns EAGAIN and the file object is non-blocking, the function returns EAGAIN.
1667    ///
1668    /// If the operation returns EAGAIN and the file object is blocking, the function will block
1669    /// until the given events are triggered. At that time, the operation is retried. Notice that
1670    /// the `op` function can be called multiple times before the operation completes.
1671    ///
1672    /// The `deadline` parameter is the deadline for the operation. If the operation does not
1673    /// complete before the deadline, the function will return ETIMEDOUT.
1674    pub fn blocking_op<L, T, Op>(
1675        &self,
1676        locked: &mut Locked<L>,
1677        current_task: &CurrentTask,
1678        events: FdEvents,
1679        deadline: Option<zx::MonotonicInstant>,
1680        mut op: Op,
1681    ) -> Result<T, Errno>
1682    where
1683        L: LockEqualOrBefore<FileOpsCore>,
1684        Op: FnMut(&mut Locked<L>) -> Result<T, Errno>,
1685    {
1686        // Don't return EAGAIN for directories. This can happen because glibc always opens a
1687        // directory with O_NONBLOCK.
1688        let can_return_eagain = self.flags().contains(OpenFlags::NONBLOCK)
1689            && !self.flags().contains(OpenFlags::DIRECTORY);
1690        // Run the operation a first time without registering a waiter in case no wait is needed.
1691        match op(locked) {
1692            Err(errno) if errno == EAGAIN && !can_return_eagain => {}
1693            result => return result,
1694        }
1695
1696        let waiter = Waiter::new();
1697        loop {
1698            // Register the waiter before running the operation to prevent a race.
1699            self.wait_async(locked, current_task, &waiter, events, WaitCallback::none());
1700            match op(locked) {
1701                Err(e) if e == EAGAIN => {}
1702                result => return result,
1703            }
1704            let locked = locked.cast_locked::<FileOpsCore>();
1705            waiter
1706                .wait_until(
1707                    locked,
1708                    current_task,
1709                    deadline.unwrap_or(zx::MonotonicInstant::INFINITE),
1710                )
1711                .map_err(|e| if e == ETIMEDOUT { errno!(EAGAIN) } else { e })?;
1712        }
1713    }
1714
1715    pub fn is_seekable(&self) -> bool {
1716        self.ops().is_seekable()
1717    }
1718
1719    pub fn has_persistent_offsets(&self) -> bool {
1720        self.ops().has_persistent_offsets()
1721    }
1722
1723    /// Common implementation for `read` and `read_at`.
1724    fn read_internal<R>(&self, current_task: &CurrentTask, read: R) -> Result<usize, Errno>
1725    where
1726        R: FnOnce() -> Result<usize, Errno>,
1727    {
1728        security::file_permission(current_task, self, security::PermissionFlags::READ)?;
1729
1730        if !self.can_read() {
1731            return error!(EBADF);
1732        }
1733        let bytes_read = read()?;
1734
1735        // TODO(steveaustin) - omit updating time_access to allow info to be immutable
1736        // and thus allow simultaneous reads.
1737        self.update_atime();
1738        if bytes_read > 0 {
1739            self.notify(InotifyMask::ACCESS);
1740        }
1741
1742        Ok(bytes_read)
1743    }
1744
1745    pub fn read<L>(
1746        &self,
1747        locked: &mut Locked<L>,
1748        current_task: &CurrentTask,
1749        data: &mut dyn OutputBuffer,
1750    ) -> Result<usize, Errno>
1751    where
1752        L: LockEqualOrBefore<FileOpsCore>,
1753    {
1754        self.read_internal(current_task, || {
1755            let locked = locked.cast_locked::<FileOpsCore>();
1756            if !self.ops().has_persistent_offsets() {
1757                if data.available() > MAX_LFS_FILESIZE {
1758                    return error!(EINVAL);
1759                }
1760                return self.ops.read(locked, self, current_task, 0, data);
1761            }
1762
1763            let mut offset_guard = self.offset.copy();
1764            let offset = *offset_guard as usize;
1765            checked_add_offset_and_length(offset, data.available())?;
1766            let read = self.ops.read(locked, self, current_task, offset, data)?;
1767            *offset_guard += read as off_t;
1768            offset_guard.update();
1769            Ok(read)
1770        })
1771    }
1772
1773    pub fn read_at<L>(
1774        &self,
1775        locked: &mut Locked<L>,
1776        current_task: &CurrentTask,
1777        offset: usize,
1778        data: &mut dyn OutputBuffer,
1779    ) -> Result<usize, Errno>
1780    where
1781        L: LockEqualOrBefore<FileOpsCore>,
1782    {
1783        if !self.ops().is_seekable() {
1784            return error!(ESPIPE);
1785        }
1786        checked_add_offset_and_length(offset, data.available())?;
1787        let locked = locked.cast_locked::<FileOpsCore>();
1788        self.read_internal(current_task, || self.ops.read(locked, self, current_task, offset, data))
1789    }
1790
1791    /// Common checks before calling ops().write.
1792    fn write_common<L>(
1793        &self,
1794        locked: &mut Locked<L>,
1795        current_task: &CurrentTask,
1796        offset: usize,
1797        data: &mut dyn InputBuffer,
1798    ) -> Result<usize, Errno>
1799    where
1800        L: LockEqualOrBefore<FileOpsCore>,
1801    {
1802        security::file_permission(current_task, self, security::PermissionFlags::WRITE)?;
1803
1804        // We need to cap the size of `data` to prevent us from growing the file too large,
1805        // according to <https://man7.org/linux/man-pages/man2/write.2.html>:
1806        //
1807        //   The number of bytes written may be less than count if, for example, there is
1808        //   insufficient space on the underlying physical medium, or the RLIMIT_FSIZE resource
1809        //   limit is encountered (see setrlimit(2)),
1810        checked_add_offset_and_length(offset, data.available())?;
1811        let locked = locked.cast_locked::<FileOpsCore>();
1812        self.ops().write(locked, self, current_task, offset, data)
1813    }
1814
1815    /// Common wrapper work for `write` and `write_at`.
1816    fn write_fn<W, L>(
1817        &self,
1818        locked: &mut Locked<L>,
1819        current_task: &CurrentTask,
1820        write: W,
1821    ) -> Result<usize, Errno>
1822    where
1823        L: LockEqualOrBefore<FileOpsCore>,
1824        W: FnOnce(&mut Locked<L>) -> Result<usize, Errno>,
1825    {
1826        if !self.can_write() {
1827            return error!(EBADF);
1828        }
1829        self.node().clear_suid_and_sgid_bits(locked, current_task)?;
1830        let bytes_written = write(locked)?;
1831        self.node().update_ctime_mtime();
1832
1833        if bytes_written > 0 {
1834            self.notify(InotifyMask::MODIFY);
1835        }
1836
1837        Ok(bytes_written)
1838    }
1839
1840    pub fn write<L>(
1841        &self,
1842        locked: &mut Locked<L>,
1843        current_task: &CurrentTask,
1844        data: &mut dyn InputBuffer,
1845    ) -> Result<usize, Errno>
1846    where
1847        L: LockEqualOrBefore<FileOpsCore>,
1848    {
1849        self.write_fn(locked, current_task, |locked| {
1850            if !self.ops().has_persistent_offsets() {
1851                return self.write_common(locked, current_task, 0, data);
1852            }
1853            // TODO(https://fxbug.dev/333540469): write_fn should take L: LockBefore<FsNodeAppend>,
1854            // but FileOpsCore must be after FsNodeAppend
1855            #[allow(
1856                clippy::undocumented_unsafe_blocks,
1857                reason = "Force documented unsafe blocks in Starnix"
1858            )]
1859            let locked = unsafe { Unlocked::new() };
1860            let mut offset = self.offset.copy();
1861            let bytes_written = if self.flags().contains(OpenFlags::APPEND) {
1862                let (_guard, locked) = self.node().ops().append_lock_write(
1863                    locked.cast_locked::<BeforeFsNodeAppend>(),
1864                    self.node(),
1865                    current_task,
1866                )?;
1867                *offset = self.ops().seek(
1868                    locked.cast_locked::<FileOpsCore>(),
1869                    self,
1870                    current_task,
1871                    *offset,
1872                    SeekTarget::End(0),
1873                )?;
1874                self.write_common(locked, current_task, *offset as usize, data)
1875            } else {
1876                let (_guard, locked) = self.node().ops().append_lock_read(
1877                    locked.cast_locked::<BeforeFsNodeAppend>(),
1878                    self.node(),
1879                    current_task,
1880                )?;
1881                self.write_common(locked, current_task, *offset as usize, data)
1882            }?;
1883            if self.ops().writes_update_seek_offset() {
1884                *offset += bytes_written as off_t;
1885            }
1886            offset.update();
1887            Ok(bytes_written)
1888        })
1889    }
1890
1891    pub fn write_at<L>(
1892        &self,
1893        locked: &mut Locked<L>,
1894        current_task: &CurrentTask,
1895        mut offset: usize,
1896        data: &mut dyn InputBuffer,
1897    ) -> Result<usize, Errno>
1898    where
1899        L: LockEqualOrBefore<FileOpsCore>,
1900    {
1901        if !self.ops().is_seekable() {
1902            return error!(ESPIPE);
1903        }
1904        self.write_fn(locked, current_task, |_locked| {
1905            // TODO(https://fxbug.dev/333540469): write_fn should take L: LockBefore<FsNodeAppend>,
1906            // but FileOpsCore must be after FsNodeAppend
1907            #[allow(
1908                clippy::undocumented_unsafe_blocks,
1909                reason = "Force documented unsafe blocks in Starnix"
1910            )]
1911            let locked = unsafe { Unlocked::new() };
1912            if self.flags().contains(OpenFlags::APPEND) {
1913                let (_guard, locked) = self.node().append_lock.write_and(locked, current_task)?;
1914                // According to LTP test pwrite04:
1915                //
1916                //   POSIX requires that opening a file with the O_APPEND flag should have no effect on the
1917                //   location at which pwrite() writes data. However, on Linux, if a file is opened with
1918                //   O_APPEND, pwrite() appends data to the end of the file, regardless of the value of offset.
1919                if self.ops().is_seekable() {
1920                    checked_add_offset_and_length(offset, data.available())?;
1921                    offset = default_eof_offset(locked, self, current_task)? as usize;
1922                }
1923                self.write_common(locked, current_task, offset, data)
1924            } else {
1925                let (_guard, locked) = self.node().append_lock.read_and(locked, current_task)?;
1926                self.write_common(locked, current_task, offset, data)
1927            }
1928        })
1929    }
1930
1931    pub fn seek<L>(
1932        &self,
1933        locked: &mut Locked<L>,
1934        current_task: &CurrentTask,
1935        target: SeekTarget,
1936    ) -> Result<off_t, Errno>
1937    where
1938        L: LockEqualOrBefore<FileOpsCore>,
1939    {
1940        let locked = locked.cast_locked::<FileOpsCore>();
1941        let locked = locked;
1942
1943        if !self.ops().is_seekable() {
1944            return error!(ESPIPE);
1945        }
1946
1947        if !self.ops().has_persistent_offsets() {
1948            return self.ops().seek(locked, self, current_task, 0, target);
1949        }
1950
1951        let mut offset_guard = self.offset.copy();
1952        let new_offset = self.ops().seek(locked, self, current_task, *offset_guard, target)?;
1953        *offset_guard = new_offset;
1954        offset_guard.update();
1955        Ok(new_offset)
1956    }
1957
1958    pub fn sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
1959        self.ops().sync(self, current_task)
1960    }
1961
1962    pub fn data_sync(&self, current_task: &CurrentTask) -> Result<(), Errno> {
1963        self.ops().data_sync(self, current_task)
1964    }
1965
1966    pub fn get_memory<L>(
1967        &self,
1968        locked: &mut Locked<L>,
1969        current_task: &CurrentTask,
1970        length: Option<usize>,
1971        prot: ProtectionFlags,
1972    ) -> Result<Arc<MemoryObject>, Errno>
1973    where
1974        L: LockEqualOrBefore<FileOpsCore>,
1975    {
1976        if prot.contains(ProtectionFlags::READ) && !self.can_read() {
1977            return error!(EACCES);
1978        }
1979        if prot.contains(ProtectionFlags::WRITE) && !self.can_write() {
1980            return error!(EACCES);
1981        }
1982        if prot.contains(ProtectionFlags::EXEC) && !self.can_exec() {
1983            return error!(EPERM);
1984        }
1985        self.ops().get_memory(locked.cast_locked::<FileOpsCore>(), self, current_task, length, prot)
1986    }
1987
1988    pub fn mmap<L>(
1989        &self,
1990        locked: &mut Locked<L>,
1991        current_task: &CurrentTask,
1992        addr: DesiredAddress,
1993        memory_offset: u64,
1994        length: usize,
1995        prot_flags: ProtectionFlags,
1996        options: MappingOptions,
1997        filename: NamespaceNode,
1998    ) -> Result<UserAddress, Errno>
1999    where
2000        L: LockEqualOrBefore<FileOpsCore>,
2001    {
2002        let locked = locked.cast_locked::<FileOpsCore>();
2003        if !self.can_read() {
2004            return error!(EACCES);
2005        }
2006        if prot_flags.contains(ProtectionFlags::WRITE)
2007            && !self.can_write()
2008            && options.contains(MappingOptions::SHARED)
2009        {
2010            return error!(EACCES);
2011        }
2012        if prot_flags.contains(ProtectionFlags::EXEC) && !self.can_exec() {
2013            return error!(EPERM);
2014        }
2015        self.ops().mmap(
2016            locked,
2017            self,
2018            current_task,
2019            addr,
2020            memory_offset,
2021            length,
2022            prot_flags,
2023            options,
2024            filename,
2025        )
2026    }
2027
2028    pub fn readdir<L>(
2029        &self,
2030        locked: &mut Locked<L>,
2031        current_task: &CurrentTask,
2032        sink: &mut dyn DirentSink,
2033    ) -> Result<(), Errno>
2034    where
2035        L: LockEqualOrBefore<FileOpsCore>,
2036    {
2037        let locked = locked.cast_locked::<FileOpsCore>();
2038        if self.name.entry.is_dead() {
2039            return error!(ENOENT);
2040        }
2041
2042        security::file_permission(current_task, self, security::PermissionFlags::READ)?;
2043
2044        self.ops().readdir(locked, self, current_task, sink)?;
2045        self.update_atime();
2046        self.notify(InotifyMask::ACCESS);
2047        Ok(())
2048    }
2049
2050    pub fn ioctl(
2051        &self,
2052        locked: &mut Locked<Unlocked>,
2053        current_task: &CurrentTask,
2054        request: u32,
2055        arg: SyscallArg,
2056    ) -> Result<SyscallResult, Errno> {
2057        security::check_file_ioctl_access(current_task, &self, request)?;
2058
2059        if request == FIBMAP {
2060            security::check_task_capable(current_task, CAP_SYS_RAWIO)?;
2061
2062            // TODO: https://fxbug.dev/404795644 - eliminate this phoney response when the SELinux
2063            // Test Suite no longer requires it.
2064            if current_task.kernel().features.selinux_test_suite {
2065                let phoney_block = 0xbadf000du32;
2066                current_task.write_object(arg.into(), &phoney_block)?;
2067                return Ok(SUCCESS);
2068            }
2069        }
2070
2071        if let Some(result) = default_vfs_ioctl(self, locked, current_task, request, arg)? {
2072            return Ok(result);
2073        }
2074
2075        self.ops().ioctl(locked, self, current_task, request, arg)
2076    }
2077
2078    pub fn fcntl(
2079        &self,
2080        current_task: &CurrentTask,
2081        cmd: u32,
2082        arg: u64,
2083    ) -> Result<SyscallResult, Errno> {
2084        self.ops().fcntl(self, current_task, cmd, arg)
2085    }
2086
2087    pub fn ftruncate<L>(
2088        &self,
2089        locked: &mut Locked<L>,
2090        current_task: &CurrentTask,
2091        length: u64,
2092    ) -> Result<(), Errno>
2093    where
2094        L: LockBefore<BeforeFsNodeAppend>,
2095    {
2096        // The file must be opened with write permissions. Otherwise
2097        // truncating it is forbidden.
2098        if !self.can_write() {
2099            return error!(EINVAL);
2100        }
2101        self.node().ftruncate(locked, current_task, length)?;
2102        self.name.entry.notify_ignoring_excl_unlink(InotifyMask::MODIFY);
2103        Ok(())
2104    }
2105
2106    pub fn fallocate<L>(
2107        &self,
2108        locked: &mut Locked<L>,
2109        current_task: &CurrentTask,
2110        mode: FallocMode,
2111        offset: u64,
2112        length: u64,
2113    ) -> Result<(), Errno>
2114    where
2115        L: LockBefore<BeforeFsNodeAppend>,
2116    {
2117        // If the file is a pipe or FIFO, ESPIPE is returned.
2118        // See https://man7.org/linux/man-pages/man2/fallocate.2.html#ERRORS
2119        if self.node().is_fifo() {
2120            return error!(ESPIPE);
2121        }
2122
2123        // Must be a regular file or directory.
2124        // See https://man7.org/linux/man-pages/man2/fallocate.2.html#ERRORS
2125        if !self.node().is_dir() && !self.node().is_reg() {
2126            return error!(ENODEV);
2127        }
2128
2129        // The file must be opened with write permissions. Otherwise operation is forbidden.
2130        // See https://man7.org/linux/man-pages/man2/fallocate.2.html#ERRORS
2131        if !self.can_write() {
2132            return error!(EBADF);
2133        }
2134
2135        security::file_permission(current_task, self, security::PermissionFlags::WRITE)?;
2136
2137        self.node().fallocate(locked, current_task, mode, offset, length)?;
2138        self.notify(InotifyMask::MODIFY);
2139        Ok(())
2140    }
2141
2142    pub fn to_handle(
2143        &self,
2144        current_task: &CurrentTask,
2145    ) -> Result<Option<zx::NullableHandle>, Errno> {
2146        self.ops().to_handle(self, current_task)
2147    }
2148
2149    pub fn get_handles(
2150        &self,
2151        current_task: &CurrentTask,
2152    ) -> Result<Vec<zx::NullableHandle>, Errno> {
2153        self.ops().get_handles(self, current_task)
2154    }
2155
2156    pub fn as_thread_group_key(&self) -> Result<ThreadGroupKey, Errno> {
2157        self.ops().as_thread_group_key(self)
2158    }
2159
2160    /// Update the file flags.
2161    ///
2162    /// Writes the bits in `value` that are set in `mask` into the file flags.
2163    ///
2164    /// Does not provide any synchronization.
2165    pub fn update_file_flags(&self, value: OpenFlags, mask: OpenFlags) {
2166        self.flags.update(value, mask, Ordering::Relaxed, Ordering::Relaxed);
2167    }
2168
2169    /// Get the async owner of this file.
2170    ///
2171    /// See fcntl(F_GETOWN)
2172    pub fn get_async_owner(&self) -> FileAsyncOwner {
2173        *self.async_owner.lock()
2174    }
2175
2176    /// Set the async owner of this file.
2177    ///
2178    /// See fcntl(F_SETOWN)
2179    pub fn set_async_owner(&self, owner: FileAsyncOwner) {
2180        *self.async_owner.lock() = owner;
2181    }
2182
2183    /// See fcntl(F_GETLEASE)
2184    pub fn get_lease(&self, _current_task: &CurrentTask) -> FileLeaseType {
2185        *self.lease.lock()
2186    }
2187
2188    /// See fcntl(F_SETLEASE)
2189    pub fn set_lease(&self, current_task: &CurrentTask, lease: FileLeaseType) -> Result<(), Errno> {
2190        if !self.node().is_reg() {
2191            return error!(EINVAL);
2192        }
2193        security::check_file_lock_access(current_task, self)?;
2194        if lease == FileLeaseType::Read && self.can_write() {
2195            return error!(EAGAIN);
2196        }
2197        *self.lease.lock() = lease;
2198        Ok(())
2199    }
2200
2201    /// Wait on the specified events and call the EventHandler when ready
2202    pub fn wait_async<L>(
2203        &self,
2204        locked: &mut Locked<L>,
2205        current_task: &CurrentTask,
2206        waiter: &Waiter,
2207        events: FdEvents,
2208        handler: EventHandler,
2209    ) -> Option<WaitCanceler>
2210    where
2211        L: LockEqualOrBefore<FileOpsCore>,
2212    {
2213        self.ops().wait_async(
2214            locked.cast_locked::<FileOpsCore>(),
2215            self,
2216            current_task,
2217            waiter,
2218            events,
2219            handler,
2220        )
2221    }
2222
2223    /// The events currently active on this file.
2224    pub fn query_events<L>(
2225        &self,
2226        locked: &mut Locked<L>,
2227        current_task: &CurrentTask,
2228    ) -> Result<FdEvents, Errno>
2229    where
2230        L: LockEqualOrBefore<FileOpsCore>,
2231    {
2232        self.ops()
2233            .query_events(locked.cast_locked::<FileOpsCore>(), self, current_task)
2234            .map(FdEvents::add_equivalent_fd_events)
2235    }
2236
2237    pub fn record_lock(
2238        &self,
2239        locked: &mut Locked<Unlocked>,
2240        current_task: &CurrentTask,
2241        cmd: RecordLockCommand,
2242        flock: uapi::flock,
2243    ) -> Result<Option<uapi::flock>, Errno> {
2244        security::check_file_lock_access(current_task, self)?;
2245        self.node().record_lock(locked, current_task, self, cmd, flock)
2246    }
2247
2248    pub fn flush<L>(&self, locked: &mut Locked<L>, current_task: &CurrentTask, id: FdTableId)
2249    where
2250        L: LockEqualOrBefore<FileOpsCore>,
2251    {
2252        self.name.entry.node.record_lock_release(RecordLockOwner::FdTable(id));
2253        self.ops().flush(locked.cast_locked::<FileOpsCore>(), self, current_task)
2254    }
2255
2256    fn update_atime(&self) {
2257        if !self.flags().contains(OpenFlags::NOATIME) {
2258            self.name.update_atime();
2259        }
2260    }
2261
2262    pub fn readahead(
2263        &self,
2264        current_task: &CurrentTask,
2265        offset: usize,
2266        length: usize,
2267    ) -> Result<(), Errno> {
2268        // readfile() fails with EBADF if the file was not open for read.
2269        if !self.can_read() {
2270            return error!(EBADF);
2271        }
2272        checked_add_offset_and_length(offset, length)?;
2273        self.ops().readahead(self, current_task, offset, length)
2274    }
2275
2276    pub fn extra_fdinfo(
2277        &self,
2278        locked: &mut Locked<FileOpsCore>,
2279        current_task: &CurrentTask,
2280    ) -> Option<FsString> {
2281        let file = self.weak_handle.upgrade()?;
2282        self.ops().extra_fdinfo(locked, &file, current_task)
2283    }
2284
2285    /// Register the fd number of an `EpollFileObject` that listens to events from this
2286    /// `FileObject`.
2287    pub fn register_epfd(&self, file: &FileHandle) {
2288        self.epoll_files.lock().insert(WeakKey::from(file), file.weak_handle.clone());
2289    }
2290
2291    pub fn unregister_epfd(&self, file: &FileHandle) {
2292        self.epoll_files.lock().remove(&WeakKey::from(file));
2293    }
2294}
2295
2296impl Releasable for FileObject {
2297    type Context<'a> = CurrentTaskAndLocked<'a>;
2298
2299    fn release<'a>(self, context: CurrentTaskAndLocked<'a>) {
2300        let (locked, current_task) = context;
2301        // Release all wake leases associated with this file in the corresponding `WaitObject`
2302        // of each registered epfd.
2303        for (_, file) in self.epoll_files.lock().drain() {
2304            if let Some(file) = file.upgrade() {
2305                if let Some(epoll_object) = file.downcast_file::<EpollFileObject>() {
2306                    let _ = epoll_object.delete(current_task, &self);
2307                }
2308            }
2309        }
2310
2311        if self.can_write() {
2312            self.name.entry.node.write_guard_state.lock().release(FileWriteGuardMode::WriteFile);
2313        }
2314
2315        let locked = locked.cast_locked::<FileOpsCore>();
2316        let ops = self.ops;
2317        let state = self.state;
2318        ops.close(locked, &state, current_task);
2319        state.name.entry.node.on_file_closed(&state);
2320        let event =
2321            if state.can_write() { InotifyMask::CLOSE_WRITE } else { InotifyMask::CLOSE_NOWRITE };
2322        state.notify(event);
2323    }
2324}
2325
2326impl fmt::Debug for FileObject {
2327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2328        f.debug_struct("FileObject")
2329            .field("name", &self.name)
2330            .field("fs", &self.fs.name())
2331            .field("offset", &self.offset)
2332            .field("flags", &self.flags)
2333            .field("ops_ty", &self.ops().type_name())
2334            .finish()
2335    }
2336}
2337
2338impl OnWakeOps for FileReleaser {
2339    fn on_wake(&self, _current_task: &CurrentTask, _baton_lease: &zx::NullableHandle) {}
2340}
2341
2342/// A FileObject with the type of its FileOps known. Dereferencing it returns the FileOps.
2343pub struct DowncastedFile<'a, Ops> {
2344    file: &'a FileObject,
2345    ops: &'a Ops,
2346}
2347impl<'a, Ops> Copy for DowncastedFile<'a, Ops> {}
2348impl<'a, Ops> Clone for DowncastedFile<'a, Ops> {
2349    fn clone(&self) -> Self {
2350        *self
2351    }
2352}
2353
2354impl<'a, Ops> DowncastedFile<'a, Ops> {
2355    pub fn file(&self) -> &'a FileObject {
2356        self.file
2357    }
2358}
2359
2360impl<'a, Ops> Deref for DowncastedFile<'a, Ops> {
2361    type Target = &'a Ops;
2362    fn deref(&self) -> &Self::Target {
2363        &self.ops
2364    }
2365}
2366
2367impl FileObject {
2368    /// Returns the `FileObject`'s `FileOps` as a `DowncastedFile<T>`, or `None` if the downcast
2369    /// fails.
2370    ///
2371    /// This is useful for syscalls that only operate on a certain type of file.
2372    pub fn downcast_file<'a, T>(&'a self) -> Option<DowncastedFile<'a, T>>
2373    where
2374        T: 'static,
2375    {
2376        let ops = self.ops().as_any().downcast_ref::<T>()?;
2377        Some(DowncastedFile { file: self, ops })
2378    }
2379}
2380
2381/// Invokes the specified one-way `method` on the `proxy` and waits until the `proxy`'s underlying
2382/// channel has been closed by the peer.
2383///
2384/// This is used in `close()` implementations when the `FileOps` wraps a FIDL resource that provides
2385/// a one-way API to request teardown, and acknowledges completion of teardown by closing the FIDL
2386/// channel, to ensure that the `close()` call does not return until the FIDL server has actually
2387/// processed the teardown request.
2388pub fn call_fidl_and_await_close<P, M>(method: M, proxy: &P)
2389where
2390    P: fidl::endpoints::SynchronousProxy,
2391    M: FnOnce(&P) -> Result<(), fidl::Error>,
2392{
2393    if let Err(e) = method(proxy) {
2394        log_error!("call_fidl_and_await_close: call {} failed: {e:?}", P::Protocol::DEBUG_NAME);
2395        return;
2396    }
2397    let channel = proxy.as_channel();
2398    let result = channel.wait_one(zx::Signals::CHANNEL_PEER_CLOSED, zx::MonotonicInstant::INFINITE);
2399    if let Err(status) = result.to_result() {
2400        log_error!(
2401            "call_fidl_and_await_close: wait_one {} failed: {status:?}",
2402            P::Protocol::DEBUG_NAME
2403        );
2404    }
2405}
2406
2407#[cfg(test)]
2408mod tests {
2409    use crate::fs::tmpfs::TmpFs;
2410    use crate::task::CurrentTask;
2411    use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
2412    use crate::testing::*;
2413    use crate::vfs::MountInfo;
2414    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
2415    use starnix_sync::{Locked, Unlocked};
2416    use starnix_uapi::auth::FsCred;
2417    use starnix_uapi::device_id::DeviceId;
2418    use starnix_uapi::file_mode::FileMode;
2419    use starnix_uapi::open_flags::OpenFlags;
2420    use std::sync::Arc;
2421    use std::sync::atomic::{AtomicBool, Ordering};
2422    use zerocopy::{FromBytes, IntoBytes, LE, U64};
2423
2424    #[::fuchsia::test]
2425    async fn test_append_truncate_race() {
2426        spawn_kernel_and_run(async |locked, current_task| {
2427            let kernel = current_task.kernel();
2428            let root_fs = TmpFs::new_fs(locked, &kernel);
2429            let mount = MountInfo::detached();
2430            let root_node = Arc::clone(root_fs.root());
2431            let file = root_node
2432                .create_entry(
2433                    locked,
2434                    &current_task,
2435                    &mount,
2436                    "test".into(),
2437                    |locked, dir, mount, name| {
2438                        dir.create_node(
2439                            locked,
2440                            &current_task,
2441                            mount,
2442                            name,
2443                            FileMode::IFREG | FileMode::ALLOW_ALL,
2444                            DeviceId::NONE,
2445                            FsCred::root(),
2446                        )
2447                    },
2448                )
2449                .expect("create_node failed");
2450            let file_handle = file
2451                .open_anonymous(locked, &current_task, OpenFlags::APPEND | OpenFlags::RDWR)
2452                .expect("open failed");
2453            let done = Arc::new(AtomicBool::new(false));
2454
2455            let fh = file_handle.clone();
2456            let done_clone = done.clone();
2457            let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
2458                for i in 0..2000 {
2459                    fh.write(
2460                        locked,
2461                        current_task,
2462                        &mut VecInputBuffer::new(U64::<LE>::new(i).as_bytes()),
2463                    )
2464                    .expect("write failed");
2465                }
2466                done_clone.store(true, Ordering::SeqCst);
2467                let result: Result<(), starnix_uapi::errors::Errno> = Ok(());
2468                result
2469            };
2470            let (write_thread, req) =
2471                SpawnRequestBuilder::new().with_sync_closure(closure).build_with_sync_result();
2472            kernel.kthreads.spawner().spawn_from_request(req);
2473
2474            let fh = file_handle.clone();
2475            let done_clone = done.clone();
2476            let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
2477                while !done_clone.load(Ordering::SeqCst) {
2478                    fh.ftruncate(locked, current_task, 0).expect("truncate failed");
2479                }
2480                let result: Result<(), starnix_uapi::errors::Errno> = Ok(());
2481                result
2482            };
2483            let (truncate_thread, req) =
2484                SpawnRequestBuilder::new().with_sync_closure(closure).build_with_sync_result();
2485            kernel.kthreads.spawner().spawn_from_request(req);
2486
2487            // If we read from the file, we should always find an increasing sequence. If there are
2488            // races, then we might unexpectedly see zeroes.
2489            while !done.load(Ordering::SeqCst) {
2490                let mut buffer = VecOutputBuffer::new(4096);
2491                let amount = file_handle
2492                    .read_at(locked, &current_task, 0, &mut buffer)
2493                    .expect("read failed");
2494                let mut last = None;
2495                let buffer = &Vec::from(buffer)[..amount];
2496                for i in
2497                    buffer.chunks_exact(8).map(|chunk| U64::<LE>::read_from_bytes(chunk).unwrap())
2498                {
2499                    if let Some(last) = last {
2500                        assert!(i.get() > last, "buffer: {:?}", buffer);
2501                    }
2502                    last = Some(i.get());
2503                }
2504            }
2505
2506            let _ = write_thread().unwrap();
2507            let _ = truncate_thread().unwrap();
2508        })
2509        .await;
2510    }
2511}