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 { size: SizedStringSize::bytes16(name_length) })]
97 pub name: SizedWideString,
98
99 #[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#[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()))]
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#[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]
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 pastey::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: "", }),
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 pastey::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, 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, #[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#[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}