iscsi_client_rs/models/logout/
request.rs1use anyhow::{Result, bail};
8use tracing::{debug, error, warn};
9use zerocopy::{
10 BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U16, U32,
11};
12
13use crate::{
14 client::pdu_connection::FromBytes,
15 models::{
16 common::{BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, SendingData},
17 data_fromat::ZeroCopyType,
18 logout::common::{LogoutReason, RawLogoutReason},
19 opcode::{BhsOpcode, Opcode, RawBhsOpcode},
20 },
21};
22
23#[repr(C)]
28#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
29pub struct LogoutRequest {
30 pub opcode: RawBhsOpcode, pub reason: RawLogoutReason, reserved0: [u8; 2], pub total_ahs_length: u8, pub data_segment_length: [u8; 3], reserved1: [u8; 8], pub initiator_task_tag: InitiatorTaskTag, pub cid: U16<BigEndian>, reserved2: [u8; 2], pub cmd_sn: U32<BigEndian>, pub exp_stat_sn: U32<BigEndian>, reserved3: [u8; 16], }
45
46impl LogoutRequest {
47 pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
49 buf.fill(0);
50 if buf.len() != HEADER_LEN {
51 bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
52 }
53 buf.copy_from_slice(self.as_bytes());
54 Ok(())
55 }
56
57 pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
59 let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
60 .map_err(|e| anyhow::anyhow!("failed convert buffer LogoutRequest: {e}"))?;
61 if hdr.opcode.opcode_known() != Some(Opcode::LogoutReq) {
62 anyhow::bail!(
63 "LogoutRequest: invalid opcode 0x{:02x}",
64 hdr.opcode.opcode_raw()
65 );
66 }
67 Ok(hdr)
68 }
69}
70
71#[derive(Debug, Default)]
76pub struct LogoutRequestBuilder {
77 pub header: LogoutRequest,
78}
79
80impl LogoutRequestBuilder {
81 pub fn new(reason: LogoutReason, itt: u32, cid: u16) -> Self {
84 Self {
85 header: LogoutRequest {
86 opcode: {
87 let mut tmp = RawBhsOpcode::default();
88 tmp.set_opcode_known(Opcode::LogoutReq);
89 tmp.set_i();
90 tmp
91 },
92 reason: reason.into(),
93 total_ahs_length: 0,
94 data_segment_length: [0, 0, 0],
95 initiator_task_tag: InitiatorTaskTag::new(itt),
96 cid: cid.into(),
97 ..Default::default()
98 },
99 }
100 }
101
102 pub fn connection_id(mut self, cid: u16) -> Self {
104 self.header.cid.set(cid);
105 self
106 }
107
108 pub fn cmd_sn(mut self, cmd_sn: u32) -> Self {
110 self.header.cmd_sn.set(cmd_sn);
111 self
112 }
113
114 pub fn exp_stat_sn(mut self, exp_stat_sn: u32) -> Self {
116 self.header.exp_stat_sn.set(exp_stat_sn);
117 self
118 }
119}
120
121impl SendingData for LogoutRequest {
122 fn get_final_bit(&self) -> bool {
123 true
124 }
125
126 fn set_final_bit(&mut self) {
127 debug!("Logout Request cannot be marked as Final");
128 }
129
130 fn get_continue_bit(&self) -> bool {
131 false
132 }
133
134 fn set_continue_bit(&mut self) {
135 warn!("Logout Request cannot be marked as Contine");
136 }
137}
138
139impl FromBytes for LogoutRequest {
140 fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
141 LogoutRequest::from_bhs_bytes(bytes)
142 }
143}
144
145impl BasicHeaderSegment for LogoutRequest {
146 #[inline]
147 fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
148 self.to_bhs_bytes(buf)
149 }
150
151 #[inline]
152 fn get_opcode(&self) -> Result<BhsOpcode> {
153 BhsOpcode::try_from(self.opcode.raw())
154 }
155
156 #[inline]
157 fn get_initiator_task_tag(&self) -> u32 {
158 self.initiator_task_tag.get()
159 }
160
161 #[inline]
162 fn get_ahs_length_bytes(&self) -> usize {
163 (self.total_ahs_length as usize) * 4
164 }
165
166 #[inline]
167 fn set_ahs_length_bytes(&mut self, len: u8) {
168 self.total_ahs_length = len >> 2;
169 }
170
171 #[inline]
172 fn get_data_length_bytes(&self) -> usize {
173 u32::from_be_bytes([
174 0,
175 self.data_segment_length[0],
176 self.data_segment_length[1],
177 self.data_segment_length[2],
178 ]) as usize
179 }
180
181 #[inline]
182 fn set_data_length_bytes(&mut self, len: u32) {
183 error!("LogoutReq must have zero DataSegmentLength");
184 let be = len.to_be_bytes();
185 self.data_segment_length = [be[1], be[2], be[3]];
186 }
187
188 #[inline]
189 fn get_header_diggest(&self, header_digest: bool) -> usize {
190 4 * (header_digest as usize)
191 }
192
193 #[inline]
194 fn get_data_diggest(&self, _: bool) -> usize {
195 0
196 }
197}
198
199impl ZeroCopyType for LogoutRequest {}