1use binrw::{NullWideString, io::TakeSeekExt, prelude::*};
3use modular_bitfield::prelude::*;
4use smb_dtyp::binrw_util::prelude::*;
5
6use crate::{Dialect, NegotiateSecurityMode};
7
8use crate::dfsc::{ReqGetDfsReferral, ReqGetDfsReferralEx, RespGetDfsReferral};
9use smb_dtyp::*;
10use smb_fscc::*;
11
12use super::common::IoctlRequestContent;
13use crate::IoctlBuffer;
14use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
15use std::ops::{Deref, DerefMut};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u32)]
19pub enum FsctlCodes {
20 DfsGetReferrals = 0x00060194,
21 OffloadRead = 0x00094264,
22 PipePeek = 0x0011400C,
23 PipeWait = 0x00110018,
24 PipeTransceive = 0x0011C017,
25 SrvCopychunk = 0x001440F2,
26 SrvEnumerateSnapshots = 0x00144064,
27 SrvRequestResumeKey = 0x00140078,
28 SrvReadHash = 0x001441bb,
29 SrvCopychunkWrite = 0x001480F2,
30 LmrRequestResiliency = 0x001401D4,
31 QueryNetworkInterfaceInfo = 0x001401FC,
32 SetReparsePoint = 0x000900A4,
33 DfsGetReferralsEx = 0x000601B0,
34 FileLevelTrim = 0x00098208,
35 ValidateNegotiateInfo = 0x00140204,
36 QueryAllocatedRanges = 0x000940CF,
37}
38
39#[binrw::binrw]
42#[derive(Debug, PartialEq, Eq)]
43pub struct SrvCopychunkCopy {
44 pub source_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
45 #[bw(try_calc = chunks.len().try_into())]
46 chunk_count: u32,
47 #[bw(calc = 0)]
48 _reserved: u32,
49 #[br(count = chunk_count)]
50 pub chunks: Vec<SrvCopychunkItem>,
51}
52
53impl SrvCopychunkCopy {
54 pub const SRV_KEY_LENGTH: usize = 24;
55 pub const SIZE: usize = Self::SRV_KEY_LENGTH + 4 + 4;
56}
57
58#[binrw::binrw]
59#[derive(Debug, PartialEq, Eq)]
60pub struct SrvCopychunkItem {
61 pub source_offset: u64,
62 pub target_offset: u64,
63 pub length: u32,
64 #[bw(calc = 0)]
65 _reserved: u32,
66}
67
68impl SrvCopychunkItem {
69 pub const SIZE: usize = size_of::<u64>() * 2 + size_of::<u32>() * 2;
70}
71
72impl IoctlRequestContent for SrvCopychunkCopy {
73 fn get_bin_size(&self) -> u32 {
74 (Self::SIZE + self.chunks.len() * SrvCopychunkItem::SIZE) as u32
75 }
76}
77
78#[binrw::binrw]
79#[derive(Debug, PartialEq, Eq)]
80pub struct SrvReadHashReq {
81 #[bw(calc = 1)]
83 #[br(assert(hash_type == 1))]
84 pub hash_type: u32,
85 #[br(assert((1..=2).contains(&hash_version)))]
87 #[bw(assert((1..=2).contains(hash_version)))]
88 pub hash_version: u32,
89 pub hash_retrieval_type: SrvHashRetrievalType,
90}
91
92impl IoctlRequestContent for SrvReadHashReq {
93 fn get_bin_size(&self) -> u32 {
94 size_of::<u32>() as u32 * 3
95 }
96}
97
98#[binrw::binrw]
99#[derive(Debug, PartialEq, Eq)]
100#[brw(repr(u32))]
101pub enum SrvHashRetrievalType {
102 HashBased = 1,
103 FileBased = 2,
104}
105
106#[binrw::binrw]
108#[derive(Debug, PartialEq, Eq)]
109pub struct NetworkResiliencyRequest {
110 pub timeout: u32,
112 #[bw(calc = 0)]
113 pub _reserved: u32,
114}
115
116impl IoctlRequestContent for NetworkResiliencyRequest {
117 fn get_bin_size(&self) -> u32 {
118 size_of::<u32>() as u32 * 2
119 }
120}
121
122#[binrw::binrw]
123#[derive(Debug, PartialEq, Eq)]
124pub struct ValidateNegotiateInfoRequest {
125 pub capabilities: u32,
126 pub guid: Guid,
127 pub security_mode: NegotiateSecurityMode,
128 #[bw(try_calc = dialects.len().try_into())]
129 dialect_count: u16,
130 #[br(count = dialect_count)]
131 pub dialects: Vec<Dialect>,
132}
133
134impl IoctlRequestContent for ValidateNegotiateInfoRequest {
135 fn get_bin_size(&self) -> u32 {
136 (size_of::<u32>()
137 + Guid::GUID_SIZE
138 + 2
139 + size_of::<u16>()
140 + self.dialects.len() * size_of::<u16>()) as u32
141 }
142}
143
144#[binrw::binrw]
145#[derive(Debug, PartialEq, Eq)]
146pub struct SrvSnapshotArray {
147 pub number_of_snap_shots: u32,
148 pub number_of_snap_shots_returned: u32,
149 #[bw(calc = PosMarker::default())]
150 pub snap_shot_array_size: PosMarker<u32>,
151 #[br(parse_with = binrw::helpers::until_eof, map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
152 #[bw(write_with = PosMarker::write_size, args(&snap_shot_array_size))]
153 pub snap_shots: Vec<NullWideString>,
154}
155
156pub trait FsctlResponseContent: for<'a> BinRead<Args<'a> = ()> + std::fmt::Debug {
158 const FSCTL_CODES: &'static [FsctlCodes];
159}
160
161macro_rules! impl_fsctl_response {
162 ($code:ident, $type:ty) => {
163 impl FsctlResponseContent for $type {
164 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$code];
165 }
166 };
167}
168
169#[binrw::binrw]
170#[derive(Debug, PartialEq, Eq)]
171pub struct SrvRequestResumeKey {
172 pub resume_key: [u8; SrvCopychunkCopy::SRV_KEY_LENGTH],
173 #[bw(calc = 0)]
174 context_length: u32,
175 #[br(count = context_length)]
177 #[bw(assert(context.len() == context_length as usize))]
178 pub context: Vec<u8>,
179}
180
181impl_fsctl_response!(SrvRequestResumeKey, SrvRequestResumeKey);
182
183#[binrw::binrw]
184#[derive(Debug, PartialEq, Eq)]
185pub struct SrvCopychunkResponse {
186 pub chunks_written: u32,
187 pub chunk_bytes_written: u32,
188 pub total_bytes_written: u32,
189}
190
191impl_fsctl_response!(SrvCopychunk, SrvCopychunkResponse);
192
193#[binrw::binrw]
194#[derive(Debug, PartialEq, Eq)]
195pub struct SrvReadHashRes {
196 #[bw(calc = 1)]
198 #[br(assert(hash_type == 1))]
199 hash_type: u32,
200 #[br(assert((1..=2).contains(&hash_version)))]
202 #[bw(assert((1..=2).contains(hash_version)))]
203 hash_version: u32,
204 source_file_change_time: FileTime,
205 source_file_size: u64,
206 hash_blob_length: PosMarker<u32>,
207 hash_blob_offset: PosMarker<u32>,
208 dirty: u16,
209 #[bw(try_calc = source_file_name.len().try_into())]
210 source_file_name_length: u16,
211 #[br(count = source_file_name_length)]
212 source_file_name: Vec<u8>,
213}
214
215impl_fsctl_response!(SrvReadHash, SrvReadHashRes);
216
217#[binrw::binrw]
218#[derive(Debug, PartialEq, Eq)]
219pub struct SrvHashRetrieveHashBased {
220 pub offset: u64,
221 #[bw(try_calc = blob.len().try_into())]
222 buffer_length: u32,
223 #[bw(calc = 0)]
224 _reserved: u32,
225 #[br(count = buffer_length)]
227 blob: Vec<u8>,
228}
229
230impl_fsctl_response!(SrvReadHash, SrvHashRetrieveHashBased);
231
232#[binrw::binrw]
233#[derive(Debug, PartialEq, Eq)]
234pub struct SrvHashRetrieveFileBased {
235 pub file_data_offset: u64,
236 pub file_data_length: u64,
237 #[bw(try_calc = buffer.len().try_into())]
238 buffer_length: u32,
239 #[bw(calc = 0)]
240 _reserved: u32,
241 #[br(count = buffer_length)]
243 pub buffer: Vec<u8>,
244}
245
246pub type NetworkInterfacesInfo = ChainedItemList<NetworkInterfaceInfo>;
247
248impl_fsctl_response!(QueryNetworkInterfaceInfo, NetworkInterfacesInfo);
249
250#[binrw::binrw]
251#[derive(Debug, PartialEq, Eq)]
252pub struct NetworkInterfaceInfo {
253 pub if_index: u32,
254 pub capability: NetworkInterfaceCapability,
255 #[bw(calc = 0)]
256 _reserved: u32,
257 pub link_speed: u64,
258 pub sockaddr: SocketAddrStorage,
260}
261
262#[bitfield]
263#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
264#[bw(map = |&x| Self::into_bytes(x))]
265#[br(map = Self::from_bytes)]
266pub struct NetworkInterfaceCapability {
267 pub rss: bool,
268 pub rdma: bool,
269 #[skip]
270 __: B30,
271}
272
273#[binrw::binrw]
274#[derive(Debug, PartialEq, Eq)]
275pub enum SocketAddrStorage {
276 V4(SocketAddrStorageV4),
277 V6(SocketAddrStorageV6),
278}
279
280impl SocketAddrStorage {
281 pub fn socket_addr(&self) -> SocketAddr {
282 match self {
283 SocketAddrStorage::V4(v4) => SocketAddr::V4(v4.to_addr()),
284 SocketAddrStorage::V6(v6) => SocketAddr::V6(v6.to_addr()),
285 }
286 }
287}
288
289#[binrw::binrw]
290#[derive(Debug, PartialEq, Eq)]
291#[brw(magic(b"\x02\x00"))] pub struct SocketAddrStorageV4 {
293 pub port: u16,
294 pub address: u32,
295 #[bw(calc = [0; 128 - (2 + 2 + 4)])]
296 _reserved: [u8; 128 - (2 + 2 + 4)],
297}
298
299impl SocketAddrStorageV4 {
300 fn to_addr(&self) -> SocketAddrV4 {
301 SocketAddrV4::new(Ipv4Addr::from(self.address.to_be()), self.port)
302 }
303}
304
305#[binrw::binrw]
306#[derive(Debug, PartialEq, Eq)]
307#[brw(magic(b"\x17\x00"))] pub struct SocketAddrStorageV6 {
309 pub port: u16,
310 pub flow_info: u32,
311 pub address: u128,
312 pub scope_id: u32,
313 #[bw(calc = [0; 128 - (2 + 2 + 4 + 16 + 4)])]
314 _reserved: [u8; 128 - (2 + 2 + 4 + 16 + 4)],
315}
316
317impl SocketAddrStorageV6 {
318 fn to_addr(&self) -> SocketAddrV6 {
319 SocketAddrV6::new(
320 Ipv6Addr::from(self.address.to_be()),
321 self.port,
322 self.flow_info,
323 self.scope_id,
324 )
325 }
326}
327
328#[binrw::binrw]
329#[derive(Debug, PartialEq, Eq)]
330pub struct ValidateNegotiateInfoResponse {
331 pub capabilities: u32,
332 pub guid: Guid,
333 pub security_mode: NegotiateSecurityMode,
334 pub dialect: Dialect,
335}
336
337impl_fsctl_response!(ValidateNegotiateInfo, ValidateNegotiateInfoResponse);
338
339impl FsctlResponseContent for RespGetDfsReferral {
341 const FSCTL_CODES: &'static [FsctlCodes] =
342 &[FsctlCodes::DfsGetReferrals, FsctlCodes::DfsGetReferralsEx];
343}
344
345impl IoctlRequestContent for ReqGetDfsReferral {
346 fn get_bin_size(&self) -> u32 {
347 (size_of::<u16>() + (self.request_file_name.len() + 1) * size_of::<u16>()) as u32
348 }
349}
350
351impl IoctlRequestContent for ReqGetDfsReferralEx {
352 fn get_bin_size(&self) -> u32 {
353 (size_of::<u16>() * 2 + size_of::<u32>() + self.request_data.get_bin_size()) as u32
354 }
355}
356
357#[binrw::binrw]
358#[derive(Debug, PartialEq, Eq)]
359pub struct QueryAllocRangesItem {
360 pub offset: u64,
361 pub len: u64,
362}
363
364impl IoctlRequestContent for QueryAllocRangesItem {
365 fn get_bin_size(&self) -> u32 {
366 (size_of::<u64>() * 2) as u32
367 }
368}
369
370#[binrw::binrw]
371#[derive(Debug, Default, PartialEq, Eq)]
372pub struct QueryAllocRangesResult {
373 #[br(parse_with = binrw::helpers::until_eof)]
374 values: Vec<QueryAllocRangesItem>,
375}
376
377impl Deref for QueryAllocRangesResult {
378 type Target = Vec<QueryAllocRangesItem>;
379 fn deref(&self) -> &Self::Target {
380 &self.values
381 }
382}
383
384impl From<Vec<QueryAllocRangesItem>> for QueryAllocRangesResult {
385 fn from(value: Vec<QueryAllocRangesItem>) -> Self {
386 Self { values: value }
387 }
388}
389
390impl_fsctl_response!(QueryAllocatedRanges, QueryAllocRangesResult);
391
392#[binrw::binrw]
397#[derive(Debug, PartialEq, Eq)]
398pub struct PipeWaitRequest {
399 pub timeout: u64,
402 #[bw(calc = name.size() as u32)]
403 name_length: u32,
404 pub timeout_specified: Boolean,
407 #[bw(calc = 0)]
408 _padding: u8,
409 #[br(args {size: SizedStringSize::bytes(name_length)})]
412 pub name: SizedWideString,
413}
414
415impl IoctlRequestContent for PipeWaitRequest {
416 fn get_bin_size(&self) -> u32 {
417 (size_of::<u64>()
418 + size_of::<u32>()
419 + size_of::<Boolean>()
420 + size_of::<u8>()
421 + self.name.size() as usize) as u32
422 }
423}
424
425#[binrw::binrw]
429#[derive(Debug, PartialEq, Eq)]
430pub struct SetReparsePointRequest {
431 #[bw(assert((reparse_tag & 0x80000000 == 0) == reparse_guid.is_some()))]
433 pub reparse_tag: u32,
434 #[bw(calc = reparse_data.len() as u32)]
435 reparse_data_length: u32,
436 #[br(if(reparse_tag & 0x80000000 == 0))]
440 pub reparse_guid: Option<Guid>,
441 #[br(count = reparse_data_length)]
443 pub reparse_data: Vec<u8>,
444}
445
446impl IoctlRequestContent for SetReparsePointRequest {
447 fn get_bin_size(&self) -> u32 {
448 (size_of::<u32>()
449 + size_of::<u32>()
450 + self.reparse_guid.as_ref().map_or(0, |_| size_of::<Guid>())
451 + self.reparse_data.len()) as u32
452 }
453}
454
455#[binrw::binrw]
456#[derive(Debug, PartialEq, Eq)]
457pub struct FileLevelTrimRequest {
458 #[bw(calc = 0)]
460 _key: u32,
461 #[bw(calc = ranges.len() as u32)]
462 num_ranges: u32,
463 #[br(count = num_ranges)]
465 pub ranges: Vec<FileLevelTrimRange>,
466}
467
468#[binrw::binrw]
472#[derive(Debug, PartialEq, Eq)]
473pub struct FileLevelTrimRange {
474 pub offset: u64,
476 pub length: u64,
478}
479
480impl IoctlRequestContent for FileLevelTrimRequest {
481 fn get_bin_size(&self) -> u32 {
482 (size_of::<u32>() + size_of::<u32>() + self.ranges.len() * size_of::<FileLevelTrimRange>())
483 as u32
484 }
485}
486
487#[binrw::binrw]
489#[derive(Debug, PartialEq, Eq)]
490pub struct PipePeekResponse {
491 pub named_pipe_state: NamedPipeState,
493 #[bw(calc = data.len() as u32)]
494 read_data_available: u32,
496 pub number_of_messages: u32,
498 pub message_length: u32,
500 #[br(count = read_data_available as u64)]
502 pub data: Vec<u8>,
503}
504
505impl_fsctl_response!(PipePeek, PipePeekResponse);
506
507#[binrw::binrw]
509#[derive(Debug, PartialEq, Eq)]
510pub struct SrvEnumerateSnapshotsResponse {
511 pub number_of_snap_shots: u32,
513 pub number_of_snap_shots_returned: u32,
517 #[bw(calc = PosMarker::default())]
519 snap_shot_array_size: PosMarker<u32>,
520 #[br(map_stream = |s| s.take_seek(snap_shot_array_size.value as u64))]
522 pub snap_shots: MultiSz,
523}
524
525impl_fsctl_response!(SrvEnumerateSnapshots, SrvEnumerateSnapshotsResponse);
526
527#[binrw::binrw]
529#[derive(Debug, PartialEq, Eq)]
530pub struct FileLevelTrimResponse {
531 pub num_ranges_processed: u32,
533}
534
535impl_fsctl_response!(FileLevelTrim, FileLevelTrimResponse);
536
537#[binrw::binrw]
539#[derive(Debug, PartialEq, Eq)]
540pub struct OffloadReadRequest {
541 #[bw(calc = 0x20)]
542 #[br(assert(_size == 0x20))]
543 _size: u32,
544 pub flags: u32,
546 pub token_time_to_live: u32,
548 #[bw(calc = 0)]
549 _reserved: u32,
550 pub file_offset: u64,
553 pub copy_length: u64,
556}
557
558impl IoctlRequestContent for OffloadReadRequest {
559 fn get_bin_size(&self) -> u32 {
560 (size_of::<u32>() * 4 + size_of::<u64>() * 2) as u32
561 }
562}
563
564#[binrw::binrw]
566#[derive(Debug, PartialEq, Eq)]
567pub struct OffloadReadResponse {
568 #[bw(calc = 528)]
569 #[br(assert(_size == 528))]
570 _size: u32,
571
572 pub all_zero_beyond_current_range: Boolean,
575 _padding: u8,
576 _padding2: u16,
577
578 pub transfer_length: u64,
583
584 pub token: [u8; 512], }
588
589impl_fsctl_response!(OffloadRead, OffloadReadResponse);
590
591macro_rules! make_newtype {
597 ($vis:vis $name:ident($inner:ty)) => {
598 #[binrw::binrw]
599 #[derive(Debug, PartialEq, Eq)]
600 pub struct $name(pub $inner);
601
602 impl $name {
603 pub fn new(inner: $inner) -> Self {
604 Self(inner)
605 }
606 }
607
608 impl From<$inner> for $name {
609 fn from(inner: $inner) -> Self {
610 Self(inner)
611 }
612 }
613
614 impl Deref for $name {
615 type Target = $inner;
616
617 fn deref(&self) -> &Self::Target {
618 &self.0
619 }
620 }
621
622 impl DerefMut for $name {
623 fn deref_mut(&mut self) -> &mut Self::Target {
624 &mut self.0
625 }
626 }
627 };
628}
629
630macro_rules! make_req_newtype {
631 ($vis:vis $name:ident($inner:ty)) => {
632 make_newtype!($vis $name($inner));
633 impl IoctlRequestContent for $name {
634 fn get_bin_size(&self) -> u32 {
635 self.0.get_bin_size()
636 }
637 }
638 }
639}
640
641macro_rules! make_res_newtype {
642 ($fsctl:ident: $vis:vis $name:ident($inner:ty)) => {
643 make_newtype!($vis $name($inner));
644 impl FsctlResponseContent for $name {
645 const FSCTL_CODES: &'static [FsctlCodes] = &[FsctlCodes::$fsctl];
646 }
647 }
648}
649
650make_req_newtype!(pub PipePeekRequest(()));
651make_req_newtype!(pub SrvEnumerateSnapshotsRequest(()));
652make_req_newtype!(pub SrvRequestResumeKeyRequest(()));
653make_req_newtype!(pub QueryNetworkInterfaceInfoRequest(()));
654make_req_newtype!(pub PipeTransceiveRequest(IoctlBuffer));
655make_req_newtype!(pub SrvCopyChunkCopyWrite(SrvCopychunkCopy));
656
657make_res_newtype!(
658 PipeWait: pub PipeWaitResponse(())
659);
660make_res_newtype!(
661 PipeTransceive: pub PipeTransceiveResponse(IoctlBuffer)
662);
663make_res_newtype!(
664 SetReparsePoint: pub SetReparsePointResponse(())
665);
666
667make_res_newtype!(
668 LmrRequestResiliency: pub LmrRequestResiliencyResponse(())
669);
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674
675 #[test]
676 fn test_fsctl_request_offload_write() {
677 let mut cursor = std::io::Cursor::new(Vec::new());
678 let req = OffloadReadRequest {
679 flags: 0,
680 token_time_to_live: 0,
681 file_offset: 0,
682 copy_length: 10485760,
683 };
684 req.write_le(&mut cursor).unwrap();
685 assert_eq!(cursor.position(), req.get_bin_size() as u64);
686 assert_eq!(
687 cursor.into_inner(),
688 [
689 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
690 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0
691 ]
692 );
693 }
694
695 #[test]
696 fn test_fsctl_request_resumekey_read() {
697 let data = [
698 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2, 0xdb,
699 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
700 ];
701 let mut cursor = std::io::Cursor::new(data);
702 let req: SrvRequestResumeKey = SrvRequestResumeKey::read_le(&mut cursor).unwrap();
703 assert_eq!(
704 req,
705 SrvRequestResumeKey {
706 resume_key: [
707 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2,
708 0xdb, 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
709 ],
710 context: vec![],
711 }
712 );
713 }
714
715 #[test]
716 fn test_fsctl_copychunk_write() {
717 let mut chunks = vec![];
718 const CHUNK_SIZE: u32 = 1048576; const TOTAL_SIZE: u32 = 10417096;
720 let block_num = u32::div_ceil(TOTAL_SIZE, CHUNK_SIZE);
721 for i in 0..block_num {
722 chunks.push(SrvCopychunkItem {
723 source_offset: (i * CHUNK_SIZE) as u64,
724 target_offset: (i * CHUNK_SIZE) as u64,
725 length: if i == block_num - 1 {
726 TOTAL_SIZE % CHUNK_SIZE
727 } else {
728 CHUNK_SIZE
729 },
730 });
731 }
732 let req = SrvCopychunkCopy {
733 source_key: [
734 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2, 0xdb,
735 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
736 ],
737 chunks,
738 };
739 let mut cursor = std::io::Cursor::new(Vec::new());
740 req.write_le(&mut cursor).unwrap();
741 assert_eq!(cursor.position(), req.get_bin_size() as u64);
742 assert_eq!(
743 cursor.into_inner(),
744 [
745 0x2d, 0x3, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x27, 0x11, 0x6a, 0x26, 0x30, 0xd2, 0xdb,
746 0x1, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
747 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
748 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
749 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
750 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0,
751 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0,
752 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
753 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0,
754 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0,
755 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
756 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x0, 0x0,
757 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0x0,
758 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0,
759 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0,
760 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0,
761 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0xf3, 0xe, 0x0, 0x0, 0x0, 0x0,
762 0x0
763 ]
764 )
765 }
766
767 #[test]
768 fn test_fsctl_copychunk_reponse_read() {
769 let data = [
770 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0xf3, 0x9e, 0x0,
771 ];
772 let mut cursor = std::io::Cursor::new(data);
773 let res: SrvCopychunkResponse = SrvCopychunkResponse::read_le(&mut cursor).unwrap();
774 assert_eq!(
775 res,
776 SrvCopychunkResponse {
777 chunks_written: 10,
778 chunk_bytes_written: 0,
779 total_bytes_written: 10417096,
780 }
781 );
782 }
783
784 #[test]
785 fn test_fsctl_query_alloc_ranges_resp() {
786 let data = [
787 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
788 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xb6, 0x00, 0x00,
789 0x00, 0x00, 0x00, 0x00,
790 ];
791
792 let mut cursor = std::io::Cursor::new(data);
793 let res = QueryAllocRangesResult::read_le(&mut cursor).unwrap();
794 assert_eq!(
795 res,
796 QueryAllocRangesResult {
797 values: vec![
798 QueryAllocRangesItem {
799 offset: 0,
800 len: 4096,
801 },
802 QueryAllocRangesItem {
803 offset: 8192,
804 len: 46801,
805 },
806 ],
807 }
808 );
809 }
810
811 #[test]
812 fn test_fsctl_query_network_interfaces_response_parse() {
813 let data = [
814 0x98, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
815 0xca, 0x9a, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xac, 0x10, 0xcc, 0x84, 0x0,
816 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
817 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
818 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
819 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
820 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
821 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
822 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
823 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
824 0xca, 0x9a, 0x3b, 0x0, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfe,
825 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xc, 0x29, 0xff, 0xfe, 0x9f, 0x8b, 0xf3, 0x0,
826 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
827 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
828 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
829 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
830 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
831 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
832 0x0,
833 ];
834
835 let mut cursor = std::io::Cursor::new(data);
836 let res = NetworkInterfacesInfo::read_le(&mut cursor).unwrap();
837 assert_eq!(
838 NetworkInterfacesInfo::from(vec![
839 NetworkInterfaceInfo {
840 if_index: 2,
841 capability: NetworkInterfaceCapability::new().with_rdma(true),
842 link_speed: 1000000000,
843 sockaddr: SocketAddrStorage::V4(SocketAddrStorageV4 {
844 port: 0,
845 address: 0xac10cc84u32.to_be(),
846 })
847 },
848 NetworkInterfaceInfo {
849 if_index: 2,
850 capability: NetworkInterfaceCapability::new().with_rdma(true),
851 link_speed: 1000000000,
852 sockaddr: SocketAddrStorage::V6(SocketAddrStorageV6 {
853 port: 0,
854 flow_info: 0,
855 address: 0xfe80000000000000020c29fffe9f8bf3u128.to_be(),
856 scope_id: 0,
857 })
858 },
859 ]),
860 res
861 );
862 }
863}