smb_msg/
file.rs

1//! File-related messages: Flush, Read, Write.
2
3use std::io::SeekFrom;
4
5use binrw::prelude::*;
6use modular_bitfield::prelude::*;
7
8use super::FileId;
9use super::header::Header;
10use smb_dtyp::binrw_util::prelude::*;
11
12#[binrw::binrw]
13#[derive(Debug)]
14pub struct FlushRequest {
15    #[bw(calc = 24)]
16    #[br(assert(_structure_size == 24))]
17    _structure_size: u16,
18    #[bw(calc = 0)]
19    #[br(assert(_reserved1 == 0))]
20    _reserved1: u16,
21    #[bw(calc = 0)]
22    #[br(assert(_reserved2 == 0))]
23    _reserved2: u32,
24    pub file_id: FileId,
25}
26
27#[binrw::binrw]
28#[derive(Debug, PartialEq, Eq)]
29pub struct FlushResponse {
30    #[bw(calc = 4)]
31    #[br(assert(_structure_size == 4))]
32    _structure_size: u16,
33    #[bw(calc = 0)]
34    #[br(assert(_reserved == 0))]
35    _reserved: u16,
36}
37
38#[binrw::binrw]
39#[derive(Debug)]
40pub struct ReadRequest {
41    #[bw(calc = 49)]
42    #[br(assert(_structure_size == 49))]
43    _structure_size: u16,
44    #[bw(calc = 0)]
45    _padding: u8,
46    pub flags: ReadFlags,
47    pub length: u32,
48    pub offset: u64,
49    pub file_id: FileId,
50    pub minimum_count: u32,
51    // Currently, we do not have support for RDMA.
52    // Therefore, all the related fields are set to zero.
53    #[bw(calc = CommunicationChannel::None)]
54    #[br(assert(channel == CommunicationChannel::None))]
55    channel: CommunicationChannel,
56    #[bw(calc = 0)]
57    #[br(assert(_remaining_bytes == 0))]
58    _remaining_bytes: u32,
59    #[bw(calc = 0)]
60    #[br(assert(_read_channel_info_offset == 0))]
61    _read_channel_info_offset: u16,
62    #[bw(calc = 0)]
63    #[br(assert(_read_channel_info_length == 0))]
64    _read_channel_info_length: u16,
65
66    // Well, that's a little awkward, but since we never provide a blob, and yet,
67    // Msft decided it makes sense to make the structure size 0x31, we need to add this padding.
68    #[bw(calc = 0)]
69    _pad_blob_placeholder: u8,
70}
71
72#[binrw::binrw]
73#[derive(Debug, PartialEq, Eq)]
74pub struct ReadResponse {
75    #[bw(calc = Self::STRUCT_SIZE as u16)]
76    #[br(assert(_structure_size == Self::STRUCT_SIZE as u16))]
77    _structure_size: u16,
78    // Sanity check: The offset is from the SMB header beginning.
79    // it should be greater than the sum of the header and the response.
80    // the STRUCT_SIZE includes the first byte of the buffer, so the offset is validated against a byte before that.
81    #[br(assert(_data_offset.value as usize >= Header::STRUCT_SIZE + Self::STRUCT_SIZE - 1))]
82    #[bw(calc = PosMarker::default())]
83    _data_offset: PosMarker<u8>,
84    #[bw(calc = 0)]
85    #[br(assert(_reserved == 0))]
86    _reserved: u8,
87    #[bw(try_calc = buffer.len().try_into())]
88    #[br(assert(_data_length > 0))] // sanity
89    _data_length: u32,
90    #[bw(calc = 0)]
91    #[br(assert(_data_remaining == 0))]
92    _data_remaining: u32,
93
94    // No RDMA support -- always zero, for both reserved and flags case:
95    #[bw(calc = 0)]
96    #[br(assert(_reserved2 == 0))]
97    _reserved2: u32,
98
99    #[br(seek_before = SeekFrom::Start(_data_offset.value as u64))]
100    #[br(count = _data_length)]
101    #[bw(assert(!buffer.is_empty()))] // sanity _data_length > 0 on write.
102    #[bw(write_with = PosMarker::write_aoff, args(&_data_offset))]
103    pub buffer: Vec<u8>,
104}
105
106impl ReadResponse {
107    const STRUCT_SIZE: usize = 17;
108}
109
110#[bitfield]
111#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy)]
112#[bw(map = |&x| Self::into_bytes(x))]
113#[br(map = Self::from_bytes)]
114pub struct ReadFlags {
115    pub read_unbuffered: bool,
116    pub read_compressed: bool,
117    #[skip]
118    __: B6,
119}
120
121#[binrw::binrw]
122#[derive(Debug, PartialEq, Eq)]
123#[brw(repr(u32))]
124pub enum CommunicationChannel {
125    None = 0,
126    RdmaV1 = 1,
127    RdmaV1Invalidate = 2,
128}
129
130/// Zero-copy write request.
131///
132///
133/// i.e. the data is not included in the message, but is sent separately.
134///
135/// **note:** it is currently assumed that the data is sent immediately after the message.
136#[binrw::binrw]
137#[derive(Debug)]
138#[allow(clippy::manual_non_exhaustive)]
139pub struct WriteRequest {
140    #[bw(calc = 49)]
141    #[br(assert(_structure_size == 49))]
142    _structure_size: u16,
143    /// internal buffer offset in packet, relative to header.
144    #[bw(calc = PosMarker::new(0))]
145    _data_offset: PosMarker<u16>,
146
147    /// Length of data to write.
148    pub length: u32,
149    /// Offset in file to write to.
150    pub offset: u64,
151    pub file_id: FileId,
152    // Again, RDMA off, all 0.
153    #[bw(calc = CommunicationChannel::None)]
154    #[br(assert(channel == CommunicationChannel::None))]
155    pub channel: CommunicationChannel,
156    #[bw(calc = 0)]
157    #[br(assert(_remaining_bytes == 0))]
158    _remaining_bytes: u32,
159    #[bw(calc = 0)]
160    #[br(assert(_write_channel_info_offset == 0))]
161    _write_channel_info_offset: u16,
162    #[bw(calc = 0)]
163    #[br(assert(_write_channel_info_length == 0))]
164    _write_channel_info_length: u16,
165    pub flags: WriteFlags,
166
167    #[bw(write_with = PosMarker::write_aoff, args(&_data_offset))]
168    _write_offset: (),
169}
170
171impl WriteRequest {
172    pub fn new(offset: u64, file_id: FileId, flags: WriteFlags, length: u32) -> Self {
173        Self {
174            length,
175            offset,
176            file_id,
177            flags,
178            _write_offset: (),
179        }
180    }
181}
182
183#[binrw::binrw]
184#[derive(Debug, PartialEq, Eq)]
185pub struct WriteResponse {
186    #[bw(calc = 17)]
187    #[br(assert(_structure_size == 17))]
188    _structure_size: u16,
189    #[bw(calc = 0)]
190    #[br(assert(_reserved == 0))]
191    _reserved: u16,
192    pub count: u32,
193    #[bw(calc = 0)] // reserved
194    #[br(assert(_remaining_bytes == 0))]
195    _remaining_bytes: u32,
196    #[bw(calc = 0)] // reserved
197    #[br(assert(_write_channel_info_offset == 0))]
198    _write_channel_info_offset: u16,
199    #[bw(calc = 0)] // reserved
200    #[br(assert(_write_channel_info_length == 0))]
201    _write_channel_info_length: u16,
202}
203
204#[bitfield]
205#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy)]
206#[bw(map = |&x| Self::into_bytes(x))]
207#[br(map = Self::from_bytes)]
208pub struct WriteFlags {
209    pub write_unbuffered: bool,
210    pub write_through: bool,
211    #[skip]
212    __: B30,
213}
214
215#[cfg(test)]
216mod tests {
217    use std::io::Cursor;
218
219    use crate::*;
220
221    use super::*;
222
223    #[test]
224    pub fn test_flush_req_write() {
225        let mut cursor = Cursor::new(Vec::new());
226        FlushRequest {
227            file_id: [
228                0x14, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x10, 0x00, 0x0c, 0x00,
229                0x00, 0x00,
230            ]
231            .into(),
232        }
233        .write_le(&mut cursor)
234        .unwrap();
235        assert_eq!(
236            cursor.into_inner(),
237            [
238                0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x14, 0x4, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0,
239                0x51, 0x0, 0x10, 0x0, 0xc, 0x0, 0x0, 0x0
240            ]
241        )
242    }
243
244    #[test]
245    pub fn test_flush_res_parse() {
246        let data = [0x4u8, 0, 0, 0, 0, 0, 0, 0];
247        let mut cursor = Cursor::new(data);
248        let resp = FlushResponse::read_le(&mut cursor).unwrap();
249        assert_eq!(resp, FlushResponse {});
250    }
251
252    #[test]
253    pub fn test_read_req_write() {
254        let req = ReadRequest {
255            flags: ReadFlags::new(),
256            length: 0x10203040,
257            offset: 0x5060708090a0b0c,
258            file_id: [
259                0x03, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x0c, 0x00,
260                0x00, 0x00,
261            ]
262            .into(),
263            minimum_count: 1,
264        };
265        let data = encode_content(req.into());
266        assert_eq![
267            data,
268            [
269                0x31, 0x0, 0x0, 0x0, 0x40, 0x30, 0x20, 0x10, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
270                0x06, 0x05, 0x3, 0x3, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0xc5, 0x0, 0x0, 0x0, 0xc, 0x0,
271                0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
272                0x0, 0x0, // The famous padding byte.
273                0x0
274            ]
275        ]
276    }
277
278    #[test]
279    pub fn test_read_resp_parse() {
280        let data = [
281            0xfeu8, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x0,
282            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
283            0xfe, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x20, 0x0, 0x30, 0x0, 0x0, 0x0,
284            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x11, 0x0,
285            0x50, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x62,
286            0x62, 0x62, 0x62, 0x62,
287        ];
288
289        let resp = decode_content(&data).content.to_read().unwrap();
290        assert_eq!(
291            resp,
292            ReadResponse {
293                buffer: b"bbbbbb".to_vec(),
294            }
295        );
296    }
297
298    #[test]
299    pub fn test_write_req_write() {
300        let data = encode_content(
301            WriteRequest::new(
302                0x1234abcd,
303                [
304                    0x14, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x10, 0x00, 0x0c,
305                    0x00, 0x00, 0x00,
306                ]
307                .into(),
308                WriteFlags::new(),
309                "MeFriend!THIS IS FINE!".as_bytes().to_vec().len() as u32,
310            )
311            .into(),
312        );
313        assert_eq!(
314            data,
315            [
316                0x31, 0x0, 0x70, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcd, 0xab, 0x34, 0x12, 0x0, 0x0, 0x0,
317                0x0, 0x14, 0x4, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x51, 0x0, 0x10, 0x0, 0xc, 0x0, 0x0,
318                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
319                0x0
320            ]
321        );
322    }
323
324    #[test]
325    pub fn test_write_resp_parse() {
326        let data = [
327            0x11u8, 0x0, 0x0, 0x0, 0xaf, 0xba, 0xef, 0xbe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
328        ];
329        let mut cursor = Cursor::new(data);
330        let resp = WriteResponse::read_le(&mut cursor).unwrap();
331        assert_eq!(resp, WriteResponse { count: 0xbeefbaaf });
332    }
333}