1use binrw::io::TakeSeekExt;
4use std::io::SeekFrom;
5
6use binrw::prelude::*;
7use modular_bitfield::prelude::*;
8
9use smb_dtyp::binrw_util::prelude::*;
10use smb_fscc::*;
11
12use super::FileId;
13
14#[binrw::binrw]
15#[derive(Debug)]
16pub struct QueryDirectoryRequest {
17 #[bw(calc = 33)]
18 #[br(assert(_structure_size == 33))]
19 _structure_size: u16,
20 pub file_information_class: QueryDirectoryInfoClass,
21 pub flags: QueryDirectoryFlags,
22 #[bw(assert(flags.index_specified() || *file_index == 0))]
25 pub file_index: u32,
26 pub file_id: FileId,
27 #[bw(calc = PosMarker::default())]
28 pub file_name_offset: PosMarker<u16>,
29 #[bw(try_calc = file_name.size().try_into())]
30 file_name_length: u16, pub output_buffer_length: u32,
32 #[br(seek_before = SeekFrom::Start(file_name_offset.value as u64))]
33 #[br(args {size: SizedStringSize::bytes16(file_name_length)})]
35 #[bw(write_with = PosMarker::write_aoff, args(&file_name_offset))]
36 pub file_name: SizedWideString,
37}
38
39#[bitfield]
40#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
41#[bw(map = |&x| Self::into_bytes(x))]
42#[br(map = Self::from_bytes)]
43pub struct QueryDirectoryFlags {
44 pub restart_scans: bool,
45 pub return_single_entry: bool,
46 pub index_specified: bool,
47 pub reopen: bool,
48 #[skip]
49 __: B4,
50}
51
52#[binrw::binrw]
53#[derive(Debug)]
54pub struct QueryDirectoryResponse {
55 #[bw(calc = 9)]
56 #[br(assert(_structure_size == 9))]
57 _structure_size: u16,
58 #[bw(calc = PosMarker::default())]
59 output_buffer_offset: PosMarker<u16>,
60 #[bw(try_calc = output_buffer.len().try_into())]
61 output_buffer_length: u32,
62 #[br(seek_before = SeekFrom::Start(output_buffer_offset.value as u64))]
63 #[br(map_stream = |s| s.take_seek(output_buffer_length as u64), parse_with = binrw::helpers::until_eof)]
64 #[bw(write_with = PosMarker::write_aoff, args(&output_buffer_offset))]
65 pub output_buffer: Vec<u8>,
66}
67
68impl QueryDirectoryResponse {
69 pub fn read_output<T>(&self) -> BinResult<Vec<T>>
70 where
71 T: QueryDirectoryInfoValue + BinRead + BinWrite,
72 for<'a> <T as BinRead>::Args<'a>: Default,
73 for<'b> <T as BinWrite>::Args<'b>: Default,
74 {
75 let mut cursor = std::io::Cursor::new(&self.output_buffer);
76 Ok(
77 ChainedItemList::<T, { QueryDirectoryInfo::CHAINED_ALIGNMENT }>::read_le(&mut cursor)?
78 .into(),
79 )
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use time::macros::datetime;
86
87 use super::*;
88 #[test]
89 pub fn test_both_directory_information_attribute_parse() {
90 let data = [
91 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x75, 0x91, 0xbf, 0xc8, 0x4b, 0xdb, 0x1,
92 0xe7, 0xb8, 0x48, 0xcd, 0xc8, 0x5d, 0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58,
93 0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
94 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0,
95 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
96 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b,
97 0x80, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0,
98 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x8, 0x38, 0x96, 0xae, 0x4b, 0xdb, 0x1, 0x10, 0x6a,
99 0x87, 0x4b, 0x49, 0x5d, 0xdb, 0x1, 0x62, 0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x62,
100 0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
101 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
102 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
103 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xe7, 0x1, 0x0,
104 0x0, 0x0, 0x4, 0x0, 0x2e, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0,
105 0x0, 0x0, 0x0, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce,
106 0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5f, 0xd9, 0xd5,
107 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
108 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
109 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
110 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa4, 0x0, 0x0, 0x0, 0x0,
111 0xa, 0x0, 0x61, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0,
112 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0xce, 0xec, 0xcf, 0x6a, 0x58,
113 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a,
114 0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x6, 0x0, 0x0, 0x0, 0x0,
115 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0,
116 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
117 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
118 0xb9, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x62, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0,
119 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x57,
120 0x8e, 0x2f, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1,
121 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58,
122 0xdb, 0x1, 0xe6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
123 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
124 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
125 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbb, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x63, 0x0,
126 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
127 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x66, 0x47, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0x3, 0xc,
128 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3, 0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3,
129 0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x26, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
130 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0,
131 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
132 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0xf8, 0x0, 0x0,
133 0x0, 0x0, 0x4, 0x0, 0x64, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0,
134 ];
135
136 let res = QueryDirectoryResponse {
137 output_buffer: data.to_vec(),
138 }
139 .read_output::<FileIdBothDirectoryInformation>()
140 .unwrap();
141
142 assert_eq!(
143 vec![
144 FileIdBothDirectoryInformation {
145 file_index: 0,
146 creation_time: FileTime::from(datetime!(2024-12-11 12:32:31.7084985)),
147 last_access_time: FileTime::from(datetime!(2025-01-03 10:18:15.6499175)),
148 last_write_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
149 change_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
150 end_of_file: 0,
151 allocation_size: 0,
152 file_attributes: FileAttributes::new().with_directory(true),
153 ea_size: Some(0),
154 reparse_tag: None,
155 short_name_length: 0,
156 short_name: Default::default(),
157 file_id: 562949953454203,
158 file_name: ".".into(),
159 },
160 FileIdBothDirectoryInformation {
161 file_index: 0,
162 creation_time: FileTime::from(datetime!(2024-12-11 9:25:15.4208828)),
163 last_access_time: FileTime::from(datetime!(2025-01-02 19:05:31.8723088)),
164 last_write_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
165 change_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
166 end_of_file: 0,
167 allocation_size: 0,
168 file_attributes: FileAttributes::new().with_directory(true),
169 ea_size: Some(0),
170 reparse_tag: None,
171 short_name_length: 0,
172 short_name: Default::default(),
173 file_id: 1125899906967338,
174 file_name: "..".into(),
175 },
176 FileIdBothDirectoryInformation {
177 file_index: 0,
178 creation_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
179 last_access_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
180 last_write_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
181 change_time: FileTime::from(datetime!(2024-12-27 14:22:49.7460575)),
182 end_of_file: 0,
183 allocation_size: 0,
184 file_attributes: FileAttributes::new().with_archive(true),
185 ea_size: Some(0),
186 reparse_tag: None,
187 short_name_length: 0,
188 short_name: Default::default(),
189 file_id: 2814749767148784,
190 file_name: "a.txt".into(),
191 },
192 FileIdBothDirectoryInformation {
193 file_index: 0,
194 creation_time: FileTime::from(datetime!(2024-12-27 14:22:51.5742424)),
195 last_access_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
196 last_write_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
197 change_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
198 end_of_file: 6,
199 allocation_size: 8,
200 file_attributes: FileAttributes::new().with_archive(true),
201 ea_size: Some(0),
202 reparse_tag: None,
203 short_name_length: 0,
204 short_name: Default::default(),
205 file_id: 1125899906906297,
206 file_name: "b.txt".into(),
207 },
208 FileIdBothDirectoryInformation {
209 file_index: 0,
210 creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.0116823)),
211 last_access_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
212 last_write_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
213 change_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
214 end_of_file: 486,
215 allocation_size: 488,
216 file_attributes: FileAttributes::new().with_archive(true),
217 ea_size: Some(0),
218 reparse_tag: None,
219 short_name_length: 0,
220 short_name: Default::default(),
221 file_id: 1125899906906299,
222 file_name: "c.txt".into(),
223 },
224 FileIdBothDirectoryInformation {
225 file_index: 0,
226 creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.167941),),
227 last_access_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
228 last_write_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
229 change_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
230 end_of_file: 15910,
231 allocation_size: 16384,
232 file_attributes: FileAttributes::new().with_archive(true),
233 ea_size: Some(0),
234 reparse_tag: None,
235 short_name_length: 0,
236 short_name: Default::default(),
237 file_id: 1125899906906300,
238 file_name: "d.txt".into(),
239 },
240 ],
241 res
242 );
243 }
244}