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, PartialEq, Eq)]
14pub struct FlushRequest {
15    #[bw(calc = 24)]
16    #[br(assert(_structure_size == 24))]
17    _structure_size: u16,
18    #[bw(calc = 0)]
19    _reserved1: u16,
20    #[bw(calc = 0)]
21    _reserved2: u32,
22    pub file_id: FileId,
23}
24
25#[binrw::binrw]
26#[derive(Debug, PartialEq, Eq)]
27pub struct FlushResponse {
28    #[bw(calc = 4)]
29    #[br(assert(_structure_size == 4))]
30    _structure_size: u16,
31    #[bw(calc = 0)]
32    _reserved: u16,
33}
34
35#[binrw::binrw]
36#[derive(Debug, PartialEq, Eq)]
37pub struct ReadRequest {
38    #[bw(calc = 49)]
39    #[br(assert(_structure_size == 49))]
40    _structure_size: u16,
41    #[bw(calc = 0)]
42    _padding: u8,
43    pub flags: ReadFlags,
44    pub length: u32,
45    pub offset: u64,
46    pub file_id: FileId,
47    pub minimum_count: u32,
48    // Currently, we do not have support for RDMA.
49    // Therefore, all the related fields are set to zero.
50    #[bw(calc = CommunicationChannel::None)]
51    #[br(assert(channel == CommunicationChannel::None))]
52    channel: CommunicationChannel,
53    #[bw(calc = 0)]
54    #[br(assert(_remaining_bytes == 0))]
55    _remaining_bytes: u32,
56    #[bw(calc = 0)]
57    #[br(assert(_read_channel_info_offset == 0))]
58    _read_channel_info_offset: u16,
59    #[bw(calc = 0)]
60    #[br(assert(_read_channel_info_length == 0))]
61    _read_channel_info_length: u16,
62
63    // Well, that's a little awkward, but since we never provide a blob, and yet,
64    // Msft decided it makes sense to make the structure size 0x31, we need to add this padding.
65    #[bw(calc = 0)]
66    _pad_blob_placeholder: u8,
67}
68
69#[binrw::binrw]
70#[derive(Debug, PartialEq, Eq)]
71pub struct ReadResponse {
72    #[bw(calc = Self::STRUCT_SIZE as u16)]
73    #[br(assert(_structure_size == Self::STRUCT_SIZE as u16))]
74    _structure_size: u16,
75    // Sanity check: The offset is from the SMB header beginning.
76    // it should be greater than the sum of the header and the response.
77    // the STRUCT_SIZE includes the first byte of the buffer, so the offset is validated against a byte before that.
78    #[br(assert(_data_offset.value as usize >= Header::STRUCT_SIZE + Self::STRUCT_SIZE - 1))]
79    #[bw(calc = PosMarker::default())]
80    _data_offset: PosMarker<u8>,
81    #[bw(calc = 0)]
82    _reserved: u8,
83    #[bw(try_calc = buffer.len().try_into())]
84    #[br(assert(_data_length > 0))] // sanity
85    _data_length: u32,
86    #[bw(calc = 0)]
87    #[br(assert(_data_remaining == 0))]
88    _data_remaining: u32,
89
90    // No RDMA support -- always zero, for both reserved and flags case:
91    #[bw(calc = 0)]
92    _reserved2: u32,
93
94    #[br(seek_before = SeekFrom::Start(_data_offset.value as u64))]
95    #[br(count = _data_length)]
96    #[bw(assert(!buffer.is_empty()))] // sanity _data_length > 0 on write.
97    #[bw(write_with = PosMarker::write_aoff, args(&_data_offset))]
98    pub buffer: Vec<u8>,
99}
100
101impl ReadResponse {
102    const STRUCT_SIZE: usize = 17;
103}
104
105#[bitfield]
106#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
107#[bw(map = |&x| Self::into_bytes(x))]
108#[br(map = Self::from_bytes)]
109pub struct ReadFlags {
110    pub read_unbuffered: bool,
111    pub read_compressed: bool,
112    #[skip]
113    __: B6,
114}
115
116#[binrw::binrw]
117#[derive(Debug, PartialEq, Eq)]
118#[brw(repr(u32))]
119pub enum CommunicationChannel {
120    None = 0,
121    RdmaV1 = 1,
122    RdmaV1Invalidate = 2,
123}
124
125/// Zero-copy write request.
126///
127///
128/// i.e. the data is not included in the message, but is sent separately.
129///
130/// **note:** it is currently assumed that the data is sent immediately after the message.
131#[binrw::binrw]
132#[derive(Debug, PartialEq, Eq)]
133#[allow(clippy::manual_non_exhaustive)]
134pub struct WriteRequest {
135    #[bw(calc = 49)]
136    #[br(assert(_structure_size == 49))]
137    _structure_size: u16,
138    /// internal buffer offset in packet, relative to header.
139    #[bw(calc = PosMarker::new(0))]
140    _data_offset: PosMarker<u16>,
141
142    /// Length of data to write.
143    pub length: u32,
144    /// Offset in file to write to.
145    pub offset: u64,
146    pub file_id: FileId,
147    // Again, RDMA off, all 0.
148    #[bw(calc = CommunicationChannel::None)]
149    #[br(assert(channel == CommunicationChannel::None))]
150    pub channel: CommunicationChannel,
151    #[bw(calc = 0)]
152    #[br(assert(_remaining_bytes == 0))]
153    _remaining_bytes: u32,
154    #[bw(calc = 0)]
155    #[br(assert(_write_channel_info_offset == 0))]
156    _write_channel_info_offset: u16,
157    #[bw(calc = 0)]
158    #[br(assert(_write_channel_info_length == 0))]
159    _write_channel_info_length: u16,
160    pub flags: WriteFlags,
161
162    #[bw(write_with = PosMarker::write_aoff, args(&_data_offset))]
163    _write_offset: (),
164}
165
166impl WriteRequest {
167    pub fn new(offset: u64, file_id: FileId, flags: WriteFlags, length: u32) -> Self {
168        Self {
169            length,
170            offset,
171            file_id,
172            flags,
173            _write_offset: (),
174        }
175    }
176}
177
178#[binrw::binrw]
179#[derive(Debug, PartialEq, Eq)]
180pub struct WriteResponse {
181    #[bw(calc = 17)]
182    #[br(assert(_structure_size == 17))]
183    _structure_size: u16,
184    #[bw(calc = 0)]
185    _reserved: u16,
186    pub count: u32,
187    #[bw(calc = 0)] // reserved
188    #[br(assert(_remaining_bytes == 0))]
189    _remaining_bytes: u32,
190    #[bw(calc = 0)] // reserved
191    #[br(assert(_write_channel_info_offset == 0))]
192    _write_channel_info_offset: u16,
193    #[bw(calc = 0)] // reserved
194    #[br(assert(_write_channel_info_length == 0))]
195    _write_channel_info_length: u16,
196}
197
198#[bitfield]
199#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
200#[bw(map = |&x| Self::into_bytes(x))]
201#[br(map = Self::from_bytes)]
202pub struct WriteFlags {
203    pub write_unbuffered: bool,
204    pub write_through: bool,
205    #[skip]
206    __: B30,
207}
208
209#[cfg(test)]
210mod tests {
211    use crate::*;
212
213    use super::*;
214    use smb_tests::*;
215
216    test_binrw! {
217        struct FlushRequest {
218            file_id: [
219                0x14, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x10, 0x00, 0x0c, 0x00,
220                0x00, 0x00,
221            ]
222            .into(),
223        } => "1800000000000000140400000c000000510010000c000000"
224    }
225
226    test_binrw! {
227        struct FlushResponse {  } => "04 00 00 00"
228    }
229
230    test_request! {
231        Read {
232            flags: ReadFlags::new(),
233            length: 0x10203040,
234            offset: 0x5060708090a0b0c,
235            file_id: [
236                0x03, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x0c, 0x00,
237                0x00, 0x00,
238            ]
239            .into(),
240            minimum_count: 1,
241        } => "31000000403020100c0b0a0908070605030300000c000000c50000000c0000000100000000000000000000000000000000"
242    }
243
244    test_response! {
245        Read {
246            buffer: b"bbbbbb".to_vec(),
247        } => "11005000060000000000000000000000626262626262"
248    }
249
250    test_request! {
251        Write {
252            offset: 0x1234abcd,
253            file_id: [
254                0x14, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x10, 0x00, 0x0c, 0x00,
255                0x00, 0x00,
256            ]
257            .into(),
258            flags: WriteFlags::new(),
259            length: "MeFriend!THIS IS FINE!".as_bytes().to_vec().len() as u32,
260            _write_offset: (),
261        } => "3100700016000000cdab341200000000140400000c000000510010000c00000000000000000000000000000000000000"
262    }
263
264    test_binrw! {
265        struct WriteResponse { count: 0xbeefbaaf, } => "11000000afbaefbe0000000000000000"
266    }
267}