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