1use crate::security;
6use crate::task::{CurrentTask, CurrentTaskAndLocked, register_delayed_release};
7use crate::vfs::{FdNumber, FileHandle, FileReleaser};
8use bitflags::bitflags;
9use fuchsia_rcu::subtle::{RcuPtrRef, rcu_ptr_to_arc};
10use fuchsia_rcu::{RcuArc, RcuReadGuard, RcuReadScope, rcu_drop};
11use fuchsia_rcu_collections::rcu_array::RcuArray;
12use linux_uapi::{FD_CLOEXEC, FIOCLEX, FIONCLEX};
13use starnix_sync::{
14 FdTableWriterQueueLock, FileOpsCore, LockBefore, LockDepGuard, LockDepMutex, LockEqualOrBefore,
15 Locked, ThreadGroupLimits, Unlocked,
16};
17use starnix_syscalls::SyscallResult;
18use starnix_types::ownership::Releasable;
19use starnix_uapi::errors::Errno;
20use starnix_uapi::open_flags::OpenFlags;
21use starnix_uapi::resource_limits::Resource;
22use starnix_uapi::{errno, error};
23use static_assertions::const_assert;
24use std::sync::Arc;
25use std::sync::atomic::{AtomicI32, AtomicUsize, Ordering, fence};
26
27bitflags! {
28 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29 pub struct FdFlags: u32 {
30 const CLOEXEC = FD_CLOEXEC;
32 }
33}
34
35impl std::convert::From<FdFlags> for SyscallResult {
36 fn from(value: FdFlags) -> Self {
37 value.bits().into()
38 }
39}
40
41#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
45pub struct FdTableId(usize);
46
47impl FdTableId {
48 fn new(id: *const FdTableInner) -> Self {
49 Self(id as usize)
50 }
51
52 pub fn raw(&self) -> usize {
53 self.0
54 }
55}
56
57const FLAGS_MASK: usize = 0x1;
60
61#[derive(Debug, Default)]
65struct EncodedEntry {
66 value: AtomicUsize,
74}
75
76const_assert!(std::mem::align_of::<*const FileReleaser>() >= 1 << FLAGS_MASK);
79
80impl EncodedEntry {
81 fn encode(file: FileHandle, flags: FdFlags) -> usize {
86 let ptr = Arc::into_raw(file) as usize;
87 let flags = (flags.bits() as usize) & FLAGS_MASK;
88 ptr | flags
89 }
90
91 unsafe fn release(id: FdTableId, value: usize) {
97 let ptr = Self::decode_ptr(value);
98 if !ptr.is_null() {
99 let file = unsafe { Arc::from_raw(ptr) };
101 register_delayed_release(FlushedFile(file.clone(), id));
106 rcu_drop(file)
107 }
108 }
109
110 fn decode_flags(value: usize) -> FdFlags {
112 FdFlags::from_bits_truncate((value & FLAGS_MASK) as u32)
113 }
114
115 fn decode_ptr(value: usize) -> *const FileReleaser {
117 (value & !FLAGS_MASK) as *const _
118 }
119
120 fn new(entry: FdTableEntry) -> Self {
122 Self { value: AtomicUsize::new(Self::encode(entry.file, entry.flags)) }
123 }
124
125 fn is_some(&self) -> bool {
127 let value = self.value.load(Ordering::Acquire);
128 value != 0
129 }
130
131 fn is_none(&self) -> bool {
133 !self.is_some()
134 }
135
136 fn set_flags(&self, flags: FdFlags) {
138 loop {
139 let old_value = self.value.load(Ordering::Relaxed);
140 assert!(old_value != 0);
141 let new_value = old_value & !FLAGS_MASK | (flags.bits() as usize) & FLAGS_MASK;
142 if self
143 .value
144 .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
145 .is_ok()
146 {
147 return;
148 }
149 }
150 }
151
152 fn set_file(&self, id: FdTableId, file: FileHandle) {
154 let ptr = Arc::into_raw(file) as usize;
155 loop {
156 let old_value = self.value.load(Ordering::Relaxed);
157 assert!(old_value != 0);
158 let flags = old_value & FLAGS_MASK;
159 let new_value = ptr | flags;
160 if self
161 .value
162 .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
163 .is_ok()
164 {
165 unsafe { Self::release(id, old_value) };
167 return;
168 }
169 }
170 }
171
172 fn read<'a>(&self, scope: &'a RcuReadScope) -> Option<FdTableEntryGuard<'a>> {
174 let value = self.value.load(Ordering::Acquire);
175 if value == 0 {
176 return None;
177 }
178 let ptr = Self::decode_ptr(value);
179 let flags = Self::decode_flags(value);
180 let file = unsafe { RcuPtrRef::new(scope, ptr) };
182 Some(FdTableEntryGuard { file, flags })
183 }
184
185 fn set_entry(&self, id: FdTableId, entry: FdTableEntry) -> bool {
187 unsafe { self.set(id, Self::encode(entry.file, entry.flags)) }
189 }
190
191 fn clear(&self, id: FdTableId) -> bool {
193 unsafe { self.set(id, 0) }
195 }
196
197 unsafe fn set(&self, id: FdTableId, value: usize) -> bool {
205 let old_value = self.value.swap(value, Ordering::AcqRel);
206 if old_value != 0 {
207 unsafe { Self::release(id, old_value) };
209 true
210 } else {
211 false
212 }
213 }
214}
215
216impl Clone for EncodedEntry {
217 fn clone(&self) -> Self {
218 if let Some(guard) = self.read(&RcuReadScope::new()) {
219 Self::new(guard.to_entry())
220 } else {
221 Self::default()
222 }
223 }
224}
225
226impl Drop for EncodedEntry {
227 fn drop(&mut self) {
228 let value = self.value.load(Ordering::Acquire);
229 let ptr = Self::decode_ptr(value);
230 if !ptr.is_null() {
231 let _file = unsafe { Arc::from_raw(ptr) };
233 }
234 }
235}
236
237#[derive(Debug, Clone)]
239struct FdTableEntry {
240 file: FileHandle,
242
243 flags: FdFlags,
245}
246
247struct FdTableEntryGuard<'a> {
251 file: RcuPtrRef<'a, FileReleaser>,
253
254 flags: FdFlags,
256}
257
258impl<'a> FdTableEntryGuard<'a> {
259 fn flags(&self) -> FdFlags {
260 self.flags
261 }
262
263 fn to_handle(&self) -> FileHandle {
265 unsafe { rcu_ptr_to_arc(self.file) }
268 }
269
270 fn to_entry(&self) -> FdTableEntry {
272 FdTableEntry { file: self.to_handle(), flags: self.flags }
273 }
274}
275
276struct FlushedFile(FileHandle, FdTableId);
278
279impl Releasable for FlushedFile {
280 type Context<'a> = CurrentTaskAndLocked<'a>;
281 fn release<'a>(self, context: Self::Context<'a>) {
282 let (locked, current_task) = context;
283 let FlushedFile(file, id) = self;
284 file.flush(locked, current_task, id);
285 }
286}
287
288struct FdTableView<'a> {
296 slice: &'a [EncodedEntry],
298}
299
300impl<'a> FdTableView<'a> {
301 fn len(&self) -> usize {
303 self.slice.len()
304 }
305
306 fn is_some(&self, fd: FdNumber) -> bool {
308 self.slice.get(fd.raw() as usize).map_or(false, |entry| entry.is_some())
309 }
310
311 fn is_none(&self, fd: FdNumber) -> bool {
313 !self.is_some(fd)
314 }
315
316 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
318 self.slice
319 .get(fd.raw() as usize)
320 .and_then(|entry| entry.read(scope))
321 .map(|guard| guard.to_handle())
322 }
323
324 fn get_entry(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FdTableEntry> {
326 self.slice
327 .get(fd.raw() as usize)
328 .and_then(|entry| entry.read(scope))
329 .map(|guard| guard.to_entry())
330 }
331}
332
333struct FdTableWriteGuard<'a> {
334 store: &'a FdTableInner,
335 _write_guard: LockDepGuard<'a, ()>,
336}
337
338impl<'a> FdTableWriteGuard<'a> {
339 fn next_fd(&self) -> FdNumber {
341 self.store.next_fd.get()
342 }
343
344 fn calculate_lowest_available_fd(&self, view: &FdTableView<'_>, minfd: &FdNumber) -> FdNumber {
346 let mut fd: FdNumber = *minfd;
347 while view.is_some(fd) {
348 fd = FdNumber::from_raw(fd.raw() + 1);
349 }
350 fd
351 }
352
353 fn get_lowest_available_fd(&self, scope: &RcuReadScope, minfd: FdNumber) -> FdNumber {
355 if minfd > self.store.next_fd.get() {
356 let view = self.store.read(scope);
357 return self.calculate_lowest_available_fd(&view, &minfd);
358 }
359 self.store.next_fd.get()
360 }
361
362 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
364 self.store.read(scope).get_file(scope, fd)
365 }
366
367 fn insert_entry(
371 &self,
372 scope: &RcuReadScope,
373 fd: FdNumber,
374 rlimit: u64,
375 entry: FdTableEntry,
376 ) -> Result<bool, Errno> {
377 let raw_fd = fd.raw();
378 if raw_fd < 0 {
379 return error!(EBADF);
380 }
381 if raw_fd as u64 >= rlimit {
382 return error!(EMFILE);
383 }
384 let mut view = self.store.read(scope);
385 if raw_fd == self.store.next_fd.get().raw() {
386 self.store
387 .next_fd
388 .set(self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(raw_fd + 1)));
389 }
390 let raw_fd = raw_fd as usize;
391 if view.len() <= raw_fd {
392 unsafe { self.store.entries.ensure_at_least(raw_fd + 1) };
394 view = self.store.read(scope);
395 }
396 let id = self.store.id();
397 Ok(view.slice[raw_fd].set_entry(id, entry))
398 }
399
400 fn remove_entry(&self, scope: &RcuReadScope, fd: &FdNumber) -> bool {
404 let raw_fd = fd.raw() as usize;
405 let view = self.store.read(scope);
406 if raw_fd >= view.len() {
407 return false;
408 }
409 let id = self.store.id();
410 let removed = view.slice[raw_fd].clear(id);
411 if removed && raw_fd < self.store.next_fd.get().raw() as usize {
412 self.store.next_fd.set(*fd);
413 }
414 removed
415 }
416
417 fn set_fd_flags(
421 &self,
422 scope: &RcuReadScope,
423 fd: FdNumber,
424 flags: FdFlags,
425 ) -> Result<(), Errno> {
426 let view = self.store.read(scope);
427 if view.is_none(fd) {
428 return error!(EBADF);
429 }
430 let raw_fd = fd.raw() as usize;
431 view.slice[raw_fd].set_flags(flags);
432 Ok(())
433 }
434
435 fn retain<F>(&self, scope: &RcuReadScope, mut predicate: F)
441 where
442 F: FnMut(FdNumber, &mut FdFlags) -> bool,
443 {
444 let id = self.store.id();
445 let view = self.store.read(scope);
446 for (index, encoded_entry) in view.slice.iter().enumerate() {
447 let fd = FdNumber::from_raw(index as i32);
448 if let Some(guard) = encoded_entry.read(scope) {
449 let mut modified_flags = guard.flags();
450 if !predicate(fd, &mut modified_flags) {
451 encoded_entry.clear(id);
452 } else if modified_flags != guard.flags() {
453 encoded_entry.set_flags(modified_flags);
454 }
455 }
456 }
457 self.store.next_fd.set(self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(0)));
458 }
459
460 fn clear(&self) {
462 self.retain(&RcuReadScope::new(), |_, _| false);
463 }
464
465 fn remap<F>(&self, scope: &RcuReadScope, predicate: F)
472 where
473 F: Fn(&FileHandle) -> Option<FileHandle>,
474 {
475 let id = self.store.id();
476 let view = self.store.read(scope);
477 for encoded_entry in view.slice.iter() {
478 if let Some(guard) = encoded_entry.read(scope) {
479 let file = guard.to_handle();
480 if let Some(replacement_file) = predicate(&file) {
481 encoded_entry.set_file(id, replacement_file);
482 }
483 }
484 }
485 }
486}
487
488#[derive(Debug, Default)]
493struct AtomicFdNumber {
494 value: AtomicI32,
496}
497
498impl AtomicFdNumber {
499 fn get(&self) -> FdNumber {
503 FdNumber::from_raw(self.value.load(Ordering::Relaxed))
504 }
505
506 fn set(&self, value: FdNumber) {
510 self.value.store(value.raw(), Ordering::Relaxed);
511 }
512}
513
514impl Clone for AtomicFdNumber {
515 fn clone(&self) -> Self {
516 Self { value: AtomicI32::new(self.value.load(Ordering::Relaxed)) }
517 }
518}
519
520#[derive(Debug)]
525struct FdTableInner {
526 share_count: AtomicUsize,
528
529 entries: RcuArray<EncodedEntry>,
531
532 next_fd: AtomicFdNumber,
534
535 writer_queue: LockDepMutex<(), FdTableWriterQueueLock>,
538}
539
540impl Default for FdTableInner {
541 fn default() -> Self {
542 FdTableInner {
543 share_count: AtomicUsize::new(1),
544 entries: Default::default(),
545 next_fd: AtomicFdNumber::default(),
546 writer_queue: LockDepMutex::new(()),
547 }
548 }
549}
550
551impl Clone for FdTableInner {
552 fn clone(&self) -> Self {
553 let _guard = self.writer_queue.lock();
554 Self {
555 share_count: AtomicUsize::new(1),
556 entries: self.entries.clone(),
557 next_fd: self.next_fd.clone(),
558 writer_queue: LockDepMutex::new(()),
559 }
560 }
561}
562
563impl Drop for FdTableInner {
564 fn drop(&mut self) {
565 let scope = RcuReadScope::new();
566 let view = self.read(&scope);
567 for entry in view.slice.iter() {
568 assert!(entry.is_none());
569 }
570 }
571}
572
573impl FdTableInner {
574 fn id(&self) -> FdTableId {
576 FdTableId::new(self as *const Self)
577 }
578
579 fn share_count(&self) -> usize {
581 self.share_count.load(Ordering::Relaxed)
582 }
583
584 fn share(&self) {
586 self.share_count.fetch_add(1, Ordering::Relaxed);
587 }
588
589 fn unshare(self: Arc<Self>) {
592 if self.share_count.fetch_sub(1, Ordering::Release) == 1 {
598 fence(Ordering::Acquire);
599 register_delayed_release(ClearFdTable(self));
606 }
607 }
608
609 fn read<'a>(&self, scope: &'a RcuReadScope) -> FdTableView<'a> {
611 let slice = self.entries.as_slice(scope);
612 FdTableView { slice }
613 }
614
615 fn write(&self) -> FdTableWriteGuard<'_> {
618 FdTableWriteGuard { store: self, _write_guard: self.writer_queue.lock() }
619 }
620}
621
622struct ClearFdTable(Arc<FdTableInner>);
624
625impl Releasable for ClearFdTable {
626 type Context<'a> = CurrentTaskAndLocked<'a>;
627 fn release<'a>(self, _context: Self::Context<'a>) {
628 self.0.write().clear();
629 }
630}
631
632#[derive(Debug)]
636struct FdTableInnerArc {
637 inner: RcuArc<FdTableInner>,
638}
639
640impl Default for FdTableInnerArc {
641 fn default() -> Self {
642 Self::new(FdTableInner::default())
643 }
644}
645
646impl Clone for FdTableInnerArc {
647 fn clone(&self) -> Self {
648 let inner = self.inner.to_arc();
649 inner.share();
650 Self { inner: RcuArc::new(inner) }
651 }
652}
653
654impl Drop for FdTableInnerArc {
655 fn drop(&mut self) {
656 self.inner.to_arc().unshare();
657 }
658}
659
660impl FdTableInnerArc {
661 pub fn new(inner: FdTableInner) -> Self {
662 assert_eq!(inner.share_count(), 1, "FdTableInner must only be shared via clone()");
663 Self { inner: RcuArc::new(Arc::new(inner)) }
664 }
665
666 pub fn read(&self) -> RcuReadGuard<FdTableInner> {
667 self.inner.read()
668 }
669
670 pub fn update(&self, scope: &RcuReadScope, new_inner: FdTableInner) {
671 let old_inner = self.inner.update_swap(scope, Arc::new(new_inner));
672 old_inner.unshare();
673 }
674}
675
676#[derive(Debug, Clone, Default)]
678pub struct FdTable {
679 inner: FdTableInnerArc,
681}
682
683pub enum TargetFdNumber {
685 Default,
687
688 Specific(FdNumber),
690
691 Minimum(FdNumber),
693}
694
695impl FdTable {
696 pub fn id(&self) -> FdTableId {
698 self.inner.read().id()
699 }
700
701 pub fn fork(&self) -> FdTable {
703 let forked = self.inner.read().clone();
704 FdTable { inner: FdTableInnerArc::new(forked) }
705 }
706
707 pub fn unshare(&self) {
709 let unshared = self.inner.read().clone();
710 self.inner.update(&RcuReadScope::new(), unshared);
711 }
712
713 pub fn release(&self) {
715 self.inner.update(&RcuReadScope::new(), Default::default());
716 }
717
718 pub fn exec(&self, locked: &mut Locked<Unlocked>, current_task: &CurrentTask) {
720 self.retain(locked, current_task, |_fd, flags| !flags.contains(FdFlags::CLOEXEC));
721 }
722
723 pub fn insert<L>(
725 &self,
726 locked: &mut Locked<L>,
727 current_task: &CurrentTask,
728 fd: FdNumber,
729 file: FileHandle,
730 ) -> Result<(), Errno>
731 where
732 L: LockBefore<ThreadGroupLimits>,
733 {
734 let flags = FdFlags::empty();
735 let rlimit = current_task.thread_group().get_rlimit(locked, Resource::NOFILE);
736 let inner = self.inner.read();
737 let guard = inner.write();
738 guard.insert_entry(inner.scope(), fd, rlimit, FdTableEntry { file, flags })?;
739 Ok(())
740 }
741
742 pub fn add<L>(
750 &self,
751 locked: &mut Locked<L>,
752 current_task: &CurrentTask,
753 file: FileHandle,
754 flags: FdFlags,
755 ) -> Result<FdNumber, Errno>
756 where
757 L: LockEqualOrBefore<FileOpsCore>,
758 {
759 let locked = locked.cast_locked::<FileOpsCore>();
760 let rlimit = current_task.thread_group().get_rlimit(locked, Resource::NOFILE);
761 let inner = self.inner.read();
762 let guard = inner.write();
763 let fd = guard.next_fd();
764 guard.insert_entry(inner.scope(), fd, rlimit, FdTableEntry { file, flags })?;
765 Ok(fd)
766 }
767
768 pub fn duplicate<L>(
773 &self,
774 locked: &mut Locked<L>,
775 current_task: &CurrentTask,
776 oldfd: FdNumber,
777 target: TargetFdNumber,
778 flags: FdFlags,
779 ) -> Result<FdNumber, Errno>
780 where
781 L: LockBefore<ThreadGroupLimits>,
782 {
783 let rlimit = current_task.thread_group().get_rlimit(locked, Resource::NOFILE);
784 let inner = self.inner.read();
785 let guard = inner.write();
786 let file = guard.get_file(inner.scope(), oldfd).ok_or_else(|| errno!(EBADF))?;
787
788 let fd = match target {
789 TargetFdNumber::Specific(fd) => {
790 if fd.raw() as u64 >= rlimit {
793 return error!(EBADF);
797 }
798 guard.remove_entry(inner.scope(), &fd);
799 fd
800 }
801 TargetFdNumber::Minimum(fd) => guard.get_lowest_available_fd(inner.scope(), fd),
802 TargetFdNumber::Default => {
803 guard.get_lowest_available_fd(inner.scope(), FdNumber::from_raw(0))
804 }
805 };
806 let existing_entry =
807 guard.insert_entry(inner.scope(), fd, rlimit, FdTableEntry { file, flags })?;
808 assert!(!existing_entry);
809 Ok(fd)
810 }
811
812 pub fn get_allowing_opath(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
819 self.get_allowing_opath_with_flags(fd).map(|(file, _flags)| file)
820 }
821
822 pub fn get_allowing_opath_with_flags(
829 &self,
830 fd: FdNumber,
831 ) -> Result<(FileHandle, FdFlags), Errno> {
832 let inner = self.inner.read();
833 let view = inner.read(inner.scope());
834 view.get_entry(inner.scope(), fd)
835 .map(|entry| (entry.file, entry.flags))
836 .ok_or_else(|| errno!(EBADF))
837 }
838
839 pub fn get(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
843 let file = self.get_allowing_opath(fd)?;
844 if file.flags().contains(OpenFlags::PATH) {
845 return error!(EBADF);
846 }
847 Ok(file)
848 }
849
850 pub fn close(&self, fd: FdNumber) -> Result<(), Errno> {
854 let inner = self.inner.read();
855 let guard = inner.write();
856 if guard.remove_entry(inner.scope(), &fd) { Ok(()) } else { error!(EBADF) }
857 }
858
859 pub fn get_fd_flags_allowing_opath(&self, fd: FdNumber) -> Result<FdFlags, Errno> {
863 self.get_allowing_opath_with_flags(fd).map(|(_file, flags)| flags)
864 }
865
866 pub fn ioctl_fd_flags(
870 &self,
871 current_task: &CurrentTask,
872 fd: FdNumber,
873 request: u32,
874 ) -> Result<(), Errno> {
875 let inner = self.inner.read();
876 let guard = inner.write();
877 let file = guard.get_file(inner.scope(), fd).ok_or_else(|| errno!(EBADF))?;
878 if file.flags().contains(OpenFlags::PATH) {
879 return error!(EBADF);
880 }
881 let flags = match request {
882 FIOCLEX => FdFlags::CLOEXEC,
883 FIONCLEX => FdFlags::empty(),
884 _ => {
885 return error!(EINVAL);
886 }
887 };
888 security::check_file_ioctl_access(current_task, &file, request)?;
889 guard.set_fd_flags(inner.scope(), fd, flags)
890 }
891
892 pub fn set_fd_flags_allowing_opath(&self, fd: FdNumber, flags: FdFlags) -> Result<(), Errno> {
896 let inner = self.inner.read();
897 let guard = inner.write();
898 guard.set_fd_flags(inner.scope(), fd, flags)
899 }
900
901 pub fn retain<L, F>(&self, _locked: &mut Locked<L>, _current_task: &CurrentTask, predicate: F)
907 where
908 L: LockEqualOrBefore<FileOpsCore>,
909 F: Fn(FdNumber, &mut FdFlags) -> bool,
910 {
911 let inner = self.inner.read();
912 let guard = inner.write();
913 guard.retain(inner.scope(), predicate);
914 }
915
916 pub fn get_all_fds(&self) -> Vec<FdNumber> {
918 let inner = self.inner.read();
919 let view = inner.read(inner.scope());
920 view.slice
921 .iter()
922 .enumerate()
923 .filter_map(|(index, encoded_entry)| {
924 if encoded_entry.is_none() { None } else { Some(FdNumber::from_raw(index as i32)) }
925 })
926 .collect()
927 }
928
929 pub fn remap<L, F: Fn(&FileHandle) -> Option<FileHandle>>(
934 &self,
935 _locked: &mut Locked<L>,
936 _current_task: &CurrentTask,
937 predicate: F,
938 ) where
939 L: LockEqualOrBefore<FileOpsCore>,
940 {
941 let inner = self.inner.read();
942 let guard = inner.write();
943 guard.remap(inner.scope(), predicate);
944 }
945}
946
947#[cfg(test)]
948mod test {
949 use super::*;
950 use crate::fs::fuchsia::SyslogFile;
951 use crate::testing::*;
952
953 fn add(
954 locked: &mut Locked<Unlocked>,
955 current_task: &CurrentTask,
956 files: &FdTable,
957 file: FileHandle,
958 ) -> Result<FdNumber, Errno> {
959 files.add(locked, current_task, file, FdFlags::empty())
960 }
961
962 #[::fuchsia::test]
963 async fn test_fd_table_install() {
964 spawn_kernel_and_run(async |locked, current_task| {
965 let files = FdTable::default();
966 let file = SyslogFile::new_file(locked, ¤t_task);
967
968 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
969 assert_eq!(fd0.raw(), 0);
970 let fd1 = add(locked, ¤t_task, &files, file.clone()).unwrap();
971 assert_eq!(fd1.raw(), 1);
972
973 assert!(Arc::ptr_eq(&files.get(fd0).unwrap(), &file));
974 assert!(Arc::ptr_eq(&files.get(fd1).unwrap(), &file));
975 assert_eq!(files.get(FdNumber::from_raw(fd1.raw() + 1)).map(|_| ()), error!(EBADF));
976
977 files.release();
978 })
979 .await;
980 }
981
982 #[::fuchsia::test]
983 async fn test_fd_table_fork() {
984 spawn_kernel_and_run(async |locked, current_task| {
985 let files = FdTable::default();
986 let file = SyslogFile::new_file(locked, ¤t_task);
987
988 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
989 let fd1 = add(locked, ¤t_task, &files, file).unwrap();
990 let fd2 = FdNumber::from_raw(2);
991
992 let forked = files.fork();
993
994 assert_eq!(
995 Arc::as_ptr(&files.get(fd0).unwrap()),
996 Arc::as_ptr(&forked.get(fd0).unwrap())
997 );
998 assert_eq!(
999 Arc::as_ptr(&files.get(fd1).unwrap()),
1000 Arc::as_ptr(&forked.get(fd1).unwrap())
1001 );
1002 assert!(files.get(fd2).is_err());
1003 assert!(forked.get(fd2).is_err());
1004
1005 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
1006 assert_eq!(FdFlags::CLOEXEC, files.get_fd_flags_allowing_opath(fd0).unwrap());
1007 assert_ne!(FdFlags::CLOEXEC, forked.get_fd_flags_allowing_opath(fd0).unwrap());
1008
1009 forked.release();
1010 files.release();
1011 })
1012 .await;
1013 }
1014
1015 #[::fuchsia::test]
1016 async fn test_fd_table_exec() {
1017 spawn_kernel_and_run(async |locked, current_task| {
1018 let files = FdTable::default();
1019 let file = SyslogFile::new_file(locked, ¤t_task);
1020
1021 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
1022 let fd1 = add(locked, ¤t_task, &files, file).unwrap();
1023
1024 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
1025
1026 assert!(files.get(fd0).is_ok());
1027 assert!(files.get(fd1).is_ok());
1028
1029 files.exec(locked, ¤t_task);
1030
1031 assert!(files.get(fd0).is_err());
1032 assert!(files.get(fd1).is_ok());
1033
1034 files.release();
1035 })
1036 .await;
1037 }
1038
1039 #[::fuchsia::test]
1040 async fn test_fd_table_pack_values() {
1041 spawn_kernel_and_run(async |locked, current_task| {
1042 let files = FdTable::default();
1043 let file = SyslogFile::new_file(locked, ¤t_task);
1044
1045 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
1047 let fd1 = add(locked, ¤t_task, &files, file.clone()).unwrap();
1048 assert_eq!(fd0.raw(), 0);
1049 assert_eq!(fd1.raw(), 1);
1050
1051 assert!(files.close(fd0).is_ok());
1053 assert!(files.close(fd0).is_err());
1054 assert!(files.get(fd0).is_err());
1056
1057 let another_fd = add(locked, ¤t_task, &files, file).unwrap();
1059 assert_eq!(another_fd.raw(), 0);
1060
1061 files.release();
1062 })
1063 .await;
1064 }
1065
1066 #[::fuchsia::test]
1067 async fn test_fd_table_shared_release() {
1068 spawn_kernel_and_run(async |locked, current_task| {
1069 let files = FdTable::default();
1070 let file = SyslogFile::new_file(locked, ¤t_task);
1071
1072 let fd = add(locked, ¤t_task, &files, file).unwrap();
1073 assert_eq!(files.get_all_fds(), vec![fd]);
1074
1075 let shared_files = files.clone();
1077 assert_eq!(shared_files.get_all_fds(), vec![fd]);
1078
1079 files.release();
1082 assert_eq!(files.get_all_fds(), vec![]);
1083 assert_eq!(shared_files.get_all_fds(), vec![fd]);
1084
1085 shared_files.release();
1087 assert_eq!(shared_files.get_all_fds(), vec![]);
1088 })
1089 .await;
1090 }
1091}