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