1use ic_stable_structures::Memory;
2
3use crate::{
4 error::Error,
5 filename_cache::FilenameCache,
6 runtime::{
7 dir::Dir,
8 fd::{FdEntry, FdTable, STDERR_FD},
9 file::File,
10 structure_helpers::{create_hard_link, find_node, rm_dir_entry},
11 types::{RIGHTS_FD_READ, RIGHTS_FD_WRITE},
12 },
13 storage::{
14 Storage,
15 types::{DirEntry, DirEntryIndex, FileType, Metadata, MountedFileSizePolicy, Node},
16 },
17};
18
19pub use crate::runtime::types::{
20 Advice, ChunkSize, ChunkType, DstBuf, DstIoVec, Fd, FdFlags, FdStat, OpenFlags, SrcBuf,
21 SrcIoVec, Whence,
22};
23pub use crate::storage::types::FileSize;
24
25pub struct FileSystem {
27 pub(crate) root_fd: Fd,
28 pub(crate) fd_table: FdTable,
29 pub(crate) names_cache: FilenameCache,
30 pub storage: Box<dyn Storage>,
31}
32
33impl FileSystem {
34 pub fn new(storage: Box<dyn Storage>) -> Result<Self, Error> {
36 let mut fd_table = FdTable::new();
37
38 if storage.get_version() == 0 {
39 return Ok(Self {
40 root_fd: 0,
41 fd_table,
42 names_cache: FilenameCache::new(),
43 storage,
44 });
45 }
46
47 let root_node = storage.root_node();
48 let root_entry = Dir::new(root_node, FdStat::default(), &*storage)?;
49 let root_fd = fd_table.open_root(FdEntry::Dir(root_entry));
50 let names_cache = FilenameCache::new();
51
52 Ok(Self {
53 root_fd,
54 fd_table,
55 names_cache,
56 storage,
57 })
58 }
59
60 pub fn get_storage_version(&self) -> u32 {
62 self.storage.get_version()
63 }
64
65 pub fn root_fd(&self) -> Fd {
68 self.root_fd
69 }
70
71 pub fn root_path(&self) -> &str {
73 "/"
74 }
75
76 pub fn advice(
79 &mut self,
80 fd: Fd,
81 _offset: FileSize,
82 _len: FileSize,
83 _advice: Advice,
84 ) -> Result<(), Error> {
85 let meta = self.metadata(fd)?;
86
87 if meta.file_type != FileType::RegularFile {
89 return Err(Error::BadFileDescriptor);
90 }
91
92 Ok(())
93 }
94
95 pub fn allocate(
100 &mut self,
101 fd: Fd,
102 _offset: FileSize,
103 _additional_size: FileSize,
104 ) -> Result<(), Error> {
105 let meta = self.metadata(fd)?;
106
107 if meta.file_type != FileType::RegularFile {
108 return Err(Error::BadFileDescriptor);
109 }
110
111 Ok(())
113 }
114
115 pub fn close(&mut self, fd: Fd) -> Result<(), Error> {
117 if fd == crate::runtime::fd::ROOT_FD {
118 return Ok(());
121 }
122
123 match self.fd_table.get(fd) {
125 Some(FdEntry::File(file)) => {
126 self.storage.flush(file.node);
127 }
128 Some(FdEntry::Dir(_dir)) => {
129 }
131 None => Err(Error::BadFileDescriptor)?,
132 };
133
134 self.fd_table.close(fd).ok_or(Error::BadFileDescriptor)?;
135
136 Ok(())
137 }
138
139 pub fn flush(&mut self, fd: Fd) -> Result<(), Error> {
141 let file = self.get_file(fd)?;
142
143 self.storage.flush(file.node);
144
145 Ok(())
146 }
147
148 pub fn renumber(&mut self, from: Fd, to: Fd) -> Result<(), Error> {
151 self.fd_table.renumber(from, to)
152 }
153
154 pub(crate) fn get_node(&self, fd: Fd) -> Result<Node, Error> {
155 match self.fd_table.get(fd) {
156 Some(FdEntry::File(file)) => Ok(file.node),
157 Some(FdEntry::Dir(dir)) => Ok(dir.node),
158 None => Err(Error::BadFileDescriptor),
159 }
160 }
161
162 pub(crate) fn get_file(&self, fd: Fd) -> Result<File, Error> {
163 match self.fd_table.get(fd) {
164 Some(FdEntry::Dir(_)) => Err(Error::BadFileDescriptor),
165 Some(FdEntry::File(file)) => Ok(file.clone()),
166 None => Err(Error::BadFileDescriptor),
167 }
168 }
169
170 pub(crate) fn put_file(&mut self, fd: Fd, file: File) {
171 self.fd_table.update(fd, FdEntry::File(file))
172 }
173
174 pub(crate) fn get_dir(&self, fd: Fd) -> Result<Dir, Error> {
175 match self.fd_table.get(fd) {
176 Some(FdEntry::Dir(dir)) => Ok(dir.clone()),
177 Some(FdEntry::File(_)) => Err(Error::NotADirectoryOrSymbolicLink),
178 None => Err(Error::BadFileDescriptor),
179 }
180 }
181
182 pub fn mount_memory_file(
185 &mut self,
186 filename: &str,
187 memory: Box<dyn Memory>,
188 policy: MountedFileSizePolicy,
189 ) -> Result<(), Error> {
190 let filename = filename.strip_prefix('/').unwrap_or(filename);
191
192 let fd = self.open(
194 self.root_fd,
195 filename,
196 FdStat::default(),
197 OpenFlags::CREATE,
198 0,
199 )?;
200
201 let result = (|| {
202 let file = self.get_file(fd)?;
203 self.storage.mount_node(file.node, memory, policy)
204 })();
205
206 let _ = self.close(fd);
207
208 result
209 }
210
211 pub fn init_memory_file(&mut self, filename: &str) -> Result<(), Error> {
213 let filename = filename.strip_prefix('/').unwrap_or(filename);
214
215 let fd = self.open(
217 self.root_fd,
218 filename,
219 FdStat::default(),
220 OpenFlags::empty(),
221 0,
222 )?;
223
224 let result = (|| {
225 let file = self.get_file(fd)?;
226 self.storage.init_mounted_memory(file.node)
227 })();
228
229 let _ = self.close(fd);
230
231 result
232 }
233
234 pub fn store_memory_file(&mut self, filename: &str) -> Result<(), Error> {
236 let filename = filename.strip_prefix('/').unwrap_or(filename);
237
238 let fd = self.open(
240 self.root_fd,
241 filename,
242 FdStat::default(),
243 OpenFlags::empty(),
244 0,
245 )?;
246
247 let result = (|| {
248 let file = self.get_file(fd)?;
249 self.storage.store_mounted_memory(file.node)
250 })();
251
252 let _ = self.close(fd);
253
254 result
255 }
256
257 pub fn unmount_memory_file(&mut self, filename: &str) -> Result<Box<dyn Memory>, Error> {
259 let filename = filename.strip_prefix('/').unwrap_or(filename);
260
261 let fd = self.open(
263 self.root_fd,
264 filename,
265 FdStat::default(),
266 OpenFlags::empty(),
267 0,
268 )?;
269
270 let result = (|| {
271 let file = self.get_file(fd)?;
272
273 let memory = self.storage.unmount_node(file.node)?;
274
275 Ok(memory)
276 })();
277
278 let _ = self.close(fd);
279
280 result
281 }
282
283 pub fn get_direntry(&self, fd: Fd, index: DirEntryIndex) -> Result<DirEntry, Error> {
285 self.get_dir(fd)?.get_entry(index, self.storage.as_ref())
286 }
287
288 pub fn with_direntries(
292 &self,
293 fd: Fd,
294 initial_index: Option<DirEntryIndex>,
295 f: &mut dyn FnMut(&DirEntryIndex, &DirEntry) -> bool,
296 ) -> Result<(), Error> {
297 let dir = self.get_dir(fd)?;
298
299 self.storage.with_direntries(dir.node, initial_index, f);
300
301 Ok(())
302 }
303
304 fn put_dir(&mut self, fd: Fd, dir: Dir) {
305 self.fd_table.update(fd, FdEntry::Dir(dir))
306 }
307
308 pub fn read(&mut self, fd: Fd, dst: &mut [u8]) -> Result<FileSize, Error> {
310 let mut file = self.get_file(fd)?;
311
312 if file.stat.rights_base & RIGHTS_FD_READ == 0 {
313 return Err(Error::OperationNotPermitted);
314 }
315
316 let read_size = file.read_with_cursor(dst, self.storage.as_mut())?;
317 self.put_file(fd, file);
318 Ok(read_size)
319 }
320
321 pub fn read_vec(&mut self, fd: Fd, dst: DstIoVec) -> Result<FileSize, Error> {
323 let mut file = self.get_file(fd)?;
324
325 if file.stat.rights_base & RIGHTS_FD_READ == 0 {
326 return Err(Error::OperationNotPermitted);
327 }
328
329 let mut read_size = 0;
330 for buf in dst {
331 let buf = unsafe { std::slice::from_raw_parts_mut(buf.buf, buf.len) };
332 let size = file.read_with_cursor(buf, self.storage.as_mut())?;
333 read_size += size;
334 }
335 self.put_file(fd, file);
336 Ok(read_size)
337 }
338
339 pub fn read_vec_with_offset(
341 &mut self,
342 fd: Fd,
343 dst: DstIoVec,
344 offset: FileSize,
345 ) -> Result<FileSize, Error> {
346 let file = self.get_file(fd)?;
347
348 if file.stat.rights_base & RIGHTS_FD_READ == 0 {
349 return Err(Error::OperationNotPermitted);
350 }
351
352 let mut read_size = 0;
353
354 for buf in dst {
355 let rbuf = unsafe { std::slice::from_raw_parts_mut(buf.buf, buf.len) };
356
357 let size = file.read_with_offset(read_size + offset, rbuf, self.storage.as_mut())?;
358
359 read_size += size;
360 }
361 self.put_file(fd, file);
362 Ok(read_size)
363 }
364
365 pub fn write(&mut self, fd: Fd, src: &[u8]) -> Result<FileSize, Error> {
367 let buf = SrcBuf {
368 buf: src.as_ptr(),
369 len: src.len(),
370 };
371
372 self.write_vec(fd, &[buf])
373 }
374
375 pub fn write_vec(&mut self, fd: Fd, src: SrcIoVec) -> Result<FileSize, Error> {
377 let mut file = self.get_file(fd)?;
378
379 if file.stat.rights_base & RIGHTS_FD_WRITE == 0 {
380 return Err(Error::OperationNotPermitted);
381 }
382
383 let is_append = file.stat.flags.contains(FdFlags::APPEND);
384
385 let offset = if is_append {
387 let meta = self.metadata_from_node(file.node)?;
388 meta.size
389 } else {
390 file.cursor
391 };
392
393 let mut written_size = 0;
394 for buf in src {
395 let buf = unsafe { std::slice::from_raw_parts(buf.buf, buf.len) };
396
397 let size = file.write_with_offset(written_size + offset, buf, self.storage.as_mut())?;
398 written_size += size;
399 }
400
401 file.cursor = written_size + offset;
403
404 self.put_file(fd, file);
405 Ok(written_size)
406 }
407
408 pub fn write_vec_with_offset(
410 &mut self,
411 fd: Fd,
412 src: SrcIoVec,
413 offset: FileSize,
414 ) -> Result<FileSize, Error> {
415 let file = self.get_file(fd)?;
416
417 if file.stat.rights_base & RIGHTS_FD_WRITE == 0 {
418 return Err(Error::OperationNotPermitted);
419 }
420
421 let is_append = file.stat.flags.contains(FdFlags::APPEND);
422
423 let offset = if is_append {
425 let meta = self.metadata_from_node(file.node)?;
426 meta.size
427 } else {
428 offset
429 };
430
431 let mut written_size = 0;
432 for buf in src {
433 let buf = unsafe { std::slice::from_raw_parts(buf.buf, buf.len) };
434
435 let size = file.write_with_offset(written_size + offset, buf, self.storage.as_mut())?;
436
437 written_size += size;
438 }
439
440 self.put_file(fd, file);
441 Ok(written_size)
442 }
443
444 pub fn seek(&mut self, fd: Fd, delta: i64, whence: Whence) -> Result<FileSize, Error> {
446 let mut file = self.get_file(fd)?;
447
448 let pos = file.seek(delta, whence, self.storage.as_mut())?;
449 self.put_file(fd, file);
450 Ok(pos)
451 }
452
453 pub fn tell(&mut self, fd: Fd) -> Result<FileSize, Error> {
455 let file = self.get_file(fd)?;
456
457 let pos = file.tell();
458 Ok(pos)
459 }
460
461 pub fn metadata_from_node(&self, node: Node) -> Result<Metadata, Error> {
463 self.storage.get_metadata(node)
464 }
465
466 pub fn metadata(&self, fd: Fd) -> Result<Metadata, Error> {
468 if fd <= STDERR_FD {
470 return Ok(Metadata {
471 node: u64::MAX, file_type: FileType::RegularFile,
473 link_count: 0,
474 size: 0,
475 times: crate::storage::types::Times::default(),
476 chunk_type: None,
477 maximum_size_allowed: None,
478 first_dir_entry: None,
479 last_dir_entry: None,
480 });
481 }
482
483 let node = self.get_node(fd)?;
484 self.storage.get_metadata(node)
485 }
486
487 pub fn set_metadata(&mut self, fd: Fd, metadata: Metadata) -> Result<(), Error> {
489 let node = self.get_node(fd)?;
490 self.storage.put_metadata(node, &metadata)?;
491
492 Ok(())
493 }
494
495 pub fn set_file_size(&mut self, fd: Fd, new_size: FileSize) -> Result<(), Error> {
496 let file = self.get_file(fd)?;
497
498 if file.stat.rights_base & RIGHTS_FD_WRITE == 0 {
499 return Err(Error::InvalidArgument);
502 }
503
504 let mut metadata = self.storage.get_metadata(file.node)?;
505
506 metadata.size = new_size;
507
508 self.storage.put_metadata(file.node, &metadata)?;
509
510 Ok(())
511 }
512
513 pub fn set_file_size_limit(&mut self, fd: Fd, max_size: FileSize) -> Result<(), Error> {
516 let file = self.get_file(fd)?;
517
518 let mut metadata = self.storage.get_metadata(file.node)?;
519
520 metadata.maximum_size_allowed = Some(max_size);
521
522 self.storage.put_metadata(file.node, &metadata)?;
523
524 Ok(())
525 }
526
527 pub fn set_accessed_time(&mut self, fd: Fd, time: u64) -> Result<(), Error> {
529 let node = self.get_node(fd)?;
530 let mut metadata = self.storage.get_metadata(node)?;
531
532 metadata.times.accessed = time;
533
534 self.storage.put_metadata(node, &metadata)?;
535
536 Ok(())
537 }
538
539 pub fn set_modified_time(&mut self, fd: Fd, time: u64) -> Result<(), Error> {
541 let node = self.get_node(fd)?;
542 let mut metadata = self.storage.get_metadata(node)?;
543
544 metadata.times.modified = time;
545
546 self.storage.put_metadata(node, &metadata)?;
547
548 Ok(())
549 }
550
551 pub fn get_stat(&self, fd: Fd) -> Result<(FileType, FdStat), Error> {
553 match self.fd_table.get(fd) {
554 None => Err(Error::BadFileDescriptor),
555 Some(FdEntry::File(file)) => Ok((FileType::RegularFile, file.stat)),
556 Some(FdEntry::Dir(dir)) => Ok((FileType::Directory, dir.stat)),
557 }
558 }
559
560 pub fn set_stat(&mut self, fd: Fd, stat: FdStat) -> Result<(), Error> {
562 match self.fd_table.get(fd) {
563 Some(FdEntry::File(file)) => {
564 let mut file = file.clone();
565 file.stat = stat;
566 self.put_file(fd, file);
567 Ok(())
568 }
569 Some(FdEntry::Dir(dir)) => {
570 if stat.flags.contains(FdFlags::APPEND) || stat.flags.contains(FdFlags::NONBLOCK) {
572 return Err(Error::BadFileDescriptor);
573 }
574
575 let mut dir = dir.clone();
576 dir.stat = stat;
577 self.put_dir(fd, dir);
578 Ok(())
579 }
580 None => Err(Error::BadFileDescriptor),
581 }
582 }
583
584 pub fn open_metadata(&mut self, parent: Fd, path: &str) -> Result<Metadata, Error> {
586 let dir = self.get_dir(parent)?;
587 let node = find_node(dir.node, path, &mut self.names_cache, self.storage.as_ref())?;
588 self.storage.get_metadata(node)
589 }
590
591 pub fn open(
593 &mut self,
594 parent_fd: Fd,
595 path: &str,
596 stat: FdStat,
597 flags: OpenFlags,
598 ctime: u64,
599 ) -> Result<Fd, Error> {
600 let dir = self.get_dir(parent_fd)?;
601
602 let res = find_node(dir.node, path, &mut self.names_cache, self.storage.as_ref());
603
604 match res {
605 Ok(node) => {
606 if flags.contains(OpenFlags::CREATE) && flags.contains(OpenFlags::EXCLUSIVE) {
608 return Err(Error::FileExists);
609 }
610
611 self.open_internal(node, stat, flags)
612 }
613
614 Err(Error::NoSuchFileOrDirectory) => {
615 if !flags.contains(OpenFlags::CREATE) {
616 return Err(Error::NoSuchFileOrDirectory);
617 }
618
619 if flags.contains(OpenFlags::DIRECTORY) {
620 self.create_open_directory(parent_fd, path, stat, ctime)
621 } else {
622 self.create_open_file(parent_fd, path, stat, ctime)
623 }
624 }
625 Err(err) => Err(err),
626 }
627 }
628
629 fn open_internal(&mut self, node: Node, stat: FdStat, flags: OpenFlags) -> Result<Fd, Error> {
631 let metadata = self.storage.get_metadata(node)?;
632
633 match metadata.file_type {
634 FileType::Directory => {
635 let dir = Dir::new(node, stat, self.storage.as_mut())?;
636 let fd = self.fd_table.open(FdEntry::Dir(dir));
637 Ok(fd)
638 }
639 FileType::RegularFile => {
640 if flags.contains(OpenFlags::DIRECTORY) {
641 return Err(Error::OperationNotPermitted);
642 }
643
644 let file = File::new(node, stat, self.storage.as_mut())?;
645
646 if flags.contains(OpenFlags::TRUNCATE) {
647 file.truncate(self.storage.as_mut())?;
648 }
649
650 let fd = self.fd_table.open(FdEntry::File(file));
651 Ok(fd)
652 }
653 FileType::SymbolicLink => unimplemented!("Symbolic links are not supported"),
654 }
655 }
656
657 pub(crate) fn create_open_file(
659 &mut self,
660 parent: Fd,
661 path: &str,
662 stat: FdStat,
663 ctime: u64,
664 ) -> Result<Fd, Error> {
665 let dir = self.get_dir(parent)?;
666
667 let child = dir.create_file(
668 path,
669 stat,
670 &mut self.names_cache,
671 self.storage.as_mut(),
672 ctime,
673 )?;
674
675 let child_fd = self.fd_table.open(FdEntry::File(child));
676 self.put_dir(parent, dir);
677
678 Ok(child_fd)
679 }
680
681 pub fn remove_file(&mut self, parent: Fd, path: &str) -> Result<(), Error> {
683 let dir = self.get_dir(parent)?;
684 dir.remove_file(
685 path,
686 self.fd_table.node_refcount(),
687 &mut self.names_cache,
688 self.storage.as_mut(),
689 )?;
690
691 self.put_dir(parent, dir);
692
693 Ok(())
694 }
695
696 pub fn mkdir(&mut self, parent: Fd, path: &str, stat: FdStat, ctime: u64) -> Result<(), Error> {
698 let dir = self.get_dir(parent)?;
699
700 dir.create_dir(
701 path,
702 stat,
703 &mut self.names_cache,
704 self.storage.as_mut(),
705 ctime,
706 )?;
707
708 self.put_dir(parent, dir);
709
710 Ok(())
711 }
712
713 pub fn create_open_directory(
715 &mut self,
716 parent: Fd,
717 path: &str,
718 stat: FdStat,
719 ctime: u64,
720 ) -> Result<Fd, Error> {
721 let dir = self.get_dir(parent)?;
722
723 let child = dir.create_dir(
724 path,
725 stat,
726 &mut self.names_cache,
727 self.storage.as_mut(),
728 ctime,
729 )?;
730
731 let child_fd = self.fd_table.open(FdEntry::Dir(child));
732 self.put_dir(parent, dir);
733
734 Ok(child_fd)
735 }
736
737 pub fn remove_dir(&mut self, parent: Fd, path: &str) -> Result<(), Error> {
739 let dir = self.get_dir(parent)?;
740 dir.remove_dir(
741 path,
742 self.fd_table.node_refcount(),
743 &mut self.names_cache,
744 self.storage.as_mut(),
745 )?;
746
747 self.put_dir(parent, dir);
748
749 Ok(())
750 }
751
752 pub(crate) fn list_dir_internal(
753 &mut self,
754 dir_fd: Fd,
755 file_type: Option<FileType>,
756 ) -> Result<Vec<(Node, String)>, Error> {
757 let mut res = vec![];
758
759 self.with_direntries(dir_fd, Some(0), &mut |_index, entry| -> bool {
760 let filename = unsafe {
762 std::str::from_utf8_unchecked(&entry.name.bytes[..(entry.name.length as usize)])
763 }
764 .to_string();
765
766 if let Some(file_type) = file_type {
767 let meta = self
768 .metadata_from_node(entry.node)
769 .expect("Metadata not found!");
770
771 if meta.file_type == file_type {
772 res.push((entry.node, filename));
773 }
774 } else {
775 res.push((entry.node, filename));
776 }
777
778 true
779 })?;
780
781 Ok(res)
782 }
783
784 pub fn remove_recursive(&mut self, parent: Fd, path: &str) -> Result<(), Error> {
786 let meta = self.open_metadata(parent, path)?;
787
788 if meta.file_type == FileType::RegularFile {
789 return self.remove_file(parent, path);
790 }
791
792 let dir_fd = self.open(parent, path, FdStat::default(), OpenFlags::DIRECTORY, 0)?;
795
796 let result = (|| {
797 let children = self.list_dir_internal(dir_fd, None)?;
799
800 for (child_node, child_name) in children {
802 let child_meta = self.storage.get_metadata(child_node)?;
803
804 match child_meta.file_type {
805 FileType::Directory => {
806 self.remove_recursive(dir_fd, &child_name)?;
808 }
809 FileType::RegularFile => {
810 self.remove_file(dir_fd, &child_name)?;
812 }
813 FileType::SymbolicLink => {
814 unimplemented!("Symbolic links are not supported yet");
815 }
816 }
817 }
818
819 Ok(())
820 })();
821
822 self.close(dir_fd)?;
824
825 self.remove_dir(parent, path)?;
827
828 result
829 }
830
831 pub fn create_hard_link(
833 &mut self,
834 old_fd: Fd,
835 old_path: &str,
836 new_fd: Fd,
837 new_path: &str,
838 ) -> Result<Fd, Error> {
839 let src_dir = self.get_dir(old_fd)?;
840 let dst_dir = self.get_dir(new_fd)?;
841
842 create_hard_link(
843 dst_dir.node,
844 new_path,
845 src_dir.node,
846 old_path,
847 false,
848 self.fd_table.node_refcount(),
849 &mut self.names_cache,
850 self.storage.as_mut(),
851 )?;
852
853 let node = find_node(
854 dst_dir.node,
855 new_path,
856 &mut self.names_cache,
857 self.storage.as_ref(),
858 )?;
859
860 self.open_internal(node, FdStat::default(), OpenFlags::empty())
861 }
862
863 pub fn rename(
865 &mut self,
866 old_fd: Fd,
867 old_path: &str,
868 new_fd: Fd,
869 new_path: &str,
870 ) -> Result<Fd, Error> {
871 let src_dir = self.get_dir(old_fd)?;
872 let dst_dir = self.get_dir(new_fd)?;
873
874 create_hard_link(
876 dst_dir.node,
877 new_path,
878 src_dir.node,
879 old_path,
880 true,
881 self.fd_table.node_refcount(),
882 &mut self.names_cache,
883 self.storage.as_mut(),
884 )?;
885
886 let (node, _metadata) = rm_dir_entry(
888 src_dir.node,
889 old_path,
890 None,
891 true,
892 self.fd_table.node_refcount(),
893 &mut self.names_cache,
894 self.storage.as_mut(),
895 )?;
896
897 self.open_internal(node, FdStat::default(), OpenFlags::empty())
898 }
899
900 #[cfg(test)]
901 pub(crate) fn get_test_storage(&mut self) -> &mut dyn Storage {
902 self.storage.as_mut()
903 }
904
905 #[cfg(test)]
906 pub(crate) fn get_test_file(&self, fd: Fd) -> File {
907 self.get_file(fd).unwrap()
908 }
909}