smb_msg/
create.rs

1//! Create & Close (files) requests and responses.
2
3use std::fmt::{Debug, Display};
4use std::io::{Cursor, SeekFrom};
5
6use super::header::Status;
7use super::*;
8use binrw::io::TakeSeekExt;
9use binrw::prelude::*;
10use modular_bitfield::prelude::*;
11use smb_dtyp::SecurityDescriptor;
12use smb_dtyp::{Guid, binrw_util::prelude::*};
13use smb_fscc::*;
14
15/// 2.2.14.1: SMB2_FILEID
16#[binrw::binrw]
17#[derive(PartialEq, Eq, Clone, Copy, Default)]
18pub struct FileId {
19    pub persistent: u64,
20    pub volatile: u64,
21}
22
23impl FileId {
24    pub const EMPTY: FileId = FileId {
25        persistent: 0,
26        volatile: 0,
27    };
28    /// A file ID that is used to indicate that the file ID is not valid,
29    /// with setting all bits to 1 - {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}.
30    pub const FULL: FileId = FileId {
31        persistent: u64::MAX,
32        volatile: u64::MAX,
33    };
34}
35
36impl From<[u8; 16]> for FileId {
37    fn from(data: [u8; 16]) -> Self {
38        let mut cursor = Cursor::new(data);
39        Self::read_le(&mut cursor).unwrap()
40    }
41}
42
43impl From<Guid> for FileId {
44    fn from(guid: Guid) -> Self {
45        let mut cursor = Cursor::new(Vec::new());
46        guid.write_le(&mut cursor).unwrap();
47        <Self as From<[u8; 16]>>::from(cursor.into_inner().try_into().unwrap())
48    }
49}
50
51impl Display for FileId {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        write!(f, "{{{:x}|{:x}}}", self.persistent, self.volatile)
54    }
55}
56
57impl Debug for FileId {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        write!(f, "FileId({})", self)
60    }
61}
62
63#[binrw::binrw]
64#[derive(Debug)]
65pub struct CreateRequest {
66    #[bw(calc = 57)]
67    #[br(assert(_structure_size == 57))]
68    _structure_size: u16,
69    #[bw(calc = 0)] // reserved
70    #[br(assert(_security_flags == 0))]
71    _security_flags: u8,
72    pub requested_oplock_level: OplockLevel,
73    pub impersonation_level: ImpersonationLevel,
74    #[bw(calc = 0)]
75    #[br(assert(_smb_create_flags == 0))]
76    _smb_create_flags: u64,
77    #[bw(calc = 0)]
78    #[br(assert(_reserved == 0))]
79    _reserved: u64,
80    pub desired_access: FileAccessMask,
81    pub file_attributes: FileAttributes,
82    pub share_access: ShareAccessFlags,
83    pub create_disposition: CreateDisposition,
84    pub create_options: CreateOptions,
85    #[bw(calc = PosMarker::default())]
86    _name_offset: PosMarker<u16>,
87    #[bw(try_calc = name.size().try_into())]
88    name_length: u16, // bytes
89    #[bw(calc = PosMarker::default())]
90    _create_contexts_offset: PosMarker<u32>,
91    #[bw(calc = PosMarker::default())]
92    _create_contexts_length: PosMarker<u32>,
93
94    #[brw(align_before = 8)]
95    #[bw(write_with = PosMarker::write_aoff, args(&_name_offset))]
96    #[br(args(name_length as u64))]
97    pub name: SizedWideString,
98
99    /// Use the `CreateContextReqData::first_...` function family to get the first context of a specific type.
100    #[brw(align_before = 8)]
101    #[br(map_stream = |s| s.take_seek(_create_contexts_length.value.into()))]
102    #[bw(write_with = PosMarker::write_roff_size, args(&_create_contexts_offset, &_create_contexts_length))]
103    pub contexts: ChainedItemList<ReqCreateContext, 8>,
104}
105
106#[binrw::binrw]
107#[derive(Debug)]
108#[brw(repr(u32))]
109pub enum ImpersonationLevel {
110    Anonymous = 0x0,
111    Identification = 0x1,
112    Impersonation = 0x2,
113    Delegate = 0x3,
114}
115
116#[binrw::binrw]
117#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
118#[brw(repr(u32))]
119pub enum CreateDisposition {
120    Superseded = 0x0,
121    #[default]
122    Open = 0x1,
123    Create = 0x2,
124    OpenIf = 0x3,
125    Overwrite = 0x4,
126    OverwriteIf = 0x5,
127}
128
129#[bitfield]
130#[derive(BinWrite, BinRead, Default, Debug, Clone, Copy)]
131#[bw(map = |&x| Self::into_bytes(x))]
132#[br(map = Self::from_bytes)]
133pub struct CreateOptions {
134    pub directory_file: bool,
135    pub write_through: bool,
136    pub sequential_only: bool,
137    pub no_intermediate_buffering: bool,
138
139    pub synchronous_io_alert: bool,
140    pub synchronous_io_nonalert: bool,
141    pub non_directory_file: bool,
142    #[skip]
143    __: bool,
144
145    pub complete_if_oplocked: bool,
146    pub no_ea_knowledge: bool,
147    pub open_remote_instance: bool,
148    pub random_access: bool,
149
150    pub delete_on_close: bool,
151    pub open_by_file_id: bool,
152    pub open_for_backup_intent: bool,
153    pub no_compression: bool,
154
155    pub open_requiring_oplock: bool,
156    pub disallow_exclusive: bool,
157    #[skip]
158    __: B2,
159
160    pub reserve_opfilter: bool,
161    pub open_reparse_point: bool,
162    pub open_no_recall: bool,
163    pub open_for_free_space_query: bool,
164
165    #[skip]
166    __: B8,
167}
168
169// share_access 4 byte flags:
170#[bitfield]
171#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
172#[bw(map = |&x| Self::into_bytes(x))]
173#[br(map = Self::from_bytes)]
174pub struct ShareAccessFlags {
175    pub read: bool,
176    pub write: bool,
177    pub delete: bool,
178    #[skip]
179    __: B29,
180}
181
182#[binrw::binrw]
183#[derive(Debug, PartialEq, Eq)]
184pub struct CreateResponse {
185    #[bw(calc = 89)]
186    #[br(assert(_structure_size == 89))]
187    _structure_size: u16,
188    pub oplock_level: OplockLevel,
189    pub flags: CreateResponseFlags,
190    pub create_action: CreateAction,
191    pub creation_time: FileTime,
192    pub last_access_time: FileTime,
193    pub last_write_time: FileTime,
194    pub change_time: FileTime,
195    pub allocation_size: u64,
196    pub endof_file: u64,
197    pub file_attributes: FileAttributes,
198    #[bw(calc = 0)]
199    #[br(assert(_reserved2 == 0))]
200    _reserved2: u32,
201    pub file_id: FileId,
202    // assert it's 8-aligned
203    #[br(assert(create_contexts_offset.value & 0x7 == 0))]
204    #[bw(calc = PosMarker::default())]
205    create_contexts_offset: PosMarker<u32>, // from smb header start
206    #[bw(calc = PosMarker::default())]
207    create_contexts_length: PosMarker<u32>, // bytes
208
209    /// Use the `CreateContextRespData::first_...` function family to get the first context of a specific type.
210    #[br(seek_before = SeekFrom::Start(create_contexts_offset.value as u64))]
211    #[br(map_stream = |s| s.take_seek(create_contexts_length.value.into()))]
212    #[bw(write_with = PosMarker::write_roff_size, args(&create_contexts_offset, &create_contexts_length))]
213    pub create_contexts: ChainedItemList<RespCreateContext, 8>,
214}
215
216#[bitfield]
217#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
218#[bw(map = |&x| Self::into_bytes(x))]
219#[br(map = Self::from_bytes)]
220pub struct CreateResponseFlags {
221    pub reparsepoint: bool,
222    #[skip]
223    __: B7,
224}
225
226// CreateAction
227#[binrw::binrw]
228#[derive(Debug, PartialEq, Eq)]
229#[brw(repr(u32))]
230pub enum CreateAction {
231    Superseded = 0x0,
232    Opened = 0x1,
233    Created = 0x2,
234    Overwritten = 0x3,
235}
236
237/// This is meant to be used within a [`ChainedItemList<T>`]!
238#[binrw::binrw]
239#[derive(Debug, PartialEq, Eq)]
240#[bw(import(is_last: bool))]
241#[allow(clippy::manual_non_exhaustive)]
242pub struct CreateContext<T>
243where
244    for<'a> T: BinRead<Args<'a> = (&'a Vec<u8>,)> + BinWrite<Args<'static> = ()>,
245{
246    #[bw(calc = PosMarker::default())]
247    _name_offset: PosMarker<u16>,
248    #[bw(calc = u16::try_from(name.len()).unwrap())]
249    name_length: u16,
250    #[bw(calc = 0)]
251    #[br(assert(_reserved == 0))]
252    _reserved: u16,
253    #[bw(calc = PosMarker::default())]
254    _data_offset: PosMarker<u16>,
255    #[bw(calc = PosMarker::default())]
256    _data_length: PosMarker<u32>,
257
258    #[brw(align_before = 8)]
259    #[br(count = name_length)]
260    #[bw(write_with = PosMarker::write_roff_plus, args(&_name_offset, CHAINED_ITEM_PREFIX_SIZE as u64))]
261    pub name: Vec<u8>,
262
263    #[brw(align_before = 8)]
264    #[bw(write_with = PosMarker::write_roff_size_b_plus, args(&_data_offset, &_data_length, &_name_offset, CHAINED_ITEM_PREFIX_SIZE as u64))]
265    #[br(map_stream = |s| s.take_seek(_data_length.value.into()), args(&name))]
266    pub data: T,
267}
268
269macro_rules! create_context_half {
270    (
271        $struct_name:ident {
272            $(
273                $context_type:ident : $req_type:ty,
274            )+
275        }
276    ) => {
277    paste::paste! {
278
279pub trait [<CreateContextData $struct_name Value>] : Into<CreateContext<[<CreateContext $struct_name Data>]>> {
280    const CONTEXT_NAME: &'static [u8];
281}
282
283#[doc = concat!("The `", stringify!($struct_name), "` Create Context data enum. This contains all the possible context types for ", stringify!($struct_name))]
284#[binrw::binrw]
285#[derive(Debug, PartialEq, Eq)]
286#[br(import(name: &Vec<u8>))]
287pub enum [<CreateContext $struct_name Data>] {
288    $(
289        #[br(pre_assert(name.as_slice() == CreateContextType::[<$context_type:upper>].name()))]
290        [<$context_type:camel $struct_name>]($req_type),
291    )+
292}
293
294impl [<CreateContext $struct_name Data>] {
295    pub fn name(&self) -> &'static [u8] {
296        match self {
297            $(
298                Self::[<$context_type:camel $struct_name>](_) => CreateContextType::[<$context_type:upper _NAME>],
299            )+
300        }
301    }
302
303    $(
304        pub fn [<as_ $context_type:snake>](&self) -> Option<&$req_type> {
305            match self {
306                Self::[<$context_type:camel $struct_name>](a) => Some(a),
307                _ => None,
308            }
309        }
310
311        pub fn [<first_ $context_type:snake>](val: &Vec<CreateContext<Self>>) -> Option<&$req_type> {
312            for ctx in val {
313                if let Self::[<$context_type:camel $struct_name>](a) = &ctx.data {
314                    return Some(a);
315                }
316            }
317            None
318        }
319    )+
320}
321
322$(
323    impl [<CreateContextData $struct_name Value>] for $req_type {
324        const CONTEXT_NAME: &'static [u8] = CreateContextType::[<$context_type:upper _NAME>];
325    }
326
327    impl From<$req_type> for CreateContext<[<CreateContext $struct_name Data>]> {
328        fn from(req: $req_type) -> Self {
329            CreateContext::<[<CreateContext $struct_name Data>]> {
330                name: <$req_type as [<CreateContextData $struct_name Value>]>::CONTEXT_NAME.to_vec(),
331                data: [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](req),
332            }
333        }
334    }
335
336    impl TryInto<$req_type> for CreateContext<[<CreateContext $struct_name Data>]> {
337        type Error = crate::SmbMsgError;
338        fn try_into(self) -> crate::Result<$req_type> {
339            match self.data {
340                [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](a) => Ok(a),
341                _ => Err(crate::SmbMsgError::UnexpectedContent {
342                    expected: stringify!($req_type),
343                    actual: "", // self.data.name(), TODO: Fix this by making name() a string.
344                }),
345            }
346        }
347    }
348)+
349
350pub type [<$struct_name CreateContext>] = CreateContext<[<CreateContext $struct_name Data>]>;
351        }
352    }
353}
354
355macro_rules! make_create_context {
356    (
357        $($context_type:ident : $class_name:literal, $req_type:ty, $res_type:ty, )+
358    ) => {
359        paste::paste!{
360
361pub enum CreateContextType {
362    $(
363        [<$context_type:upper>],
364    )+
365}
366
367impl CreateContextType {
368    $(
369        pub const [<$context_type:upper _NAME>]: &[u8] = $class_name;
370    )+
371
372    pub fn from_name(name: &[u8]) -> Option<CreateContextType> {
373        match name {
374            $(
375                Self::[<$context_type:upper _NAME>] => Some(Self::[<$context_type:upper>]),
376            )+
377            _ => None,
378        }
379    }
380
381    pub fn name(&self) -> &[u8] {
382        match self {
383            $(
384                Self::[<$context_type:upper>] => Self::[<$context_type:upper _NAME>],
385            )+
386        }
387    }
388}
389        }
390
391        create_context_half! {
392            Req {
393                $($context_type: $req_type,)+
394            }
395        }
396
397        create_context_half! {
398            Resp {
399                $($context_type: $res_type,)+
400            }
401        }
402    }
403}
404
405make_create_context!(
406    exta: b"ExtA", ChainedItemList<FileGetEaInformation>, ChainedItemList<FileFullEaInformation>,
407    secd: b"SecD", SdBuffer, SdBuffer,
408    dhnq: b"DHnQ", DurableHandleRequest, DurableHandleResponse,
409    dhnc: b"DHNc", DurableHandleReconnect, DurableHandleReconnect,
410    alsi: b"AlSi", AllocationSize, AllocationSize,
411    mxac: b"MxAc", QueryMaximalAccessRequest,  QueryMaximalAccessResponse,
412    twrp: b"TWrp", TimewarpToken, TimewarpToken,
413    qfid: b"QFid", QueryOnDiskIdReq,  QueryOnDiskIdResp,
414    rqls: b"RqLs", RequestLease, RequestLease, // v1+2
415    dh2q: b"DH2Q", DurableHandleRequestV2, DH2QResp,
416    dh2c: b"DH2C", DurableHandleReconnectV2, DurableHandleReconnectV2,
417    appinstid: b"\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74", AppInstanceId, AppInstanceId,
418    appinstver: b"\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10", AppInstanceVersion, AppInstanceVersion,
419    svhdxopendev: b"\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83", SvhdxOpenDeviceContext, SvhdxOpenDeviceContext,
420);
421
422macro_rules! empty_req {
423    ($name:ident) => {
424        #[binrw::binrw]
425        #[derive(Debug, PartialEq, Eq)]
426        pub struct $name;
427    };
428}
429
430pub type SdBuffer = SecurityDescriptor;
431
432#[binrw::binrw]
433#[derive(Debug, PartialEq, Eq, Default)]
434pub struct DurableHandleRequest {
435    #[bw(calc = 0)]
436    #[br(assert(durable_request == 0))]
437    durable_request: u128,
438}
439
440#[binrw::binrw]
441#[derive(Debug, PartialEq, Eq, Default)]
442pub struct DurableHandleResponse {
443    #[bw(calc = 0)]
444    _reserved: u64,
445}
446
447#[binrw::binrw]
448#[derive(Debug, PartialEq, Eq)]
449pub struct DurableHandleReconnect {
450    pub durable_request: FileId,
451}
452#[binrw::binrw]
453#[derive(Debug, PartialEq, Eq, Default)]
454pub struct QueryMaximalAccessRequest {
455    pub timestamp: Option<FileTime>,
456}
457
458#[binrw::binrw]
459#[derive(Debug, PartialEq, Eq)]
460pub struct AllocationSize {
461    pub allocation_size: u64,
462}
463
464#[binrw::binrw]
465#[derive(Debug, PartialEq, Eq)]
466pub struct TimewarpToken {
467    pub tiemstamp: FileTime,
468}
469
470#[binrw::binrw]
471#[derive(Debug, PartialEq, Eq)]
472pub enum RequestLease {
473    RqLsReqv1(RequestLeaseV1),
474    RqLsReqv2(RequestLeaseV2),
475}
476
477#[binrw::binrw]
478#[derive(Debug, PartialEq, Eq)]
479pub struct RequestLeaseV1 {
480    pub lease_key: u128,
481    pub lease_state: LeaseState,
482    #[bw(calc = 0)]
483    #[br(assert(lease_flags == 0))]
484    lease_flags: u32,
485    #[bw(calc = 0)]
486    #[br(assert(lease_duration == 0))]
487    lease_duration: u64,
488}
489#[binrw::binrw]
490#[derive(Debug, PartialEq, Eq)]
491pub struct RequestLeaseV2 {
492    pub lease_key: u128,
493    pub lease_state: LeaseState,
494    #[br(assert(lease_flags == 0 || lease_flags == 4))]
495    pub lease_flags: u32,
496    #[bw(calc = 0)]
497    #[br(assert(lease_duration == 0))]
498    lease_duration: u64,
499    pub parent_lease_key: u128,
500    pub epoch: u16,
501    #[bw(calc = 0)]
502    #[br(assert(reserved == 0))]
503    reserved: u16,
504}
505
506empty_req!(QueryOnDiskIdReq);
507
508#[binrw::binrw]
509#[derive(Debug, PartialEq, Eq)]
510pub struct DurableHandleRequestV2 {
511    pub timeout: u32,
512    pub flags: DurableHandleV2Flags,
513    #[bw(calc = 0)]
514    #[br(assert(_reserved == 0))]
515    _reserved: u64,
516    pub create_guid: Guid,
517}
518
519#[bitfield]
520#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
521#[bw(map = |&x| Self::into_bytes(x))]
522#[br(map = Self::from_bytes)]
523pub struct DurableHandleV2Flags {
524    #[skip]
525    __: bool,
526    pub persistent: bool, // 0x2
527    #[skip]
528    __: B30,
529}
530
531#[binrw::binrw]
532#[derive(Debug, PartialEq, Eq)]
533pub struct DurableHandleReconnectV2 {
534    file_id: FileId,
535    create_guid: Guid,
536    flags: DurableHandleV2Flags,
537}
538
539#[binrw::binrw]
540#[derive(Debug, PartialEq, Eq)]
541pub struct AppInstanceId {
542    #[bw(calc = 20)]
543    #[br(assert(structure_size == 20))]
544    structure_size: u16,
545    #[bw(calc = 0)]
546    #[br(assert(_reserved == 0))]
547    _reserved: u16,
548    pub app_instance_id: Guid,
549}
550#[binrw::binrw]
551#[derive(Debug, PartialEq, Eq)]
552pub struct AppInstanceVersion {
553    #[bw(calc = 24)]
554    #[br(assert(structure_size == 24))]
555    structure_size: u16,
556    #[bw(calc = 0)]
557    #[br(assert(_reserved == 0))]
558    _reserved: u16,
559    #[bw(calc = 0)]
560    #[br(assert(_reserved2 == 0))]
561    _reserved2: u32,
562    pub app_instance_version_high: u64,
563    pub app_instance_version_low: u64,
564}
565
566#[binrw::binrw]
567#[derive(Debug, PartialEq, Eq)]
568pub enum SvhdxOpenDeviceContext {
569    V1(SvhdxOpenDeviceContextV1),
570    V2(SvhdxOpenDeviceContextV2),
571}
572
573/// [MS-RSVD sections 2.2.4.12 and 2.2.4.32.](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rsvd/6ec20c83-a6a7-49d5-ae60-72070f91d5e0)
574#[binrw::binrw]
575#[derive(Debug, PartialEq, Eq)]
576pub struct SvhdxOpenDeviceContextV1 {
577    pub version: u32,
578    pub has_initiator_id: Boolean,
579    #[bw(calc = 0)]
580    #[br(assert(reserved1 == 0))]
581    reserved1: u8,
582    #[bw(calc = 0)]
583    #[br(assert(reserved2 == 0))]
584    reserved2: u16,
585    pub initiator_id: Guid,
586    pub flags: u32,
587    pub originator_flags: u32,
588    pub open_request_id: u64,
589    pub initiator_host_name_length: u16,
590    pub initiator_host_name: [u16; 126 / 2],
591}
592
593#[binrw::binrw]
594#[derive(Debug, PartialEq, Eq)]
595pub struct SvhdxOpenDeviceContextV2 {
596    pub version: u32,
597    pub has_initiator_id: Boolean,
598    #[bw(calc = 0)]
599    #[br(assert(reserved1 == 0))]
600    reserved1: u8,
601    #[bw(calc = 0)]
602    #[br(assert(reserved2 == 0))]
603    reserved2: u16,
604    pub initiator_id: Guid,
605    pub flags: u32,
606    pub originator_flags: u32,
607    pub open_request_id: u64,
608    pub initiator_host_name_length: u16,
609    pub initiator_host_name: [u16; 126 / 2],
610    pub virtual_disk_properties_initialized: u32,
611    pub server_service_version: u32,
612    pub virtual_sector_size: u32,
613    pub physical_sector_size: u32,
614    pub virtual_size: u64,
615}
616
617#[binrw::binrw]
618#[derive(Debug, PartialEq, Eq)]
619pub struct QueryMaximalAccessResponse {
620    pub query_status: Status,
621    pub maximal_access: FileAccessMask,
622}
623
624#[binrw::binrw]
625#[derive(Debug, PartialEq, Eq)]
626pub struct QueryOnDiskIdResp {
627    pub file_id: u64,
628    pub volume_id: u64,
629    #[bw(calc = 0)]
630    #[br(assert(_reserved == 0))]
631    _reserved: u128,
632}
633
634#[binrw::binrw]
635#[derive(Debug, PartialEq, Eq)]
636pub struct DH2QResp {
637    pub timeout: u32,
638    pub flags: DurableHandleV2Flags,
639}
640
641#[binrw::binrw]
642#[derive(Debug)]
643pub struct CloseRequest {
644    #[bw(calc = 24)]
645    #[br(assert(_structure_size == 24))]
646    _structure_size: u16,
647    #[bw(calc = CloseFlags::new().with_postquery_attrib(true))]
648    #[br(assert(_flags == CloseFlags::new().with_postquery_attrib(true)))]
649    _flags: CloseFlags,
650    #[bw(calc = 0)]
651    #[br(assert(_reserved == 0))]
652    _reserved: u32,
653    pub file_id: FileId,
654}
655
656#[binrw::binrw]
657#[derive(Debug)]
658pub struct CloseResponse {
659    #[bw(calc = 60)]
660    #[br(assert(_structure_size == 60))]
661    _structure_size: u16,
662    pub flags: CloseFlags,
663    #[bw(calc = 0)]
664    #[br(assert(_reserved == 0))]
665    _reserved: u32,
666    pub creation_time: FileTime,
667    pub last_access_time: FileTime,
668    pub last_write_time: FileTime,
669    pub change_time: FileTime,
670    pub allocation_size: u64,
671    pub endof_file: u64,
672    pub file_attributes: FileAttributes,
673}
674
675#[bitfield]
676#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
677#[bw(map = |&x| Self::into_bytes(x))]
678#[br(map = Self::from_bytes)]
679pub struct CloseFlags {
680    pub postquery_attrib: bool,
681    #[skip]
682    __: B15,
683}
684
685#[cfg(test)]
686mod tests {
687    use crate::*;
688
689    use super::*;
690
691    #[test]
692    pub fn test_create_request_written_correctly() {
693        let file_name = "hello";
694        let request = CreateRequest {
695            requested_oplock_level: OplockLevel::None,
696            impersonation_level: ImpersonationLevel::Impersonation,
697            desired_access: FileAccessMask::from_bytes(0x00100081u32.to_le_bytes()),
698            file_attributes: FileAttributes::new(),
699            share_access: ShareAccessFlags::new()
700                .with_read(true)
701                .with_write(true)
702                .with_delete(true),
703            create_disposition: CreateDisposition::Open,
704            create_options: CreateOptions::new()
705                .with_synchronous_io_nonalert(true)
706                .with_disallow_exclusive(true),
707            name: file_name.into(),
708            contexts: vec![
709                DurableHandleRequestV2 {
710                    timeout: 0,
711                    flags: DurableHandleV2Flags::new(),
712                    create_guid: 0x821680290c007b8b11efc0a0c679a320u128.to_le_bytes().into(),
713                }
714                .into(),
715                QueryMaximalAccessRequest::default().into(),
716                QueryOnDiskIdReq.into(),
717            ]
718            .into(),
719        };
720        let data_without_header = encode_content(request.into());
721        assert_eq!(
722            data_without_header,
723            vec![
724                0x39, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
725                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x81, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
726                0x7, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x20, 0x0, 0x2, 0x0, 0x78, 0x0, 0xa, 0x0,
727                0x88, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x68, 0x0, 0x65, 0x0, 0x6c, 0x0, 0x6c,
728                0x0, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x10, 0x0, 0x4,
729                0x0, 0x0, 0x0, 0x18, 0x0, 0x20, 0x0, 0x0, 0x0, 0x44, 0x48, 0x32, 0x51, 0x0, 0x0,
730                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
731                0x0, 0x0, 0x20, 0xa3, 0x79, 0xc6, 0xa0, 0xc0, 0xef, 0x11, 0x8b, 0x7b, 0x0, 0xc,
732                0x29, 0x80, 0x16, 0x82, 0x18, 0x0, 0x0, 0x0, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x18,
733                0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x78, 0x41, 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
734                0x0, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x51, 0x46,
735                0x69, 0x64, 0x0, 0x0, 0x0, 0x0
736            ]
737        )
738    }
739
740    #[test]
741    pub fn test_create_response_parsed_correctly() {
742        let data: [u8; 240] = [
743            0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
744            0x01, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
745            0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x61, 0x00,
746            0x00, 0x14, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
747            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x00,
748            0x00, 0x00, 0x3c, 0x08, 0x38, 0x96, 0xae, 0x4b, 0xdb, 0x01, 0xc8, 0x55, 0x4b, 0x70,
749            0x6b, 0x58, 0xdb, 0x01, 0x62, 0x0c, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x01, 0x62, 0x0c,
750            0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
751            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
752            0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
753            0x0c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x20, 0x00,
754            0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00,
755            0x4d, 0x78, 0x41, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
756            0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00,
757            0x20, 0x00, 0x00, 0x00, 0x51, 0x46, 0x69, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xe7,
758            0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd9, 0xcf, 0x17, 0xb0, 0x00, 0x00, 0x00, 0x00,
759            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
760            0x00, 0x00,
761        ];
762        let m = decode_content(&data).content.to_create().unwrap();
763        assert_eq!(
764            m,
765            CreateResponse {
766                oplock_level: OplockLevel::None,
767                flags: CreateResponseFlags::new(),
768                create_action: CreateAction::Opened,
769                creation_time: 133783827154208828.into(),
770                last_access_time: 133797832406291912.into(),
771                last_write_time: 133783939554544738.into(),
772                change_time: 133783939554544738.into(),
773                allocation_size: 0,
774                endof_file: 0,
775                file_attributes: FileAttributes::new().with_directory(true),
776                file_id: 950737950337192747837452976457u128.to_le_bytes().into(),
777                create_contexts: vec![
778                    QueryMaximalAccessResponse {
779                        query_status: Status::Success,
780                        maximal_access: FileAccessMask::from_bytes(0x001f01ffu32.to_le_bytes()),
781                    }
782                    .into(),
783                    QueryOnDiskIdResp {
784                        file_id: 0x400000001e72a,
785                        volume_id: 0xb017cfd9,
786                    }
787                    .into(),
788                ]
789                .into()
790            }
791        )
792    }
793}