1use 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 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#[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
56const FLAGS_MASK: usize = 0x1;
59
60#[derive(Debug, Default, RcuDroppable)]
68struct EncodedEntry {
69 value: AtomicUsize,
77}
78
79const_assert!(std::mem::align_of::<*const FileReleaser>() >= 1 << FLAGS_MASK);
82
83impl EncodedEntry {
84 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 unsafe fn release(id: FdTableId, value: usize) {
100 let ptr = Self::decode_ptr(value);
101 if !ptr.is_null() {
102 let file = unsafe { Arc::from_raw(ptr) };
104 let weak = Arc::downgrade(&file);
110 rcu_drop(weak);
111 register_delayed_release(FlushedFile(file, id));
112 }
113 }
114
115 fn decode_flags(value: usize) -> FdFlags {
117 FdFlags::from_bits_truncate((value & FLAGS_MASK) as u32)
118 }
119
120 fn decode_ptr(value: usize) -> *const FileReleaser {
122 (value & !FLAGS_MASK) as *const _
123 }
124
125 fn new(entry: FdTableEntry) -> Self {
127 Self { value: AtomicUsize::new(Self::encode(entry.file, entry.flags)) }
128 }
129
130 fn is_some(&self) -> bool {
132 let value = self.value.load(Ordering::Acquire);
133 value != 0
134 }
135
136 fn is_none(&self) -> bool {
138 !self.is_some()
139 }
140
141 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 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 unsafe { Self::release(id, old_value) };
172 return;
173 }
174 }
175 }
176
177 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 let file = unsafe { RcuPtrRef::new(scope, ptr) };
187 Some(FdTableEntryGuard { file, flags })
188 }
189
190 fn set_entry(&self, id: FdTableId, entry: FdTableEntry) -> bool {
192 unsafe { self.set(id, Self::encode(entry.file, entry.flags)) }
194 }
195
196 fn clear(&self, id: FdTableId) -> bool {
198 unsafe { self.set(id, 0) }
200 }
201
202 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 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#[derive(Debug, Clone)]
229struct FdTableEntry {
230 file: FileHandle,
232
233 flags: FdFlags,
235}
236
237struct FdTableEntryGuard<'a> {
241 file: RcuPtrRef<'a, FileReleaser>,
243
244 flags: FdFlags,
246}
247
248impl<'a> FdTableEntryGuard<'a> {
249 fn flags(&self) -> FdFlags {
250 self.flags
251 }
252
253 fn to_handle(&self) -> Option<FileHandle> {
255 unsafe { rcu_ptr_upgrade(self.file) }
258 }
259
260 fn to_entry(&self) -> Option<FdTableEntry> {
262 self.to_handle().map(|file| FdTableEntry { file, flags: self.flags })
263 }
264}
265
266struct 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
278struct FdTableView<'a> {
286 slice: &'a [EncodedEntry],
288}
289
290impl<'a> FdTableView<'a> {
291 fn len(&self) -> usize {
293 self.slice.len()
294 }
295
296 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 fn is_none(&self, fd: FdNumber) -> bool {
303 !self.is_some(fd)
304 }
305
306 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 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 pub share_count: usize,
329
330 pub next_fd: FdNumber,
332}
333
334#[apply(state_implementation!)]
335impl FdTableMutableState<Base = FdTable> {
336 fn share(&mut self) {
338 assert!(self.share_count > 0, "Cannot share unshared table");
339 self.share_count += 1;
340 }
341
342 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 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 fn next_fd(&self) -> FdNumber {
378 self.next_fd
379 }
380
381 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 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 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
401 self.base.read_entries(scope).get_file(scope, fd)
402 }
403
404 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 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 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 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 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 fn clear(&mut self) {
498 self.retain(&RcuReadScope::new(), |_, _| false);
499 }
500
501 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#[derive(Debug)]
532pub struct FdTable {
533 entries: RcuArray<EncodedEntry>,
539
540 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 pub fn id(&self) -> FdTableId {
575 FdTableId::new(self as *const Self)
576 }
577
578 fn read_entries<'a>(&self, scope: &'a RcuReadScope) -> FdTableView<'a> {
580 let slice = self.entries.as_slice(scope);
581 FdTableView { slice }
582 }
583
584 pub fn fork(&self) -> Arc<Self> {
586 Arc::new(self.read().fork())
587 }
588
589 pub fn exec(&self) {
591 self.retain(|_fd, flags| !flags.contains(FdFlags::CLOEXEC));
592 }
593
594 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 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 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 if fd.raw() as u64 >= rlimit {
649 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 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 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 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 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 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 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 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 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 pub fn for_each_fd<F: FnMut(FdNumber)>(&self, mut f: F) {
770 let scope = RcuReadScope::new();
771 let view = self.read_entries(&scope);
772 for (index, encoded_entry) in view.slice.iter().enumerate() {
773 if !encoded_entry.is_none() {
774 f(FdNumber::from_raw(index as i32));
775 }
776 }
777 }
778
779 pub fn get_all_fds(&self) -> Vec<FdNumber> {
781 let mut fds = Vec::new();
782 self.for_each_fd(|fd| fds.push(fd));
783 fds
784 }
785
786 pub fn remap<F: Fn(&FileHandle) -> Option<FileHandle>>(
791 &self,
792 _current_task: &CurrentTask,
793 predicate: F,
794 ) {
795 if let Ok(state) = self.write_active() {
796 state.remap(&RcuReadScope::new(), predicate);
797 }
798 }
799
800 fn write_active(&self) -> Result<FdTableWriteGuard<'_>, Errno> {
808 let state = self.write();
809 if state.share_count == 0 {
810 return error!(ESRCH);
811 }
812 Ok(state)
813 }
814
815 state_accessor!(FdTable, mutable_state);
816}
817
818#[derive(Debug, Default)]
824pub struct SharedFdTable {
825 pub table: Arc<FdTable>,
826}
827
828impl Clone for SharedFdTable {
829 fn clone(&self) -> Self {
830 let mut state = self.table.write_active().expect("FdTable must be active");
831 state.share();
832 Self { table: self.table.clone() }
833 }
834}
835
836impl std::ops::Deref for SharedFdTable {
837 type Target = FdTable;
838 fn deref(&self) -> &Self::Target {
839 &self.table
840 }
841}
842
843impl Drop for SharedFdTable {
844 fn drop(&mut self) {
845 self.table.write().unshare();
846 }
847}
848
849impl SharedFdTable {
850 pub fn new(table: Arc<FdTable>) -> Self {
851 Self { table }
852 }
853
854 pub fn unshare(&mut self) {
856 if let Ok(mut state) = self.table.clone().write_active() {
857 if state.share_count > 1 {
858 let table = Arc::new(state.fork());
859 state.share_count -= 1;
860 self.table = table;
861 }
862 }
863 }
864}
865
866pub enum TargetFdNumber {
868 Default,
870
871 Specific(FdNumber),
873
874 Minimum(FdNumber),
876}
877
878#[cfg(test)]
879mod test {
880 use super::*;
881 use crate::fs::fuchsia::SyslogFile;
882 use crate::testing::*;
883
884 fn add(
885 current_task: &CurrentTask,
886 files: &FdTable,
887 file: FileHandle,
888 ) -> Result<FdNumber, Errno> {
889 files.add(current_task, file, FdFlags::empty())
890 }
891
892 #[::fuchsia::test]
893 async fn test_fd_table_install() {
894 spawn_kernel_and_run(async |current_task| {
895 let files = SharedFdTable::default();
896 let file = SyslogFile::new_file(¤t_task);
897
898 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
899 assert_eq!(fd0.raw(), 0);
900 let fd1 = add(¤t_task, &files, file.clone()).unwrap();
901 assert_eq!(fd1.raw(), 1);
902
903 assert!(Arc::ptr_eq(&files.get(fd0).unwrap(), &file));
904 assert!(Arc::ptr_eq(&files.get(fd1).unwrap(), &file));
905 assert_eq!(files.get(FdNumber::from_raw(fd1.raw() + 1)).map(|_| ()), error!(EBADF));
906 })
907 .await;
908 }
909
910 #[::fuchsia::test]
911 async fn test_fd_table_fork() {
912 spawn_kernel_and_run(async |current_task| {
913 let files = SharedFdTable::default();
914 let file = SyslogFile::new_file(¤t_task);
915
916 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
917 let fd1 = add(¤t_task, &files, file).unwrap();
918 let fd2 = FdNumber::from_raw(2);
919
920 let forked = SharedFdTable::new(files.fork());
921
922 assert_eq!(
923 Arc::as_ptr(&files.get(fd0).unwrap()),
924 Arc::as_ptr(&forked.get(fd0).unwrap())
925 );
926 assert_eq!(
927 Arc::as_ptr(&files.get(fd1).unwrap()),
928 Arc::as_ptr(&forked.get(fd1).unwrap())
929 );
930 assert!(files.get(fd2).is_err());
931 assert!(forked.get(fd2).is_err());
932
933 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
934 assert_eq!(FdFlags::CLOEXEC, files.get_fd_flags_allowing_opath(fd0).unwrap());
935 assert_ne!(FdFlags::CLOEXEC, forked.get_fd_flags_allowing_opath(fd0).unwrap());
936 })
937 .await;
938 }
939
940 #[::fuchsia::test]
941 async fn test_fd_table_exec() {
942 spawn_kernel_and_run(async |current_task| {
943 let files = SharedFdTable::default();
944 let file = SyslogFile::new_file(¤t_task);
945
946 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
947 let fd1 = add(¤t_task, &files, file).unwrap();
948
949 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
950
951 assert!(files.get(fd0).is_ok());
952 assert!(files.get(fd1).is_ok());
953
954 files.exec();
955
956 assert!(files.get(fd0).is_err());
957 assert!(files.get(fd1).is_ok());
958 })
959 .await;
960 }
961
962 #[::fuchsia::test]
963 async fn test_fd_table_pack_values() {
964 spawn_kernel_and_run(async |current_task| {
965 let files = SharedFdTable::default();
966 let file = SyslogFile::new_file(¤t_task);
967
968 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
970 let fd1 = add(¤t_task, &files, file.clone()).unwrap();
971 assert_eq!(fd0.raw(), 0);
972 assert_eq!(fd1.raw(), 1);
973
974 assert!(files.close(fd0).is_ok());
976 assert!(files.close(fd0).is_err());
977 assert!(files.get(fd0).is_err());
979
980 let another_fd = add(¤t_task, &files, file).unwrap();
982 assert_eq!(another_fd.raw(), 0);
983 })
984 .await;
985 }
986
987 #[::fuchsia::test]
988 async fn test_fd_table_shared_release() {
989 spawn_kernel_and_run(async |current_task| {
990 let files = SharedFdTable::default();
991 let file = SyslogFile::new_file(¤t_task);
992
993 let fd = add(¤t_task, &files, file).unwrap();
994 assert_eq!(files.get_all_fds(), vec![fd]);
995
996 let shared_files = files.clone();
997 assert_eq!(shared_files.get_all_fds(), vec![fd]);
998
999 drop(files);
1002 assert_eq!(shared_files.get_all_fds(), vec![fd]);
1003 })
1004 .await;
1005 }
1006
1007 #[::fuchsia::test]
1008 async fn test_fd_table_mutate_after_clear() {
1009 spawn_kernel_and_run(async |current_task| {
1010 let shared_files = SharedFdTable::default();
1011 let file = SyslogFile::new_file(¤t_task);
1012
1013 let fd_table_clone = shared_files.table.clone();
1016
1017 drop(shared_files);
1019
1020 let result = fd_table_clone.add(¤t_task, file, FdFlags::empty());
1022 assert_eq!(result.map(|_| ()), error!(ESRCH));
1023
1024 drop(fd_table_clone);
1027 })
1028 .await;
1029 }
1030
1031 #[::fuchsia::test]
1032 async fn test_fd_table_for_each_fd() {
1033 spawn_kernel_and_run(async |current_task| {
1034 let files = SharedFdTable::default();
1035 let file1 = SyslogFile::new_file(¤t_task);
1036 let file2 = SyslogFile::new_file(¤t_task);
1037
1038 let fd1 = add(¤t_task, &files, file1).unwrap();
1039 let fd2 = add(¤t_task, &files, file2).unwrap();
1040
1041 let mut visited = Vec::new();
1042 files.for_each_fd(|fd| visited.push(fd));
1043 assert_eq!(visited, vec![fd1, fd2]);
1044 })
1045 .await;
1046 }
1047}