Skip to main content

starnix_core/fs/
devtmpfs.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::device::DeviceMode;
6use crate::device::kobject::DeviceMetadata;
7use crate::fs::tmpfs::{TmpFs, TmpFsData, TmpFsNodeType};
8use crate::security;
9use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
10use crate::task::{CurrentTask, Kernel};
11use crate::vfs::{
12    DirEntryHandle, FileSystemHandle, FileSystemOptions, FsStr, LookupContext, MountInfo,
13    NamespaceNode, path,
14};
15use starnix_uapi::auth::FsCred;
16use starnix_uapi::device_id::DeviceId;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::file_mode::mode;
19use std::collections::BTreeMap;
20
21pub fn dev_tmp_fs(
22    current_task: &CurrentTask,
23    _options: FileSystemOptions,
24) -> Result<FileSystemHandle, Errno> {
25    Ok(DevTmpFs::from_kernel(current_task.kernel()))
26}
27
28pub struct DevTmpFs(());
29
30impl DevTmpFs {
31    pub fn from_kernel(kernel: &Kernel) -> FileSystemHandle {
32        struct DevTmpFsHandle(FileSystemHandle);
33
34        kernel.expando.get_or_init(|| DevTmpFsHandle(Self::init(kernel))).0.clone()
35    }
36
37    fn init(kernel: &Kernel) -> FileSystemHandle {
38        let fs = TmpFs::new_fs_with_name(kernel, "devtmpfs".into());
39
40        let initial_content = TmpFsData {
41            owner: FsCred::root(),
42            perm: 0o755,
43            node_type: TmpFsNodeType::Directory(BTreeMap::from([
44                (
45                    "shm".into(),
46                    TmpFsData {
47                        owner: FsCred::root(),
48                        perm: 0o755,
49                        node_type: TmpFsNodeType::Directory(Default::default()),
50                    },
51                ),
52                (
53                    "pts".into(),
54                    TmpFsData {
55                        owner: FsCred::root(),
56                        perm: 0o755,
57                        node_type: TmpFsNodeType::Directory(Default::default()),
58                    },
59                ),
60                (
61                    "fd".into(),
62                    TmpFsData {
63                        owner: FsCred::root(),
64                        perm: 0o777,
65                        node_type: TmpFsNodeType::Link("/proc/self/fd".into()),
66                    },
67                ),
68            ])),
69        };
70        TmpFs::set_initial_content(kernel, &fs, initial_content);
71
72        fs
73    }
74}
75
76/// Creates a device node in the devtmpfs filesystem.
77///
78/// This function executes the creation in a background thread and blocks the
79/// calling thread until the device node is fully created. This blocking behavior
80/// matches Linux.
81pub fn devtmpfs_create_device(
82    kernel: &Kernel,
83    device_metadata: DeviceMetadata,
84) -> Result<(), Errno> {
85    let closure = move |current_task: &CurrentTask| {
86        current_task.override_creds(security::creds_start_internal_operation(current_task), || {
87            devtmpfs_create_device_internal(current_task, &device_metadata)
88        })
89    };
90    let (result_fn, req) = SpawnRequestBuilder::new()
91        .with_debug_name("devtmpfs-create-device")
92        .with_sync_closure(closure)
93        .build_with_sync_result();
94    kernel.kthreads.spawner().spawn_from_request(req);
95    result_fn()?
96}
97
98fn devtmpfs_create_device_internal(
99    current_task: &CurrentTask,
100    device_metadata: &DeviceMetadata,
101) -> Result<(), Errno> {
102    let separator_pos = device_metadata.devname.iter().rposition(|&c| c == path::SEPARATOR);
103    let (device_path, device_name) = match separator_pos {
104        Some(pos) => device_metadata.devname.split_at(pos + 1),
105        None => (&[] as &[u8], device_metadata.devname.as_slice()),
106    };
107    let parent_dir = device_path
108        .split(|&c| c == path::SEPARATOR)
109        // Avoid EEXIST for 'foo//bar' and the last directory name.
110        .filter(|dir_name| dir_name.len() > 0)
111        .try_fold(
112            DevTmpFs::from_kernel(current_task.kernel()).root().clone(),
113            |parent_dir, dir_name| {
114                devtmpfs_get_or_create_directory_at(current_task, parent_dir, dir_name.into())
115            },
116        )?;
117    devtmpfs_create_device_node(
118        current_task,
119        parent_dir,
120        device_name.into(),
121        device_metadata.mode,
122        device_metadata.devt,
123    )
124}
125
126pub fn devtmpfs_get_or_create_directory_at(
127    current_task: &CurrentTask,
128    parent_dir: DirEntryHandle,
129    dir_name: &FsStr,
130) -> Result<DirEntryHandle, Errno> {
131    parent_dir.get_or_create_entry(
132        current_task,
133        &MountInfo::detached(),
134        dir_name,
135        |dir, mount, name| {
136            dir.create_node(
137                current_task,
138                mount,
139                name,
140                mode!(IFDIR, 0o755),
141                DeviceId::NONE,
142                FsCred::root(),
143            )
144        },
145    )
146}
147
148fn devtmpfs_create_device_node(
149    current_task: &CurrentTask,
150    parent_dir: DirEntryHandle,
151    device_name: &FsStr,
152    device_mode: DeviceMode,
153    devt: DeviceId,
154) -> Result<(), Errno> {
155    let mode = match device_mode {
156        DeviceMode::Char => mode!(IFCHR, 0o666),
157        DeviceMode::Block => mode!(IFBLK, 0o666),
158    };
159    // This creates content inside the temporary FS. This doesn't depend on the mount
160    // information.
161    parent_dir.create_entry(
162        current_task,
163        &MountInfo::detached(),
164        device_name,
165        |dir, mount, name| dir.create_node(current_task, mount, name, mode, devt, FsCred::root()),
166    )?;
167    Ok(())
168}
169
170pub fn devtmpfs_remove_path(current_task: &CurrentTask, path: &FsStr) -> Result<(), Errno> {
171    let root_node =
172        NamespaceNode::new_anonymous(DevTmpFs::from_kernel(current_task.kernel()).root().clone());
173    let mut context = LookupContext::default();
174    let (parent_node, device_name) = current_task.lookup_parent(&mut context, &root_node, path)?;
175    parent_node.entry.remove_child(device_name.into(), &current_task.kernel().mounts);
176    Ok(())
177}