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_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
76pub 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 .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 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(), ¤t_task.kernel().mounts);
176 Ok(())
177}