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.actual + buffer.len() > self.user_capacity {
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 map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
124 match result {
125 Ok(()) => Ok(self.actual),
126 Err(_) if self.actual > 0 => Ok(self.actual),
127 Err(errno) if errno == ENOSPC => error!(EINVAL),
128 Err(e) => Err(e),
129 }
130 }
131}
132
133pub struct DirentSink64<'a> {
134 base: BaseDirentSink<'a>,
135}
136
137impl<'a> DirentSink64<'a> {
138 pub fn new(
139 current_task: &'a CurrentTask,
140 offset: &'a mut off_t,
141 user_buffer: UserAddress,
142 user_capacity: usize,
143 ) -> Self {
144 Self {
145 base: BaseDirentSink { current_task, offset, user_buffer, user_capacity, actual: 0 },
146 }
147 }
148
149 pub fn map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
150 self.base.map_result_with_actual(result)
151 }
152}
153
154impl DirentSink for DirentSink64<'_> {
155 fn add(
156 &mut self,
157 inode_num: ino_t,
158 offset: off_t,
159 entry_type: DirectoryEntryType,
160 name: &FsStr,
161 ) -> Result<(), Errno> {
162 let content_size = DIRENT64_HEADER_SIZE + name.len();
163 let entry_size = round_up_to_increment(content_size + 1, 8)?; let mut buffer = Vec::with_capacity(entry_size);
165 let header = DirentHeader64 {
166 d_ino: inode_num,
167 d_off: offset,
168 d_reclen: entry_size as u16,
169 d_type: entry_type.bits(),
170 ..DirentHeader64::default()
171 };
172 let header_bytes = header.as_bytes();
173 buffer.extend_from_slice(&header_bytes[..DIRENT64_HEADER_SIZE]);
174 buffer.extend_from_slice(name);
175 buffer.resize(buffer.len() + (entry_size - content_size), b'\0');
176 assert_eq!(buffer.len(), entry_size);
177 self.base.add(offset, &buffer)
178 }
179
180 fn offset(&self) -> off_t {
181 self.base.offset()
182 }
183
184 fn user_capacity(&self) -> Option<usize> {
185 Some(self.base.user_capacity)
186 }
187}
188
189#[cfg(target_arch = "x86_64")]
190pub use x86_64::*;
191
192#[cfg(target_arch = "x86_64")]
193mod x86_64 {
194 use super::{BaseDirentSink, DirectoryEntryType, DirentSink};
195 use crate::task::CurrentTask;
196 use crate::vfs::FsStr;
197 use starnix_uapi::errors::Errno;
198 use starnix_uapi::math::round_up_to_increment;
199 use starnix_uapi::user_address::UserAddress;
200 use starnix_uapi::{ino_t, off_t};
201 use std::mem;
202 use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
203
204 const DIRENT32_PADDING_SIZE: usize = 6;
205 const DIRENT32_HEADER_SIZE: usize = mem::size_of::<DirentHeader32>() - DIRENT32_PADDING_SIZE;
206
207 #[repr(C)]
208 #[derive(Debug, Default, Copy, Clone, IntoBytes, KnownLayout, FromBytes, Immutable)]
209 struct DirentHeader32 {
210 d_ino: u64,
211 d_off: i64,
212 d_reclen: u16,
213 padding: [u8; DIRENT32_PADDING_SIZE],
214 }
217
218 pub struct DirentSink32<'a> {
219 base: BaseDirentSink<'a>,
220 }
221
222 impl<'a> DirentSink32<'a> {
223 pub fn new(
224 current_task: &'a CurrentTask,
225 offset: &'a mut off_t,
226 user_buffer: UserAddress,
227 user_capacity: usize,
228 ) -> Self {
229 Self {
230 base: BaseDirentSink {
231 current_task,
232 offset,
233 user_buffer,
234 user_capacity,
235 actual: 0,
236 },
237 }
238 }
239
240 pub fn map_result_with_actual(&self, result: Result<(), Errno>) -> Result<usize, Errno> {
241 self.base.map_result_with_actual(result)
242 }
243 }
244
245 impl DirentSink for DirentSink32<'_> {
246 fn add(
247 &mut self,
248 inode_num: ino_t,
249 offset: off_t,
250 entry_type: DirectoryEntryType,
251 name: &FsStr,
252 ) -> Result<(), Errno> {
253 let content_size = DIRENT32_HEADER_SIZE + name.len();
254 let entry_size = round_up_to_increment(content_size + 2, 8)?;
256 let mut buffer = Vec::with_capacity(entry_size);
257 let header = DirentHeader32 {
258 d_ino: inode_num,
259 d_off: offset,
260 d_reclen: entry_size as u16,
261 ..DirentHeader32::default()
262 };
263 let header_bytes = header.as_bytes();
264 buffer.extend_from_slice(&header_bytes[..DIRENT32_HEADER_SIZE]);
265 buffer.extend_from_slice(name);
266 buffer.resize(buffer.len() + (entry_size - content_size - 1), b'\0');
267 buffer.push(entry_type.bits()); assert_eq!(buffer.len(), entry_size);
269 self.base.add(offset, &buffer)
270 }
271
272 fn offset(&self) -> off_t {
273 self.base.offset()
274 }
275
276 fn user_capacity(&self) -> Option<usize> {
277 Some(self.base.user_capacity)
278 }
279 }
280}