1use 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#[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 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)] #[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, #[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 #[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#[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 #[br(assert(create_contexts_offset.value & 0x7 == 0))]
204 #[bw(calc = PosMarker::default())]
205 create_contexts_offset: PosMarker<u32>, #[bw(calc = PosMarker::default())]
207 create_contexts_length: PosMarker<u32>, #[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#[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)] pub fn write_chained_roff_size(
285 value: &Vec<CreateContext<T>>,
286 offset_dest: &PosMarker<u32>,
287 size_dest: &PosMarker<u32>,
288 ) -> BinResult<()> {
289 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_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: "", }),
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, 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, #[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#[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}