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::{meta::ReadEndian, prelude::*};
19use modular_bitfield::prelude::*;
20use smb_dtyp::access_mask;
21
22pub mod chained_list;
23pub mod common_info;
24pub mod directory_info;
25pub mod error;
26pub mod filesystem_info;
27mod notify;
28pub mod query_file_info;
29pub mod quota;
30pub mod set_file_info;
31
32pub use chained_list::{CHAINED_ITEM_PREFIX_SIZE, ChainedItem, ChainedItemList};
33pub use common_info::*;
34pub use directory_info::*;
35pub use error::SmbFsccError;
36pub use filesystem_info::*;
37pub use notify::*;
38pub use query_file_info::*;
39pub use quota::*;
40pub use set_file_info::*;
41
42/// MS-FSCC 2.6
43#[bitfield]
44#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
45#[bw(map = |&x| Self::into_bytes(x))]
46#[br(map = Self::from_bytes)]
47pub struct FileAttributes {
48    pub readonly: bool,
49    pub hidden: bool,
50    pub system: bool,
51    #[skip]
52    __: bool,
53
54    pub directory: bool,
55    pub archive: bool,
56    #[skip]
57    __: bool,
58    pub normal: bool,
59
60    pub temporary: bool,
61    pub sparse_file: bool,
62    pub reparse_point: bool,
63    pub compressed: bool,
64
65    pub offline: bool,
66    pub not_content_indexed: bool,
67    pub encrypted: bool,
68    pub integrity_stream: bool,
69
70    #[skip]
71    __: bool,
72    pub no_scrub_data: bool,
73    pub recall_on_open: bool,
74    pub pinned: bool,
75
76    pub unpinned: bool,
77    #[skip]
78    __: bool,
79    pub recall_on_data_access: bool,
80    #[skip]
81    __: B9,
82}
83
84access_mask! {
85pub struct FileAccessMask {
86    file_read_data: bool,
87    file_write_data: bool,
88    file_append_data: bool,
89    file_read_ea: bool,
90
91    file_write_ea: bool,
92    file_execute: bool,
93    file_delete_child: bool,
94    file_read_attributes: bool,
95
96    file_write_attributes: bool,
97    #[skip]
98    __: B7,
99}}
100
101access_mask! {
102pub struct DirAccessMask {
103    list_directory: bool,
104    add_file: bool,
105    add_subdirectory: bool,
106    read_ea: bool,
107
108    write_ea: bool,
109    traverse: bool,
110    delete_child: bool,
111    read_attributes: bool,
112
113    write_attributes: bool,
114    #[skip]
115    __: B7,
116}}
117
118impl From<FileAccessMask> for DirAccessMask {
119    fn from(mask: FileAccessMask) -> Self {
120        // The bits are the same, just the names are different.
121        Self::from_bytes(mask.into_bytes())
122    }
123}
124
125impl From<DirAccessMask> for FileAccessMask {
126    fn from(val: DirAccessMask) -> Self {
127        // The bits are the same, just the names are different.
128        FileAccessMask::from_bytes(val.into_bytes())
129    }
130}
131
132/// Trait for file information types.
133/// This trait contains all types of all file info types and classes, specified in MS-FSCC.
134///
135/// It's role is to allow converting an instance of a file information type to a class,
136/// and to provide the class type from the file information type.
137pub trait FileInfoType:
138    Sized + for<'a> BinRead<Args<'static> = (Self::Class,)> + ReadEndian + std::fmt::Debug
139{
140    /// The class of the file information.
141    type Class;
142
143    /// Get the class of the file information.
144    fn class(&self) -> Self::Class;
145}
146
147/// A macro for generating a file class enums,
148/// for both the file information class, and information value.
149/// including a trait for the value types.
150#[macro_export]
151macro_rules! file_info_classes {
152    ($svis:vis $name:ident {
153        $($vis:vis $field_name:ident = $cid:literal,)+
154    }, $brw_ty:ty) => {
155        #[allow(unused_imports)]
156        use binrw::prelude::*;
157        paste::paste! {
158            // Trait to be implemented for all the included value types.
159            pub trait [<$name Value>] :
160                TryFrom<$name, Error = $crate::SmbFsccError>
161                + Send + 'static
162                + Into<$name>
163                + for <'a> [<Bin $brw_ty>]<Args<'a> = ()> {
164                const CLASS_ID: [<$name Class>];
165            }
166
167            // Enum for Class IDs
168            #[binrw::binrw]
169            #[derive(Debug, PartialEq, Eq, Clone, Copy)]
170            #[brw(repr(u8))]
171            $svis enum [<$name Class>] {
172                $(
173                    $vis [<$field_name Information>] = $cid,
174                )*
175            }
176
177            // Enum for class values
178            #[binrw::binrw]
179            #[derive(Debug, PartialEq, Eq)]
180            #[brw(little)]
181            #[br(import(c: [<$name Class>]))]
182            $svis enum $name {
183                $(
184                    #[br(pre_assert(matches!(c, [<$name Class>]::[<$field_name Information>])))]
185                    [<$field_name Information>]([<File $field_name Information>]),
186                )*
187            }
188
189            impl std::fmt::Display for [<$name Class>] {
190                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191                    match self {
192                        $(
193                            [<$name Class>]::[<$field_name Information>] => write!(f, stringify!([<$field_name Information>])),
194                        )*
195                    }
196                }
197            }
198
199            impl $crate::FileInfoType for $name {
200                type Class = [<$name Class>];
201                fn class(&self) -> Self::Class {
202                    match self {
203                        $(
204                            $name::[<$field_name Information>](_) => [<$name Class>]::[<$field_name Information>],
205                        )*
206                    }
207                }
208            }
209
210            $(
211                impl From<[<File $field_name Information>]> for $name {
212                    fn from(value: [<File $field_name Information>]) -> $name {
213                        $name::[<$field_name Information>](value)
214                    }
215                }
216
217                impl TryFrom<$name> for [<File $field_name Information>] {
218                    type Error = $crate::SmbFsccError;
219
220                    fn try_from(value: $name) -> Result<Self, Self::Error> {
221                        pub use $crate::FileInfoType;
222                        match value {
223                            $name::[<$field_name Information>](v) => Ok(v),
224                            _ => Err($crate::SmbFsccError::UnexpectedInformationType(<Self as [<$name Value>]>::CLASS_ID as u8, value.class() as u8)),
225                        }
226                    }
227                }
228
229                impl [<$name Value>] for [<File $field_name Information>] {
230                    const CLASS_ID: [<$name Class>] = [<$name Class>]::[<$field_name Information>];
231                }
232            )*
233        }
234    }
235}