1use std::mem;
6use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
7
8use crate::mm::MemoryAccessor;
9use crate::task::CurrentTask;
10use crate::vfs::FsStr;
11use starnix_uapi::errors::{ENOSPC, Errno};
12use starnix_uapi::file_mode::FileMode;
13use starnix_uapi::math::round_up_to_increment;
14use starnix_uapi::user_address::UserAddress;
15use starnix_uapi::{error, ino_t, off_t};
16
17#[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
18pub struct DirectoryEntryType(u8);
19
20impl DirectoryEntryType {
22 pub const UNKNOWN: DirectoryEntryType = DirectoryEntryType(0);
23 pub const FIFO: DirectoryEntryType = DirectoryEntryType(1);
24 pub const CHR: DirectoryEntryType = DirectoryEntryType(2);
25 pub const DIR: DirectoryEntryType = DirectoryEntryType(4);
26 pub const BLK: DirectoryEntryType = DirectoryEntryType(6);
27 pub const REG: DirectoryEntryType = DirectoryEntryType(8);
28 pub const LNK: DirectoryEntryType = DirectoryEntryType(10);
29 pub const SOCK: DirectoryEntryType = DirectoryEntryType(12);
30
31 pub fn from_bits(bits: u8) -> DirectoryEntryType {
32 Self(bits)
33 }
34
35 pub fn from_mode(mode: FileMode) -> DirectoryEntryType {
36 match mode.fmt() {
37 FileMode::IFLNK => DirectoryEntryType::LNK,
38 FileMode::IFREG => DirectoryEntryType::REG,
39 FileMode::IFDIR => DirectoryEntryType::DIR,
40 FileMode::IFCHR => DirectoryEntryType::CHR,
41 FileMode::IFBLK => DirectoryEntryType::BLK,
42 FileMode::IFIFO => DirectoryEntryType::FIFO,
43 FileMode::IFSOCK => DirectoryEntryType::SOCK,
44 _ => DirectoryEntryType::UNKNOWN,
45 }
46 }
47
48 pub fn bits(&self) -> u8 {
49 self.0
50 }
51}
52
53const DIRENT64_PADDING_SIZE: usize = 5;
54
55#[repr(C)]
56#[derive(Debug, Default, Copy, Clone, IntoBytes, KnownLayout, FromBytes, Immutable)]
57struct DirentHeader64 {
58 d_ino: u64,
59 d_off: i64,
60 d_reclen: u16,
61 d_type: u8,
62 padding: [u8; DIRENT64_PADDING_SIZE],
63}
64
65const DIRENT64_HEADER_SIZE: usize = mem::size_of::<DirentHeader64>() - DIRENT64_PADDING_SIZE;
66
67pub trait DirentSink {
68 fn add(
75 &mut self,
76 inode_num: ino_t,
77 offset: off_t,
78 entry_type: DirectoryEntryType,
79 name: &FsStr,
80 ) -> Result<(), Errno>;
81
82 fn offset(&self) -> off_t;
84
85 fn user_capacity(&self) -> Option<usize> {
87 None
88 }
89}
90
91struct BaseDirentSink<'a> {
92 current_task: &'a CurrentTask,
93 offset: &'a mut off_t,
94 user_buffer: UserAddress,
95 user_capacity: usize,
96 actual: usize,
97}
98
99impl<'a> BaseDirentSink<'a> {
100 fn add(&mut self, offset: off_t, buffer: &[u8]) -> Result<(), Errno> {
101 if self.user_capacity().is_none_or(|cap| self.actual + buffer.len() > cap) {
102 return error!(ENOSPC);
103 }
104 self.current_task.write_memory((self.user_buffer + self.actual)?, buffer)?;
105 self.actual += buffer.len();
106 *self.offset = offset;
107 Ok(())
108 }
109
110 fn offset(&self) -> off_t {
111 *self.offset
112 }
113
114 fn user_capacity(&self) -> Option<usize> {
117 if self.user_capacity > i32::MAX as usize { None } else { Some(self.user_capacity) }
118 }
119
120 fn map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
130 match result {
131 Ok(()) => Ok(self.actual),
132 Err(_) if self.actual > 0 => Ok(self.actual),
133 Err(errno) if errno == ENOSPC => error!(EINVAL),
134 Err(e) => Err(e),
135 }
136 }
137}
138
139pub struct DirentSink64<'a> {
140 base: BaseDirentSink<'a>,
141}
142
143impl<'a> DirentSink64<'a> {
144 pub fn new(
145 current_task: &'a CurrentTask,
146 offset: &'a mut off_t,
147 user_buffer: UserAddress,
148 user_capacity: usize,
149 ) -> Self {
150 Self {
151 base: BaseDirentSink { current_task, offset, user_buffer, user_capacity, actual: 0 },
152 }
153 }
154
155 pub fn map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
156 self.base.map_result_with_actual(result)
157 }
158}
159
160impl DirentSink for DirentSink64<'_> {
161 fn add(
162 &mut self,
163 inode_num: ino_t,
164 offset: off_t,
165 entry_type: DirectoryEntryType,
166 name: &FsStr,
167 ) -> Result<(), Errno> {
168 let content_size = DIRENT64_HEADER_SIZE + name.len();
169 let entry_size = round_up_to_increment(content_size + 1, 8)?; let mut buffer = Vec::with_capacity(entry_size);
171 let header = DirentHeader64 {
172 d_ino: inode_num,
173 d_off: offset,
174 d_reclen: entry_size as u16,
175 d_type: entry_type.bits(),
176 ..DirentHeader64::default()
177 };
178 let header_bytes = header.as_bytes();
179 buffer.extend_from_slice(&header_bytes[..DIRENT64_HEADER_SIZE]);
180 buffer.extend_from_slice(name);
181 buffer.resize(buffer.len() + (entry_size - content_size), b'\0');
182 assert_eq!(buffer.len(), entry_size);
183 self.base.add(offset, &buffer)
184 }
185
186 fn offset(&self) -> off_t {
187 self.base.offset()
188 }
189
190 fn user_capacity(&self) -> Option<usize> {
191 self.base.user_capacity()
192 }
193}
194
195#[cfg(target_arch = "x86_64")]
196pub use x86_64::*;
197
198#[cfg(target_arch = "x86_64")]
199mod x86_64 {
200 use super::{BaseDirentSink, DirectoryEntryType, DirentSink};
201 use crate::task::CurrentTask;
202 use crate::vfs::FsStr;
203 use starnix_uapi::errors::Errno;
204 use starnix_uapi::math::round_up_to_increment;
205 use starnix_uapi::user_address::UserAddress;
206 use starnix_uapi::{ino_t, off_t};
207 use std::mem;
208 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
209
210 const DIRENT32_PADDING_SIZE: usize = 6;
211 const DIRENT32_HEADER_SIZE: usize = mem::size_of::<DirentHeader32>() - DIRENT32_PADDING_SIZE;
212
213 #[repr(C)]
214 #[derive(Debug, Default, Copy, Clone, IntoBytes, KnownLayout, FromBytes, Immutable)]
215 struct DirentHeader32 {
216 d_ino: u64,
217 d_off: i64,
218 d_reclen: u16,
219 padding: [u8; DIRENT32_PADDING_SIZE],
220 }
223
224 pub struct DirentSink32<'a> {
225 base: BaseDirentSink<'a>,
226 }
227
228 impl<'a> DirentSink32<'a> {
229 pub fn new(
230 current_task: &'a CurrentTask,
231 offset: &'a mut off_t,
232 user_buffer: UserAddress,
233 user_capacity: usize,
234 ) -> Self {
235 Self {
236 base: BaseDirentSink {
237 current_task,
238 offset,
239 user_buffer,
240 user_capacity,
241 actual: 0,
242 },
243 }
244 }
245
246 pub fn map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
247 self.base.map_result_with_actual(result)
248 }
249 }
250
251 impl DirentSink for DirentSink32<'_> {
252 fn add(
253 &mut self,
254 inode_num: ino_t,
255 offset: off_t,
256 entry_type: DirectoryEntryType,
257 name: &FsStr,
258 ) -> Result<(), Errno> {
259 let content_size = DIRENT32_HEADER_SIZE + name.len();
260 let entry_size = round_up_to_increment(content_size + 2, 8)?;
262 let mut buffer = Vec::with_capacity(entry_size);
263 let header = DirentHeader32 {
264 d_ino: inode_num,
265 d_off: offset,
266 d_reclen: entry_size as u16,
267 ..DirentHeader32::default()
268 };
269 let header_bytes = header.as_bytes();
270 buffer.extend_from_slice(&header_bytes[..DIRENT32_HEADER_SIZE]);
271 buffer.extend_from_slice(name);
272 buffer.resize(buffer.len() + (entry_size - content_size - 1), b'\0');
273 buffer.push(entry_type.bits()); assert_eq!(buffer.len(), entry_size);
275 self.base.add(offset, &buffer)
276 }
277
278 fn offset(&self) -> off_t {
279 self.base.offset()
280 }
281
282 fn user_capacity(&self) -> Option<usize> {
283 self.base.user_capacity()
284 }
285 }
286}