iscsi_client_rs/models/logout/
response.rs1use anyhow::{Result, bail};
9use tracing::{error, warn};
10use zerocopy::{
11 BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U16, U32,
12};
13
14use crate::{
15 client::pdu_connection::FromBytes,
16 models::{
17 common::{BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, SendingData},
18 data_fromat::ZeroCopyType,
19 logout::common::RawLogoutResponseCode,
20 opcode::{BhsOpcode, Opcode, RawBhsOpcode},
21 },
22};
23
24#[repr(C)]
26#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
27pub struct LogoutResponse {
28 pub opcode: RawBhsOpcode, pub flags: u8, pub response: RawLogoutResponseCode, reserved0: u8, pub total_ahs_length: u8, pub data_segment_length: [u8; 3], reserved1: [u8; 8], pub initiator_task_tag: InitiatorTaskTag, reserved2: [u8; 4], pub stat_sn: U32<BigEndian>, pub exp_cmd_sn: U32<BigEndian>, pub max_cmd_sn: U32<BigEndian>, reserved3: [u8; 4], pub time2wait: U16<BigEndian>, pub time2retain: U16<BigEndian>, reserved4: [u8; 4], }
45
46impl LogoutResponse {
47 pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
49 if buf.len() != HEADER_LEN {
50 bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
51 }
52 buf.copy_from_slice(self.as_bytes());
53 Ok(())
54 }
55
56 pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
58 let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
59 .map_err(|e| anyhow::anyhow!("failed convert buffer LogoutResponse: {e}"))?;
60 if hdr.opcode.opcode_known() != Some(Opcode::LogoutResp) {
61 anyhow::bail!(
62 "LogoutResponse: invalid opcode 0x{:02x}",
63 hdr.opcode.opcode_raw()
64 );
65 }
66 Ok(hdr)
67 }
68
69 #[inline]
71 pub fn is_final(&self) -> bool {
72 (self.flags & 0b1000_0000) != 0
73 }
74
75 #[inline]
77 pub fn set_final(&mut self) {
78 self.flags |= 0b1000_0000;
79 }
80}
81
82impl SendingData for LogoutResponse {
83 fn get_final_bit(&self) -> bool {
84 self.is_final()
85 }
86
87 fn set_final_bit(&mut self) {
88 self.set_final();
89 }
90
91 fn get_continue_bit(&self) -> bool {
92 false
93 }
94
95 fn set_continue_bit(&mut self) {
96 warn!("Logout Response cannot be marked as Contine");
97 }
98}
99
100impl FromBytes for LogoutResponse {
101 fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
102 LogoutResponse::from_bhs_bytes(bytes)
103 }
104}
105
106impl BasicHeaderSegment for LogoutResponse {
107 #[inline]
108 fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
109 self.to_bhs_bytes(buf)
110 }
111
112 #[inline]
113 fn get_opcode(&self) -> Result<BhsOpcode> {
114 BhsOpcode::try_from(self.opcode.raw())
115 }
116
117 #[inline]
118 fn get_initiator_task_tag(&self) -> u32 {
119 self.initiator_task_tag.get()
120 }
121
122 #[inline]
123 fn get_ahs_length_bytes(&self) -> usize {
124 (self.total_ahs_length as usize) * 4
125 }
126
127 #[inline]
128 fn set_ahs_length_bytes(&mut self, len: u8) {
129 self.total_ahs_length = len >> 2;
130 }
131
132 #[inline]
133 fn get_data_length_bytes(&self) -> usize {
134 u32::from_be_bytes([
135 0,
136 self.data_segment_length[0],
137 self.data_segment_length[1],
138 self.data_segment_length[2],
139 ]) as usize
140 }
141
142 #[inline]
143 fn set_data_length_bytes(&mut self, len: u32) {
144 error!("LogoutResp must have zero DataSegmentLength");
145 let be = len.to_be_bytes();
146 self.data_segment_length = [be[1], be[2], be[3]];
147 }
148
149 #[inline]
150 fn get_header_diggest(&self, _: bool) -> usize {
151 0
152 }
153
154 #[inline]
155 fn get_data_diggest(&self, _: bool) -> usize {
156 0
157 }
158}
159
160impl ZeroCopyType for LogoutResponse {}