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 {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}