Skip to main content

iscsi_client_rs/models/reject/
response.rs

1//! This module defines the structures for iSCSI Reject PDUs.
2//! It includes the `RejectPdu` header and related methods for handling rejected
3//! PDUs.
4
5// SPDX-License-Identifier: AGPL-3.0-or-later
6// Copyright (C) 2012-2025 Andrei Maltsev
7
8use anyhow::{Result, bail};
9use tracing::warn;
10use zerocopy::{
11    BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32, U64,
12};
13
14use crate::{
15    client::pdu_connection::FromBytes,
16    models::{
17        common::{BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, SendingData},
18        data_fromat::ZeroCopyType,
19        opcode::{BhsOpcode, Opcode, RawBhsOpcode},
20        reject::reject_description::RawRejectReason,
21    },
22};
23
24/// Represents the Basic Header Segment (BHS) for a Reject PDU.
25#[repr(C)]
26#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
27pub struct RejectPdu {
28    pub opcode: RawBhsOpcode,                 // 0
29    reserved1: u8,                            // 1
30    pub reason: RawRejectReason,              // 2
31    pub reserved2: u8,                        // 3
32    pub total_ahs_length: u8,                 // 4
33    pub data_segment_length: [u8; 3],         // 5..8
34    pub reserved3: U64<BigEndian>,            // 8..16
35    pub initiator_task_tag: InitiatorTaskTag, // 16..20
36    pub reserved4: U32<BigEndian>,            // 20..24
37    pub stat_sn: U32<BigEndian>,              // 24..28
38    pub exp_cmd_sn: U32<BigEndian>,           // 28..32
39    pub max_cmd_sn: U32<BigEndian>,           // 32..36
40    pub data_sn_or_r2_sn: U32<BigEndian>,     // 36..40
41    pub reserved5: U64<BigEndian>,            // 40..48
42}
43
44impl RejectPdu {
45    /// Serializes the BHS into a byte buffer.
46    pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
47        if buf.len() != HEADER_LEN {
48            bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
49        }
50        buf.copy_from_slice(self.as_bytes());
51        Ok(())
52    }
53
54    /// Deserializes the BHS from a byte buffer.
55    pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
56        let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
57            .map_err(|e| anyhow::anyhow!("failed convert buffer RejectPdu: {e}"))?;
58        if hdr.opcode.opcode_known() != Some(Opcode::Reject) {
59            anyhow::bail!(
60                "RejectPdu: invalid opcode 0x{:02x}",
61                hdr.opcode.opcode_raw()
62            );
63        }
64        Ok(hdr)
65    }
66}
67
68impl SendingData for RejectPdu {
69    fn get_final_bit(&self) -> bool {
70        true
71    }
72
73    fn set_final_bit(&mut self) {
74        warn!("RejectPdu Response cannot be marked as Final");
75    }
76
77    fn get_continue_bit(&self) -> bool {
78        false
79    }
80
81    fn set_continue_bit(&mut self) {
82        warn!("RejectPdu Response cannot be marked as Contine");
83    }
84}
85
86impl FromBytes for RejectPdu {
87    fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
88        RejectPdu::from_bhs_bytes(bytes)
89    }
90}
91
92impl BasicHeaderSegment for RejectPdu {
93    #[inline]
94    fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
95        self.to_bhs_bytes(buf)
96    }
97
98    #[inline]
99    fn get_opcode(&self) -> Result<BhsOpcode> {
100        BhsOpcode::try_from(self.opcode.raw())
101    }
102
103    #[inline]
104    fn get_initiator_task_tag(&self) -> u32 {
105        self.initiator_task_tag.get()
106    }
107
108    #[inline]
109    fn get_ahs_length_bytes(&self) -> usize {
110        (self.total_ahs_length as usize) * 4
111    }
112
113    #[inline]
114    fn set_ahs_length_bytes(&mut self, len: u8) {
115        self.total_ahs_length = len >> 2;
116    }
117
118    #[inline]
119    fn get_data_length_bytes(&self) -> usize {
120        u32::from_be_bytes([
121            0,
122            self.data_segment_length[0],
123            self.data_segment_length[1],
124            self.data_segment_length[2],
125        ]) as usize
126    }
127
128    #[inline]
129    fn set_data_length_bytes(&mut self, len: u32) {
130        let be = len.to_be_bytes();
131        self.data_segment_length = [be[1], be[2], be[3]];
132    }
133}
134
135impl ZeroCopyType for RejectPdu {}