1use crate::device::DeviceMode;
6use crate::device::kobject::DeviceMetadata;
7use crate::fs::tmpfs::TmpFs;
8use crate::security;
9use crate::task::dynamic_thread_spawner::SpawnRequestBuilder;
10use crate::task::{CurrentTask, Kernel};
11use crate::vfs::{
12 DirEntryHandle, FileSystemHandle, FileSystemOptions, FsNodeHandle, FsStr, LookupContext,
13 MountInfo, 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::sync::OnceLock;
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
30#[derive(Default)]
31struct DevTmpFsHandle(OnceLock<FileSystemHandle>);
32
33impl DevTmpFs {
34 pub fn from_kernel(kernel: &Kernel) -> FileSystemHandle {
35 assert!(kernel.features.devtmpfs, "devtmpfs must be enabled in KernelFeatures");
36 kernel.expando.get::<DevTmpFsHandle>().0.get_or_init(|| Self::init(kernel)).clone()
37 }
38
39 fn from_kthread(current_task: &CurrentTask) -> FileSystemHandle {
40 let kernel = current_task.kernel();
41 assert!(kernel.features.devtmpfs, "devtmpfs must be enabled in KernelFeatures");
42 kernel
43 .expando
44 .get::<DevTmpFsHandle>()
45 .0
46 .get_or_init(|| Self::init_on_kthread(current_task))
47 .clone()
48 }
49
50 fn init(kernel: &Kernel) -> FileSystemHandle {
51 let (result_fn, req) = SpawnRequestBuilder::new()
52 .with_debug_name("devtmpfs-init")
53 .with_sync_closure(Self::init_on_kthread)
54 .build_with_sync_result();
55 kernel.kthreads.spawner().spawn_from_request(req);
56 result_fn().unwrap()
57 }
58
59 fn init_on_kthread(current_task: &CurrentTask) -> FileSystemHandle {
60 let kernel = current_task.kernel();
61 let fs = TmpFs::new_fs_with_name(kernel, "devtmpfs".into());
62 fs.root().node.update_info(|info| info.chmod(mode!(IFDIR, 0o755)));
63 security::file_system_resolve_security(current_task, &fs)
64 .expect("resolve devtmpfs security");
65 let root = fs.root();
66 devtmpfs_get_or_create_directory_at(current_task, root, "shm".into()).unwrap();
67 devtmpfs_get_or_create_directory_at(current_task, root, "pts".into()).unwrap();
68 let create_symlink = |dir: &FsNodeHandle, mount: &MountInfo, name: &FsStr| {
69 let target = "/proc/self/fd".into();
70 dir.create_symlink(current_task, mount, name, target, FsCred::root())
71 };
72 let mount = MountInfo::detached();
73 root.create_entry(current_task, &mount, "fd".into(), create_symlink).unwrap();
74 fs
75 }
76}
77
78pub fn devtmpfs_create_device(
84 kernel: &Kernel,
85 device_metadata: DeviceMetadata,
86) -> Result<(), Errno> {
87 let closure = move |current_task: &CurrentTask| {
88 devtmpfs_create_device_internal(current_task, &device_metadata)
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 .filter(|dir_name| dir_name.len() > 0)
111 .try_fold(DevTmpFs::from_kthread(current_task).root().clone(), |parent_dir, dir_name| {
112 devtmpfs_get_or_create_directory_at(current_task, &parent_dir, dir_name.into())
113 })?;
114 devtmpfs_create_device_node(
115 current_task,
116 parent_dir,
117 device_name.into(),
118 device_metadata.mode,
119 device_metadata.devt,
120 )
121}
122
123fn devtmpfs_get_or_create_directory_at(
124 current_task: &CurrentTask,
125 parent_dir: &DirEntryHandle,
126 dir_name: &FsStr,
127) -> Result<DirEntryHandle, Errno> {
128 let create_dir = |dir: &FsNodeHandle, mount: &MountInfo, name: &FsStr| {
129 let mode = mode!(IFDIR, 0o755);
130 dir.create_node(current_task, mount, name, mode, DeviceId::NONE, FsCred::root())
131 };
132 let mount = MountInfo::detached();
133 let (entry, _status) =
134 parent_dir.get_or_create_entry(current_task, &mount, dir_name, create_dir)?;
135 Ok(entry)
136}
137
138fn devtmpfs_create_device_node(
139 current_task: &CurrentTask,
140 parent_dir: DirEntryHandle,
141 device_name: &FsStr,
142 device_mode: DeviceMode,
143 devt: DeviceId,
144) -> Result<(), Errno> {
145 let mode = match device_mode {
146 DeviceMode::Char => mode!(IFCHR, 0o666),
147 DeviceMode::Block => mode!(IFBLK, 0o666),
148 };
149 parent_dir.create_entry(
152 current_task,
153 &MountInfo::detached(),
154 device_name,
155 |dir, mount, name| dir.create_node(current_task, mount, name, mode, devt, FsCred::root()),
156 )?;
157 Ok(())
158}
159
160pub fn devtmpfs_remove_path(current_task: &CurrentTask, path: &FsStr) -> Result<(), Errno> {
161 let root_node =
162 NamespaceNode::new_anonymous(DevTmpFs::from_kernel(current_task.kernel()).root().clone());
163 let mut context = LookupContext::default();
164 let (parent_node, device_name) = current_task.lookup_parent(&mut context, &root_node, path)?;
165 parent_node.entry.remove_child(device_name.into(), ¤t_task.kernel().mounts);
166 Ok(())
167}