1use core::{ffi::CStr, slice};
2
3#[derive(Debug, Eq, PartialEq, Clone)]
5pub struct DirEntry<'a> {
6 pub ino: linux_unsafe::ino64_t,
7 pub off: linux_unsafe::off64_t,
8 pub entry_type: DirEntryType,
9 pub name: &'a CStr,
10}
11
12#[derive(Debug, Eq, PartialEq, Clone, Copy)]
14#[repr(u8)]
15pub enum DirEntryType {
16 Unknown = 0,
17 Fifo = 1,
18 Chr = 2,
19 Dir = 4,
20 Blk = 6,
21 Reg = 8,
22 Lnk = 10,
23 Sock = 12,
24 Wht = 14,
25}
26
27impl From<linux_unsafe::uchar> for DirEntryType {
28 fn from(value: linux_unsafe::uchar) -> Self {
29 match value {
30 linux_unsafe::DT_FIFO => Self::Fifo,
31 linux_unsafe::DT_CHR => Self::Chr,
32 linux_unsafe::DT_DIR => Self::Dir,
33 linux_unsafe::DT_BLK => Self::Blk,
34 linux_unsafe::DT_REG => Self::Reg,
35 linux_unsafe::DT_LNK => Self::Lnk,
36 linux_unsafe::DT_SOCK => Self::Sock,
37 linux_unsafe::DT_WHT => Self::Wht,
38 _ => Self::Unknown,
39 }
40 }
41}
42
43pub struct DirEntries<'a> {
46 remain: &'a [u8],
47}
48
49impl<'a> DirEntries<'a> {
50 pub fn from_getdents64_buffer(buf: &'a [u8]) -> Self {
51 Self { remain: buf }
52 }
53
54 pub fn to_remaining_bytes(self) -> &'a [u8] {
60 self.remain
61 }
62}
63
64impl<'a> Iterator for DirEntries<'a> {
65 type Item = DirEntry<'a>;
66
67 fn next(&mut self) -> Option<Self::Item> {
68 let (ret, remain) = dir_entry_from_buf(self.remain);
69 self.remain = remain;
70 ret
71 }
72}
73
74fn dir_entry_from_buf<'a>(buf: &'a [u8]) -> (Option<DirEntry<'a>>, &'a [u8]) {
75 #[derive(Debug)]
76 #[repr(C)]
77 struct DirEntryHeader {
78 d_ino: linux_unsafe::ino64_t,
80 d_off: linux_unsafe::off64_t,
81 d_reclen: linux_unsafe::ushort,
82 d_type: linux_unsafe::uchar,
83 d_name: (),
84 }
85 const HEADER_SIZE: usize = core::mem::size_of::<DirEntryHeader>();
90 const NAME_OFFSET: usize = core::mem::offset_of!(DirEntryHeader, d_name);
91
92 if buf.len() < HEADER_SIZE {
93 return (None, buf);
95 }
96
97 let (raw_len, ino, off, entry_type) = {
98 let hdr_ptr = buf.as_ptr() as *const DirEntryHeader;
99 let hdr = unsafe { &*hdr_ptr };
100 let claimed_len = hdr.d_reclen as usize;
101 if buf.len() < claimed_len || claimed_len < HEADER_SIZE {
102 return (None, buf);
105 }
106 (claimed_len, hdr.d_ino, hdr.d_off, hdr.d_type)
107 };
108
109 let name_len = raw_len - NAME_OFFSET;
110 let name_start = unsafe { buf.as_ptr().add(NAME_OFFSET) } as *const u8;
111 let name = unsafe { slice::from_raw_parts::<'a, _>(name_start, name_len) };
112 let name = CStr::from_bytes_until_nul(name).unwrap();
113
114 let remain = &buf[raw_len..];
115 let ret = DirEntry {
116 ino,
117 off,
118 entry_type: entry_type.into(),
119 name,
120 };
121 (Some(ret), remain)
122}
123
124pub struct AllDirEntries<'file, 'buf, TF, R, D>
125where
126 TF: FnMut(DirEntry<'buf>) -> R,
127{
128 f: Option<&'file crate::File<D>>,
129 buf: &'buf mut [u8],
130 rng: Range,
131 transform: TF,
132}
133
134impl<'file, 'buf, TF, R, D> AllDirEntries<'file, 'buf, TF, R, D>
135where
136 TF: for<'tmp> FnMut(DirEntry<'tmp>) -> R,
137{
138 pub(crate) fn new(f: &'file crate::File<D>, buf: &'buf mut [u8], transform: TF) -> Self {
139 Self {
140 f: Some(f),
141 buf,
142 rng: Range::new(0, 0),
143 transform,
144 }
145 }
146}
147
148fn try_read_entry<'a>(buf: &'a [u8]) -> (Option<DirEntry<'a>>, usize) {
149 if buf.len() == 0 {
150 return (None, 0);
151 }
152 let (ret, remain) = dir_entry_from_buf(buf);
153 if let Some(entry) = ret {
154 let advance = remain.as_ptr() as usize - buf.as_ptr() as usize;
155 (Some(entry), advance)
156 } else {
157 (None, buf.len())
158 }
159}
160
161impl<'file, 'buf, TF, R, D> Iterator for AllDirEntries<'file, 'buf, TF, R, D>
162where
163 TF: for<'tmp> FnMut(DirEntry<'tmp>) -> R,
164{
165 type Item = Result<R, crate::result::Error>;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 if let Some(f) = self.f {
169 {
170 let buf = &self.buf[self.rng.start..self.rng.end];
171 let (maybe_entry, advance) = try_read_entry(buf);
172 self.rng.start += advance;
173 if let Some(entry) = maybe_entry {
174 return Some(Ok((self.transform)(entry)));
175 }
176 }
177 let result = unsafe {
178 f.getdents_raw(
179 self.buf.as_mut_ptr() as *mut _,
180 self.buf.len() as linux_unsafe::int,
181 )
182 };
183 match result {
184 Ok(result_len) => {
185 self.rng = Range::new(0, result_len);
186 let buf = &self.buf[self.rng.start..self.rng.end];
187 let (maybe_entry, advance) = try_read_entry(buf);
188 self.rng.start += advance;
189 maybe_entry.map(|entry| Ok((self.transform)(entry)))
190 }
191 Err(e) => {
192 self.f = None; Some(Err(e))
194 }
195 }
196 } else {
197 None
198 }
199 }
200}
201
202#[derive(Debug, Clone, Copy)]
203struct Range {
204 start: usize,
205 end: usize,
206}
207
208impl Range {
209 #[inline(always)]
210 fn new(start: usize, end: usize) -> Self {
211 Self { start, end }
212 }
213}