Skip to main content

iscsi_client_rs/models/data/
common.rs

1//! This module defines common structures and flags for iSCSI Data-In and
2//! Data-Out PDUs. It includes flag definitions and zero-copy wrappers for
3//! efficient PDU handling.
4
5// SPDX-License-Identifier: AGPL-3.0-or-later
6// Copyright (C) 2012-2025 Andrei Maltsev
7
8use core::fmt;
9
10use anyhow::{Result, bail};
11use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
12
13bitflags::bitflags! {
14    #[derive(Default, Debug, PartialEq)]
15    /// Flags for iSCSI SCSI Data-Out PDU
16    ///
17    /// Contains control flags for Data-Out PDUs that carry SCSI write data
18    /// from initiator to target.
19    pub struct DataOutFlags: u8 {
20        /// Final bit (F) - indicates this is the last Data-Out PDU for the command
21        const FINAL = 0b1000_0000;
22    }
23}
24
25impl TryFrom<u8> for DataOutFlags {
26    type Error = anyhow::Error;
27
28    fn try_from(value: u8) -> Result<Self, Self::Error> {
29        DataOutFlags::from_bits(value)
30            .ok_or_else(|| anyhow::anyhow!("invalid DataOutFlags: {:#08b}", value))
31    }
32}
33
34/// Wire view for **Data-OUT flags** (byte 1 of the PDU).
35///
36/// Transparent wrapper over a single `u8` with zerocopy semantics.
37#[repr(transparent)]
38#[derive(Default, Clone, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable)]
39pub struct RawDataOutFlags(u8);
40
41impl RawDataOutFlags {
42    /// Bitmask for the Final flag.
43    pub const FINAL: u8 = 0b1000_0000;
44
45    /// Returns the raw 8-bit value of the flags.
46    #[inline]
47    pub const fn raw(&self) -> u8 {
48        self.0
49    }
50
51    /// Creates a new `RawDataOutFlags` from a raw 8-bit value.
52    #[inline]
53    pub const fn new_raw(v: u8) -> Self {
54        Self(v)
55    }
56
57    /// Checks if the Final (F) bit is set.
58    #[inline]
59    pub fn fin(&self) -> bool {
60        self.0 & Self::FINAL != 0
61    }
62
63    /// Sets or clears the Final (F) bit.
64    #[inline]
65    pub fn set_fin(&mut self, on: bool) {
66        if on {
67            self.0 |= Self::FINAL;
68        } else {
69            self.0 &= !Self::FINAL;
70        }
71    }
72}
73
74/* Optional: interop with the bitflags type */
75
76impl From<DataOutFlags> for RawDataOutFlags {
77    #[inline]
78    fn from(f: DataOutFlags) -> Self {
79        Self(f.bits())
80    }
81}
82
83impl TryFrom<RawDataOutFlags> for DataOutFlags {
84    type Error = anyhow::Error;
85
86    #[inline]
87    fn try_from(r: RawDataOutFlags) -> Result<Self> {
88        DataOutFlags::from_bits(r.raw())
89            .ok_or_else(|| anyhow::anyhow!("invalid DataOutFlags: {:#010b}", r.raw()))
90    }
91}
92
93bitflags::bitflags! {
94    #[derive(Default, Debug, PartialEq)]
95    /// Flags for iSCSI SCSI Data-In PDU
96    ///
97    /// Contains control flags for Data-In PDUs that carry SCSI read data
98    /// from target to initiator, including status and residual information.
99    pub struct DataInFlags: u8 {
100        /// Final bit (F) - indicates this is the last Data-In PDU for the command
101        const FINAL = 1 << 7;
102        /// Acknowledge bit (A) - requests DataACK SNACK for error recovery (ERL>0)
103        const A = 1 << 6;
104        // bits 5..3 reserved (0)
105        /// Residual Overflow bit (O) - valid only when S=1
106        const O = 1 << 2;
107        /// Residual Underflow bit (U) - valid only when S=1
108        const U = 1 << 1;
109        /// Status present bit (S) - when set, F must also be set
110        const S = 1 << 0;
111    }
112}
113
114impl TryFrom<u8> for DataInFlags {
115    type Error = anyhow::Error;
116
117    fn try_from(value: u8) -> Result<Self, Self::Error> {
118        let tmp = DataInFlags::from_bits(value)
119            .ok_or_else(|| anyhow::anyhow!("invalid DataOutFlags: {:#08b}", value))?;
120
121        if tmp.contains(DataInFlags::U) && tmp.contains(DataInFlags::O) {
122            bail!("Protocol error cause U && O both presented")
123        }
124
125        Ok(tmp)
126    }
127}
128
129/// Wire format representation of Data-In PDU flags
130///
131/// Zero-copy wrapper around the flags byte in Data-In PDU header.
132/// Provides direct access to the raw byte value for serialization.
133#[repr(transparent)]
134#[derive(Default, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable)]
135pub struct RawDataInFlags(u8);
136
137impl RawDataInFlags {
138    /// Bitmask for the Acknowledge (A) flag.
139    pub const A: u8 = 1 << 6;
140    /// Bitmask for the Final (F) flag.
141    pub const FINAL: u8 = 1 << 7;
142    /// Bitmask for the Residual Overflow (O) flag.
143    pub const O: u8 = 1 << 2;
144    const RESERVED_MASK: u8 = 0b0011_1000;
145    /// Bitmask for the Status Present (S) flag.
146    pub const S: u8 = 1 << 0;
147    /// Bitmask for the Residual Underflow (U) flag.
148    pub const U: u8 = 1 << 1;
149
150    /// Returns the raw 8-bit value of the flags.
151    #[inline]
152    pub const fn raw(&self) -> u8 {
153        self.0
154    }
155
156    /// Creates a new `RawDataInFlags` from a raw 8-bit value.
157    #[inline]
158    pub const fn new_raw(v: u8) -> Self {
159        Self(v)
160    }
161
162    /// Checks if the Final (F) bit is set.
163    #[inline]
164    pub fn fin(&self) -> bool {
165        self.0 & Self::FINAL != 0
166    }
167
168    /// Checks if the Acknowledge (A) bit is set.
169    #[inline]
170    pub fn ack(&self) -> bool {
171        self.0 & Self::A != 0
172    }
173
174    /// Checks if the Residual Overflow (O) bit is set.
175    #[inline]
176    pub fn o(&self) -> bool {
177        self.0 & Self::O != 0
178    }
179
180    /// Checks if the Residual Underflow (U) bit is set.
181    #[inline]
182    pub fn u(&self) -> bool {
183        self.0 & Self::U != 0
184    }
185
186    /// Checks if the Status Present (S) bit is set.
187    #[inline]
188    pub fn s(&self) -> bool {
189        self.0 & Self::S != 0
190    }
191
192    /// Sets or clears the Final (F) bit.
193    #[inline]
194    pub fn set_fin(&mut self, on: bool) {
195        Self::set_bit(&mut self.0, Self::FINAL, on)
196    }
197
198    /// Sets or clears the Acknowledge (A) bit.
199    #[inline]
200    pub fn set_ack(&mut self, on: bool) {
201        Self::set_bit(&mut self.0, Self::A, on)
202    }
203
204    /// Sets or clears the Residual Overflow (O) bit.
205    #[inline]
206    pub fn set_o(&mut self, on: bool) {
207        Self::set_pair(&mut self.0, Self::O, Self::U, on)
208    }
209
210    /// Sets or clears the Residual Underflow (U) bit.
211    #[inline]
212    pub fn set_u(&mut self, on: bool) {
213        Self::set_pair(&mut self.0, Self::U, Self::O, on)
214    }
215
216    /// Sets or clears the Status Present (S) bit.
217    #[inline]
218    pub fn set_s(&mut self, on: bool) {
219        Self::set_bit(&mut self.0, Self::S, on);
220        if on {
221            self.set_fin(true);
222        } // enforce S => F
223    }
224
225    #[inline]
226    fn set_bit(v: &mut u8, bit: u8, on: bool) {
227        if on {
228            *v |= bit;
229        } else {
230            *v &= !bit;
231        }
232    }
233
234    // keep mutual exclusion for U/O pair
235    #[inline]
236    fn set_pair(v: &mut u8, set_bit: u8, clear_bit: u8, on: bool) {
237        if on {
238            *v |= set_bit;
239            *v &= !clear_bit;
240        } else {
241            *v &= !set_bit;
242        }
243    }
244
245    /// Validates the protocol constraints of the flags.
246    #[inline]
247    pub fn validate(&self) -> Result<()> {
248        if self.0 & Self::RESERVED_MASK != 0 {
249            bail!(
250                "protocol error: reserved bits set in DataInFlags: {:#010b}",
251                self.0
252            );
253        }
254        if self.u() && self.o() {
255            bail!("protocol error: both U and O set");
256        }
257        if self.s() && !self.fin() {
258            bail!("protocol error: S=1 requires F=1");
259        }
260        Ok(())
261    }
262}
263
264/* Optional: interop с bitflags-типом */
265
266impl From<DataInFlags> for RawDataInFlags {
267    #[inline]
268    fn from(f: DataInFlags) -> Self {
269        Self(f.bits())
270    }
271}
272
273impl TryFrom<RawDataInFlags> for DataInFlags {
274    type Error = anyhow::Error;
275
276    #[inline]
277    fn try_from(r: RawDataInFlags) -> Result<Self> {
278        r.validate()?;
279        DataInFlags::from_bits(r.raw())
280            .ok_or_else(|| anyhow::anyhow!("invalid DataInFlags: {:#010b}", r.raw()))
281    }
282}
283
284// ---------- RawDataOutFlags ----------
285impl fmt::Debug for RawDataOutFlags {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        write!(f, "RawDataOutFlags {{ ")?;
288        if self.fin() {
289            write!(f, "FIN")?;
290        }
291        write!(f, " }}")
292    }
293}
294
295impl fmt::Debug for RawDataInFlags {
296    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297        let raw = self.raw();
298        let reserved_bits = (raw & Self::RESERVED_MASK) >> 3;
299
300        write!(f, "RawDataInFlags {{ ")?;
301
302        if self.fin() {
303            write!(f, "FIN|")?;
304        }
305        if self.ack() {
306            write!(f, "A|")?;
307        }
308        if self.o() {
309            write!(f, "O|")?;
310        }
311        if self.u() {
312            write!(f, "U|")?;
313        }
314        if self.s() {
315            write!(f, "S|")?;
316        }
317
318        if reserved_bits != 0 {
319            write!(f, "reserved_bits=0b{:03b}|", reserved_bits)?;
320        }
321        if self.u() && self.o() {
322            write!(f, "INVALID:U&O|")?;
323        }
324        if self.s() && !self.fin() {
325            write!(f, "INVALID:S_without_F|")?;
326        }
327
328        write!(f, " }}")
329    }
330}