Skip to main content

starnix_core/vfs/
file_object.rs

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