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()), parse_with = binrw::helpers::until_eof)]
102    #[bw(write_with = ReqCreateContext::write_chained_roff_size, args(&_create_contexts_offset, &_create_contexts_length))]
103    pub contexts: Vec<ReqCreateContext>,
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()), parse_with = binrw::helpers::until_eof)]
212    #[bw(write_with = RespCreateContext::write_chained_roff_size, args(&create_contexts_offset, &create_contexts_length))]
213    pub create_contexts: Vec<RespCreateContext>,
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#[binrw::binrw]
238#[derive(Debug, PartialEq, Eq)]
239#[bw(import(is_last: bool))]
240#[allow(clippy::manual_non_exhaustive)]
241pub struct CreateContext<T>
242where
243    for<'a> T: BinRead<Args<'a> = (&'a Vec<u8>,)> + BinWrite<Args<'static> = ()>,
244{
245    #[br(assert(next_entry_offset.value % 8 == 0))]
246    #[bw(calc = PosMarker::default())]
247    next_entry_offset: PosMarker<u32>,
248
249    #[bw(calc = PosMarker::default())]
250    _name_offset: PosMarker<u16>,
251    #[bw(calc = u16::try_from(name.len()).unwrap())]
252    name_length: u16,
253    #[bw(calc = 0)]
254    #[br(assert(_reserved == 0))]
255    _reserved: u16,
256    #[bw(calc = PosMarker::default())]
257    _data_offset: PosMarker<u16>,
258    #[bw(calc = PosMarker::default())]
259    _data_length: PosMarker<u32>,
260
261    #[brw(align_before = 8)]
262    #[br(count = name_length)]
263    #[bw(write_with = PosMarker::write_roff_b, args(&_name_offset, &next_entry_offset))]
264    pub name: Vec<u8>,
265
266    #[brw(align_before = 8)]
267    #[bw(write_with = PosMarker::write_roff_size_b, args(&_data_offset, &_data_length, &next_entry_offset))]
268    #[br(map_stream = |s| s.take_seek(_data_length.value.into()), args(&name))]
269    pub data: T,
270
271    #[br(seek_before = next_entry_offset.seek_relative(true))]
272    #[bw(if(!is_last))]
273    #[bw(align_before = 8)]
274    #[bw(write_with = PosMarker::write_roff, args(&next_entry_offset))]
275    _write_offset_placeholder: (),
276}
277
278impl<T> CreateContext<T>
279where
280    for<'a> T: BinRead<Args<'a> = (&'a Vec<u8>,)> + BinWrite<Args<'static> = ()>,
281{
282    #[binrw::writer(writer, endian)]
283    #[allow(clippy::ptr_arg)] // writer accepts exact type.
284    pub fn write_chained_roff_size(
285        value: &Vec<CreateContext<T>>,
286        offset_dest: &PosMarker<u32>,
287        size_dest: &PosMarker<u32>,
288    ) -> BinResult<()> {
289        // Offset needs the absolute position of the start of the list.
290        let start_offset = offset_dest.write_offset(writer, endian)?;
291        for (i, item) in value.iter().enumerate() {
292            item.write_options(writer, endian, (i == value.len() - 1,))?;
293        }
294        // Size is the difference between the start of the list and the current position.
295        size_dest.write_back(writer.stream_position()? - start_offset, writer, endian)?;
296        Ok(())
297    }
298}
299
300macro_rules! create_context_half {
301    (
302        $struct_name:ident {
303            $(
304                $context_type:ident : $req_type:ty,
305            )+
306        }
307    ) => {
308    paste::paste! {
309
310pub trait [<CreateContextData $struct_name Value>] : Into<CreateContext<[<CreateContext $struct_name Data>]>> {
311    const CONTEXT_NAME: &'static [u8];
312}
313
314#[binrw::binrw]
315#[derive(Debug, PartialEq, Eq)]
316#[br(import(name: &Vec<u8>))]
317pub enum [<CreateContext $struct_name Data>] {
318    $(
319        #[br(pre_assert(name.as_slice() == CreateContextType::[<$context_type:upper>].name()))]
320        [<$context_type:camel $struct_name>]($req_type),
321    )+
322}
323
324impl [<CreateContext $struct_name Data>] {
325    pub fn name(&self) -> &'static [u8] {
326        match self {
327            $(
328                Self::[<$context_type:camel $struct_name>](_) => CreateContextType::[<$context_type:upper _NAME>],
329            )+
330        }
331    }
332
333    $(
334        pub fn [<as_ $context_type:snake>](&self) -> Option<&$req_type> {
335            match self {
336                Self::[<$context_type:camel $struct_name>](a) => Some(a),
337                _ => None,
338            }
339        }
340
341        pub fn [<first_ $context_type:snake>](val: &Vec<CreateContext<Self>>) -> Option<&$req_type> {
342            for ctx in val {
343                if let Self::[<$context_type:camel $struct_name>](a) = &ctx.data {
344                    return Some(a);
345                }
346            }
347            None
348        }
349    )+
350}
351
352$(
353    impl [<CreateContextData $struct_name Value>] for $req_type {
354        const CONTEXT_NAME: &'static [u8] = CreateContextType::[<$context_type:upper _NAME>];
355    }
356
357    impl From<$req_type> for CreateContext<[<CreateContext $struct_name Data>]> {
358        fn from(req: $req_type) -> Self {
359            CreateContext::<[<CreateContext $struct_name Data>]> {
360                name: <$req_type as [<CreateContextData $struct_name Value>]>::CONTEXT_NAME.to_vec(),
361                data: [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](req),
362                _write_offset_placeholder: (),
363            }
364        }
365    }
366
367    impl TryInto<$req_type> for CreateContext<[<CreateContext $struct_name Data>]> {
368        type Error = crate::SmbMsgError;
369        fn try_into(self) -> crate::Result<$req_type> {
370            match self.data {
371                [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](a) => Ok(a),
372                _ => Err(crate::SmbMsgError::UnexpectedContent {
373                    expected: stringify!($req_type),
374                    actual: "", // self.data.name(), TODO: Fix this by making name() a string.
375                }),
376            }
377        }
378    }
379)+
380
381pub type [<$struct_name CreateContext>] = CreateContext<[<CreateContext $struct_name Data>]>;
382        }
383    }
384}
385
386macro_rules! make_create_context {
387    (
388        $($context_type:ident : $class_name:literal, $req_type:ident, $res_type:ident, )+
389    ) => {
390        paste::paste!{
391
392pub enum CreateContextType {
393    $(
394        [<$context_type:upper>],
395    )+
396}
397
398impl CreateContextType {
399    $(
400        pub const [<$context_type:upper _NAME>]: &[u8] = $class_name;
401    )+
402
403    pub fn from_name(name: &[u8]) -> Option<CreateContextType> {
404        match name {
405            $(
406                Self::[<$context_type:upper _NAME>] => Some(Self::[<$context_type:upper>]),
407            )+
408            _ => None,
409        }
410    }
411
412    pub fn name(&self) -> &[u8] {
413        match self {
414            $(
415                Self::[<$context_type:upper>] => Self::[<$context_type:upper _NAME>],
416            )+
417        }
418    }
419}
420        }
421
422        create_context_half! {
423            Req {
424                $($context_type: $req_type,)+
425            }
426        }
427
428        create_context_half! {
429            Resp {
430                $($context_type: $res_type,)+
431            }
432        }
433    }
434}
435
436make_create_context!(
437    exta: b"ExtA", EaBuffer, EaBuffer,
438    secd: b"SecD", SdBuffer, SdBuffer,
439    dhnq: b"DHnQ", DurableHandleRequest, DurableHandleResponse,
440    dhnc: b"DHNc", DurableHandleReconnect, DurableHandleReconnect,
441    alsi: b"AlSi", AllocationSize, AllocationSize,
442    mxac: b"MxAc", QueryMaximalAccessRequest,  QueryMaximalAccessResponse,
443    twrp: b"TWrp", TimewarpToken, TimewarpToken,
444    qfid: b"QFid", QueryOnDiskIdReq,  QueryOnDiskIdResp,
445    rqls: b"RqLs", RequestLease, RequestLease, // v1+2
446    dh2q: b"DH2Q", DurableHandleRequestV2, DH2QResp,
447    dh2c: b"DH2C", DurableHandleReconnectV2, DurableHandleReconnectV2,
448    appinstid: b"\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74", AppInstanceId, AppInstanceId,
449    appinstver: b"\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10", AppInstanceVersion, AppInstanceVersion,
450    svhdxopendev: b"\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83", SvhdxOpenDeviceContext, SvhdxOpenDeviceContext,
451);
452
453macro_rules! empty_req {
454    ($name:ident) => {
455        #[binrw::binrw]
456        #[derive(Debug, PartialEq, Eq)]
457        pub struct $name;
458    };
459}
460
461#[binrw::binrw]
462#[derive(Debug, PartialEq, Eq)]
463pub struct EaBuffer {
464    #[br(parse_with = binrw::helpers::until_eof)]
465    #[bw(write_with = FileFullEaInformationCommon::write_chained)]
466    info: Vec<FileFullEaInformationCommon>,
467}
468
469pub type SdBuffer = SecurityDescriptor;
470
471#[binrw::binrw]
472#[derive(Debug, PartialEq, Eq, Default)]
473pub struct DurableHandleRequest {
474    #[bw(calc = 0)]
475    #[br(assert(durable_request == 0))]
476    durable_request: u128,
477}
478
479#[binrw::binrw]
480#[derive(Debug, PartialEq, Eq, Default)]
481pub struct DurableHandleResponse {
482    #[bw(calc = 0)]
483    _reserved: u64,
484}
485
486#[binrw::binrw]
487#[derive(Debug, PartialEq, Eq)]
488pub struct DurableHandleReconnect {
489    pub durable_request: FileId,
490}
491#[binrw::binrw]
492#[derive(Debug, PartialEq, Eq, Default)]
493pub struct QueryMaximalAccessRequest {
494    pub timestamp: Option<FileTime>,
495}
496
497#[binrw::binrw]
498#[derive(Debug, PartialEq, Eq)]
499pub struct AllocationSize {
500    pub allocation_size: u64,
501}
502
503#[binrw::binrw]
504#[derive(Debug, PartialEq, Eq)]
505pub struct TimewarpToken {
506    pub tiemstamp: FileTime,
507}
508
509#[binrw::binrw]
510#[derive(Debug, PartialEq, Eq)]
511pub enum RequestLease {
512    RqLsReqv1(RequestLeaseV1),
513    RqLsReqv2(RequestLeaseV2),
514}
515
516#[binrw::binrw]
517#[derive(Debug, PartialEq, Eq)]
518pub struct RequestLeaseV1 {
519    pub lease_key: u128,
520    pub lease_state: LeaseState,
521    #[bw(calc = 0)]
522    #[br(assert(lease_flags == 0))]
523    lease_flags: u32,
524    #[bw(calc = 0)]
525    #[br(assert(lease_duration == 0))]
526    lease_duration: u64,
527}
528#[binrw::binrw]
529#[derive(Debug, PartialEq, Eq)]
530pub struct RequestLeaseV2 {
531    pub lease_key: u128,
532    pub lease_state: LeaseState,
533    #[br(assert(lease_flags == 0 || lease_flags == 4))]
534    pub lease_flags: u32,
535    #[bw(calc = 0)]
536    #[br(assert(lease_duration == 0))]
537    lease_duration: u64,
538    pub parent_lease_key: u128,
539    pub epoch: u16,
540    #[bw(calc = 0)]
541    #[br(assert(reserved == 0))]
542    reserved: u16,
543}
544
545empty_req!(QueryOnDiskIdReq);
546
547#[binrw::binrw]
548#[derive(Debug, PartialEq, Eq)]
549pub struct DurableHandleRequestV2 {
550    pub timeout: u32,
551    pub flags: DurableHandleV2Flags,
552    #[bw(calc = 0)]
553    #[br(assert(_reserved == 0))]
554    _reserved: u64,
555    pub create_guid: Guid,
556}
557
558#[bitfield]
559#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
560#[bw(map = |&x| Self::into_bytes(x))]
561#[br(map = Self::from_bytes)]
562pub struct DurableHandleV2Flags {
563    #[skip]
564    __: bool,
565    pub persistent: bool, // 0x2
566    #[skip]
567    __: B30,
568}
569
570#[binrw::binrw]
571#[derive(Debug, PartialEq, Eq)]
572pub struct DurableHandleReconnectV2 {
573    file_id: FileId,
574    create_guid: Guid,
575    flags: DurableHandleV2Flags,
576}
577
578#[binrw::binrw]
579#[derive(Debug, PartialEq, Eq)]
580pub struct AppInstanceId {
581    #[bw(calc = 20)]
582    #[br(assert(structure_size == 20))]
583    structure_size: u16,
584    #[bw(calc = 0)]
585    #[br(assert(_reserved == 0))]
586    _reserved: u16,
587    pub app_instance_id: Guid,
588}
589#[binrw::binrw]
590#[derive(Debug, PartialEq, Eq)]
591pub struct AppInstanceVersion {
592    #[bw(calc = 24)]
593    #[br(assert(structure_size == 24))]
594    structure_size: u16,
595    #[bw(calc = 0)]
596    #[br(assert(_reserved == 0))]
597    _reserved: u16,
598    #[bw(calc = 0)]
599    #[br(assert(_reserved2 == 0))]
600    _reserved2: u32,
601    pub app_instance_version_high: u64,
602    pub app_instance_version_low: u64,
603}
604
605#[binrw::binrw]
606#[derive(Debug, PartialEq, Eq)]
607pub enum SvhdxOpenDeviceContext {
608    V1(SvhdxOpenDeviceContextV1),
609    V2(SvhdxOpenDeviceContextV2),
610}
611
612/// [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)
613#[binrw::binrw]
614#[derive(Debug, PartialEq, Eq)]
615pub struct SvhdxOpenDeviceContextV1 {
616    pub version: u32,
617    pub has_initiator_id: Boolean,
618    #[bw(calc = 0)]
619    #[br(assert(reserved1 == 0))]
620    reserved1: u8,
621    #[bw(calc = 0)]
622    #[br(assert(reserved2 == 0))]
623    reserved2: u16,
624    pub initiator_id: Guid,
625    pub flags: u32,
626    pub originator_flags: u32,
627    pub open_request_id: u64,
628    pub initiator_host_name_length: u16,
629    pub initiator_host_name: [u16; 126 / 2],
630}
631
632#[binrw::binrw]
633#[derive(Debug, PartialEq, Eq)]
634pub struct SvhdxOpenDeviceContextV2 {
635    pub version: u32,
636    pub has_initiator_id: Boolean,
637    #[bw(calc = 0)]
638    #[br(assert(reserved1 == 0))]
639    reserved1: u8,
640    #[bw(calc = 0)]
641    #[br(assert(reserved2 == 0))]
642    reserved2: u16,
643    pub initiator_id: Guid,
644    pub flags: u32,
645    pub originator_flags: u32,
646    pub open_request_id: u64,
647    pub initiator_host_name_length: u16,
648    pub initiator_host_name: [u16; 126 / 2],
649    pub virtual_disk_properties_initialized: u32,
650    pub server_service_version: u32,
651    pub virtual_sector_size: u32,
652    pub physical_sector_size: u32,
653    pub virtual_size: u64,
654}
655
656#[binrw::binrw]
657#[derive(Debug, PartialEq, Eq)]
658pub struct QueryMaximalAccessResponse {
659    pub query_status: Status,
660    pub maximal_access: FileAccessMask,
661}
662
663#[binrw::binrw]
664#[derive(Debug, PartialEq, Eq)]
665pub struct QueryOnDiskIdResp {
666    pub file_id: u64,
667    pub volume_id: u64,
668    #[bw(calc = 0)]
669    #[br(assert(_reserved == 0))]
670    _reserved: u128,
671}
672
673#[binrw::binrw]
674#[derive(Debug, PartialEq, Eq)]
675pub struct DH2QResp {
676    pub timeout: u32,
677    pub flags: DurableHandleV2Flags,
678}
679
680#[binrw::binrw]
681#[derive(Debug)]
682pub struct CloseRequest {
683    #[bw(calc = 24)]
684    #[br(assert(_structure_size == 24))]
685    _structure_size: u16,
686    #[bw(calc = CloseFlags::new().with_postquery_attrib(true))]
687    #[br(assert(_flags == CloseFlags::new().with_postquery_attrib(true)))]
688    _flags: CloseFlags,
689    #[bw(calc = 0)]
690    #[br(assert(_reserved == 0))]
691    _reserved: u32,
692    pub file_id: FileId,
693}
694
695#[binrw::binrw]
696#[derive(Debug)]
697pub struct CloseResponse {
698    #[bw(calc = 60)]
699    #[br(assert(_structure_size == 60))]
700    _structure_size: u16,
701    pub flags: CloseFlags,
702    #[bw(calc = 0)]
703    #[br(assert(_reserved == 0))]
704    _reserved: u32,
705    pub creation_time: FileTime,
706    pub last_access_time: FileTime,
707    pub last_write_time: FileTime,
708    pub change_time: FileTime,
709    pub allocation_size: u64,
710    pub endof_file: u64,
711    pub file_attributes: FileAttributes,
712}
713
714#[bitfield]
715#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
716#[bw(map = |&x| Self::into_bytes(x))]
717#[br(map = Self::from_bytes)]
718pub struct CloseFlags {
719    pub postquery_attrib: bool,
720    #[skip]
721    __: B15,
722}
723
724#[cfg(test)]
725mod tests {
726    use crate::*;
727
728    use super::*;
729
730    #[test]
731    pub fn test_create_request_written_correctly() {
732        let file_name = "hello";
733        let request = CreateRequest {
734            requested_oplock_level: OplockLevel::None,
735            impersonation_level: ImpersonationLevel::Impersonation,
736            desired_access: FileAccessMask::from_bytes(0x00100081u32.to_le_bytes()),
737            file_attributes: FileAttributes::new(),
738            share_access: ShareAccessFlags::new()
739                .with_read(true)
740                .with_write(true)
741                .with_delete(true),
742            create_disposition: CreateDisposition::Open,
743            create_options: CreateOptions::new()
744                .with_synchronous_io_nonalert(true)
745                .with_disallow_exclusive(true),
746            name: file_name.into(),
747            contexts: vec![
748                DurableHandleRequestV2 {
749                    timeout: 0,
750                    flags: DurableHandleV2Flags::new(),
751                    create_guid: 0x821680290c007b8b11efc0a0c679a320u128.to_le_bytes().into(),
752                }
753                .into(),
754                QueryMaximalAccessRequest::default().into(),
755                QueryOnDiskIdReq.into(),
756            ],
757        };
758        let data_without_header = encode_content(request.into());
759        assert_eq!(
760            data_without_header,
761            vec![
762                0x39, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
763                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x81, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
764                0x7, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x20, 0x0, 0x2, 0x0, 0x78, 0x0, 0xa, 0x0,
765                0x88, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x68, 0x0, 0x65, 0x0, 0x6c, 0x0, 0x6c,
766                0x0, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x10, 0x0, 0x4,
767                0x0, 0x0, 0x0, 0x18, 0x0, 0x20, 0x0, 0x0, 0x0, 0x44, 0x48, 0x32, 0x51, 0x0, 0x0,
768                0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
769                0x0, 0x0, 0x20, 0xa3, 0x79, 0xc6, 0xa0, 0xc0, 0xef, 0x11, 0x8b, 0x7b, 0x0, 0xc,
770                0x29, 0x80, 0x16, 0x82, 0x18, 0x0, 0x0, 0x0, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x18,
771                0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x78, 0x41, 0x63, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
772                0x0, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x0, 0x0, 0x51, 0x46,
773                0x69, 0x64, 0x0, 0x0, 0x0, 0x0
774            ]
775        )
776    }
777
778    #[test]
779    pub fn test_create_response_parsed_correctly() {
780        let data: [u8; 240] = [
781            0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
782            0x01, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
783            0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x61, 0x00,
784            0x00, 0x14, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
785            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x01, 0x00,
786            0x00, 0x00, 0x3c, 0x08, 0x38, 0x96, 0xae, 0x4b, 0xdb, 0x01, 0xc8, 0x55, 0x4b, 0x70,
787            0x6b, 0x58, 0xdb, 0x01, 0x62, 0x0c, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x01, 0x62, 0x0c,
788            0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
789            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
790            0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
791            0x0c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x20, 0x00,
792            0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x08, 0x00, 0x00, 0x00,
793            0x4d, 0x78, 0x41, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
794            0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00,
795            0x20, 0x00, 0x00, 0x00, 0x51, 0x46, 0x69, 0x64, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xe7,
796            0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd9, 0xcf, 0x17, 0xb0, 0x00, 0x00, 0x00, 0x00,
797            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
798            0x00, 0x00,
799        ];
800        let m = decode_content(&data).content.to_create().unwrap();
801        assert_eq!(
802            m,
803            CreateResponse {
804                oplock_level: OplockLevel::None,
805                flags: CreateResponseFlags::new(),
806                create_action: CreateAction::Opened,
807                creation_time: 133783827154208828.into(),
808                last_access_time: 133797832406291912.into(),
809                last_write_time: 133783939554544738.into(),
810                change_time: 133783939554544738.into(),
811                allocation_size: 0,
812                endof_file: 0,
813                file_attributes: FileAttributes::new().with_directory(true),
814                file_id: 950737950337192747837452976457u128.to_le_bytes().into(),
815                create_contexts: vec![
816                    QueryMaximalAccessResponse {
817                        query_status: Status::Success,
818                        maximal_access: FileAccessMask::from_bytes(0x001f01ffu32.to_le_bytes()),
819                    }
820                    .into(),
821                    QueryOnDiskIdResp {
822                        file_id: 0x400000001e72a,
823                        volume_id: 0xb017cfd9,
824                    }
825                    .into(),
826                ]
827            }
828        )
829    }
830}