smb_fscc/
set_file_info.rs

1//! File Information Classes for setting file information.
2//!
3//! This module exports [`SetFileInfo`] enum and all structs that can be used to set file information.
4//!
5//! [MS-FSCC 2.4](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4718fc40-e539-4014-8e33-b675af74e3e1>)
6
7use std::ops::Deref;
8
9use crate::file_info_classes;
10
11use smb_dtyp::binrw_util::prelude::*;
12
13use super::{
14    FileBasicInformation, FileFullEaInformation, FileModeInformation, FileNameInformation,
15    FilePipeInformation, FilePositionInformation,
16};
17
18file_info_classes! {
19    /// Set file information classes.
20    pub SetFileInfo {
21        pub Allocation = 19,
22        pub Basic = 4,
23        pub Disposition = 13,
24        pub EndOfFile = 20,
25        pub FullEa = 15,
26        pub Link = 11,
27        pub Mode = 16,
28        pub Pipe = 23,
29        pub Position = 14,
30        pub Rename = 10,
31        pub ShortName = 40,
32        pub ValidDataLength = 39,
33    }, Write
34}
35
36/// Set end-of-file information for a file.
37///
38/// [MS-FSCC 2.4.14](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/75241cca-3167-472f-8058-a52d77c6bb17>)
39#[binrw::binrw]
40#[derive(Debug, PartialEq, Eq)]
41pub struct FileEndOfFileInformation {
42    /// The absolute new end of file position as a byte offset from the start of the file.
43    /// Specifies the offset from the beginning of the file of the byte following the last byte in the file.
44    /// That is, it is the offset from the beginning of the file at which new bytes appended to the file will be written.
45    /// The value of this field MUST be greater than or equal to 0.
46    pub end_of_file: u64,
47}
48
49/// Mark a file for deletion.
50///
51/// [MS-FSCC 2.4.11](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/12c3dd1c-14f6-4229-9d29-75fb2cb392f6>)
52#[binrw::binrw]
53#[derive(Debug, PartialEq, Eq)]
54pub struct FileDispositionInformation {
55    /// Set to TRUE to indicate that a file should be deleted when it is closed; set to FALSE otherwise.
56    /// **Note:** Default is TRUE
57    pub delete_pending: Boolean,
58}
59
60impl Default for FileDispositionInformation {
61    fn default() -> Self {
62        Self {
63            delete_pending: true.into(),
64        }
65    }
66}
67
68/// Rename a file within the SMB2 protocol.
69///
70/// [MS-FSCC 2.4.42.2](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/52aa0b70-8094-4971-862d-79793f41e6a8>) - FileRenameInformation for SMB2 protocol
71#[binrw::binrw]
72#[derive(Debug, PartialEq, Eq)]
73pub struct FileRenameInformation {
74    /// Set to TRUE to indicate that if a file with the given name already exists, it should be replaced with the given file. Set to FALSE if the rename operation should fail if a file with the given name already exists.
75    pub replace_if_exists: Boolean,
76    #[bw(calc = 0)]
77    _reserved: u8,
78    #[bw(calc = 0)]
79    _reserved2: u16,
80    #[bw(calc = 0)]
81    _reserved3: u32,
82    /// A file handle for the root directory. For network operations, this value must be zero.
83    pub root_directory: u64,
84    #[bw(try_calc = file_name.size().try_into())]
85    _file_name_length: u32,
86    /// The new name for the file, including the full path.
87    #[br(args { size: SizedStringSize::bytes(_file_name_length) })]
88    pub file_name: SizedWideString,
89}
90
91/// Set the allocation size for a file.
92///
93/// The file system is passed a 64-bit signed integer containing the file allocation size, in bytes.
94/// The file system rounds the requested allocation size up to an integer multiple of the cluster size for nonresident files,
95/// or an implementation-defined multiple for resident files.
96/// All unused allocation (beyond EOF) is freed on the last handle close.
97///
98/// [MS-FSCC 2.4.4](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/0201c69b-50db-412d-bab3-dd97aeede13b>)
99#[binrw::binrw]
100#[derive(Debug, PartialEq, Eq)]
101pub struct FileAllocationInformation {
102    /// The new allocation size in bytes. Usually a multiple of the sector or cluster size of the underlying physical device.
103    pub allocation_size: u64,
104}
105
106/// Create a hard link to an existing file via the SMB Version 2 Protocol, as specified in [MS-SMB2].
107///
108/// WARNING: This operation is currently unstable and untested, and may lead to data loss or corruption if used improperly!
109///
110/// [MS-FSCC 2.4.8.2](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/58f44021-120d-4662-bf2c-9905ed4940dc>) - FileLinkInformation for SMB2 protocol
111#[binrw::binrw]
112#[derive(Debug, PartialEq, Eq)]
113pub struct FileLinkInformation {
114    /// Set to TRUE to indicate that if a file with the given name already exists, it should be replaced with the given file. Set to FALSE if the link operation should fail if a file with the given name already exists.
115    pub replace_if_exists: Boolean,
116    #[bw(calc = 0)]
117    _reserved: u8,
118    #[bw(calc = 0)]
119    _reserved2: u16,
120    #[bw(calc = 0)]
121    _reserved3: u32,
122    /// A file handle for the root directory. For network operations, this value must be zero.
123    #[bw(calc = 0)]
124    #[br(assert(root_directory == 0))]
125    root_directory: u64,
126    #[bw(try_calc = file_name.size().try_into())]
127    _file_name_length: u32,
128    /// The name to be assigned to the newly created link.
129    #[br(args {size: SizedStringSize::bytes(_file_name_length)})]
130    pub file_name: SizedWideString,
131}
132
133/// change a file's short name.
134///
135/// If the supplied name is of zero length, the file's existing short name, if any,
136/// SHOULD be deleted.
137/// Otherwise, the supplied name MUST be a valid short name as specified in section 2.1.5.2.1
138/// and be unique among all file names and short names in the same directory as the file being operated on.
139/// A caller changing the file's short name MUST have SeRestorePrivilege.
140///
141/// [MS-FSCC 2.4.46](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/80cecad8-9172-4c42-af90-f890a84f2abc>)
142#[binrw::binrw]
143#[derive(Debug, PartialEq, Eq)]
144pub struct FileShortNameInformation {
145    /// The short name information, following the same structure as FileNameInformation.
146    inner: FileNameInformation,
147}
148
149impl Deref for FileShortNameInformation {
150    type Target = FileNameInformation;
151
152    fn deref(&self) -> &Self::Target {
153        &self.inner
154    }
155}
156
157/// set the valid data length information for a file.
158///
159/// A file's valid data length is the length, in bytes, of the data that has been written to the file.
160/// This valid data extends from the beginning of the file to the last byte in the file that has not been zeroed or left uninitialized
161///
162/// [MS-FSCC 2.4.49](<https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/5c9f9d50-f0e0-40b1-9b84-0b78f59158b1>)
163#[binrw::binrw]
164#[derive(Debug, PartialEq, Eq)]
165pub struct FileValidDataLengthInformation {
166    /// The new valid data length for the file.
167    /// This parameter must be a positive value that is greater than the current valid data length, but less than or equal to the current file size.
168    pub valid_data_length: u64,
169}
170
171#[cfg(test)]
172mod tests {
173    use crate::FileAttributes;
174
175    use super::*;
176    use smb_tests::{test_binrw, test_binrw_read, test_binrw_write};
177    use time::macros::datetime;
178
179    test_binrw! {
180        struct FileAllocationInformation {
181            allocation_size: 500,
182        } => "f401000000000000"
183    }
184
185    test_binrw! {
186        struct FileEndOfFileInformation {
187            end_of_file: 777,
188        } => "0903000000000000"
189    }
190
191    test_binrw! {
192        struct FileDispositionInformation {
193            delete_pending: true.into(),
194        } => "01"
195    }
196
197    test_binrw_read! {
198        struct FileRenameInformation {
199            replace_if_exists: false.into(),
200            root_directory: 0,
201            file_name: SizedWideString::from("b.txt"),
202        } => "0002750062006c0000000000000000000a00000062002e00740078007400"
203    }
204
205    test_binrw_write! {
206        struct FileRenameInformation {
207            replace_if_exists: false.into(),
208            root_directory: 0,
209            file_name: SizedWideString::from("b.txt"),
210        } => "000000000000000000000000000000000a00000062002e00740078007400"
211    }
212
213    test_binrw! {
214        struct FileBasicInformation {
215            creation_time: FileTime::ZERO,
216            last_access_time: FileTime::ZERO,
217            last_write_time: datetime!(2025-04-11 17:24:47.489599300).into(),
218            change_time: datetime!(2025-04-11 17:24:47.489599300).into(),
219            file_attributes: FileAttributes::new(),
220        } => "00000000000000000000000000000000790eb19f06abdb01790eb19f06abdb010000000000000000"
221    }
222
223    test_binrw! {
224        struct FileValidDataLengthInformation {
225            valid_data_length: 0x123456789,
226        } => "8967452301000000"
227    }
228
229    test_binrw! {
230        struct FileShortNameInformation {
231            inner: FileNameInformation {
232                file_name: SizedWideString::from("SHORTN~1.TXT"),
233            },
234        } => "18000000530048004f00520054004e007e0031002e00540058005400"
235    }
236
237    // TODO: the following test is currently missing.
238    //     pub Link = 11,
239}