smb_server_proto_smb1/
find.rs1use smb_server_proto::types::FileTime;
5
6#[derive(Debug, Clone)]
9pub struct FindEntry {
10 pub name: String,
12 pub meta: crate::query::QueryMeta,
14}
15
16#[cfg(test)]
18const fn fixed_size(level: u16) -> usize {
19 match level {
20 crate::find_level::DIRECTORY => 64,
23 crate::find_level::FULL => 68,
25 crate::find_level::BOTH => 94,
28 crate::find_level::NAMES => 12,
30 crate::find_level::STANDARD => 31,
32 _ => 94,
33 }
34}
35
36fn push_fixed(buf: &mut Vec<u8>, level: u16, m: &crate::query::QueryMeta, name_len: usize) {
38 match level {
39 crate::find_level::STANDARD => {
40 buf.extend_from_slice(&0u32.to_le_bytes());
42 for t in &m.times[..3] {
43 let ft = FileTime(*t);
44 let (secs, _) = ft.to_unix();
45 buf.extend_from_slice(&(secs as u32).to_le_bytes());
46 }
47 buf.extend_from_slice(&(m.eof as u32).to_le_bytes());
48 buf.extend_from_slice(&(m.alloc as u32).to_le_bytes());
49 buf.extend_from_slice(&m.attrs.0.to_le_bytes());
50 buf.push(name_len as u8);
51 }
52 crate::find_level::NAMES => {
53 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&(name_len as u32).to_le_bytes());
55 }
56 crate::find_level::DIRECTORY => {
57 buf.extend_from_slice(&0u32.to_le_bytes()); for t in &m.times {
59 buf.extend_from_slice(&t.to_le_bytes());
60 }
61 buf.extend_from_slice(&m.eof.to_le_bytes());
62 buf.extend_from_slice(&m.alloc.to_le_bytes());
63 buf.extend_from_slice(&m.attrs.0.to_le_bytes());
64 buf.extend_from_slice(&(name_len as u32).to_le_bytes());
65 }
66 _ => {
67 buf.extend_from_slice(&0u32.to_le_bytes()); for t in &m.times {
70 buf.extend_from_slice(&t.to_le_bytes());
71 }
72 buf.extend_from_slice(&m.eof.to_le_bytes());
73 buf.extend_from_slice(&m.alloc.to_le_bytes());
74 buf.extend_from_slice(&m.attrs.0.to_le_bytes());
75 buf.extend_from_slice(&(name_len as u32).to_le_bytes());
76 buf.extend_from_slice(&0u32.to_le_bytes()); if level == crate::find_level::BOTH {
78 buf.push(0); buf.push(0); buf.extend_from_slice(&[0u8; 24]); }
82 }
83 }
84}
85
86pub fn encode_entries(
94 entries: &[FindEntry],
95 level: u16,
96 unicode: bool,
97) -> (Vec<u8>, Option<usize>) {
98 let mut buf: Vec<u8> = Vec::new();
99 let mut last_off = None;
100
101 for (i, e) in entries.iter().enumerate() {
102 let start = buf.len();
103 let m = &e.meta;
104 let name_bytes: Vec<u8> = if unicode {
105 let mut v = Vec::with_capacity(e.name.len() * 2);
106 for u in e.name.encode_utf16() {
107 v.extend_from_slice(&u.to_le_bytes());
108 }
109 v
110 } else {
111 e.name.as_bytes().to_vec()
112 };
113
114 let next_pos = buf.len();
116 buf.extend_from_slice(&0u32.to_le_bytes());
117
118 push_fixed(&mut buf, level, m, name_bytes.len());
119
120 buf.extend_from_slice(&name_bytes);
123 if level == crate::find_level::STANDARD {
124 buf.push(0);
125 }
126
127 let stride = buf.len() - start;
128 if i + 1 < entries.len() {
129 let padded = (stride + 3) & !3usize;
130 while buf.len() < start + padded {
131 buf.push(0);
132 }
133 let next = (buf.len() - start) as u32;
134 buf[next_pos..next_pos + 4].copy_from_slice(&next.to_le_bytes());
135 }
136 last_off = Some(start);
137 }
138 (buf, last_off)
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::query::QueryMeta;
145
146 fn mk(n: &str) -> FindEntry {
147 FindEntry {
148 name: n.into(),
149 meta: QueryMeta {
150 times: [1, 2, 3, 4],
151 attrs: smb_server_proto::types::AttrFlags(0x20),
152 eof: 19,
153 alloc: 4096,
154 is_dir: false,
155 },
156 }
157 }
158
159 fn next_at(buf: &[u8], off: usize) -> u32 {
160 u32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
161 }
162
163 #[test]
164 fn both_level_chain_is_consistent() {
165 let entries = vec![mk("a.txt"), mk("b.txt")];
166 let (buf, _) = encode_entries(&entries, crate::find_level::BOTH, true);
167
168 let mut off = 0usize;
171 let mut count = 0;
172 loop {
173 assert!(off + 4 <= buf.len(), "chain walked past end");
174 let next = next_at(&buf, off);
175 if next == 0 {
176 break;
177 }
178 assert!(next >= 94, "stride smaller than BOTH header");
179 count += 1;
180 off += next as usize;
181 }
182 count += 1; assert_eq!(count, entries.len(), "every entry must be reachable");
184 }
185
186 #[test]
187 fn final_entry_has_zero_next_and_names_decode() {
188 let entries = vec![mk("alpha"), mk("beta"), mk("gamma")];
189 let levels = [
190 crate::find_level::DIRECTORY,
191 crate::find_level::FULL,
192 crate::find_level::BOTH,
193 crate::find_level::NAMES,
194 ];
195 for level in levels {
196 let fixed = fixed_size(level);
197 let (buf, last) =
198 encode_entries(&entries, level, true);
199 let mut off = 0usize;
200 for e in &entries {
201 let next = next_at(&buf, off);
202 let expect_units: Vec<u8> = e.name.encode_utf16().flat_map(|u| u.to_le_bytes()).collect();
203 let namelen = match level {
207 crate::find_level::FULL => u32::from_le_bytes(
208 buf[off + fixed - 8..off + fixed - 4].try_into().unwrap(),
209 ),
210 crate::find_level::BOTH => u32::from_le_bytes(
211 buf[off + fixed - 34..off + fixed - 30].try_into().unwrap(),
212 ),
213 crate::find_level::STANDARD => buf[off + fixed - 1] as u32,
214 _ => {
215 u32::from_le_bytes(buf[off + fixed - 4..off + fixed].try_into().unwrap())
216 }
217 };
218 assert_eq!(namelen as usize, expect_units.len(), "level {level:#x}");
219 assert_eq!(
220 &buf[off + fixed..off + fixed + expect_units.len()],
221 &expect_units[..],
222 "level {level:#x}"
223 );
224 if off == last.unwrap() {
225 assert_eq!(next, 0, "last entry must terminate the chain");
226 } else {
227 assert_eq!(next as usize % 4, 0, "strides must stay aligned");
228 off += next as usize;
229 }
230 }
231 }
232 }
233}