Skip to main content

smb_server_proto_smb1/
legacy.rs

1//! Core/LANMAN-era path commands ([MS-CIFS]): MKDIR, RMDIR, CHECK_DIRECTORY,
2//! DELETE, RENAME, QUERY_INFORMATION and SET_INFORMATION.
3
4use smb_server_proto::buf::Reader;
5
6/// Read one NUL-terminated path string from the command's data area,
7/// honouring the Unicode flag and BufferFormat `0x04` prefixes.
8///
9/// `data_base` is the absolute frame offset of `data[0]`.
10pub fn read_path(req_data: &[u8], unicode: bool, data_base: usize) -> String {
11    let mut rd = Reader::new(req_data, 0);
12    if !req_data.is_empty() && req_data[0] == 0x04 {
13        rd.skip(1);
14    }
15    rd.zstring(unicode, data_base)
16}
17
18/// Read the two NUL-terminated strings of a RENAME request.
19pub fn read_two_paths(req_data: &[u8], unicode: bool, data_base: usize) -> (String, String) {
20    let mut rd = Reader::new(req_data, 0);
21    if (!req_data.is_empty() && req_data[0] == 0x04)
22        || (unicode && !data_base.is_multiple_of(2) && !req_data.is_empty())
23    {
24        rd.skip(1);
25    }
26    let a = rd.zstring(unicode, data_base);
27    if !rd.at_end() {
28        // Realign for the second string.
29        if unicode && (data_base + rd.pos()) & 1 != 0 {
30            rd.skip(1);
31        }
32        if rd.pos() < req_data.len() && req_data[rd.pos()] == 0x04 {
33            rd.skip(1);
34        }
35    }
36    let b = rd.zstring(unicode, data_base + rd.pos());
37    (a, b)
38}
39
40/// QUERY_INFORMATION (0x08) response: attributes, DOS write time, size.
41#[derive(Debug)]
42pub struct QueryInfoResp {
43    /// Attribute flags.
44    pub attrs: u16,
45    /// Last-write time in DOS encoding.
46    pub dos_time: u32,
47    /// File size in bytes.
48    pub size: u32,
49}
50
51impl QueryInfoResp {
52    /// Encode the WC=10 parameter block.
53    pub fn encode(&self) -> Vec<u8> {
54        let mut p = Vec::with_capacity(20);
55        p.extend_from_slice(&self.attrs.to_le_bytes());
56        p.extend_from_slice(&self.dos_time.to_le_bytes());
57        p.extend_from_slice(&self.size.to_le_bytes());
58        p.extend_from_slice(&[0u8; 14]); // reserved
59        p
60    }
61}
62
63/// Parse a legacy DOS-encoded UTC time into Unix seconds
64/// ([MS-DTYP] DOSDATE/TIME packing used by core protocol fields).
65pub fn dos_time_to_unix(dos: u32) -> u64 {
66    let date = dos >> 16;
67    let time = dos & 0xffff;
68    let year = 1980 + ((date >> 9) & 0x7f) as i64;
69    let month = (date >> 5) & 0xf;
70    let day = date & 0x1f;
71    let hours = (time >> 11) & 0x1f;
72    let minutes = (time >> 5) & 0x3f;
73    let seconds = ((time & 0x1f) * 2) as i64;
74    // Days since epoch via simple civil-date conversion.
75    let days = days_from_civil(year, month.max(1) as u64, day.max(1) as u64);
76    (days * 86400 + (hours as i64) * 3600 + (minutes as i64) * 60 + seconds) as u64
77}
78
79fn days_from_civil(y: i64, m: u64, d: u64) -> i64 {
80    let y = y - if m <= 2 { 1 } else { 0 };
81    let era = y.div_euclid(400);
82    let yoe = (y - era * 400) as u64;
83    let mp = (m + 9) % 12;
84    let doy = (153 * mp + 2) / 5 + d - 1;
85    let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
86    era * 146_097 + doe as i64 - 719_468
87}