1use 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 .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 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(), ¤t_task.kernel().mounts);
213 Ok(())
214}