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_logging::log_warn;
16use starnix_sync::{FileOpsCore, InterruptibleEvent, LockEqualOrBefore, Locked, Unlocked};
17use starnix_uapi::auth::FsCred;
18use starnix_uapi::device_type::DeviceType;
19use starnix_uapi::errors::Errno;
20use starnix_uapi::file_mode::mode;
21use std::collections::BTreeMap;
22use std::sync::Arc;
23
24pub fn dev_tmp_fs(
25    locked: &mut Locked<Unlocked>,
26    current_task: &CurrentTask,
27    _options: FileSystemOptions,
28) -> Result<FileSystemHandle, Errno> {
29    Ok(DevTmpFs::from_kernel(locked, current_task.kernel()))
30}
31
32pub struct DevTmpFs(());
33
34impl DevTmpFs {
35    pub fn from_kernel<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
36    where
37        L: LockEqualOrBefore<FileOpsCore>,
38    {
39        struct DevTmpFsHandle(FileSystemHandle);
40
41        kernel.expando.get_or_init(|| DevTmpFsHandle(Self::init(locked, kernel))).0.clone()
42    }
43
44    fn init<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
45    where
46        L: LockEqualOrBefore<FileOpsCore>,
47    {
48        let fs = TmpFs::new_fs_with_name(locked, kernel, "devtmpfs".into());
49
50        let initial_content = TmpFsData {
51            owner: FsCred::root(),
52            perm: 0o755,
53            node_type: TmpFsNodeType::Directory(BTreeMap::from([
54                (
55                    "shm".into(),
56                    TmpFsData {
57                        owner: FsCred::root(),
58                        perm: 0o755,
59                        node_type: TmpFsNodeType::Directory(Default::default()),
60                    },
61                ),
62                (
63                    "pts".into(),
64                    TmpFsData {
65                        owner: FsCred::root(),
66                        perm: 0o755,
67                        node_type: TmpFsNodeType::Directory(Default::default()),
68                    },
69                ),
70                (
71                    "fd".into(),
72                    TmpFsData {
73                        owner: FsCred::root(),
74                        perm: 0o777,
75                        node_type: TmpFsNodeType::Link("/proc/self/fd".into()),
76                    },
77                ),
78            ])),
79        };
80        TmpFs::set_initial_content(kernel, &fs, initial_content);
81
82        fs
83    }
84}
85
86pub fn devtmpfs_create_device(
87    kernel: &Kernel,
88    device_metadata: DeviceMetadata,
89    event: Option<Arc<InterruptibleEvent>>,
90) {
91    let closure = move |locked: &mut Locked<Unlocked>, current_task: &CurrentTask| {
92        current_task.override_creds(security::creds_start_internal_operation, || {
93            if let Err(e) = devtmpfs_create_device_internal(locked, current_task, &device_metadata)
94            {
95                log_warn!("Cannot add device {device_metadata:?} in devtmpfs ({e:?})");
96            }
97            if let Some(event) = event {
98                event.notify();
99            }
100        });
101    };
102    let req = SpawnRequestBuilder::new()
103        .with_debug_name("devtmpfs-create-device")
104        .with_sync_closure(closure)
105        .build();
106    kernel.kthreads.spawner().spawn_from_request(req);
107}
108
109fn devtmpfs_create_device_internal(
110    locked: &mut Locked<Unlocked>,
111    current_task: &CurrentTask,
112    device_metadata: &DeviceMetadata,
113) -> Result<(), Errno> {
114    let separator_pos = device_metadata.devname.iter().rposition(|&c| c == path::SEPARATOR);
115    let (device_path, device_name) = match separator_pos {
116        Some(pos) => device_metadata.devname.split_at(pos + 1),
117        None => (&[] as &[u8], device_metadata.devname.as_slice()),
118    };
119    let parent_dir = device_path
120        .split(|&c| c == path::SEPARATOR)
121        // Avoid EEXIST for 'foo//bar' and the last directory name.
122        .filter(|dir_name| dir_name.len() > 0)
123        .try_fold(
124            DevTmpFs::from_kernel(locked, current_task.kernel()).root().clone(),
125            |parent_dir, dir_name| {
126                devtmpfs_get_or_create_directory_at(
127                    locked,
128                    current_task,
129                    parent_dir,
130                    dir_name.into(),
131                )
132            },
133        )?;
134    devtmpfs_create_device_node(
135        locked,
136        current_task,
137        parent_dir,
138        device_name.into(),
139        device_metadata.mode,
140        device_metadata.device_type,
141    )
142}
143
144pub fn devtmpfs_get_or_create_directory_at<L>(
145    locked: &mut Locked<L>,
146    current_task: &CurrentTask,
147    parent_dir: DirEntryHandle,
148    dir_name: &FsStr,
149) -> Result<DirEntryHandle, Errno>
150where
151    L: LockEqualOrBefore<FileOpsCore>,
152{
153    parent_dir.get_or_create_entry(
154        locked,
155        current_task,
156        &MountInfo::detached(),
157        dir_name,
158        |locked, dir, mount, name| {
159            dir.create_node(
160                locked,
161                current_task,
162                mount,
163                name,
164                mode!(IFDIR, 0o755),
165                DeviceType::NONE,
166                FsCred::root(),
167            )
168        },
169    )
170}
171
172fn devtmpfs_create_device_node(
173    locked: &mut Locked<Unlocked>,
174    current_task: &CurrentTask,
175    parent_dir: DirEntryHandle,
176    device_name: &FsStr,
177    device_mode: DeviceMode,
178    device_type: DeviceType,
179) -> Result<(), Errno> {
180    let mode = match device_mode {
181        DeviceMode::Char => mode!(IFCHR, 0o666),
182        DeviceMode::Block => mode!(IFBLK, 0o666),
183    };
184    // This creates content inside the temporary FS. This doesn't depend on the mount
185    // information.
186    parent_dir.create_entry(
187        locked,
188        current_task,
189        &MountInfo::detached(),
190        device_name,
191        |locked, dir, mount, name| {
192            dir.create_node(locked, current_task, mount, name, mode, device_type, FsCred::root())
193        },
194    )?;
195    Ok(())
196}
197
198pub fn devtmpfs_remove_path<L>(
199    locked: &mut Locked<L>,
200    current_task: &CurrentTask,
201    path: &FsStr,
202) -> Result<(), Errno>
203where
204    L: LockEqualOrBefore<FileOpsCore>,
205{
206    let root_node = NamespaceNode::new_anonymous(
207        DevTmpFs::from_kernel(locked, current_task.kernel()).root().clone(),
208    );
209    let mut context = LookupContext::default();
210    let (parent_node, device_name) =
211        current_task.lookup_parent(locked, &mut context, &root_node, path)?;
212    parent_node.entry.remove_child(device_name.into(), &current_task.kernel().mounts);
213    Ok(())
214}