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_to_arc};
11use fuchsia_rcu::{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)]
64struct EncodedEntry {
65 value: AtomicUsize,
73}
74
75const_assert!(std::mem::align_of::<*const FileReleaser>() >= 1 << FLAGS_MASK);
78
79impl EncodedEntry {
80 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 unsafe fn release(id: FdTableId, value: usize) {
96 let ptr = Self::decode_ptr(value);
97 if !ptr.is_null() {
98 let file = unsafe { Arc::from_raw(ptr) };
100 register_delayed_release(FlushedFile(file.clone(), id));
105 rcu_drop(file)
106 }
107 }
108
109 fn decode_flags(value: usize) -> FdFlags {
111 FdFlags::from_bits_truncate((value & FLAGS_MASK) as u32)
112 }
113
114 fn decode_ptr(value: usize) -> *const FileReleaser {
116 (value & !FLAGS_MASK) as *const _
117 }
118
119 fn new(entry: FdTableEntry) -> Self {
121 Self { value: AtomicUsize::new(Self::encode(entry.file, entry.flags)) }
122 }
123
124 fn is_some(&self) -> bool {
126 let value = self.value.load(Ordering::Acquire);
127 value != 0
128 }
129
130 fn is_none(&self) -> bool {
132 !self.is_some()
133 }
134
135 fn set_flags(&self, flags: FdFlags) {
137 loop {
138 let old_value = self.value.load(Ordering::Relaxed);
139 assert!(old_value != 0);
140 let new_value = old_value & !FLAGS_MASK | (flags.bits() as usize) & FLAGS_MASK;
141 if self
142 .value
143 .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
144 .is_ok()
145 {
146 return;
147 }
148 }
149 }
150
151 fn set_file(&self, id: FdTableId, file: FileHandle) {
153 let ptr = Arc::into_raw(file) as usize;
154 loop {
155 let old_value = self.value.load(Ordering::Relaxed);
156 assert!(old_value != 0);
157 let flags = old_value & FLAGS_MASK;
158 let new_value = ptr | flags;
159 if self
160 .value
161 .compare_exchange_weak(old_value, new_value, Ordering::AcqRel, Ordering::Relaxed)
162 .is_ok()
163 {
164 unsafe { Self::release(id, old_value) };
166 return;
167 }
168 }
169 }
170
171 fn read<'a>(&self, scope: &'a RcuReadScope) -> Option<FdTableEntryGuard<'a>> {
173 let value = self.value.load(Ordering::Acquire);
174 if value == 0 {
175 return None;
176 }
177 let ptr = Self::decode_ptr(value);
178 let flags = Self::decode_flags(value);
179 let file = unsafe { RcuPtrRef::new(scope, ptr) };
181 Some(FdTableEntryGuard { file, flags })
182 }
183
184 fn set_entry(&self, id: FdTableId, entry: FdTableEntry) -> bool {
186 unsafe { self.set(id, Self::encode(entry.file, entry.flags)) }
188 }
189
190 fn clear(&self, id: FdTableId) -> bool {
192 unsafe { self.set(id, 0) }
194 }
195
196 unsafe fn set(&self, id: FdTableId, value: usize) -> bool {
204 let old_value = self.value.swap(value, Ordering::AcqRel);
205 if old_value != 0 {
206 unsafe { Self::release(id, old_value) };
208 true
209 } else {
210 false
211 }
212 }
213}
214
215impl Clone for EncodedEntry {
216 fn clone(&self) -> Self {
217 if let Some(guard) = self.read(&RcuReadScope::new()) {
218 Self::new(guard.to_entry())
219 } else {
220 Self::default()
221 }
222 }
223}
224
225impl Drop for EncodedEntry {
226 fn drop(&mut self) {
227 let value = self.value.load(Ordering::Acquire);
228 let ptr = Self::decode_ptr(value);
229 if !ptr.is_null() {
230 let _file = unsafe { Arc::from_raw(ptr) };
232 }
233 }
234}
235
236#[derive(Debug, Clone)]
238struct FdTableEntry {
239 file: FileHandle,
241
242 flags: FdFlags,
244}
245
246struct FdTableEntryGuard<'a> {
250 file: RcuPtrRef<'a, FileReleaser>,
252
253 flags: FdFlags,
255}
256
257impl<'a> FdTableEntryGuard<'a> {
258 fn flags(&self) -> FdFlags {
259 self.flags
260 }
261
262 fn to_handle(&self) -> FileHandle {
264 unsafe { rcu_ptr_to_arc(self.file) }
267 }
268
269 fn to_entry(&self) -> FdTableEntry {
271 FdTableEntry { file: self.to_handle(), flags: self.flags }
272 }
273}
274
275struct FlushedFile(FileHandle, FdTableId);
277
278impl Releasable for FlushedFile {
279 type Context<'a> = &'a CurrentTask;
280 fn release<'a>(self, context: Self::Context<'a>) {
281 let current_task = context;
282 let FlushedFile(file, id) = self;
283 file.flush(current_task, id);
284 }
285}
286
287struct FdTableView<'a> {
295 slice: &'a [EncodedEntry],
297}
298
299impl<'a> FdTableView<'a> {
300 fn len(&self) -> usize {
302 self.slice.len()
303 }
304
305 fn is_some(&self, fd: FdNumber) -> bool {
307 self.slice.get(fd.raw() as usize).map_or(false, |entry| entry.is_some())
308 }
309
310 fn is_none(&self, fd: FdNumber) -> bool {
312 !self.is_some(fd)
313 }
314
315 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
317 self.slice
318 .get(fd.raw() as usize)
319 .and_then(|entry| entry.read(scope))
320 .map(|guard| guard.to_handle())
321 }
322
323 fn get_entry(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FdTableEntry> {
325 self.slice
326 .get(fd.raw() as usize)
327 .and_then(|entry| entry.read(scope))
328 .map(|guard| guard.to_entry())
329 }
330}
331
332#[derive(Debug)]
333pub struct FdTableMutableState {
334 pub share_count: usize,
338
339 pub next_fd: FdNumber,
341}
342
343#[apply(state_implementation!)]
344impl FdTableMutableState<Base = FdTable> {
345 fn share(&mut self) {
347 assert!(self.share_count > 0, "Cannot share unshared table");
348 self.share_count += 1;
349 }
350
351 fn unshare(&mut self) {
354 if self.share_count > 0 {
355 self.share_count -= 1;
356 if self.share_count == 0 {
357 self.clear();
358 }
359 }
360 }
361
362 fn fork(&self) -> FdTable {
364 FdTable {
365 entries: self.base.entries.clone(),
366 mutable_state: LockDepRwLock::new(FdTableMutableState {
367 share_count: 1,
368 next_fd: self.next_fd,
369 }),
370 }
371 }
372
373 fn next_fd(&self) -> FdNumber {
375 self.next_fd
376 }
377
378 fn calculate_lowest_available_fd(&self, view: &FdTableView<'_>, minfd: &FdNumber) -> FdNumber {
380 let mut fd: FdNumber = *minfd;
381 while view.is_some(fd) {
382 fd = FdNumber::from_raw(fd.raw() + 1);
383 }
384 fd
385 }
386
387 fn get_lowest_available_fd(&self, scope: &RcuReadScope, minfd: FdNumber) -> FdNumber {
389 if minfd > self.next_fd {
390 let view = self.base.read_entries(scope);
391 return self.calculate_lowest_available_fd(&view, &minfd);
392 }
393 self.next_fd
394 }
395
396 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
398 self.base.read_entries(scope).get_file(scope, fd)
399 }
400
401 fn insert_entry(
405 &mut self,
406 scope: &RcuReadScope,
407 fd: FdNumber,
408 rlimit: u64,
409 entry: FdTableEntry,
410 ) -> Result<bool, Errno> {
411 let raw_fd = fd.raw();
412 if raw_fd < 0 {
413 return error!(EBADF);
414 }
415 if raw_fd as u64 >= rlimit {
416 return error!(EMFILE);
417 }
418 let mut view = self.base.read_entries(scope);
419 if raw_fd == self.next_fd.raw() {
420 self.next_fd =
421 self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(raw_fd + 1));
422 }
423 let raw_fd = raw_fd as usize;
424 if view.len() <= raw_fd {
425 unsafe { self.base.entries.ensure_at_least(raw_fd + 1) };
427 view = self.base.read_entries(scope);
428 }
429 let id = self.base.id();
430 Ok(view.slice[raw_fd].set_entry(id, entry))
431 }
432
433 fn remove_entry(&mut self, scope: &RcuReadScope, fd: &FdNumber) -> bool {
437 let raw_fd = fd.raw() as usize;
438 let view = self.base.read_entries(scope);
439 if raw_fd >= view.len() {
440 return false;
441 }
442 let id = self.base.id();
443 let removed = view.slice[raw_fd].clear(id);
444 if removed && raw_fd < self.next_fd.raw() as usize {
445 self.next_fd = *fd;
446 }
447 removed
448 }
449
450 fn set_fd_flags(
454 &self,
455 scope: &RcuReadScope,
456 fd: FdNumber,
457 flags: FdFlags,
458 ) -> Result<(), Errno> {
459 let view = self.base.read_entries(scope);
460 if view.is_none(fd) {
461 return error!(EBADF);
462 }
463 let raw_fd = fd.raw() as usize;
464 view.slice[raw_fd].set_flags(flags);
465 Ok(())
466 }
467
468 fn retain<F>(&mut self, scope: &RcuReadScope, mut predicate: F)
474 where
475 F: FnMut(FdNumber, &mut FdFlags) -> bool,
476 {
477 let id = self.base.id();
478 let view = self.base.read_entries(scope);
479 for (index, encoded_entry) in view.slice.iter().enumerate() {
480 let fd = FdNumber::from_raw(index as i32);
481 if let Some(guard) = encoded_entry.read(scope) {
482 let mut modified_flags = guard.flags();
483 if !predicate(fd, &mut modified_flags) {
484 encoded_entry.clear(id);
485 } else if modified_flags != guard.flags() {
486 encoded_entry.set_flags(modified_flags);
487 }
488 }
489 }
490 self.next_fd = self.calculate_lowest_available_fd(&view, &FdNumber::from_raw(0));
491 }
492
493 fn clear(&mut self) {
495 self.retain(&RcuReadScope::new(), |_, _| false);
496 }
497
498 fn remap<F>(&self, scope: &RcuReadScope, predicate: F)
505 where
506 F: Fn(&FileHandle) -> Option<FileHandle>,
507 {
508 let id = self.base.id();
509 let view = self.base.read_entries(scope);
510 for encoded_entry in view.slice.iter() {
511 if let Some(guard) = encoded_entry.read(scope) {
512 let file = guard.to_handle();
513 if let Some(replacement_file) = predicate(&file) {
514 encoded_entry.set_file(id, replacement_file);
515 }
516 }
517 }
518 }
519}
520
521#[derive(Debug)]
528pub struct FdTable {
529 entries: RcuArray<EncodedEntry>,
535
536 mutable_state: LockDepRwLock<FdTableMutableState, FdTableMutableStateLock>,
538}
539
540impl Default for FdTable {
541 fn default() -> Self {
542 Self {
543 entries: Default::default(),
544 mutable_state: LockDepRwLock::new(FdTableMutableState {
545 share_count: 1,
546 next_fd: FdNumber::from_raw(0),
547 }),
548 }
549 }
550}
551
552impl Clone for FdTable {
553 fn clone(&self) -> Self {
554 let state = self.read();
555 Self {
556 entries: self.entries.clone(),
557 mutable_state: LockDepRwLock::new(FdTableMutableState {
558 share_count: 1,
559 next_fd: state.next_fd,
560 }),
561 }
562 }
563}
564
565impl Drop for FdTable {
566 fn drop(&mut self) {
567 let scope = RcuReadScope::new();
568 let view = self.read_entries(&scope);
569 for entry in view.slice.iter() {
570 assert!(entry.is_none());
571 }
572 }
573}
574
575impl FdTable {
576 pub fn id(&self) -> FdTableId {
578 FdTableId::new(self as *const Self)
579 }
580
581 fn read_entries<'a>(&self, scope: &'a RcuReadScope) -> FdTableView<'a> {
583 let slice = self.entries.as_slice(scope);
584 FdTableView { slice }
585 }
586
587 pub fn fork(&self) -> Arc<Self> {
589 Arc::new(self.read().fork())
590 }
591
592 pub fn exec(&self) {
594 self.retain(|_fd, flags| !flags.contains(FdFlags::CLOEXEC));
595 }
596
597 pub fn insert(
599 &self,
600 current_task: &CurrentTask,
601 fd: FdNumber,
602 file: FileHandle,
603 ) -> Result<(), Errno> {
604 let flags = FdFlags::empty();
605 let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
606 let mut state = self.write_active()?;
607 state.insert_entry(&RcuReadScope::new(), fd, rlimit, FdTableEntry { file, flags })?;
608 Ok(())
609 }
610
611 pub fn add(
619 &self,
620 current_task: &CurrentTask,
621 file: FileHandle,
622 flags: FdFlags,
623 ) -> Result<FdNumber, Errno> {
624 let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
625 let mut state = self.write_active()?;
626 let fd = state.next_fd();
627 state.insert_entry(&RcuReadScope::new(), fd, rlimit, FdTableEntry { file, flags })?;
628 Ok(fd)
629 }
630
631 pub fn duplicate(
636 &self,
637 current_task: &CurrentTask,
638 oldfd: FdNumber,
639 target: TargetFdNumber,
640 flags: FdFlags,
641 ) -> Result<FdNumber, Errno> {
642 let rlimit = current_task.thread_group().get_rlimit(Resource::NOFILE);
643 let mut state = self.write_active()?;
644 let scope = RcuReadScope::new();
645 let file = state.get_file(&scope, oldfd).ok_or_else(|| errno!(EBADF))?;
646
647 let fd = match target {
648 TargetFdNumber::Specific(fd) => {
649 if fd.raw() as u64 >= rlimit {
652 return error!(EBADF);
656 }
657 state.remove_entry(&scope, &fd);
658 fd
659 }
660 TargetFdNumber::Minimum(fd) => state.get_lowest_available_fd(&scope, fd),
661 TargetFdNumber::Default => state.get_lowest_available_fd(&scope, FdNumber::from_raw(0)),
662 };
663 let existing_entry =
664 state.insert_entry(&scope, fd, rlimit, FdTableEntry { file, flags })?;
665 assert!(!existing_entry);
666 Ok(fd)
667 }
668
669 pub fn get_allowing_opath(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
676 self.get_allowing_opath_with_flags(fd).map(|(file, _flags)| file)
677 }
678
679 pub fn get_allowing_opath_with_flags(
686 &self,
687 fd: FdNumber,
688 ) -> Result<(FileHandle, FdFlags), Errno> {
689 let scope = RcuReadScope::new();
690 let view = self.read_entries(&scope);
691 view.get_entry(&scope, fd)
692 .map(|entry| (entry.file, entry.flags))
693 .ok_or_else(|| errno!(EBADF))
694 }
695
696 pub fn get(&self, fd: FdNumber) -> Result<FileHandle, Errno> {
700 let file = self.get_allowing_opath(fd)?;
701 if file.flags().contains(OpenFlags::PATH) {
702 return error!(EBADF);
703 }
704 Ok(file)
705 }
706
707 pub fn close(&self, fd: FdNumber) -> Result<(), Errno> {
711 let mut state = self.write_active()?;
712 let scope = RcuReadScope::new();
713 if state.remove_entry(&scope, &fd) { Ok(()) } else { error!(EBADF) }
714 }
715
716 pub fn get_fd_flags_allowing_opath(&self, fd: FdNumber) -> Result<FdFlags, Errno> {
720 self.get_allowing_opath_with_flags(fd).map(|(_file, flags)| flags)
721 }
722
723 pub fn ioctl_fd_flags(
727 &self,
728 current_task: &CurrentTask,
729 fd: FdNumber,
730 request: u32,
731 ) -> Result<(), Errno> {
732 let state = self.write_active()?;
733 let scope = RcuReadScope::new();
734 let file = state.get_file(&scope, fd).ok_or_else(|| errno!(EBADF))?;
735 if file.flags().contains(OpenFlags::PATH) {
736 return error!(EBADF);
737 }
738 let flags = match request {
739 FIOCLEX => FdFlags::CLOEXEC,
740 FIONCLEX => FdFlags::empty(),
741 _ => {
742 return error!(EINVAL);
743 }
744 };
745 security::check_file_ioctl_access(current_task, &file, request)?;
746 state.set_fd_flags(&scope, fd, flags)
747 }
748
749 pub fn set_fd_flags_allowing_opath(&self, fd: FdNumber, flags: FdFlags) -> Result<(), Errno> {
753 let state = self.write_active()?;
754 state.set_fd_flags(&RcuReadScope::new(), fd, flags)
755 }
756
757 pub fn retain<F>(&self, predicate: F)
763 where
764 F: Fn(FdNumber, &mut FdFlags) -> bool,
765 {
766 if let Ok(mut state) = self.write_active() {
767 state.retain(&RcuReadScope::new(), predicate);
768 }
769 }
770
771 pub fn get_all_fds(&self) -> Vec<FdNumber> {
773 let scope = RcuReadScope::new();
774 let view = self.read_entries(&scope);
775 view.slice
776 .iter()
777 .enumerate()
778 .filter_map(|(index, encoded_entry)| {
779 if encoded_entry.is_none() { None } else { Some(FdNumber::from_raw(index as i32)) }
780 })
781 .collect()
782 }
783
784 pub fn remap<F: Fn(&FileHandle) -> Option<FileHandle>>(
789 &self,
790 _current_task: &CurrentTask,
791 predicate: F,
792 ) {
793 if let Ok(state) = self.write_active() {
794 state.remap(&RcuReadScope::new(), predicate);
795 }
796 }
797
798 fn write_active(&self) -> Result<FdTableWriteGuard<'_>, Errno> {
806 let state = self.write();
807 if state.share_count == 0 {
808 return error!(ESRCH);
809 }
810 Ok(state)
811 }
812
813 state_accessor!(FdTable, mutable_state);
814}
815
816#[derive(Debug, Default)]
822pub struct SharedFdTable {
823 pub table: Arc<FdTable>,
824}
825
826impl Clone for SharedFdTable {
827 fn clone(&self) -> Self {
828 let mut state = self.table.write_active().expect("FdTable must be active");
829 state.share();
830 Self { table: self.table.clone() }
831 }
832}
833
834impl std::ops::Deref for SharedFdTable {
835 type Target = FdTable;
836 fn deref(&self) -> &Self::Target {
837 &self.table
838 }
839}
840
841impl Drop for SharedFdTable {
842 fn drop(&mut self) {
843 self.table.write().unshare();
844 }
845}
846
847impl SharedFdTable {
848 pub fn new(table: Arc<FdTable>) -> Self {
849 Self { table }
850 }
851
852 pub fn unshare(&mut self) {
854 if let Ok(mut state) = self.table.clone().write_active() {
855 if state.share_count > 1 {
856 let table = Arc::new(state.fork());
857 state.share_count -= 1;
858 self.table = table;
859 }
860 }
861 }
862}
863
864pub enum TargetFdNumber {
866 Default,
868
869 Specific(FdNumber),
871
872 Minimum(FdNumber),
874}
875
876#[cfg(test)]
877mod test {
878 use super::*;
879 use crate::fs::fuchsia::SyslogFile;
880 use crate::testing::*;
881
882 fn add(
883 current_task: &CurrentTask,
884 files: &FdTable,
885 file: FileHandle,
886 ) -> Result<FdNumber, Errno> {
887 files.add(current_task, file, FdFlags::empty())
888 }
889
890 #[::fuchsia::test]
891 async fn test_fd_table_install() {
892 spawn_kernel_and_run(async |current_task| {
893 let files = SharedFdTable::default();
894 let file = SyslogFile::new_file(¤t_task);
895
896 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
897 assert_eq!(fd0.raw(), 0);
898 let fd1 = add(¤t_task, &files, file.clone()).unwrap();
899 assert_eq!(fd1.raw(), 1);
900
901 assert!(Arc::ptr_eq(&files.get(fd0).unwrap(), &file));
902 assert!(Arc::ptr_eq(&files.get(fd1).unwrap(), &file));
903 assert_eq!(files.get(FdNumber::from_raw(fd1.raw() + 1)).map(|_| ()), error!(EBADF));
904 })
905 .await;
906 }
907
908 #[::fuchsia::test]
909 async fn test_fd_table_fork() {
910 spawn_kernel_and_run(async |current_task| {
911 let files = SharedFdTable::default();
912 let file = SyslogFile::new_file(¤t_task);
913
914 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
915 let fd1 = add(¤t_task, &files, file).unwrap();
916 let fd2 = FdNumber::from_raw(2);
917
918 let forked = SharedFdTable::new(files.fork());
919
920 assert_eq!(
921 Arc::as_ptr(&files.get(fd0).unwrap()),
922 Arc::as_ptr(&forked.get(fd0).unwrap())
923 );
924 assert_eq!(
925 Arc::as_ptr(&files.get(fd1).unwrap()),
926 Arc::as_ptr(&forked.get(fd1).unwrap())
927 );
928 assert!(files.get(fd2).is_err());
929 assert!(forked.get(fd2).is_err());
930
931 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
932 assert_eq!(FdFlags::CLOEXEC, files.get_fd_flags_allowing_opath(fd0).unwrap());
933 assert_ne!(FdFlags::CLOEXEC, forked.get_fd_flags_allowing_opath(fd0).unwrap());
934 })
935 .await;
936 }
937
938 #[::fuchsia::test]
939 async fn test_fd_table_exec() {
940 spawn_kernel_and_run(async |current_task| {
941 let files = SharedFdTable::default();
942 let file = SyslogFile::new_file(¤t_task);
943
944 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
945 let fd1 = add(¤t_task, &files, file).unwrap();
946
947 files.set_fd_flags_allowing_opath(fd0, FdFlags::CLOEXEC).unwrap();
948
949 assert!(files.get(fd0).is_ok());
950 assert!(files.get(fd1).is_ok());
951
952 files.exec();
953
954 assert!(files.get(fd0).is_err());
955 assert!(files.get(fd1).is_ok());
956 })
957 .await;
958 }
959
960 #[::fuchsia::test]
961 async fn test_fd_table_pack_values() {
962 spawn_kernel_and_run(async |current_task| {
963 let files = SharedFdTable::default();
964 let file = SyslogFile::new_file(¤t_task);
965
966 let fd0 = add(¤t_task, &files, file.clone()).unwrap();
968 let fd1 = add(¤t_task, &files, file.clone()).unwrap();
969 assert_eq!(fd0.raw(), 0);
970 assert_eq!(fd1.raw(), 1);
971
972 assert!(files.close(fd0).is_ok());
974 assert!(files.close(fd0).is_err());
975 assert!(files.get(fd0).is_err());
977
978 let another_fd = add(¤t_task, &files, file).unwrap();
980 assert_eq!(another_fd.raw(), 0);
981 })
982 .await;
983 }
984
985 #[::fuchsia::test]
986 async fn test_fd_table_shared_release() {
987 spawn_kernel_and_run(async |current_task| {
988 let files = SharedFdTable::default();
989 let file = SyslogFile::new_file(¤t_task);
990
991 let fd = add(¤t_task, &files, file).unwrap();
992 assert_eq!(files.get_all_fds(), vec![fd]);
993
994 let shared_files = files.clone();
995 assert_eq!(shared_files.get_all_fds(), vec![fd]);
996
997 drop(files);
1000 assert_eq!(shared_files.get_all_fds(), vec![fd]);
1001 })
1002 .await;
1003 }
1004
1005 #[::fuchsia::test]
1006 async fn test_fd_table_mutate_after_clear() {
1007 spawn_kernel_and_run(async |current_task| {
1008 let shared_files = SharedFdTable::default();
1009 let file = SyslogFile::new_file(¤t_task);
1010
1011 let fd_table_clone = shared_files.table.clone();
1014
1015 drop(shared_files);
1017
1018 let result = fd_table_clone.add(¤t_task, file, FdFlags::empty());
1020 assert_eq!(result.map(|_| ()), error!(ESRCH));
1021
1022 drop(fd_table_clone);
1025 })
1026 .await;
1027 }
1028}