smb_fscc/
lib.rs

1//! File System Control Codes [MS-FSCC](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/efbfe127-73ad-4140-9967-ec6500e66d5e) For SMB
2//!
3//! The FSCC types are widely used in SMB messages.
4//! This module contains implementation of many structs supported in SMB from the FSCC specification,
5//! to allow a wide variety of SMB operations, with a well defined, convenient typing system,
6//! and with an extensive set of structures.
7//!
8//! This module also contains common utility structures to wrap around common FSCC structures.
9//!
10//! The module contains the following implementations:
11//! * [Querying file information][`smb::ResourceHandle::query_info`]
12//! * [Setting file information][`smb::ResourceHandle::set_info`]
13//! * [Directory query types][`smb::Directory::query`]
14//! * Access masks types, including the [`access_mask!{...}`][`access_mask!`] macro.
15
16#![allow(unused_parens)]
17
18use binrw::{io::TakeSeekExt, meta::ReadEndian, prelude::*};
19use modular_bitfield::prelude::*;
20use smb_dtyp::access_mask;
21
22use smb_dtyp::SID;
23use smb_dtyp::binrw_util::prelude::*;
24pub mod chained_item;
25pub mod common_info;
26pub mod directory_info;
27pub mod error;
28pub mod filesystem_info;
29pub mod query_file_info;
30pub mod set_file_info;
31
32pub use chained_item::{ChainedItem, ChainedItemList};
33pub use common_info::*;
34pub use directory_info::*;
35pub use error::SmbFsccError;
36pub use filesystem_info::*;
37pub use query_file_info::*;
38pub use set_file_info::*;
39
40/// MS-FSCC 2.6
41#[bitfield]
42#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
43#[bw(map = |&x| Self::into_bytes(x))]
44#[br(map = Self::from_bytes)]
45pub struct FileAttributes {
46    pub readonly: bool,
47    pub hidden: bool,
48    pub system: bool,
49    #[skip]
50    __: bool,
51
52    pub directory: bool,
53    pub archive: bool,
54    #[skip]
55    __: bool,
56    pub normal: bool,
57
58    pub temporary: bool,
59    pub sparse_file: bool,
60    pub reparse_point: bool,
61    pub compressed: bool,
62
63    pub offline: bool,
64    pub not_content_indexed: bool,
65    pub encrypted: bool,
66    pub integrity_stream: bool,
67
68    #[skip]
69    __: bool,
70    pub no_scrub_data: bool,
71    pub recall_on_open: bool,
72    pub pinned: bool,
73
74    pub unpinned: bool,
75    #[skip]
76    __: bool,
77    pub recall_on_data_access: bool,
78    #[skip]
79    __: B9,
80}
81
82access_mask! {
83pub struct FileAccessMask {
84    file_read_data: bool,
85    file_write_data: bool,
86    file_append_data: bool,
87    file_read_ea: bool,
88
89    file_write_ea: bool,
90    file_execute: bool,
91    file_delete_child: bool,
92    file_read_attributes: bool,
93
94    file_write_attributes: bool,
95    #[skip]
96    __: B7,
97}}
98
99access_mask! {
100pub struct DirAccessMask {
101    list_directory: bool,
102    add_file: bool,
103    add_subdirectory: bool,
104    read_ea: bool,
105
106    write_ea: bool,
107    traverse: bool,
108    delete_child: bool,
109    read_attributes: bool,
110
111    write_attributes: bool,
112    #[skip]
113    __: B7,
114}}
115
116impl From<FileAccessMask> for DirAccessMask {
117    fn from(mask: FileAccessMask) -> Self {
118        // The bits are the same, just the names are different.
119        Self::from_bytes(mask.into_bytes())
120    }
121}
122
123impl From<DirAccessMask> for FileAccessMask {
124    fn from(val: DirAccessMask) -> Self {
125        // The bits are the same, just the names are different.
126        FileAccessMask::from_bytes(val.into_bytes())
127    }
128}
129
130#[binrw::binrw]
131#[derive(Debug, PartialEq, Eq)]
132#[bw(import(has_next: bool))]
133pub struct FileNotifyInformationInner {
134    pub action: NotifyAction,
135    #[bw(try_calc = file_name.size().try_into())]
136    file_name_length: u32,
137    #[br(args(file_name_length.into()))]
138    pub file_name: SizedWideString,
139}
140
141pub type FileNotifyInformation = ChainedItem<FileNotifyInformationInner>;
142
143#[binrw::binrw]
144#[derive(Debug, PartialEq, Eq)]
145#[brw(repr(u32))]
146pub enum NotifyAction {
147    Added = 0x1,
148    Removed = 0x2,
149    Modified = 0x3,
150    RenamedOldName = 0x4,
151    RenamedNewName = 0x5,
152    AddedStream = 0x6,
153    RemovedStream = 0x7,
154    ModifiedStream = 0x8,
155    RemovedByDelete = 0x9,
156    IdNotTunnelled = 0xa,
157    TunnelledIdCollision = 0xb,
158}
159
160/// Trait for file information types.
161/// This trait contains all types of all file info types and classes, specified in MS-FSCC.
162///
163/// It's role is to allow converting an instance of a file information type to a class,
164/// and to provide the class type from the file information type.
165pub trait FileInfoType:
166    Sized + for<'a> BinRead<Args<'static> = (Self::Class,)> + ReadEndian + std::fmt::Debug
167{
168    /// The class of the file information.
169    type Class;
170
171    /// Get the class of the file information.
172    fn class(&self) -> Self::Class;
173}
174
175/// A macro for generating a file class enums,
176/// for both the file information class, and information value.
177/// including a trait for the value types.
178#[macro_export]
179macro_rules! file_info_classes {
180    ($svis:vis $name:ident {
181        $($vis:vis $field_name:ident = $cid:literal,)+
182    }, $brw_ty:ty) => {
183        #[allow(unused_imports)]
184        use binrw::prelude::*;
185        paste::paste! {
186            // Trait to be implemented for all the included value types.
187            pub trait [<$name Value>] :
188                TryFrom<$name, Error = $crate::SmbFsccError>
189                + Send + 'static
190                + Into<$name>
191                + for <'a> [<Bin $brw_ty>]<Args<'a> = ()> {
192                const CLASS_ID: [<$name Class>];
193            }
194
195            // Enum for Class IDs
196            #[binrw::binrw]
197            #[derive(Debug, PartialEq, Eq, Clone, Copy)]
198            #[brw(repr(u8))]
199            $svis enum [<$name Class>] {
200                $(
201                    $vis [<$field_name Information>] = $cid,
202                )*
203            }
204
205            // Enum for class values
206            #[binrw::binrw]
207            #[derive(Debug, PartialEq, Eq)]
208            #[brw(little)]
209            #[br(import(c: [<$name Class>]))]
210            $svis enum $name {
211                $(
212                    #[br(pre_assert(matches!(c, [<$name Class>]::[<$field_name Information>])))]
213                    [<$field_name Information>]([<File $field_name Information>]),
214                )*
215            }
216
217            impl std::fmt::Display for [<$name Class>] {
218                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219                    match self {
220                        $(
221                            [<$name Class>]::[<$field_name Information>] => write!(f, stringify!([<$field_name Information>])),
222                        )*
223                    }
224                }
225            }
226
227            impl $crate::FileInfoType for $name {
228                type Class = [<$name Class>];
229                fn class(&self) -> Self::Class {
230                    match self {
231                        $(
232                            $name::[<$field_name Information>](_) => [<$name Class>]::[<$field_name Information>],
233                        )*
234                    }
235                }
236            }
237
238            $(
239                impl From<[<File $field_name Information>]> for $name {
240                    fn from(value: [<File $field_name Information>]) -> $name {
241                        $name::[<$field_name Information>](value)
242                    }
243                }
244
245                impl TryFrom<$name> for [<File $field_name Information>] {
246                    type Error = $crate::SmbFsccError;
247
248                    fn try_from(value: $name) -> Result<Self, Self::Error> {
249                        pub use $crate::FileInfoType;
250                        match value {
251                            $name::[<$field_name Information>](v) => Ok(v),
252                            _ => Err($crate::SmbFsccError::UnexpectedInformationType(<Self as [<$name Value>]>::CLASS_ID as u8, value.class() as u8)),
253                        }
254                    }
255                }
256
257                impl [<$name Value>] for [<File $field_name Information>] {
258                    const CLASS_ID: [<$name Class>] = [<$name Class>]::[<$field_name Information>];
259                }
260            )*
261        }
262    }
263}
264
265#[binrw::binrw]
266#[derive(Debug)]
267pub struct FileQuotaInformationInner {
268    #[bw(calc = PosMarker::default())]
269    sid_length: PosMarker<u32>,
270    pub change_time: FileTime,
271    pub quota_used: u64,
272    pub quota_threshold: u64,
273    pub quota_limit: u64,
274    #[br(map_stream = |s| s.take_seek(sid_length.value as u64))]
275    #[bw(write_with = PosMarker::write_size, args(&sid_length))]
276    pub sid: SID,
277}
278
279pub type FileQuotaInformation = ChainedItem<FileQuotaInformationInner>;
280
281#[binrw::binrw]
282#[derive(Debug)]
283pub struct FileGetQuotaInformationInner {
284    #[bw(calc = PosMarker::default())]
285    sid_length: PosMarker<u32>,
286    #[br(map_stream = |s| s.take_seek(sid_length.value as u64))]
287    #[bw(write_with = PosMarker::write_size, args(&sid_length))]
288    pub sid: SID,
289}
290
291pub type FileGetQuotaInformation = ChainedItem<FileGetQuotaInformationInner>;