1use 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 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 FdTableInner) -> 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, id));
101 }
102 }
103
104 fn decode_flags(value: usize) -> FdFlags {
106 FdFlags::from_bits_truncate((value & FLAGS_MASK) as u32)
107 }
108
109 fn decode_ptr(value: usize) -> *const FileReleaser {
111 (value & !FLAGS_MASK) as *const _
112 }
113
114 fn new(entry: FdTableEntry) -> Self {
116 Self { value: AtomicUsize::new(Self::encode(entry.file, entry.flags)) }
117 }
118
119 fn is_some(&self) -> bool {
121 let value = self.value.load(Ordering::Acquire);
122 value != 0
123 }
124
125 fn is_none(&self) -> bool {
127 !self.is_some()
128 }
129
130 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 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 fn file(&self) -> Option<FileHandle> {
157 self.to_entry().map(|entry| entry.file)
158 }
159
160 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 unsafe { Self::release(id, old_value) };
175 return;
176 }
177 }
178 }
179
180 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 let file = unsafe {
190 Arc::increment_strong_count(ptr);
191 Arc::from_raw(ptr)
192 };
193 Some(FdTableEntry { file, flags })
194 }
195
196 fn set_entry(&self, id: FdTableId, entry: FdTableEntry) -> bool {
198 unsafe { self.set(id, Self::encode(entry.file, entry.flags)) }
200 }
201
202 fn clear(&self, id: FdTableId) -> bool {
204 unsafe { self.set(id, 0) }
206 }
207
208 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 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 let _file = unsafe { Arc::from_raw(ptr) };
240 }
241 }
242}
243
244#[derive(Debug, Clone)]
246struct FdTableEntry {
247 file: FileHandle,
249
250 flags: FdFlags,
252}
253
254struct 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
266struct FdTableView<'a> {
274 slice: &'a [EncodedEntry],
276}
277
278impl<'a> FdTableView<'a> {
279 fn len(&self) -> usize {
281 self.slice.len()
282 }
283
284 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 fn is_none(&self, fd: FdNumber) -> bool {
291 !self.is_some(fd)
292 }
293
294 fn get_file(&self, fd: FdNumber) -> Option<FileHandle> {
296 self.slice.get(fd.raw() as usize).and_then(|entry| entry.file())
297 }
298
299 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 fn next_fd(&self) -> FdNumber {
313 self.store.next_fd.get()
314 }
315
316 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 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 fn get_file(&self, scope: &RcuReadScope, fd: FdNumber) -> Option<FileHandle> {
336 self.store.read(scope).get_file(fd)
337 }
338
339 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 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 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 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 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 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#[derive(Debug, Default)]
459struct AtomicFdNumber {
460 value: AtomicI32,
462}
463
464impl AtomicFdNumber {
465 fn get(&self) -> FdNumber {
469 FdNumber::from_raw(self.value.load(Ordering::Relaxed))
470 }
471
472 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#[derive(Debug)]
491struct FdTableInner {
492 entries: RcuArray<EncodedEntry>,
494
495 next_fd: AtomicFdNumber,
497
498 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 fn id(&self) -> FdTableId {
538 FdTableId::new(self as *const Self)
539 }
540
541 fn unshare(&self) -> Arc<Self> {
543 Arc::new(self.clone())
544 }
545
546 fn read<'a>(&self, scope: &'a RcuReadScope) -> FdTableView<'a> {
548 let slice = self.entries.as_slice(scope);
549 FdTableView { slice }
550 }
551
552 fn write(&self) -> FdTableWriteGuard<'_> {
555 FdTableWriteGuard { store: self, _write_guard: self.writer_queue.lock() }
556 }
557}
558
559#[derive(Debug, Default)]
561pub struct FdTable {
562 inner: RcuArc<FdTableInner>,
564}
565
566pub enum TargetFdNumber {
568 Default,
570
571 Specific(FdNumber),
573
574 Minimum(FdNumber),
576}
577
578impl FdTable {
579 pub fn id(&self) -> FdTableId {
581 self.inner.read().id()
582 }
583
584 pub fn fork(&self) -> FdTable {
586 let unshared = self.inner.read().unshare();
587 FdTable { inner: RcuArc::new(unshared) }
588 }
589
590 pub fn unshare(&self) {
592 let unshared = self.inner.read().unshare();
593 self.inner.update(unshared);
594 }
595
596 pub fn release(&self) {
598 self.inner.update(Default::default());
599 }
600
601 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 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 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 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 if fd.raw() as u64 >= rlimit {
676 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 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 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 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 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 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 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 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 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 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 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, ¤t_task);
854
855 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
856 assert_eq!(fd0.raw(), 0);
857 let fd1 = add(locked, ¤t_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, ¤t_task);
874
875 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
876 let fd1 = add(locked, ¤t_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, ¤t_task);
907
908 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
909 let fd1 = add(locked, ¤t_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, ¤t_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, ¤t_task);
931
932 let fd0 = add(locked, ¤t_task, &files, file.clone()).unwrap();
934 let fd1 = add(locked, ¤t_task, &files, file.clone()).unwrap();
935 assert_eq!(fd0.raw(), 0);
936 assert_eq!(fd1.raw(), 1);
937
938 assert!(files.close(fd0).is_ok());
940 assert!(files.close(fd0).is_err());
941 assert!(files.get(fd0).is_err());
943
944 let another_fd = add(locked, ¤t_task, &files, file).unwrap();
946 assert_eq!(another_fd.raw(), 0);
947
948 files.release();
949 })
950 .await;
951 }
952}