smb_msg/
query_dir.rs

1//! Directory-related messages.
2
3use 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    // If SMB2_INDEX_SPECIFIED is set in Flags, this value MUST be supplied.
23    // Otherwise, it MUST be set to zero and the server MUST ignore it.
24    #[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, // in bytes.
31    pub output_buffer_length: u32,
32    #[br(seek_before = SeekFrom::Start(file_name_offset.value as u64))]
33    // map stream take until eof:
34    #[br(args(file_name_length as u64))]
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(ChainedItemList::<T>::read_le(&mut cursor)?.into())
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use time::macros::datetime;
83
84    use super::*;
85    #[test]
86    pub fn test_both_directory_information_attribute_parse() {
87        let data = [
88            0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x75, 0x91, 0xbf, 0xc8, 0x4b, 0xdb, 0x1,
89            0xe7, 0xb8, 0x48, 0xcd, 0xc8, 0x5d, 0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58,
90            0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
91            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0,
92            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
93            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b,
94            0x80, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0,
95            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x8, 0x38, 0x96, 0xae, 0x4b, 0xdb, 0x1, 0x10, 0x6a,
96            0x87, 0x4b, 0x49, 0x5d, 0xdb, 0x1, 0x62, 0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x62,
97            0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
98            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
99            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
100            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xe7, 0x1, 0x0,
101            0x0, 0x0, 0x4, 0x0, 0x2e, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0,
102            0x0, 0x0, 0x0, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce,
103            0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5f, 0xd9, 0xd5,
104            0xce, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
105            0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
106            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
107            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa4, 0x0, 0x0, 0x0, 0x0,
108            0xa, 0x0, 0x61, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0,
109            0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0xce, 0xec, 0xcf, 0x6a, 0x58,
110            0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a,
111            0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x6, 0x0, 0x0, 0x0, 0x0,
112            0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0,
113            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
114            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
115            0xb9, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x62, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0,
116            0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x57,
117            0x8e, 0x2f, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1,
118            0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58,
119            0xdb, 0x1, 0xe6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
120            0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
121            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
122            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbb, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x63, 0x0,
123            0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
124            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x66, 0x47, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0x3, 0xc,
125            0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3, 0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3,
126            0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x26, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
127            0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0,
128            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
129            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0xf8, 0x0, 0x0,
130            0x0, 0x0, 0x4, 0x0, 0x64, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0,
131        ];
132
133        let res = QueryDirectoryResponse {
134            output_buffer: data.to_vec(),
135        }
136        .read_output::<FileIdBothDirectoryInformation>()
137        .unwrap();
138
139        assert_eq!(
140            vec![
141                FileIdBothDirectoryInformation {
142                    file_index: 0,
143                    creation_time: FileTime::from(datetime!(2024-12-11 12:32:31.7084985)),
144                    last_access_time: FileTime::from(datetime!(2025-01-03 10:18:15.6499175)),
145                    last_write_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
146                    change_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
147                    end_of_file: 0,
148                    allocation_size: 0,
149                    file_attributes: FileAttributes::new().with_directory(true),
150                    ea_size: Some(0),
151                    reparse_tag: None,
152                    short_name_length: 0,
153                    short_name: [0; 12],
154                    fild_id: 562949953454203,
155                    file_name: ".".into(),
156                },
157                FileIdBothDirectoryInformation {
158                    file_index: 0,
159                    creation_time: FileTime::from(datetime!(2024-12-11 9:25:15.4208828)),
160                    last_access_time: FileTime::from(datetime!(2025-01-02 19:05:31.8723088)),
161                    last_write_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
162                    change_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
163                    end_of_file: 0,
164                    allocation_size: 0,
165                    file_attributes: FileAttributes::new().with_directory(true),
166                    ea_size: Some(0),
167                    reparse_tag: None,
168                    short_name_length: 0,
169                    short_name: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,],
170                    fild_id: 1125899906967338,
171                    file_name: "..".into(),
172                },
173                FileIdBothDirectoryInformation {
174                    file_index: 0,
175                    creation_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
176                    last_access_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
177                    last_write_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
178                    change_time: FileTime::from(datetime!(2024-12-27 14:22:49.7460575)),
179                    end_of_file: 0,
180                    allocation_size: 0,
181                    file_attributes: FileAttributes::new().with_archive(true),
182                    ea_size: Some(0),
183                    reparse_tag: None,
184                    short_name_length: 0,
185                    short_name: [0; 12],
186                    fild_id: 2814749767148784,
187                    file_name: "a.txt".into(),
188                },
189                FileIdBothDirectoryInformation {
190                    file_index: 0,
191                    creation_time: FileTime::from(datetime!(2024-12-27 14:22:51.5742424)),
192                    last_access_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
193                    last_write_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
194                    change_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
195                    end_of_file: 6,
196                    allocation_size: 8,
197                    file_attributes: FileAttributes::new().with_archive(true),
198                    ea_size: Some(0),
199                    reparse_tag: None,
200                    short_name_length: 0,
201                    short_name: [0; 12],
202                    fild_id: 1125899906906297,
203                    file_name: "b.txt".into(),
204                },
205                FileIdBothDirectoryInformation {
206                    file_index: 0,
207                    creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.0116823)),
208                    last_access_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
209                    last_write_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
210                    change_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
211                    end_of_file: 486,
212                    allocation_size: 488,
213                    file_attributes: FileAttributes::new().with_archive(true),
214                    ea_size: Some(0),
215                    reparse_tag: None,
216                    short_name_length: 0,
217                    short_name: [0; 12],
218                    fild_id: 1125899906906299,
219                    file_name: "c.txt".into(),
220                },
221                FileIdBothDirectoryInformation {
222                    file_index: 0,
223                    creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.167941),),
224                    last_access_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
225                    last_write_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
226                    change_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
227                    end_of_file: 15910,
228                    allocation_size: 16384,
229                    file_attributes: FileAttributes::new().with_archive(true),
230                    ea_size: Some(0),
231                    reparse_tag: None,
232                    short_name_length: 0,
233                    short_name: [0; 12],
234                    fild_id: 1125899906906300,
235                    file_name: "d.txt".into(),
236                },
237            ],
238            res
239        );
240    }
241}