smb_msg/info/
set.rs

1//! SMB2 Set Info Request/Response messages.
2
3use crate::{FileId, query_info_data};
4
5use super::{NullByte, common::*};
6use binrw::io::TakeSeekExt;
7use binrw::prelude::*;
8use smb_dtyp::{SecurityDescriptor, binrw_util::prelude::*};
9use smb_fscc::*;
10
11#[binrw::binrw]
12#[derive(Debug)]
13pub struct SetInfoRequest {
14    #[bw(calc = 33)]
15    #[br(assert(_structure_size == 33))]
16    _structure_size: u16,
17    #[bw(calc = data.info_type())]
18    pub info_type: InfoType,
19    pub info_class: SetInfoClass,
20    #[bw(calc = PosMarker::default())]
21    buffer_length: PosMarker<u32>,
22    #[bw(calc = PosMarker::default())]
23    _buffer_offset: PosMarker<u16>,
24    #[bw(calc = 0)]
25    _reserved: u16,
26    pub additional_information: AdditionalInfo,
27    pub file_id: FileId,
28    #[br(map_stream = |s| s.take_seek(buffer_length.value as u64))]
29    #[br(args(info_type))]
30    #[bw(write_with = PosMarker::write_aoff_size, args(&_buffer_offset, &buffer_length))]
31    pub data: SetInfoData,
32}
33
34query_info_data! {
35    SetInfoData
36    File: RawSetInfoData<SetFileInfo>,
37    FileSystem: RawSetInfoData<SetFileSystemInfo>,
38    Security: SecurityDescriptor,
39    Quota: ChainedItemList<FileQuotaInformation>,
40}
41
42/// A helper class for [SetInfoRequest] to contain the information
43/// class to set. In cases of no class, it will be set to a null byte (0u8).
44#[binrw::binrw]
45#[derive(Debug, PartialEq, Eq)]
46pub enum SetInfoClass {
47    File(SetFileInfoClass),
48    FileSystem(SetFileSystemInfoClass),
49    Security(NullByte),
50    Quota(NullByte),
51}
52
53impl From<SetFileInfoClass> for SetInfoClass {
54    fn from(val: SetFileInfoClass) -> Self {
55        SetInfoClass::File(val)
56    }
57}
58
59impl From<SetFileSystemInfoClass> for SetInfoClass {
60    fn from(val: SetFileSystemInfoClass) -> Self {
61        SetInfoClass::FileSystem(val)
62    }
63}
64
65impl SetInfoData {
66    /// This is a helper function to convert the [SetInfoData] to
67    /// a [SetInfoRequest].
68    pub fn to_req(
69        self,
70        info_class: SetInfoClass,
71        file_id: FileId,
72        additional_info: AdditionalInfo,
73    ) -> SetInfoRequest {
74        // Validate the info class and data combination
75        // to ensure they are compatible.
76        match (&info_class, &self) {
77            (SetInfoClass::File(_), SetInfoData::File(_)) => {}
78            (SetInfoClass::FileSystem(_), SetInfoData::FileSystem(_)) => {}
79            (SetInfoClass::Security(_), SetInfoData::Security(_)) => {}
80            (SetInfoClass::Quota(_), SetInfoData::Quota(_)) => {}
81            _ => panic!("Invalid info class and data combination"),
82        }
83
84        SetInfoRequest {
85            info_class,
86            additional_information: additional_info,
87            file_id,
88            data: self,
89        }
90    }
91}
92
93#[binrw::binrw]
94#[derive(Debug, PartialEq, Eq)]
95pub struct SetInfoResponse {
96    #[bw(calc = 2)]
97    #[br(assert(_structure_size == 2))]
98    _structure_size: u16,
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::*;
105    use smb_dtyp::*;
106
107    #[test]
108    fn test_set_info_request_write() {
109        let set_info = SetFileInfo::RenameInformation(FileRenameInformation2 {
110            replace_if_exists: false.into(),
111            root_directory: 0,
112            file_name: "hello\\myNewFile.txt".into(),
113        });
114
115        let cls = set_info.class();
116        let req = SetInfoData::from(RawSetInfoData::<SetFileInfo>::from(set_info)).to_req(
117            cls.into(),
118            guid!("00000042-000e-0000-0500-10000e000000").into(),
119            AdditionalInfo::new(),
120        );
121        let req_data = encode_content(req.into());
122        assert_eq!(
123            req_data,
124            [
125                0x21, 0x0, 0x1, 0xa, 0x3a, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
126                0x42, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x5, 0x0, 0x10, 0x0, 0xe, 0x0, 0x0, 0x0,
127                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
128                0x26, 0x0, 0x0, 0x0, 0x68, 0x0, 0x65, 0x0, 0x6c, 0x0, 0x6c, 0x0, 0x6f, 0x0, 0x5c,
129                0x0, 0x6d, 0x0, 0x79, 0x0, 0x4e, 0x0, 0x65, 0x0, 0x77, 0x0, 0x46, 0x0, 0x69, 0x0,
130                0x6c, 0x0, 0x65, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0
131            ]
132        );
133    }
134
135    #[test]
136    fn test_set_info_response_parse() {
137        let data = [0x2, 0x0, 0x0, 0x0];
138        let response = SetInfoResponse::read_le(&mut std::io::Cursor::new(&data)).unwrap();
139        assert_eq!(response, SetInfoResponse {});
140    }
141}