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 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 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 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#[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 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
861pub enum TargetFdNumber {
863 Default,
865
866 Specific(FdNumber),
868
869 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(¤t_task);
892
893 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
894 assert_eq!(fd0.raw(), 0);
895 let fd1 = add(¤t_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(¤t_task);
910
911 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
912 let fd1 = add(¤t_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(¤t_task);
940
941 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
942 let fd1 = add(¤t_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(¤t_task);
962
963 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
965 let fd1 = add(¤t_task, &files, file.clone()).unwrap();
966 assert_eq!(fd0.raw(), 0);
967 assert_eq!(fd1.raw(), 1);
968
969 assert!(files.close(fd0).is_ok());
971 assert!(files.close(fd0).is_err());
972 assert!(files.get(fd0).is_err());
974
975 let another_fd = add(¤t_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(¤t_task);
987
988 let fd = add(¤t_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 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(¤t_task);
1007
1008 let fd_table_clone = shared_files.table.clone();
1011
1012 drop(shared_files);
1014
1015 let result = fd_table_clone.add(¤t_task, file, FdFlags::empty());
1017 assert_eq!(result.map(|_| ()), error!(ESRCH));
1018
1019 drop(fd_table_clone);
1022 })
1023 .await;
1024 }
1025}