1use std::fmt::{Debug, Display};
4use std::io::Cursor;
5
6#[cfg(feature = "client")]
7use std::io::SeekFrom;
8
9use super::header::Status;
10use super::*;
11use binrw::io::TakeSeekExt;
12use binrw::prelude::*;
13use modular_bitfield::prelude::*;
14use smb_dtyp::SecurityDescriptor;
15use smb_dtyp::{Guid, binrw_util::prelude::*};
16use smb_fscc::*;
17use smb_msg_derive::*;
18
19#[binrw::binrw]
21#[derive(PartialEq, Eq, Clone, Copy, Default)]
22pub struct FileId {
23 pub persistent: u64,
24 pub volatile: u64,
25}
26
27impl FileId {
28 pub const EMPTY: FileId = FileId {
29 persistent: 0,
30 volatile: 0,
31 };
32 pub const FULL: FileId = FileId {
35 persistent: u64::MAX,
36 volatile: u64::MAX,
37 };
38}
39
40impl From<[u8; 16]> for FileId {
41 fn from(data: [u8; 16]) -> Self {
42 let mut cursor = Cursor::new(data);
43 Self::read_le(&mut cursor).unwrap()
44 }
45}
46
47impl From<Guid> for FileId {
48 fn from(guid: Guid) -> Self {
49 let mut cursor = Cursor::new(Vec::new());
50 guid.write_le(&mut cursor).unwrap();
51 <Self as From<[u8; 16]>>::from(cursor.into_inner().try_into().unwrap())
52 }
53}
54
55impl Display for FileId {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 write!(f, "{{{:x}|{:x}}}", self.persistent, self.volatile)
58 }
59}
60
61impl Debug for FileId {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "FileId({})", self)
64 }
65}
66
67#[smb_request(size = 57)]
72pub struct CreateRequest {
73 reserved: u8,
75 pub requested_oplock_level: OplockLevel,
77 pub impersonation_level: ImpersonationLevel,
79 reserved: u64,
81 reserved: u64,
82 pub desired_access: FileAccessMask,
84 pub file_attributes: FileAttributes,
86 pub share_access: ShareAccessFlags,
88 pub create_disposition: CreateDisposition,
90 pub create_options: CreateOptions,
92 #[bw(calc = PosMarker::default())]
93 #[br(temp)]
94 _name_offset: PosMarker<u16>,
95 #[bw(try_calc = name.size().try_into())]
96 #[br(temp)]
97 name_length: u16, #[bw(calc = PosMarker::default())]
99 #[br(temp)]
100 _create_contexts_offset: PosMarker<u32>,
101 #[bw(calc = PosMarker::default())]
102 #[br(temp)]
103 _create_contexts_length: PosMarker<u32>,
104
105 #[brw(align_before = 8)]
107 #[bw(write_with = PosMarker::write_aoff, args(&_name_offset))]
108 #[br(args { size: SizedStringSize::bytes16(name_length) })]
109 pub name: SizedWideString,
110
111 #[brw(align_before = 8)]
114 #[br(map_stream = |s| s.take_seek(_create_contexts_length.value.into()))]
115 #[bw(write_with = PosMarker::write_roff_size, args(&_create_contexts_offset, &_create_contexts_length))]
116 pub contexts: ChainedItemList<CreateContextRequest, 8>,
117}
118
119#[smb_request_binrw]
123#[derive(Copy, Clone)]
124#[brw(repr(u32))]
125pub enum ImpersonationLevel {
126 Anonymous = 0x0,
128 Identification = 0x1,
130 Impersonation = 0x2,
132 Delegate = 0x3,
134}
135
136#[smb_request_binrw]
141#[derive(Copy, Clone, Default)]
142#[brw(repr(u32))]
143pub enum CreateDisposition {
144 Superseded = 0x0,
146 #[default]
148 Open = 0x1,
149 Create = 0x2,
151 OpenIf = 0x3,
153 Overwrite = 0x4,
155 OverwriteIf = 0x5,
157}
158
159#[smb_dtyp::mbitfield]
163pub struct CreateOptions {
164 pub directory_file: bool,
166 pub write_through: bool,
168 pub sequential_only: bool,
170 pub no_intermediate_buffering: bool,
172
173 pub synchronous_io_alert: bool,
175 pub synchronous_io_nonalert: bool,
177 pub non_directory_file: bool,
179 #[skip]
180 __: bool,
181
182 pub complete_if_oplocked: bool,
184 pub no_ea_knowledge: bool,
186 pub open_remote_instance: bool,
188 pub random_access: bool,
190
191 pub delete_on_close: bool,
193 pub open_by_file_id: bool,
195 pub open_for_backup_intent: bool,
197 pub no_compression: bool,
199
200 pub open_requiring_oplock: bool,
202 pub disallow_exclusive: bool,
204 #[skip]
205 __: B2,
206
207 pub reserve_opfilter: bool,
209 pub open_reparse_point: bool,
211 pub open_no_recall: bool,
213 pub open_for_free_space_query: bool,
215
216 #[skip]
217 __: B8,
218}
219
220#[smb_dtyp::mbitfield]
224pub struct ShareAccessFlags {
225 pub read: bool,
227 pub write: bool,
229 pub delete: bool,
231 #[skip]
232 __: B29,
233}
234
235#[smb_response(size = 89)]
240pub struct CreateResponse {
241 pub oplock_level: OplockLevel,
243 pub flags: CreateResponseFlags,
245 pub create_action: CreateAction,
247 pub creation_time: FileTime,
249 pub last_access_time: FileTime,
251 pub last_write_time: FileTime,
253 pub change_time: FileTime,
255 pub allocation_size: u64,
257 pub endof_file: u64,
259 pub file_attributes: FileAttributes,
261 reserved: u32,
262 pub file_id: FileId,
264 #[br(assert(create_contexts_offset.value & 0x7 == 0))]
266 #[bw(calc = PosMarker::default())]
267 #[br(temp)]
268 create_contexts_offset: PosMarker<u32>, #[bw(calc = PosMarker::default())]
270 #[br(temp)]
271 create_contexts_length: PosMarker<u32>, #[br(seek_before = SeekFrom::Start(create_contexts_offset.value as u64))]
276 #[br(map_stream = |s| s.take_seek(create_contexts_length.value.into()))]
277 #[bw(write_with = PosMarker::write_roff_size, args(&create_contexts_offset, &create_contexts_length))]
278 pub create_contexts: ChainedItemList<CreateContextResponse, 8>,
279}
280
281#[smb_dtyp::mbitfield]
286pub struct CreateResponseFlags {
287 pub reparsepoint: bool,
289 #[skip]
290 __: B7,
291}
292
293#[smb_response_binrw]
297#[brw(repr(u32))]
298pub enum CreateAction {
299 Superseded = 0x0,
301 Opened = 0x1,
303 Created = 0x2,
305 Overwritten = 0x3,
307}
308
309macro_rules! create_context_half {
310 (
311 $struct_name:ident {
312 $(
313 $context_type:ident : $req_type:ty,
314 )+
315 }
316 ) => {
317 pastey::paste! {
318
319
320#[[<smb_ $struct_name:lower _binrw>]]
328#[bw(import(is_last: bool))]
329#[allow(clippy::manual_non_exhaustive)]
330pub struct [<CreateContext $struct_name:camel>]
331{
332 #[bw(calc = PosMarker::default())]
333 #[br(temp)]
334 _name_offset: PosMarker<u16>, #[bw(calc = u16::try_from(name.len()).unwrap())]
336 #[br(temp)]
337 name_length: u16,
338 reserved: u16,
339 #[bw(calc = PosMarker::default())]
340 #[br(temp)]
341 _data_offset: PosMarker<u16>,
342 #[bw(calc = PosMarker::default())]
343 #[br(temp)]
344 _data_length: PosMarker<u32>,
345
346 #[brw(align_before = 8)]
348 #[br(count = name_length)]
349 #[br(seek_before = _name_offset.seek_from(_name_offset.value as u64 - CHAINED_ITEM_PREFIX_SIZE as u64))]
350 #[bw(write_with = PosMarker::write_roff_plus, args(&_name_offset, CHAINED_ITEM_PREFIX_SIZE as u64))]
351 pub name: Vec<u8>,
352
353 #[bw(align_before = 8)]
355 #[br(assert(_data_offset.value % 8 == 0))]
356 #[bw(write_with = PosMarker::write_roff_size_b_plus, args(&_data_offset, &_data_length, &_name_offset, CHAINED_ITEM_PREFIX_SIZE as u64))]
357 #[br(seek_before = _name_offset.seek_from_if(_data_offset.value as u64 - CHAINED_ITEM_PREFIX_SIZE as u64, _data_length.value > 0))]
358 #[br(map_stream = |s| s.take_seek(_data_length.value.into()), args(&name))]
359 pub data: [<CreateContext $struct_name Data>],
360}
361
362#[doc = concat!("[`Create", stringify!($struct_name), "`]")]
364pub trait [<CreateContextData $struct_name Value>] : Into<[<CreateContext $struct_name:camel>]> {
366 const CONTEXT_NAME: &'static [u8];
367}
368
369#[doc = concat!("The [`Create", stringify!($struct_name), "`] Context data enum. ")]
370#[[<smb_ $struct_name:lower _binrw>]]
371#[br(import(name: &Vec<u8>))]
372pub enum [<CreateContext $struct_name Data>] {
373 $(
374 #[br(pre_assert(name.as_slice() == CreateContextType::[<$context_type:upper>].name()))]
375 [<$context_type:camel $struct_name>]($req_type),
376 )+
377}
378
379impl [<CreateContext $struct_name Data>] {
380 pub fn name(&self) -> &'static [u8] {
381 match self {
382 $(
383 Self::[<$context_type:camel $struct_name>](_) => CreateContextType::[<$context_type:upper _NAME>],
384 )+
385 }
386 }
387
388 $(
389 pub fn [<as_ $context_type:snake>](&self) -> Option<&$req_type> {
390 match self {
391 Self::[<$context_type:camel $struct_name>](a) => Some(a),
392 _ => None,
393 }
394 }
395
396 #[doc = concat!("Get the first `", stringify!($context_type), "` create context from the list, if any.")]
397 pub fn [<first_ $context_type:snake>](val: &[[<CreateContext $struct_name:camel>]]) -> Option<&$req_type> {
400 for ctx in val {
401 if let Self::[<$context_type:camel $struct_name>](a) = &ctx.data {
402 return Some(a);
403 }
404 }
405 None
406 }
407 )+
408}
409
410$(
411 impl [<CreateContextData $struct_name Value>] for $req_type {
412 const CONTEXT_NAME: &'static [u8] = CreateContextType::[<$context_type:upper _NAME>];
413 }
414
415 impl From<$req_type> for [<CreateContext $struct_name:camel>] {
416 fn from(req: $req_type) -> Self {
417 [<CreateContext $struct_name:camel>] {
418 name: <$req_type as [<CreateContextData $struct_name Value>]>::CONTEXT_NAME.to_vec(),
419 data: [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](req),
420 }
421 }
422 }
423
424 impl TryInto<$req_type> for [<CreateContext $struct_name:camel>] {
425 type Error = crate::SmbMsgError;
426 fn try_into(self) -> crate::Result<$req_type> {
427 match self.data {
428 [<CreateContext $struct_name Data>]::[<$context_type:camel $struct_name>](a) => Ok(a),
429 _ => Err(crate::SmbMsgError::UnexpectedContent {
430 expected: stringify!($req_type),
431 actual: "", }),
433 }
434 }
435 }
436)+
437 }
438 }
439}
440
441macro_rules! make_create_context {
443 (
444 $(
445 $(#[doc = $docstring:literal])*
446 $context_type:ident : $class_name:literal, $req_type:ty $(, $res_type:ty)?;
447 )+
448 ) => {
449 pastey::paste!{
450
451pub enum CreateContextType {
453 $(
454 $(#[doc = $docstring])*
455 [<$context_type:upper>],
456 )+
457}
458
459impl CreateContextType {
460 $(
461 #[doc = concat!("The name for the `", stringify!($context_type), "` create context.")]
462 pub const [<$context_type:upper _NAME>]: &[u8] = $class_name;
463 )+
464
465 pub fn from_name(name: &[u8]) -> Option<CreateContextType> {
466 match name {
467 $(
468 Self::[<$context_type:upper _NAME>] => Some(Self::[<$context_type:upper>]),
469 )+
470 _ => None,
471 }
472 }
473
474 pub fn name(&self) -> &[u8] {
475 match self {
476 $(
477 Self::[<$context_type:upper>] => Self::[<$context_type:upper _NAME>],
478 )+
479 }
480 }
481}
482 }
483
484 create_context_half! {
485 Request {
486 $($context_type: $req_type,)+
487 }
488 }
489
490 create_context_half! {
491 Response {
492 $($($context_type: $res_type,)?)+
493 }
494 }
495 }
496}
497
498make_create_context!(
499 exta: b"ExtA", ChainedItemList<FileFullEaInformation>;
501 secd: b"SecD", SecurityDescriptor;
503 dhnq: b"DHnQ", DurableHandleRequest, DurableHandleResponse;
505 dhnc: b"DHNc", DurableHandleReconnect;
507 alsi: b"AlSi", AllocationSize;
509 mxac: b"MxAc", QueryMaximalAccessRequest, QueryMaximalAccessResponse;
511 twrp: b"TWrp", TimewarpToken;
513 qfid: b"QFid", QueryOnDiskIdReq, QueryOnDiskIdResp;
515 rqls: b"RqLs", RequestLease, RequestLease; dh2q: b"DH2Q", DurableHandleRequestV2, DH2QResp;
519 dh2c: b"DH2C", DurableHandleReconnectV2;
521 appinstid: b"\x45\xBC\xA6\x6A\xEF\xA7\xF7\x4A\x90\x08\xFA\x46\x2E\x14\x4D\x74", AppInstanceId, AppInstanceId;
523 appinstver: b"\xB9\x82\xD0\xB7\x3B\x56\x07\x4F\xA0\x7B\x52\x4A\x81\x16\xA0\x10", AppInstanceVersion, AppInstanceVersion;
525 svhdxopendev: b"\x9C\xCB\xCF\x9E\x04\xC1\xE6\x43\x98\x0E\x15\x8D\xA1\xF6\xEC\x83", SvhdxOpenDeviceContext, SvhdxOpenDeviceContext;
528);
529
530#[smb_request_binrw]
534pub struct DurableHandleRequest {
535 reserved: u128,
536}
537
538#[smb_response_binrw]
542pub struct DurableHandleResponse {
543 reserved: u64,
544}
545
546#[smb_request_binrw]
550pub struct DurableHandleReconnect {
551 pub durable_request: FileId,
553}
554#[smb_request_binrw]
558#[derive(Default)]
559pub struct QueryMaximalAccessRequest {
560 #[br(parse_with = binread_if_has_data)]
562 pub timestamp: Option<FileTime>,
563}
564
565#[smb_request_binrw]
569pub struct AllocationSize {
570 pub allocation_size: u64,
572}
573
574#[smb_request_binrw]
578pub struct TimewarpToken {
579 pub timestamp: FileTime,
581}
582
583#[smb_message_binrw]
588pub enum RequestLease {
589 RqLsReqv1(RequestLeaseV1),
590 RqLsReqv2(RequestLeaseV2),
591}
592
593#[smb_message_binrw]
598pub struct RequestLeaseV1 {
599 pub lease_key: u128,
601 pub lease_state: LeaseState,
603 #[bw(calc = 0)]
604 #[br(assert(lease_flags == 0))]
605 lease_flags: u32,
606 #[bw(calc = 0)]
607 #[br(assert(lease_duration == 0))]
608 lease_duration: u64,
609}
610
611#[smb_message_binrw]
616pub struct RequestLeaseV2 {
617 pub lease_key: u128,
619 pub lease_state: LeaseState,
621 pub lease_flags: LeaseFlags,
623 #[bw(calc = 0)]
624 #[br(assert(lease_duration == 0))]
625 lease_duration: u64,
626 pub parent_lease_key: u128,
628 pub epoch: u16,
630 reserved: u16,
631}
632
633#[smb_dtyp::mbitfield]
637pub struct LeaseFlags {
638 #[skip]
639 __: B2,
640 pub parent_lease_key_set: bool,
642 #[skip]
643 __: B29,
644}
645
646#[smb_request_binrw]
650pub struct QueryOnDiskIdReq;
651
652#[smb_request_binrw]
656pub struct DurableHandleRequestV2 {
657 pub timeout: u32,
659 pub flags: DurableHandleV2Flags,
661 reserved: u64,
662 pub create_guid: Guid,
664}
665
666#[smb_dtyp::mbitfield]
670pub struct DurableHandleV2Flags {
671 #[skip]
672 __: bool,
673 pub persistent: bool,
675 #[skip]
676 __: B30,
677}
678
679#[smb_request_binrw]
683pub struct DurableHandleReconnectV2 {
684 file_id: FileId,
686 create_guid: Guid,
688 flags: DurableHandleV2Flags,
690}
691
692#[smb_request_response(size = 20)]
696pub struct AppInstanceId {
697 reserved: u16,
698 pub app_instance_id: Guid,
700}
701
702#[smb_request_response(size = 24)]
706pub struct AppInstanceVersion {
707 reserved: u16,
708 reserved: u16,
709 reserved: u32,
710 pub app_instance_version_high: u64,
712 pub app_instance_version_low: u64,
714}
715
716#[smb_message_binrw]
720pub enum SvhdxOpenDeviceContext {
721 V1(SvhdxOpenDeviceContextV1),
722 V2(SvhdxOpenDeviceContextV2),
723}
724
725#[smb_message_binrw]
729pub struct SvhdxOpenDeviceContextV1 {
730 pub version: u32,
731 pub has_initiator_id: Boolean,
732 reserved: u8,
733 reserved: u16,
734 pub initiator_id: Guid,
735 pub flags: u32,
736 pub originator_flags: u32,
737 pub open_request_id: u64,
738 pub initiator_host_name_length: u16,
739 pub initiator_host_name: [u16; 126 / 2],
740}
741
742#[smb_message_binrw]
746pub struct SvhdxOpenDeviceContextV2 {
747 pub version: u32,
748 pub has_initiator_id: Boolean,
749 reserved: u8,
750 reserved: u16,
751 pub initiator_id: Guid,
752 pub flags: u32,
753 pub originator_flags: u32,
754 pub open_request_id: u64,
755 pub initiator_host_name_length: u16,
756 pub initiator_host_name: [u16; 126 / 2],
757 pub virtual_disk_properties_initialized: u32,
758 pub server_service_version: u32,
759 pub virtual_sector_size: u32,
760 pub physical_sector_size: u32,
761 pub virtual_size: u64,
762}
763
764#[smb_response_binrw]
765pub struct QueryMaximalAccessResponse {
766 pub query_status: Status,
770
771 pub maximal_access: FileAccessMask,
775}
776
777impl QueryMaximalAccessResponse {
778 pub fn is_success(&self) -> bool {
780 self.query_status == Status::Success
781 }
782
783 pub fn maximal_access(&self) -> Option<FileAccessMask> {
785 if self.is_success() {
786 Some(self.maximal_access)
787 } else {
788 None
789 }
790 }
791}
792
793#[smb_response_binrw]
797pub struct QueryOnDiskIdResp {
798 pub file_id: u64,
800 pub volume_id: u64,
802 reserved: u128,
803}
804
805#[smb_response_binrw]
810pub struct DH2QResp {
811 pub timeout: u32,
813 pub flags: DurableHandleV2Flags,
815}
816
817#[smb_request(size = 24)]
822pub struct CloseRequest {
823 #[bw(calc = CloseFlags::new().with_postquery_attrib(true))]
824 #[br(assert(_flags == CloseFlags::new().with_postquery_attrib(true)))]
825 _flags: CloseFlags,
826 reserved: u32,
827 pub file_id: FileId,
829}
830
831#[smb_response(size = 60)]
836pub struct CloseResponse {
837 pub flags: CloseFlags,
838 reserved: u32,
839 pub creation_time: FileTime,
841 pub last_access_time: FileTime,
843 pub last_write_time: FileTime,
845 pub change_time: FileTime,
847 pub allocation_size: u64,
849 pub endof_file: u64,
851 pub file_attributes: FileAttributes,
853}
854
855#[smb_dtyp::mbitfield]
859pub struct CloseFlags {
860 pub postquery_attrib: bool,
863 #[skip]
864 __: B15,
865}
866
867#[cfg(test)]
868mod tests {
869 use crate::*;
870
871 use super::*;
872
873 test_request! {
874 Create {
875 requested_oplock_level: OplockLevel::None,
876 impersonation_level: ImpersonationLevel::Impersonation,
877 desired_access: FileAccessMask::from_bytes(0x00100081u32.to_le_bytes()),
878 file_attributes: FileAttributes::new(),
879 share_access: ShareAccessFlags::new()
880 .with_read(true)
881 .with_write(true)
882 .with_delete(true),
883 create_disposition: CreateDisposition::Open,
884 create_options: CreateOptions::new()
885 .with_synchronous_io_nonalert(true)
886 .with_disallow_exclusive(true),
887 name: "hello".into(),
888 contexts: vec![
889 DurableHandleRequestV2 {
890 timeout: 0,
891 flags: DurableHandleV2Flags::new(),
892 create_guid: 0x821680290c007b8b11efc0a0c679a320u128.to_le_bytes().into(),
893 }
894 .into(),
895 QueryMaximalAccessRequest::default().into(),
896 QueryOnDiskIdReq.into(),
897 ]
898 .into(),
899 } => "390000000200000000000000000000000000000000000000810010000000000007000000010000002000020078000a008800000068
900 000000680065006c006c006f0000000000000038000000100004000000180020000000444832510000000000000000000000000000000000
901 00000020a379c6a0c0ef118b7b000c29801682180000001000040000001800000000004d7841630000000000000000100004000000180000
902 0000005146696400000000"
903 }
904
905 test_response! {
906 Create {
907 oplock_level: OplockLevel::None,
908 flags: CreateResponseFlags::new(),
909 create_action: CreateAction::Opened,
910 creation_time: 133783827154208828.into(),
911 last_access_time: 133797832406291912.into(),
912 last_write_time: 133783939554544738.into(),
913 change_time: 133783939554544738.into(),
914 allocation_size: 0,
915 endof_file: 0,
916 file_attributes: FileAttributes::new().with_directory(true),
917 file_id: 950737950337192747837452976457u128.to_le_bytes().into(),
918 create_contexts: vec![
919 QueryMaximalAccessResponse {
920 query_status: Status::Success,
921 maximal_access: FileAccessMask::from_bytes(0x001f01ffu32.to_le_bytes()),
922 }
923 .into(),
924 QueryOnDiskIdResp {
925 file_id: 0x400000001e72a,
926 volume_id: 0xb017cfd9,
927 }
928 .into(),
929 ]
930 .into()
931 } => "59000000010000003c083896ae4bdb01c8554b706b58db01620ccdc1c84bdb01620ccdc1c84bdb0100000000000000000000
932 0000000000001000000000000000490100000c000000090000000c0000009800000058000000200000001000040000001800080000
933 004d7841630000000000000000ff011f000000000010000400000018002000000051466964000000002ae7010000000400d9cf17b0
934 0000000000000000000000000000000000000000"
935 }
936
937 #[cfg(feature = "client")]
938 use smb_dtyp::make_guid;
939
940 test_response_read! {
941 server2016: Create {
942 oplock_level: OplockLevel::None,
943 flags: CreateResponseFlags::new(),
944 create_action: CreateAction::Opened,
945 creation_time: FileTime::ZERO,
946 last_access_time: FileTime::ZERO,
947 last_write_time: FileTime::ZERO,
948 change_time: FileTime::ZERO,
949 allocation_size: 4096,
950 endof_file: 0,
951 file_attributes: FileAttributes::new().with_normal(true),
952 file_id: make_guid!("00000001-0001-0000-0100-000001000000").into(),
953 create_contexts: vec![
954 QueryMaximalAccessResponse {
955 query_status: Status::NotMapped, maximal_access: FileAccessMask::default(),
957 }
958 .into(),
959 QueryOnDiskIdResp {
960 file_id: 0xffff870415d75290,
961 volume_id: 0xffffe682cb589c90,
962 }
963 .into(),
964 ].into(),
965 } => "59000000010000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000
966 000000008000000076007300010000000100000001000000010000009800000058000000200000001000040000001800080000004d7841
967 6300000000730000c0000000000000000010000400000018002000000051466964000000009052d7150487ffff909c58cb82e6ffff0000
968 0000000000000000000000000000"
969 }
970
971 use smb_dtyp::guid;
981 use time::macros::datetime;
982
983 test_binrw_request! {
988 struct DurableHandleRequest {} => "00000000000000000000000000000000"
989 }
990
991 test_binrw_response! {
992 struct DurableHandleResponse {} => "0000000000000000"
993 }
994
995 test_binrw_request! {
996 struct QueryMaximalAccessRequest {
997 timestamp: None,
998 } => ""
999 }
1000
1001 test_binrw_response! {
1002 struct QueryMaximalAccessResponse {
1003 query_status: Status::Success,
1004 maximal_access: FileAccessMask::from_bytes(0x001f01ffu32.to_le_bytes()),
1005 } => "00000000ff011f00"
1006 }
1007
1008 test_binrw_request! {
1009 struct QueryOnDiskIdReq {} => ""
1010 }
1011
1012 test_binrw_response! {
1013 struct QueryOnDiskIdResp {
1014 file_id: 0x2ae7010000000400,
1015 volume_id: 0xd9cf17b000000000,
1016 } => "000400000001e72a 00000000b017cfd9 00000000000000000000000000000000"
1017 }
1018
1019 test_binrw_request! {
1021 RequestLease => rqlsv2: RequestLease::RqLsReqv2(RequestLeaseV2 {
1022 lease_key: guid!("b69d8fd8-184b-7c4d-a359-40c8a53cd2b7").as_u128(),
1023 lease_state: LeaseState::new().with_read_caching(true).with_handle_caching(true),
1024 lease_flags: LeaseFlags::new().with_parent_lease_key_set(true),
1025 parent_lease_key: guid!("2d158ea3-55db-f749-9cd1-095496a06627").as_u128(),
1026 epoch: 0
1027 }) => "d88f9db64b184d7ca35940c8a53cd2b703000000040000000000000000000000a38e152ddb5549f79cd1095496a0662700000000"
1028 }
1029
1030 test_binrw_request! {
1031 struct AllocationSize {
1032 allocation_size: 0xebfef0d4c000,
1033 } => "00c0d4f0feeb0000"
1034 }
1035
1036 test_binrw_request! {
1037 struct DurableHandleRequestV2 {
1038 create_guid: guid!("5a08e844-45c3-234d-87c6-596d2bc8bca5"),
1039 flags: DurableHandleV2Flags::new(),
1040 timeout: 0,
1041 } => "0000000000000000000000000000000044e8085ac3454d2387c6596d2bc8bca5"
1042 }
1043
1044 test_binrw_response! {
1045 struct DH2QResp {
1046 timeout: 180000,
1047 flags: DurableHandleV2Flags::new(),
1048 } => "20bf020000000000"
1049 }
1050
1051 test_binrw_request! {
1052 struct TimewarpToken {
1053 timestamp: datetime!(2025-01-20 15:36:20.277632400).into(),
1054 } => "048fa10d516bdb01"
1055 }
1056
1057 test_binrw_request! {
1058 struct DurableHandleReconnectV2 {
1059 file_id: guid!("000000b3-0008-0000-dd00-000008000000").into(),
1060 create_guid: guid!("a23e428c-1bac-7e43-8451-91f9f2277a95"),
1061 flags: DurableHandleV2Flags::new(),
1062 } => "b300000008000000dd000000080000008c423ea2ac1b437e845191f9f2277a9500000000"
1063 }
1064}