Skip to main content

starnix_core/fs/
tmpfs.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::mm::PAGE_SIZE;
6use crate::security;
7use crate::task::{CurrentTask, Kernel};
8use crate::vfs::memory_directory::MemoryDirectoryFile;
9use crate::vfs::{
10    CacheMode, DirEntry, DirEntryHandle, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
11    FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
12    MemoryRegularNode, MemoryXattrStorage, RenameContext, SymlinkNode, XattrStorage as _, fs_args,
13    fs_node_impl_not_dir, fs_node_impl_xattr_delegate,
14};
15use starnix_logging::{log_warn, track_stub};
16use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
17use starnix_types::vfs::default_statfs;
18use starnix_uapi::auth::FsCred;
19use starnix_uapi::device_id::DeviceId;
20use starnix_uapi::errors::Errno;
21use starnix_uapi::file_mode::{FileMode, mode};
22use starnix_uapi::open_flags::OpenFlags;
23use starnix_uapi::seal_flags::SealFlags;
24use starnix_uapi::{TMPFS_MAGIC, error, gid_t, statfs, uid_t};
25use std::collections::BTreeMap;
26use std::sync::Arc;
27use std::sync::atomic::{AtomicU32, Ordering};
28
29pub struct TmpFs(&'static FsStr);
30
31impl FileSystemOps for Arc<TmpFs> {
32    fn statfs(
33        &self,
34        _locked: &mut Locked<FileOpsCore>,
35        _fs: &FileSystem,
36        _current_task: &CurrentTask,
37    ) -> Result<statfs, Errno> {
38        Ok(statfs {
39            // Pretend we have a ton of free space.
40            f_blocks: 0x100000000,
41            f_bavail: 0x100000000,
42            f_bfree: 0x100000000,
43            ..default_statfs(TMPFS_MAGIC)
44        })
45    }
46    fn name(&self) -> &'static FsStr {
47        self.0
48    }
49
50    fn rename(
51        &self,
52        _locked: &mut Locked<FileOpsCore>,
53        _fs: &FileSystem,
54        _current_task: &CurrentTask,
55        context: &mut RenameContext<'_>,
56        _old_name: &FsStr,
57        _new_name: &FsStr,
58    ) -> Result<(), Errno> {
59        fn child_count(node: &FsNodeHandle) -> &AtomicU32 {
60            // The following cast are safe, unless something is seriously wrong:
61            // - The filesystem should not be asked to rename node that it doesn't handle.
62            // - Parents in a rename operation need to be directories.
63            // - TmpFsDirectory is the ops for directories in this filesystem.
64            &node.downcast_ops::<TmpFsDirectory>().unwrap().child_count
65        }
66        let replaced = context.replaced.map(|r| &r.node);
67        let renamed_is_dir = context.renamed_is_dir();
68        let replaced_is_dir = context.replaced_is_dir();
69
70        if let Some(replaced) = replaced {
71            if replaced_is_dir {
72                // Ensures that replaces is empty.
73                if child_count(replaced).load(Ordering::Acquire) != 0 {
74                    return error!(ENOTEMPTY);
75                }
76            }
77        }
78
79        // Update child counts before borrowing parent_infos_mut to
80        // avoid borrow checker issues with context methods.
81        {
82            let old_parent = &context.old_parent().node;
83            let new_parent = &context.new_parent().node;
84            if !Arc::ptr_eq(old_parent, new_parent) {
85                child_count(new_parent).fetch_add(1, Ordering::Release);
86                child_count(old_parent).fetch_sub(1, Ordering::Release);
87            }
88            if replaced.is_some() {
89                child_count(new_parent).fetch_sub(1, Ordering::Release);
90            }
91        }
92
93        let (old_parent_info, mut new_parent_info) = context.parent_infos_mut();
94        if renamed_is_dir {
95            if let Some(new_info) = new_parent_info.as_deref_mut() {
96                new_info.link_count += 1;
97                old_parent_info.link_count -= 1;
98            }
99        }
100        // Fix the wrong changes to new_parent due to the fact that the
101        // target element has been replaced instead of added.
102        if replaced_is_dir {
103            if let Some(new_info) = new_parent_info.as_deref_mut() {
104                new_info.link_count -= 1;
105            } else {
106                old_parent_info.link_count -= 1;
107            }
108        }
109        Ok(())
110    }
111
112    fn exchange(
113        &self,
114        _fs: &FileSystem,
115        _current_task: &CurrentTask,
116        context: &mut RenameContext<'_>,
117        _name1: &FsStr,
118        _name2: &FsStr,
119    ) -> Result<(), Errno> {
120        let is_dir1 = context.renamed_is_dir();
121        let is_dir2 = context.replaced_is_dir();
122        let (parent1_info, mut parent2_info) = context.parent_infos_mut();
123        if let Some(parent2_info) = parent2_info.as_deref_mut() {
124            if is_dir1 != is_dir2 {
125                if is_dir1 {
126                    parent1_info.link_count -= 1;
127                    parent2_info.link_count += 1;
128                } else {
129                    parent1_info.link_count += 1;
130                    parent2_info.link_count -= 1;
131                }
132            }
133        }
134
135        Ok(())
136    }
137}
138
139pub fn tmp_fs(
140    locked: &mut Locked<Unlocked>,
141    current_task: &CurrentTask,
142    options: FileSystemOptions,
143) -> Result<FileSystemHandle, Errno> {
144    TmpFs::new_fs_with_options(locked, &current_task.kernel(), options)
145}
146
147impl TmpFs {
148    pub fn new_fs<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
149    where
150        L: LockEqualOrBefore<FileOpsCore>,
151    {
152        Self::new_fs_with_options(locked, kernel, Default::default())
153            .expect("empty options cannot fail")
154    }
155
156    pub fn new_fs_with_name<L>(
157        locked: &mut Locked<L>,
158        kernel: &Kernel,
159        name: &'static FsStr,
160    ) -> FileSystemHandle
161    where
162        L: LockEqualOrBefore<FileOpsCore>,
163    {
164        Self::new_fs_with_options_and_name(locked, kernel, Default::default(), name)
165            .expect("empty options cannot fail")
166    }
167
168    pub fn new_fs_with_options<L>(
169        locked: &mut Locked<L>,
170        kernel: &Kernel,
171        options: FileSystemOptions,
172    ) -> Result<FileSystemHandle, Errno>
173    where
174        L: LockEqualOrBefore<FileOpsCore>,
175    {
176        Self::new_fs_with_options_and_name(locked, kernel, options, "tmpfs".into())
177    }
178
179    fn new_fs_with_options_and_name<L>(
180        locked: &mut Locked<L>,
181        kernel: &Kernel,
182        options: FileSystemOptions,
183        name: &'static FsStr,
184    ) -> Result<FileSystemHandle, Errno>
185    where
186        L: LockEqualOrBefore<FileOpsCore>,
187    {
188        let fs =
189            FileSystem::new(locked, kernel, CacheMode::Permanent, Arc::new(TmpFs(name)), options)?;
190        let mut mount_options = fs.options.params.clone();
191        let mode = if let Some(mode) = mount_options.remove(b"mode") {
192            FileMode::from_string(mode.as_ref())?
193        } else {
194            mode!(IFDIR, 0o777)
195        };
196        let uid = if let Some(uid) = mount_options.remove(b"uid") {
197            fs_args::parse::<uid_t>(uid.as_ref())?
198        } else {
199            0
200        };
201        let gid = if let Some(gid) = mount_options.remove(b"gid") {
202            fs_args::parse::<gid_t>(gid.as_ref())?
203        } else {
204            0
205        };
206        let root_ino = fs.allocate_ino();
207        let mut info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred { uid, gid });
208        info.chmod(mode);
209        fs.create_root_with_info(root_ino, TmpFsDirectory::new(), info);
210
211        if !mount_options.is_empty() {
212            track_stub!(
213                TODO("https://fxbug.dev/322873419"),
214                "unknown tmpfs options, see logs for strings"
215            );
216            log_warn!("Unknown tmpfs options: {}", mount_options);
217        }
218
219        Ok(fs)
220    }
221
222    pub fn set_initial_content(kernel: &Kernel, fs: &FileSystemHandle, data: TmpFsData) {
223        fn create_dir_entry_from_data(
224            kernel: &Kernel,
225            fs: &FileSystemHandle,
226            data: TmpFsData,
227            this: Option<DirEntryHandle>,
228            name: FsString,
229        ) -> DirEntryHandle {
230            // TODO: https://fxbug.dev/455771186 - Revise FsNode initialization to better ensure
231            // that all the things are appropriately labeled.
232            let new_direntry = |node, parent, name| {
233                let dir_entry = DirEntry::new(node, parent, name);
234                security::fs_node_init_with_dentry_deferred(kernel, &dir_entry);
235                dir_entry
236            };
237
238            match data.node_type {
239                TmpFsNodeType::Link(target) => {
240                    assert!(this.is_none());
241                    let node = TmpFsDirectory::new_symlink(fs, target.as_ref(), data.owner);
242                    new_direntry(node, None, name)
243                }
244                TmpFsNodeType::Directory(children) => {
245                    let this = this.unwrap_or_else(|| {
246                        let info = FsNodeInfo::new(mode!(IFDIR, data.perm), data.owner);
247                        let node = fs.create_node_and_allocate_node_id(TmpFsDirectory::new(), info);
248                        new_direntry(node, None, name.clone())
249                    });
250                    this.node
251                        .downcast_ops::<TmpFsDirectory>()
252                        .expect("directory must be from tmpfs")
253                        .child_count
254                        .fetch_add(children.len() as u32, Ordering::Release);
255                    let children = children
256                        .into_iter()
257                        .map(|(name, data)| {
258                            let child =
259                                create_dir_entry_from_data(kernel, fs, data, None, name.clone());
260                            (name, child)
261                        })
262                        .collect::<BTreeMap<_, _>>();
263                    this.set_children(children);
264                    this
265                }
266            }
267        }
268
269        create_dir_entry_from_data(kernel, fs, data, Some(Arc::clone(fs.root())), "".into());
270    }
271}
272
273pub enum TmpFsNodeType {
274    Link(FsString),
275    Directory(BTreeMap<FsString, TmpFsData>),
276}
277
278pub struct TmpFsData {
279    pub owner: FsCred,
280    pub perm: u32,
281    pub node_type: TmpFsNodeType,
282}
283
284pub struct TmpFsDirectory {
285    xattrs: MemoryXattrStorage,
286    child_count: AtomicU32,
287}
288
289impl TmpFsDirectory {
290    pub fn new() -> Self {
291        Self { xattrs: MemoryXattrStorage::default(), child_count: AtomicU32::new(0) }
292    }
293
294    fn new_symlink(fs: &Arc<FileSystem>, target: &FsStr, owner: FsCred) -> FsNodeHandle {
295        let (link, info) = SymlinkNode::new(target, owner);
296        fs.create_node_and_allocate_node_id(link, info)
297    }
298}
299
300fn create_child_node(
301    parent: &FsNode,
302    mode: FileMode,
303    dev: DeviceId,
304    owner: FsCred,
305) -> Result<FsNodeHandle, Errno> {
306    let ops: Box<dyn FsNodeOps> = match mode.fmt() {
307        FileMode::IFREG => Box::new(MemoryRegularNode::new()?),
308        FileMode::IFIFO | FileMode::IFBLK | FileMode::IFCHR | FileMode::IFSOCK => {
309            Box::new(TmpFsSpecialNode::new())
310        }
311        _ => return error!(EACCES),
312    };
313    let mut info = FsNodeInfo::new(mode, owner);
314    info.rdev = dev;
315    // blksize is PAGE_SIZE for in memory node.
316    info.blksize = *PAGE_SIZE as usize;
317    let child = parent.fs().create_node_and_allocate_node_id(ops, info);
318    if mode.fmt() == FileMode::IFREG {
319        // For files created in tmpfs, forbid sealing, by sealing the seal operation.
320        child.write_guard_state.lock().enable_sealing(SealFlags::SEAL);
321    }
322    Ok(child)
323}
324
325impl FsNodeOps for TmpFsDirectory {
326    fs_node_impl_xattr_delegate!(self, self.xattrs);
327
328    fn create_file_ops(
329        &self,
330        _locked: &mut Locked<FileOpsCore>,
331        _node: &FsNode,
332        _current_task: &CurrentTask,
333        _flags: OpenFlags,
334    ) -> Result<Box<dyn FileOps>, Errno> {
335        Ok(Box::new(MemoryDirectoryFile::new()))
336    }
337
338    fn mkdir(
339        &self,
340        _locked: &mut Locked<FileOpsCore>,
341        node: &FsNode,
342        _current_task: &CurrentTask,
343        _name: &FsStr,
344        mode: FileMode,
345        owner: FsCred,
346    ) -> Result<FsNodeHandle, Errno> {
347        node.update_info(|info| {
348            info.link_count += 1;
349        });
350        self.child_count.fetch_add(1, Ordering::Release);
351        Ok(node
352            .fs()
353            .create_node_and_allocate_node_id(TmpFsDirectory::new(), FsNodeInfo::new(mode, owner)))
354    }
355
356    fn mknod(
357        &self,
358        _locked: &mut Locked<FileOpsCore>,
359        node: &FsNode,
360        _current_task: &CurrentTask,
361        _name: &FsStr,
362        mode: FileMode,
363        dev: DeviceId,
364        owner: FsCred,
365    ) -> Result<FsNodeHandle, Errno> {
366        let child = create_child_node(node, mode, dev, owner)?;
367        self.child_count.fetch_add(1, Ordering::Release);
368        Ok(child)
369    }
370
371    fn create_symlink(
372        &self,
373        _locked: &mut Locked<FileOpsCore>,
374        node: &FsNode,
375        _current_task: &CurrentTask,
376        _name: &FsStr,
377        target: &FsStr,
378        owner: FsCred,
379    ) -> Result<FsNodeHandle, Errno> {
380        self.child_count.fetch_add(1, Ordering::Release);
381        Ok(Self::new_symlink(&node.fs(), target, owner))
382    }
383
384    fn create_tmpfile(
385        &self,
386        node: &FsNode,
387        _current_task: &CurrentTask,
388        mode: FileMode,
389        owner: FsCred,
390    ) -> Result<FsNodeHandle, Errno> {
391        assert!(mode.is_reg());
392        create_child_node(node, mode, DeviceId::NONE, owner)
393    }
394
395    fn link(
396        &self,
397        _locked: &mut Locked<FileOpsCore>,
398        _node: &FsNode,
399        _current_task: &CurrentTask,
400        _name: &FsStr,
401        child: &FsNodeHandle,
402    ) -> Result<(), Errno> {
403        child.update_info(|info| {
404            info.link_count += 1;
405        });
406        self.child_count.fetch_add(1, Ordering::Release);
407        Ok(())
408    }
409
410    fn unlink(
411        &self,
412        _locked: &mut Locked<FileOpsCore>,
413        node: &FsNode,
414        _current_task: &CurrentTask,
415        _name: &FsStr,
416        child_to_unlink: &FsNodeHandle,
417    ) -> Result<(), Errno> {
418        if child_to_unlink.is_dir() {
419            // The following cast is safe, unless something is seriously wrong:
420            // - The filesystem should not be asked to unlink a node that it doesn't handle.
421            // - The child has already been determined to be a directory.
422            // - TmpFsDirectory is the ops for directories in this filesystem.
423            let child_count =
424                &child_to_unlink.downcast_ops::<TmpFsDirectory>().unwrap().child_count;
425            if child_count.load(Ordering::Relaxed) != 0 {
426                return error!(ENOTEMPTY);
427            }
428
429            node.update_info(|info| {
430                info.link_count -= 1;
431            });
432        }
433        child_to_unlink.update_info(|info| {
434            info.link_count -= 1;
435        });
436        self.child_count.fetch_sub(1, Ordering::Release);
437        Ok(())
438    }
439}
440
441struct TmpFsSpecialNode {
442    xattrs: MemoryXattrStorage,
443}
444
445impl TmpFsSpecialNode {
446    pub fn new() -> Self {
447        Self { xattrs: MemoryXattrStorage::default() }
448    }
449}
450
451impl FsNodeOps for TmpFsSpecialNode {
452    fs_node_impl_not_dir!();
453    fs_node_impl_xattr_delegate!(self, self.xattrs);
454
455    fn create_file_ops(
456        &self,
457        _locked: &mut Locked<FileOpsCore>,
458        _node: &FsNode,
459        _current_task: &CurrentTask,
460        _flags: OpenFlags,
461    ) -> Result<Box<dyn FileOps>, Errno> {
462        unreachable!("Special nodes cannot be opened.");
463    }
464}
465
466#[cfg(test)]
467mod test {
468    use super::*;
469    use crate::testing::spawn_kernel_and_run;
470    use crate::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
471    use crate::vfs::fs_args::MountParams;
472    use crate::vfs::{FdNumber, UnlinkKind};
473    use starnix_uapi::errno;
474    use starnix_uapi::file_mode::AccessCheck;
475    use starnix_uapi::mount_flags::FileSystemFlags;
476    use starnix_uapi::vfs::ResolveFlags;
477    use zerocopy::IntoBytes;
478
479    #[::fuchsia::test]
480    async fn test_tmpfs() {
481        spawn_kernel_and_run(async |locked, current_task| {
482            let kernel = current_task.kernel();
483            let fs = TmpFs::new_fs(locked, &kernel);
484            let root = fs.root();
485            let usr = root.create_dir(locked, &current_task, "usr".into()).unwrap();
486            let _etc = root.create_dir(locked, &current_task, "etc".into()).unwrap();
487            let _usr_bin = usr.create_dir(locked, &current_task, "bin".into()).unwrap();
488            let mut names = root.copy_child_names();
489            names.sort();
490            assert!(names.iter().eq(["etc", "usr"].iter()));
491        })
492        .await;
493    }
494
495    #[::fuchsia::test]
496    async fn test_write_read() {
497        spawn_kernel_and_run(async |locked, current_task| {
498            let path = "test.bin";
499            let _file = current_task
500                .fs()
501                .root()
502                .create_node(
503                    locked,
504                    &current_task,
505                    path.into(),
506                    mode!(IFREG, 0o777),
507                    DeviceId::NONE,
508                )
509                .unwrap();
510
511            let wr_file = current_task.open_file(locked, path.into(), OpenFlags::RDWR).unwrap();
512
513            let test_seq = 0..10000u16;
514            let test_vec = test_seq.collect::<Vec<_>>();
515            let test_bytes = test_vec.as_slice().as_bytes();
516
517            let written =
518                wr_file.write(locked, &current_task, &mut VecInputBuffer::new(test_bytes)).unwrap();
519            assert_eq!(written, test_bytes.len());
520
521            let mut read_buffer = VecOutputBuffer::new(test_bytes.len() + 1);
522            let read = wr_file.read_at(locked, &current_task, 0, &mut read_buffer).unwrap();
523            assert_eq!(read, test_bytes.len());
524            assert_eq!(test_bytes, read_buffer.data());
525        })
526        .await;
527    }
528
529    #[::fuchsia::test]
530    async fn test_read_past_eof() {
531        spawn_kernel_and_run(async |locked, current_task| {
532            // Open an empty file
533            let path = "test.bin";
534            let _file = current_task
535                .fs()
536                .root()
537                .create_node(
538                    locked,
539                    &current_task,
540                    path.into(),
541                    mode!(IFREG, 0o777),
542                    DeviceId::NONE,
543                )
544                .unwrap();
545            let rd_file = current_task.open_file(locked, path.into(), OpenFlags::RDONLY).unwrap();
546
547            // Verify that attempting to read past the EOF (i.e. at a non-zero offset) returns 0
548            let buffer_size = 0x10000;
549            let mut output_buffer = VecOutputBuffer::new(buffer_size);
550            let test_offset = 100;
551            let result =
552                rd_file.read_at(locked, &current_task, test_offset, &mut output_buffer).unwrap();
553            assert_eq!(result, 0);
554        })
555        .await;
556    }
557
558    #[::fuchsia::test]
559    async fn test_permissions() {
560        spawn_kernel_and_run(async |locked, current_task| {
561            let path = "test.bin";
562            let file = current_task
563                .open_file_at(
564                    locked,
565                    FdNumber::AT_FDCWD,
566                    path.into(),
567                    OpenFlags::CREAT | OpenFlags::RDONLY,
568                    FileMode::from_bits(0o777),
569                    ResolveFlags::empty(),
570                    AccessCheck::default(),
571                )
572                .expect("failed to create file");
573            assert_eq!(
574                0,
575                file.read(locked, &current_task, &mut VecOutputBuffer::new(0))
576                    .expect("failed to read")
577            );
578
579            assert!(file.write(locked, &current_task, &mut VecInputBuffer::new(&[])).is_err());
580
581            let file = current_task
582                .open_file_at(
583                    locked,
584                    FdNumber::AT_FDCWD,
585                    path.into(),
586                    OpenFlags::WRONLY,
587                    FileMode::EMPTY,
588                    ResolveFlags::empty(),
589                    AccessCheck::default(),
590                )
591                .expect("failed to open file WRONLY");
592
593            assert!(file.read(locked, &current_task, &mut VecOutputBuffer::new(0)).is_err());
594
595            assert_eq!(
596                0,
597                file.write(locked, &current_task, &mut VecInputBuffer::new(&[]))
598                    .expect("failed to write")
599            );
600
601            let file = current_task
602                .open_file_at(
603                    locked,
604                    FdNumber::AT_FDCWD,
605                    path.into(),
606                    OpenFlags::RDWR,
607                    FileMode::EMPTY,
608                    ResolveFlags::empty(),
609                    AccessCheck::default(),
610                )
611                .expect("failed to open file RDWR");
612
613            assert_eq!(
614                0,
615                file.read(locked, &current_task, &mut VecOutputBuffer::new(0))
616                    .expect("failed to read")
617            );
618
619            assert_eq!(
620                0,
621                file.write(locked, &current_task, &mut VecInputBuffer::new(&[]))
622                    .expect("failed to write")
623            );
624        })
625        .await;
626    }
627
628    #[::fuchsia::test]
629    async fn test_persistence() {
630        spawn_kernel_and_run(async |locked, current_task| {
631            {
632                let root = &current_task.fs().root().entry;
633                let usr = root
634                    .create_dir(locked, &current_task, "usr".into())
635                    .expect("failed to create usr");
636                root.create_dir(locked, &current_task, "etc".into())
637                    .expect("failed to create usr/etc");
638                usr.create_dir(locked, &current_task, "bin".into())
639                    .expect("failed to create usr/bin");
640            }
641
642            // At this point, all the nodes are dropped.
643
644            current_task
645                .open_file(locked, "/usr/bin".into(), OpenFlags::RDONLY | OpenFlags::DIRECTORY)
646                .expect("failed to open /usr/bin");
647            assert_eq!(
648                errno!(ENOENT),
649                current_task
650                    .open_file(locked, "/usr/bin/test.txt".into(), OpenFlags::RDWR)
651                    .unwrap_err()
652            );
653            current_task
654                .open_file_at(
655                    locked,
656                    FdNumber::AT_FDCWD,
657                    "/usr/bin/test.txt".into(),
658                    OpenFlags::RDWR | OpenFlags::CREAT,
659                    FileMode::from_bits(0o777),
660                    ResolveFlags::empty(),
661                    AccessCheck::default(),
662                )
663                .expect("failed to create test.txt");
664            let txt = current_task
665                .open_file(locked, "/usr/bin/test.txt".into(), OpenFlags::RDWR)
666                .expect("failed to open test.txt");
667
668            let usr_bin = current_task
669                .open_file(locked, "/usr/bin".into(), OpenFlags::RDONLY)
670                .expect("failed to open /usr/bin");
671            usr_bin
672                .name
673                .unlink(locked, &current_task, "test.txt".into(), UnlinkKind::NonDirectory, false)
674                .expect("failed to unlink test.text");
675            assert_eq!(
676                errno!(ENOENT),
677                current_task
678                    .open_file(locked, "/usr/bin/test.txt".into(), OpenFlags::RDWR)
679                    .unwrap_err()
680            );
681            assert_eq!(
682                errno!(ENOENT),
683                usr_bin
684                    .name
685                    .unlink(
686                        locked,
687                        &current_task,
688                        "test.txt".into(),
689                        UnlinkKind::NonDirectory,
690                        false
691                    )
692                    .unwrap_err()
693            );
694
695            assert_eq!(
696                0,
697                txt.read(locked, &current_task, &mut VecOutputBuffer::new(0))
698                    .expect("failed to read")
699            );
700            std::mem::drop(txt);
701            assert_eq!(
702                errno!(ENOENT),
703                current_task
704                    .open_file(locked, "/usr/bin/test.txt".into(), OpenFlags::RDWR)
705                    .unwrap_err()
706            );
707            std::mem::drop(usr_bin);
708
709            let usr = current_task
710                .open_file(locked, "/usr".into(), OpenFlags::RDONLY)
711                .expect("failed to open /usr");
712            assert_eq!(
713                errno!(ENOENT),
714                current_task.open_file(locked, "/usr/foo".into(), OpenFlags::RDONLY).unwrap_err()
715            );
716            usr.name
717                .unlink(locked, &current_task, "bin".into(), UnlinkKind::Directory, false)
718                .expect("failed to unlink /usr/bin");
719        })
720        .await;
721    }
722
723    #[::fuchsia::test]
724    async fn test_data() {
725        spawn_kernel_and_run(async |locked, current_task| {
726            let kernel = current_task.kernel();
727            let fs = TmpFs::new_fs_with_options(
728                locked,
729                &kernel,
730                FileSystemOptions {
731                    source: Default::default(),
732                    flags: FileSystemFlags::empty().into(),
733                    params: MountParams::parse(b"mode=0123,uid=42,gid=84".into())
734                        .expect("parsed correctly"),
735                },
736            )
737            .expect("new_fs");
738            let info = fs.root().node.info();
739            assert_eq!(info.mode, mode!(IFDIR, 0o123));
740            assert_eq!(info.uid, 42);
741            assert_eq!(info.gid, 84);
742        })
743        .await;
744    }
745}