Skip to main content

starnix_core/vfs/
fd_table.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::mutable_state::{state_accessor, state_implementation};
6use crate::security;
7use crate::task::{CurrentTask, register_delayed_release};
8use crate::vfs::{FdNumber, FileHandle, FileReleaser};
9use bitflags::bitflags;
10use fuchsia_rcu::subtle::{RcuPtrRef, rcu_ptr_to_arc};
11use fuchsia_rcu::{RcuReadScope, rcu_drop};
12use fuchsia_rcu_collections::rcu_array::RcuArray;
13use linux_uapi::{FD_CLOEXEC, FIOCLEX, FIONCLEX};
14use macro_rules_attribute::apply;
15use starnix_sync::{FdTableMutableStateLock, LockDepRwLock};
16use starnix_syscalls::SyscallResult;
17use starnix_types::ownership::Releasable;
18use starnix_uapi::errors::Errno;
19use starnix_uapi::open_flags::OpenFlags;
20use starnix_uapi::resource_limits::Resource;
21use starnix_uapi::{errno, error};
22use static_assertions::const_assert;
23use std::sync::Arc;
24use std::sync::atomic::{AtomicUsize, Ordering};
25
26bitflags! {
27    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28    pub struct FdFlags: u32 {
29        /// Whether the file descriptor should be closed when the process execs.
30        const CLOEXEC = FD_CLOEXEC;
31    }
32}
33
34impl std::convert::From<FdFlags> for SyscallResult {
35    fn from(value: FdFlags) -> Self {
36        value.bits().into()
37    }
38}
39
40/// An identifier for an `FdTable`.
41///
42/// Used by flock to drop file locks when a file descriptor is closed.
43#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
44pub struct FdTableId(usize);
45
46impl FdTableId {
47    fn new(id: *const FdTable) -> Self {
48        Self(id as usize)
49    }
50
51    pub fn raw(&self) -> usize {
52        self.0
53    }
54}
55
56/// We store the CLOEXEC bit and the address of the `FileObject` in a single `usize` so that we can
57/// operate on an FdTable entry atomically. This mask is used to select the CLOEXEC bit.
58const FLAGS_MASK: usize = 0x1;
59
60/// An encoded entry in an `FdTable`.
61///
62/// Encodes both the `FileHandle` and the CLOEXEC bit. Can either hold an entry or be empty.
63#[derive(Debug, Default)]
64struct EncodedEntry {
65    /// Rather than using a separate "flags" field, we encode the table entry into a single usize.
66    ///
67    /// If `value` is zero, the entry is empty.
68    ///
69    /// The lowest bit of `value` is the CLOEXEC bit.
70    ///
71    /// The remaining bits of `value` are a `FileHandle` converted to a raw pointer.
72    value: AtomicUsize,
73}
74
75// An assert to ensure that the lowest bit of the `FileHandle` is available to store the CLOEXEC
76// bit.
77const_assert!(std::mem::align_of::<*const FileReleaser>() >= 1 << FLAGS_MASK);
78
79impl EncodedEntry {
80    /// Encodes a `FileHandle` and `FdFlags` into a single `usize`.
81    ///
82    /// The returned value holds a reference to the `FileObject` and must be released to avoid a
83    /// memory leak.
84    fn encode(file: FileHandle, flags: FdFlags) -> usize {
85        let ptr = Arc::into_raw(file) as usize;
86        let flags = (flags.bits() as usize) & FLAGS_MASK;
87        ptr | flags
88    }
89
90    /// Releases the `FileHandle` for a previously encoded value.
91    ///
92    /// # Safety
93    ///
94    /// `value` must have been encoded by `Self::encode`.
95    unsafe fn release(id: FdTableId, value: usize) {
96        let ptr = Self::decode_ptr(value);
97        if !ptr.is_null() {
98            // SAFETY: The pointer is valid because it was encoded in `self.value`.
99            let file = unsafe { Arc::from_raw(ptr) };
100            // Concurrent readers expect the `FileHandle` to be retained for the entire RCU grace
101            // period. `FlushedFile` delayed release may be processed before the grace period
102            // expires. We must defer a reference to RCU to ensure delayed release does not drop the
103            // last reference and free the file before RCU readers are done with it.
104            register_delayed_release(FlushedFile(file.clone(), id));
105            rcu_drop(file)
106        }
107    }
108
109    /// Decodes the `FdFlags` from an encoded `usize`.
110    fn decode_flags(value: usize) -> FdFlags {
111        FdFlags::from_bits_truncate((value & FLAGS_MASK) as u32)
112    }
113
114    /// Decodes the `FileHandle` from an encoded `usize`.
115    fn decode_ptr(value: usize) -> *const FileReleaser {
116        (value & !FLAGS_MASK) as *const _
117    }
118
119    /// Creates a new `EncodedEntry` from a `FdTableEntry`.
120    fn new(entry: FdTableEntry) -> Self {
121        Self { value: AtomicUsize::new(Self::encode(entry.file, entry.flags)) }
122    }
123
124    /// Whether this entry contains a valid `FileHandle`.
125    fn is_some(&self) -> bool {
126        let value = self.value.load(Ordering::Acquire);
127        value != 0
128    }
129
130    /// Whether this entry is empty.
131    fn is_none(&self) -> bool {
132        !self.is_some()
133    }
134
135    /// Sets the `FdFlags` for this entry, preserving the `FileHandle`.
136    fn set_flags(&self, flags: FdFlags) {
137        loop {
138            let old_value = self.value.load(Ordering::Relaxed);
139            assert!(old_value != 0);
140            let new_value = old_value & !FLAGS_MASK | (flags.bits() as usize) & FLAGS_MASK;
141            if self
142                .value
143                .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
144                .is_ok()
145            {
146                return;
147            }
148        }
149    }
150
151    /// Sets the `FileHandle` for this entry, preserving the `FdFlags`.
152    fn set_file(&self, id: FdTableId, file: FileHandle) {
153        let ptr = Arc::into_raw(file) as usize;
154        loop {
155            let old_value = self.value.load(Ordering::Relaxed);
156            assert!(old_value != 0);
157            let flags = old_value & FLAGS_MASK;
158            let new_value = ptr | flags;
159            if self
160                .value
161                .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
162                .is_ok()
163            {
164                // SAFETY: The value was previously encoded by `Self::encode`.
165                unsafe { Self::release(id, old_value) };
166                return;
167            }
168        }
169    }
170
171    /// Reads the entry, returning a guard that maintains a consistent view of it.
172    fn read<'a>(&self, scope: &'a RcuReadScope) -> Option<FdTableEntryGuard<'a>> {
173        let value = self.value.load(Ordering::Acquire);
174        if value == 0 {
175            return None;
176        }
177        let ptr = Self::decode_ptr(value);
178        let flags = Self::decode_flags(value);
179        // SAFETY: The pointer is valid because it was encoded in `self.value`.
180        let file = unsafe { RcuPtrRef::new(scope, ptr) };
181        Some(FdTableEntryGuard { file, flags })
182    }
183
184    /// Sets the `FileHandle` and `FdFlags` for this entry.
185    fn set_entry(&self, id: FdTableId, entry: FdTableEntry) -> bool {
186        // SAFETY: The value is encoded by `Self::encode`.
187        unsafe { self.set(id, Self::encode(entry.file, entry.flags)) }
188    }
189
190    /// Makes the entry empty.
191    fn clear(&self, id: FdTableId) -> bool {
192        // SAFETY: The value is zero.
193        unsafe { self.set(id, 0) }
194    }
195
196    /// Sets the value of this entry to the given value.
197    ///
198    /// Most clients should call `set_entry` or `clear` instead.
199    ///
200    /// # Safety
201    ///
202    /// The value must be encoded by `Self::encode` or be zero.
203    unsafe fn set(&self, id: FdTableId, value: usize) -> bool {
204        let old_value = self.value.swap(value, Ordering::AcqRel);
205        if old_value != 0 {
206            // SAFETY: The value was previously encoded by `Self::encode`.
207            unsafe { Self::release(id, old_value) };
208            true
209        } else {
210            false
211        }
212    }
213}
214
215impl Clone for EncodedEntry {
216    fn clone(&self) -> Self {
217        if let Some(guard) = self.read(&RcuReadScope::new()) {
218            Self::new(guard.to_entry())
219        } else {
220            Self::default()
221        }
222    }
223}
224
225impl Drop for EncodedEntry {
226    fn drop(&mut self) {
227        let value = self.value.load(Ordering::Acquire);
228        let ptr = Self::decode_ptr(value);
229        if !ptr.is_null() {
230            // SAFETY: The pointer is valid because it was encoded in `self.value`.
231            let _file = unsafe { Arc::from_raw(ptr) };
232        }
233    }
234}
235
236/// An entry in the `FdTable`.
237#[derive(Debug, Clone)]
238struct FdTableEntry {
239    /// The file handle.
240    file: FileHandle,
241
242    /// The flags associated with the file handle.
243    flags: FdFlags,
244}
245
246/// A guard for reading an `FdTableEntry`.
247///
248/// This provides memory-safe access to decoded `FdTableEntry` data, which is guarded by RCU.
249struct FdTableEntryGuard<'a> {
250    /// The pointer to the file handle.
251    file: RcuPtrRef<'a, FileReleaser>,
252
253    /// The flags associated with the file handle.
254    flags: FdFlags,
255}
256
257impl<'a> FdTableEntryGuard<'a> {
258    fn flags(&self) -> FdFlags {
259        self.flags
260    }
261
262    /// Acquire a strong reference to the file handle.
263    fn to_handle(&self) -> FileHandle {
264        // SAFETY: We can pass `self.file` to `rcu_ptr_to_arc` because it was obtained from
265        // `Arc::into_raw` via `EncodedEntry::encode` and `EncodedEntry::decode_ptr`.
266        unsafe { rcu_ptr_to_arc(self.file) }
267    }
268
269    /// Upgrade this guard to a full `FdTableEntry` independent of the guard lifetime.
270    fn to_entry(&self) -> FdTableEntry {
271        FdTableEntry { file: self.to_handle(), flags: self.flags }
272    }
273}
274
275/// A `FileHandle` that has been closed and is waiting to be flushed.
276struct FlushedFile(FileHandle, FdTableId);
277
278impl Releasable for FlushedFile {
279    type Context<'a> = &'a CurrentTask;
280    fn release<'a>(self, context: Self::Context<'a>) {
281        let current_task = context;
282        let FlushedFile(file, id) = self;
283        file.flush(current_task, id);
284    }
285}
286
287/// A read-only view of an `FdTable`.
288///
289/// When reading an `FdTable`, we use an `FdTableView` to have a coherent view of the table even
290/// though the table can be modified by other threads concurrently.
291///
292/// The actual entries in the slice can still be modified by other threads. However, the view
293/// provided by the `FdTableView` is protected by an RCU read lock.
294struct FdTableView<'a> {
295    /// The entries in the table.
296    slice: &'a [EncodedEntry],
297}
298
299impl<'a> FdTableView<'a> {
300    /// Returns the number of entries in the table.
301    fn len(&self) -> usize {
302        self.slice.len()
303    }
304
305    /// Whether the view contains a given `FdNumber`.
306    fn is_some(&self, fd: FdNumber) -> bool {
307        self.slice.get(fd.raw() as usize).map_or(false, |entry| entry.is_some())
308    }
309
310    /// Whether the view does not contain a given `FdNumber`.
311    fn is_none(&self, fd: FdNumber) -> bool {
312        !self.is_some(fd)
313    }
314
315    /// Returns the `FileHandle` for a given `FdNumber`, if any.
316    fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
317        self.slice
318            .get(fd.raw() as usize)
319            .and_then(|entry| entry.read(scope))
320            .map(|guard| guard.to_handle())
321    }
322
323    /// Returns the `FdTableEntry` for a given `FdNumber`, if any.
324    fn get_entry(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FdTableEntry> {
325        self.slice
326            .get(fd.raw() as usize)
327            .and_then(|entry| entry.read(scope))
328            .map(|guard| guard.to_entry())
329    }
330}
331
332#[derive(Debug)]
333pub struct FdTableMutableState {
334    /// The number of shared references to this table.
335    ///
336    /// If the value is 0, the table is read-only and empty.
337    pub share_count: usize,
338
339    /// The next available `FdNumber`.
340    pub next_fd: FdNumber,
341}
342
343#[apply(state_implementation!)]
344impl FdTableMutableState<Base = FdTable> {
345    /// Increases the share count for this `FdTable`.
346    fn share(&mut self) {
347        assert!(self.share_count > 0, "Cannot share unshared table");
348        self.share_count += 1;
349    }
350
351    /// Decreases the share count for this `FdTable`. The table is cleared when the count reaches
352    /// zero.
353    fn unshare(&mut self) {
354        if self.share_count > 0 {
355            self.share_count -= 1;
356            if self.share_count == 0 {
357                self.clear();
358            }
359        }
360    }
361
362    /// Creates a snapshot of the table with the same files but a separate share count.
363    fn fork(&self) -> FdTable {
364        FdTable {
365            entries: self.base.entries.clone(),
366            mutable_state: LockDepRwLock::new(FdTableMutableState {
367                share_count: 1,
368                next_fd: self.next_fd,
369            }),
370        }
371    }
372
373    /// The lowest available `FdNumber`.
374    fn next_fd(&self) -> FdNumber {
375        self.next_fd
376    }
377
378    /// Recalculates the lowest available FD >= minfd based on the contents of the map.
379    fn calculate_lowest_available_fd(&self, view: &FdTableView<'_>, minfd: &FdNumber) -> FdNumber {
380        let mut fd: FdNumber = *minfd;
381        while view.is_some(fd) {
382            fd = FdNumber::from_raw(fd.raw() + 1);
383        }
384        fd
385    }
386
387    // Returns the (possibly memoized) lowest available FD >= minfd in this map.
388    fn get_lowest_available_fd(&self, scope: &RcuReadScope, minfd: FdNumber) -> FdNumber {
389        if minfd > self.next_fd {
390            let view = self.base.read_entries(scope);
391            return self.calculate_lowest_available_fd(&view, &minfd);
392        }
393        self.next_fd
394    }
395
396    /// Returns the `FileHandle` for a given `FdNumber`, if any.
397    fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
398        self.base.read_entries(scope).get_file(scope, fd)
399    }
400
401    /// Inserts a new entry into the `FdTable`.
402    ///
403    /// Returns whether the `FdTable` previously contained an entry for the given `FdNumber`.
404    fn insert_entry(
405        &mut self,
406        scope: &RcuReadScope,
407        fd: FdNumber,
408        rlimit: u64,
409        entry: FdTableEntry,
410    ) -> Result<bool, Errno> {
411        let raw_fd = fd.raw();
412        if raw_fd < 0 {
413            return error!(EBADF);
414        }
415        if raw_fd as u64 >= rlimit {
416            return error!(EMFILE);
417        }
418        let mut view = self.base.read_entries(scope);
419        if raw_fd == self.next_fd.raw() {
420            self.next_fd =
421                self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(raw_fd + 1));
422        }
423        let raw_fd = raw_fd as usize;
424        if view.len() <= raw_fd {
425            // SAFETY: The write guard excludes concurrent writers.
426            unsafe { self.base.entries.ensure_at_least(raw_fd + 1) };
427            view = self.base.read_entries(scope);
428        }
429        let id = self.base.id();
430        Ok(view.slice[raw_fd].set_entry(id, entry))
431    }
432
433    /// Removes an entry from the `FdTable`.
434    ///
435    /// Returns whether the `FdTable` previously contained an entry for the given `FdNumber`.
436    fn remove_entry(&mut self, scope: &RcuReadScope, fd: &FdNumber) -> bool {
437        let raw_fd = fd.raw() as usize;
438        let view = self.base.read_entries(scope);
439        if raw_fd >= view.len() {
440            return false;
441        }
442        let id = self.base.id();
443        let removed = view.slice[raw_fd].clear(id);
444        if removed && raw_fd < self.next_fd.raw() as usize {
445            self.next_fd = *fd;
446        }
447        removed
448    }
449
450    /// Sets the flags for a given `FdNumber`.
451    ///
452    /// Returns `Errno` if the `FdTable` does not contain an entry for the given `FdNumber`.
453    fn set_fd_flags(
454        &self,
455        scope: &RcuReadScope,
456        fd: FdNumber,
457        flags: FdFlags,
458    ) -> Result<(), Errno> {
459        let view = self.base.read_entries(scope);
460        if view.is_none(fd) {
461            return error!(EBADF);
462        }
463        let raw_fd = fd.raw() as usize;
464        view.slice[raw_fd].set_flags(flags);
465        Ok(())
466    }
467
468    /// Retains only the entries for which the given predicate returns `true`.
469    ///
470    /// The predicate is called with the `FdNumber` and a mutable reference to the `FdFlags` for
471    /// each entry in the `FdTable`. If the predicate returns `false`, the entry is removed from
472    /// the `FdTable`. Otherwise, the `FdFlags` are updated to the value modified by the predicate.
473    fn retain<F>(&mut self, scope: &RcuReadScope, mut predicate: F)
474    where
475        F: FnMut(FdNumber, &mut FdFlags) -> bool,
476    {
477        let id = self.base.id();
478        let view = self.base.read_entries(scope);
479        for (index, encoded_entry) in view.slice.iter().enumerate() {
480            let fd = FdNumber::from_raw(index as i32);
481            if let Some(guard) = encoded_entry.read(scope) {
482                let mut modified_flags = guard.flags();
483                if !predicate(fd, &mut modified_flags) {
484                    encoded_entry.clear(id);
485                } else if modified_flags != guard.flags() {
486                    encoded_entry.set_flags(modified_flags);
487                }
488            }
489        }
490        self.next_fd = self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(0));
491    }
492
493    /// Retain none of the entries in the table.
494    fn clear(&mut self) {
495        self.retain(&RcuReadScope::new(), |_, _| false);
496    }
497
498    /// Replaces the `FileHandle` for each entry in the `FdTable` with the result of the given
499    /// predicate.
500    ///
501    /// The predicate is called with the `FileHandle` for each entry in the `FdTable`. If the
502    /// predicate returns `Some(file)`, the entry is updated with the new `FileHandle`. Otherwise,
503    /// the entry is left unchanged.
504    fn remap<F>(&self, scope: &RcuReadScope, predicate: F)
505    where
506        F: Fn(&FileHandle) -> Option<FileHandle>,
507    {
508        let id = self.base.id();
509        let view = self.base.read_entries(scope);
510        for encoded_entry in view.slice.iter() {
511            if let Some(guard) = encoded_entry.read(scope) {
512                let file = guard.to_handle();
513                if let Some(replacement_file) = predicate(&file) {
514                    encoded_entry.set_file(id, replacement_file);
515                }
516            }
517        }
518    }
519}
520
521/// A file descriptor table which is shared between tasks.
522///
523/// # Thread Safety
524///
525/// The table supports concurrent, lock-free reads via RCU. Writers serialize on the share count
526/// mutex independently from readers.
527#[derive(Debug)]
528pub struct FdTable {
529    /// The entries of the `FdTable`.
530    ///
531    /// # Thread Safety
532    ///
533    /// Must only be modified while holding the `mutable_state` lock.
534    entries: RcuArray<EncodedEntry>,
535
536    /// The mutable state of the `FdTable`.
537    mutable_state: LockDepRwLock<FdTableMutableState, FdTableMutableStateLock>,
538}
539
540impl Default for FdTable {
541    fn default() -> Self {
542        Self {
543            entries: Default::default(),
544            mutable_state: LockDepRwLock::new(FdTableMutableState {
545                share_count: 1,
546                next_fd: FdNumber::from_raw(0),
547            }),
548        }
549    }
550}
551
552impl Clone for FdTable {
553    fn clone(&self) -> Self {
554        let state = self.read();
555        Self {
556            entries: self.entries.clone(),
557            mutable_state: LockDepRwLock::new(FdTableMutableState {
558                share_count: 1,
559                next_fd: state.next_fd,
560            }),
561        }
562    }
563}
564
565impl Drop for FdTable {
566    fn drop(&mut self) {
567        let scope = RcuReadScope::new();
568        let view = self.read_entries(&scope);
569        for entry in view.slice.iter() {
570            assert!(entry.is_none());
571        }
572    }
573}
574
575impl FdTable {
576    /// Returns the `FdTableId` of the `FdTable`.
577    pub fn id(&self) -> FdTableId {
578        FdTableId::new(self as *const Self)
579    }
580
581    /// Returns a `FdTableView` that provides read-only access to the state of the `FdTable`.
582    fn read_entries<'a>(&self, scope: &'a RcuReadScope) -> FdTableView<'a> {
583        let slice = self.entries.as_slice(scope);
584        FdTableView { slice }
585    }
586
587    /// Returns new unshared `FdTable` that is a snapshot of the state of the `FdTable`.
588    pub fn fork(&self) -> Arc<Self> {
589        Arc::new(self.read().fork())
590    }
591
592    /// Trims close-on-exec file descriptors from the table.
593    pub fn exec(&self) {
594        self.retain(|_fd, flags| !flags.contains(FdFlags::CLOEXEC));
595    }
596
597    /// Inserts a file descriptor into the table.
598    pub fn insert(
599        &self,
600        current_task: &CurrentTask,
601        fd: FdNumber,
602        file: FileHandle,
603    ) -> Result<(), Errno> {
604        let flags = FdFlags::empty();
605        let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
606        let mut state = self.write_active()?;
607        state.insert_entry(&RcuReadScope::new(), fd, rlimit, FdTableEntry { file, flags })?;
608        Ok(())
609    }
610
611    /// Adds a file descriptor to the table.
612    ///
613    /// The file descriptor will be assigned the next available number.
614    ///
615    /// Returns the assigned file descriptor number.
616    ///
617    /// This function is the most common way to add a file descriptor to the table.
618    pub fn add(
619        &self,
620        current_task: &CurrentTask,
621        file: FileHandle,
622        flags: FdFlags,
623    ) -> Result<FdNumber, Errno> {
624        let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
625        let mut state = self.write_active()?;
626        let fd = state.next_fd();
627        state.insert_entry(&RcuReadScope::new(), fd, rlimit, FdTableEntry { file, flags })?;
628        Ok(fd)
629    }
630
631    /// Duplicates a file descriptor.
632    ///
633    /// If `target` is `TargetFdNumber::Minimum`, a new `FdNumber` is allocated. Returns the new
634    /// `FdNumber`.
635    pub fn duplicate(
636        &self,
637        current_task: &CurrentTask,
638        oldfd: FdNumber,
639        target: TargetFdNumber,
640        flags: FdFlags,
641    ) -> Result<FdNumber, Errno> {
642        let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
643        let mut state = self.write_active()?;
644        let scope = RcuReadScope::new();
645        let file = state.get_file(&scope, oldfd).ok_or_else(|| errno!(EBADF))?;
646
647        let fd = match target {
648            TargetFdNumber::Specific(fd) => {
649                // We need to check the rlimit before we remove the entry from state
650                // because we cannot error out after removing the entry.
651                if fd.raw() as u64 >= rlimit {
652                    // ltp_dup201 shows that we're supposed to return EBADF in this
653                    // situation, instead of EMFILE, which is what we normally return
654                    // when we're past the rlimit.
655                    return error!(EBADF);
656                }
657                state.remove_entry(&scope, &fd);
658                fd
659            }
660            TargetFdNumber::Minimum(fd) => state.get_lowest_available_fd(&scope, fd),
661            TargetFdNumber::Default => state.get_lowest_available_fd(&scope, FdNumber::from_raw(0)),
662        };
663        let existing_entry =
664            state.insert_entry(&scope, fd, rlimit, FdTableEntry { file, flags })?;
665        assert!(!existing_entry);
666        Ok(fd)
667    }
668
669    /// Returns the file handle associated with the given file descriptor.
670    ///
671    /// Returns the file handle even if the file was opened with `O_PATH`.
672    ///
673    /// This operation is uncommon. Most clients should use `get` instead, which fails if the file
674    /// was opened with `O_PATH`.
675    pub fn get_allowing_opath(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
676        self.get_allowing_opath_with_flags(fd).map(|(file, _flags)| file)
677    }
678
679    /// Returns the file handle and flags associated with the given file descriptor.
680    ///
681    /// Returns the file handle even if the file was opened with `O_PATH`.
682    ///
683    /// This operation is uncommon. Most clients should use `get` instead, which fails if the file
684    /// was opened with `O_PATH`.
685    pub fn get_allowing_opath_with_flags(
686        &self,
687        fd: FdNumber,
688    ) -> Result<(FileHandle, FdFlags), Errno> {
689        let scope = RcuReadScope::new();
690        let view = self.read_entries(&scope);
691        view.get_entry(&scope, fd)
692            .map(|entry| (entry.file, entry.flags))
693            .ok_or_else(|| errno!(EBADF))
694    }
695
696    /// Returns the file handle associated with the given file descriptor.
697    ///
698    /// This operation fails if the file was opened with `O_PATH`.
699    pub fn get(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
700        let file = self.get_allowing_opath(fd)?;
701        if file.flags().contains(OpenFlags::PATH) {
702            return error!(EBADF);
703        }
704        Ok(file)
705    }
706
707    /// Closes the file descriptor associated with the given file descriptor.
708    ///
709    /// This operation fails if the file descriptor is not valid.
710    pub fn close(&self, fd: FdNumber) -> Result<(), Errno> {
711        let mut state = self.write_active()?;
712        let scope = RcuReadScope::new();
713        if state.remove_entry(&scope, &fd) { Ok(()) } else { error!(EBADF) }
714    }
715
716    /// Returns the flags associated with the given file descriptor.
717    ///
718    /// Returns the flags even if the file was opened with `O_PATH`.
719    pub fn get_fd_flags_allowing_opath(&self, fd: FdNumber) -> Result<FdFlags, Errno> {
720        self.get_allowing_opath_with_flags(fd).map(|(_file, flags)| flags)
721    }
722
723    /// Updates the flags of the specified FD with the `request`ed change.
724    ///
725    /// This operation fails if the file descriptor was opened with `O_PATH` or is not valid.
726    pub fn ioctl_fd_flags(
727        &self,
728        current_task: &CurrentTask,
729        fd: FdNumber,
730        request: u32,
731    ) -> Result<(), Errno> {
732        let state = self.write_active()?;
733        let scope = RcuReadScope::new();
734        let file = state.get_file(&scope, fd).ok_or_else(|| errno!(EBADF))?;
735        if file.flags().contains(OpenFlags::PATH) {
736            return error!(EBADF);
737        }
738        let flags = match request {
739            FIOCLEX => FdFlags::CLOEXEC,
740            FIONCLEX => FdFlags::empty(),
741            _ => {
742                return error!(EINVAL);
743            }
744        };
745        security::check_file_ioctl_access(current_task, &file, request)?;
746        state.set_fd_flags(&scope, fd, flags)
747    }
748
749    /// Sets the flags associated with the given file descriptor.
750    ///
751    /// This operation fails if the file descriptor is not valid.
752    pub fn set_fd_flags_allowing_opath(&self, fd: FdNumber, flags: FdFlags) -> Result<(), Errno> {
753        let state = self.write_active()?;
754        state.set_fd_flags(&RcuReadScope::new(), fd, flags)
755    }
756
757    /// Retains only the FDs matching the given `predicate`.
758    ///
759    /// The predicate is called with the `FdNumber` and a mutable reference to the `FdFlags` for
760    /// each entry in the `FdTable`. If the predicate returns `false`, the entry is removed from
761    /// the `FdTable`. Otherwise, the `FdFlags` are updated to the value modified by the predicate.
762    pub fn retain<F>(&self, predicate: F)
763    where
764        F: Fn(FdNumber, &mut FdFlags) -> bool,
765    {
766        if let Ok(mut state) = self.write_active() {
767            state.retain(&RcuReadScope::new(), predicate);
768        }
769    }
770
771    /// Returns a vector of all current file descriptors in the table.
772    pub fn get_all_fds(&self) -> Vec<FdNumber> {
773        let scope = RcuReadScope::new();
774        let view = self.read_entries(&scope);
775        view.slice
776            .iter()
777            .enumerate()
778            .filter_map(|(index, encoded_entry)| {
779                if encoded_entry.is_none() { None } else { Some(FdNumber::from_raw(index as i32)) }
780            })
781            .collect()
782    }
783
784    /// Executes `predicate(file) => maybe_replacement` on every non-empty table entry.
785    ///
786    /// Replaces `file` with `replacement_file` in the table when
787    /// `maybe_replacement == Some(replacement_file)`.
788    pub fn remap<F: Fn(&FileHandle) -> Option<FileHandle>>(
789        &self,
790        _current_task: &CurrentTask,
791        predicate: F,
792    ) {
793        if let Ok(state) = self.write_active() {
794            state.remap(&RcuReadScope::new(), predicate);
795        }
796    }
797
798    /// Returns a `FdTableWriteGuard` that provides exclusive access to the state of an active
799    /// `FdTable`.
800    ///
801    /// # Errors
802    ///
803    /// Returns [`Err(ESRCH)`] if the table has no active sharers, indicating it is in the process
804    /// of being destroyed.
805    fn write_active(&self) -> Result<FdTableWriteGuard<'_>, Errno> {
806        let state = self.write();
807        if state.share_count == 0 {
808            return error!(ESRCH);
809        }
810        Ok(state)
811    }
812
813    state_accessor!(FdTable, mutable_state);
814}
815
816/// A wrapper around `FdTable` that manages the table's logical share count.
817///
818/// This type represents the primary reference to the file descriptor table held by a task. Cloning
819/// and dropping `SharedFdTable` increment and decrement the share count of the `FdTable`,
820/// respectively. When the last `SharedFdTable` for the table is dropped, the table is cleared.
821#[derive(Debug, Default)]
822pub struct SharedFdTable {
823    pub table: Arc<FdTable>,
824}
825
826impl Clone for SharedFdTable {
827    fn clone(&self) -> Self {
828        let mut state = self.table.write_active().expect("FdTable must be active");
829        state.share();
830        Self { table: self.table.clone() }
831    }
832}
833
834impl std::ops::Deref for SharedFdTable {
835    type Target = FdTable;
836    fn deref(&self) -> &Self::Target {
837        &self.table
838    }
839}
840
841impl Drop for SharedFdTable {
842    fn drop(&mut self) {
843        self.table.write().unshare();
844    }
845}
846
847impl SharedFdTable {
848    pub fn new(table: Arc<FdTable>) -> Self {
849        Self { table }
850    }
851
852    /// Replaces the wrapped table with a fork that has an independent share count.
853    pub fn unshare(&mut self) {
854        if let Ok(mut state) = self.table.clone().write_active() {
855            if state.share_count > 1 {
856                let table = Arc::new(state.fork());
857                state.share_count -= 1;
858                self.table = table;
859            }
860        }
861    }
862}
863
864/// The target `FdNumber` for a duplicated file descriptor.
865pub enum TargetFdNumber {
866    /// The duplicated `FdNumber` will be the smallest available `FdNumber`.
867    Default,
868
869    /// The duplicated `FdNumber` should be this specific `FdNumber`.
870    Specific(FdNumber),
871
872    /// The duplicated `FdNumber` should be greater than this `FdNumber`.
873    Minimum(FdNumber),
874}
875
876#[cfg(test)]
877mod test {
878    use super::*;
879    use crate::fs::fuchsia::SyslogFile;
880    use crate::testing::*;
881
882    fn add(
883        current_task: &CurrentTask,
884        files: &FdTable,
885        file: FileHandle,
886    ) -> Result<FdNumber, Errno> {
887        files.add(current_task, file, FdFlags::empty())
888    }
889
890    #[::fuchsia::test]
891    async fn test_fd_table_install() {
892        spawn_kernel_and_run(async |current_task| {
893            let files = SharedFdTable::default();
894            let file = SyslogFile::new_file(&current_task);
895
896            let fd0 = add(&current_task, &files, file.clone()).unwrap();
897            assert_eq!(fd0.raw(), 0);
898            let fd1 = add(&current_task, &files, file.clone()).unwrap();
899            assert_eq!(fd1.raw(), 1);
900
901            assert!(Arc::ptr_eq(&files.get(fd0).unwrap(), &file));
902            assert!(Arc::ptr_eq(&files.get(fd1).unwrap(), &file));
903            assert_eq!(files.get(FdNumber::from_raw(fd1.raw() + 1)).map(|_| ()), error!(EBADF));
904        })
905        .await;
906    }
907
908    #[::fuchsia::test]
909    async fn test_fd_table_fork() {
910        spawn_kernel_and_run(async |current_task| {
911            let files = SharedFdTable::default();
912            let file = SyslogFile::new_file(&current_task);
913
914            let fd0 = add(&current_task, &files, file.clone()).unwrap();
915            let fd1 = add(&current_task, &files, file).unwrap();
916            let fd2 = FdNumber::from_raw(2);
917
918            let forked = SharedFdTable::new(files.fork());
919
920            assert_eq!(
921                Arc::as_ptr(&files.get(fd0).unwrap()),
922                Arc::as_ptr(&forked.get(fd0).unwrap())
923            );
924            assert_eq!(
925                Arc::as_ptr(&files.get(fd1).unwrap()),
926                Arc::as_ptr(&forked.get(fd1).unwrap())
927            );
928            assert!(files.get(fd2).is_err());
929            assert!(forked.get(fd2).is_err());
930
931            files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
932            assert_eq!(FdFlags::CLOEXEC, files.get_fd_flags_allowing_opath(fd0).unwrap());
933            assert_ne!(FdFlags::CLOEXEC, forked.get_fd_flags_allowing_opath(fd0).unwrap());
934        })
935        .await;
936    }
937
938    #[::fuchsia::test]
939    async fn test_fd_table_exec() {
940        spawn_kernel_and_run(async |current_task| {
941            let files = SharedFdTable::default();
942            let file = SyslogFile::new_file(&current_task);
943
944            let fd0 = add(&current_task, &files, file.clone()).unwrap();
945            let fd1 = add(&current_task, &files, file).unwrap();
946
947            files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
948
949            assert!(files.get(fd0).is_ok());
950            assert!(files.get(fd1).is_ok());
951
952            files.exec();
953
954            assert!(files.get(fd0).is_err());
955            assert!(files.get(fd1).is_ok());
956        })
957        .await;
958    }
959
960    #[::fuchsia::test]
961    async fn test_fd_table_pack_values() {
962        spawn_kernel_and_run(async |current_task| {
963            let files = SharedFdTable::default();
964            let file = SyslogFile::new_file(&current_task);
965
966            // Add two FDs.
967            let fd0 = add(&current_task, &files, file.clone()).unwrap();
968            let fd1 = add(&current_task, &files, file.clone()).unwrap();
969            assert_eq!(fd0.raw(), 0);
970            assert_eq!(fd1.raw(), 1);
971
972            // Close FD 0
973            assert!(files.close(fd0).is_ok());
974            assert!(files.close(fd0).is_err());
975            // Now it's gone.
976            assert!(files.get(fd0).is_err());
977
978            // The next FD we insert fills in the hole we created.
979            let another_fd = add(&current_task, &files, file).unwrap();
980            assert_eq!(another_fd.raw(), 0);
981        })
982        .await;
983    }
984
985    #[::fuchsia::test]
986    async fn test_fd_table_shared_release() {
987        spawn_kernel_and_run(async |current_task| {
988            let files = SharedFdTable::default();
989            let file = SyslogFile::new_file(&current_task);
990
991            let fd = add(&current_task, &files, file).unwrap();
992            assert_eq!(files.get_all_fds(), vec![fd]);
993
994            let shared_files = files.clone();
995            assert_eq!(shared_files.get_all_fds(), vec![fd]);
996
997            // Release the original files. Since `shared_files` holds a shared reference, the table
998            // should not be cleared.
999            drop(files);
1000            assert_eq!(shared_files.get_all_fds(), vec![fd]);
1001        })
1002        .await;
1003    }
1004
1005    #[::fuchsia::test]
1006    async fn test_fd_table_mutate_after_clear() {
1007        spawn_kernel_and_run(async |current_task| {
1008            let shared_files = SharedFdTable::default();
1009            let file = SyslogFile::new_file(&current_task);
1010
1011            // Clone the underlying FdTable. This does not increment the share_count, but it does
1012            // increment the Arc reference count.
1013            let fd_table_clone = shared_files.table.clone();
1014
1015            // Drop the SharedFdTable. This decrements share_count to 0, triggering a table clear.
1016            drop(shared_files);
1017
1018            // Now attempt to add a file to the cloned FdTable. It should fail with ESRCH.
1019            let result = fd_table_clone.add(&current_task, file, FdFlags::empty());
1020            assert_eq!(result.map(|_| ()), error!(ESRCH));
1021
1022            // When fd_table_clone is dropped, it should not panic because the above add() call
1023            // failed to insert an entry.
1024            drop(fd_table_clone);
1025        })
1026        .await;
1027    }
1028}