btrfs_diskformat/extent/
extent_inline_ref.rs

1use crate::{ExtentDataRef, SharedDataRef};
2use num_enum::{IntoPrimitive, TryFromPrimitive};
3use static_assertions::{const_assert, const_assert_eq};
4use strum::EnumIter;
5use zerocopy::little_endian::U64 as U64LE;
6use zerocopy_derive::*;
7
8/// This acts as a header for different types of inline extent back references inside extent or
9/// metadata items.
10///
11/// For the full version that contains a union of all possible inline extent references, see
12/// [`ExtentInlineRefFull`]. That type may be more convenient if you are reading the entire
13/// reference from disk at once to save trips to disk.
14///
15/// [`ExtentInlineRefFull`]: crate::ExtentInlineRefFull
16#[derive(Copy, Clone, Debug, Hash, TryFromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
17#[repr(C, packed)]
18pub struct ExtentInlineRefHeader {
19    /// The type of reference, which corresponds with a value from [`ExtentInlineRefType`].
20    /// This field also determines the semantic importance of [`offset`].
21    ///
22    /// [`ExtentInlineRefType`]: crate::ExtentInlineRefType
23    /// [`offset`]: ExtentInlineRef::offset
24    pub ref_type: ExtentInlineRefType,
25
26    /// This field has different functions depending on the value of [`ref_type`].
27    ///
28    /// [`ref_type`]: ExtentInlineRef::ref_type
29    pub offset: U64LE,
30}
31const_assert_eq!(core::mem::size_of::<ExtentInlineRefHeader>(), 9);
32
33/// This type contains a union of all possible inline extent references. Using this can be helpful
34/// when an entire inline reference is read from disk to save trips to the disk but the type of
35/// the reference is not known in advance.
36///
37/// For the header-only version, see [`ExtentInlineRefHeader`].
38#[derive(Copy, Clone, TryFromBytes, Unaligned, KnownLayout, Immutable)]
39#[repr(C, packed)]
40pub struct ExtentInlineRefFull {
41    /// This should be equal to [`ExtentInlineRefType::ExtentDataRef`].
42    pub ref_type: ExtentInlineRefType,
43
44    /// This field has different functions depending on the value of [`ref_type`].
45    ///
46    /// [`ref_type`]: ExtentInlineRef::ref_type
47    pub tail: ExtentInlineRefTail,
48}
49const_assert_eq!(core::mem::size_of::<ExtentInlineRefFull>(), 29);
50
51impl ExtentInlineRefFull {
52    /// Returns the value of the offset field, if it is valid for the given [`ref_type`].
53    ///
54    /// [`ref_type`]: Self::ref_type
55    pub fn offset(&self) -> Option<u64> {
56        match self.ref_type {
57            ExtentInlineRefType::TreeBlockRef | ExtentInlineRefType::SharedBlockRef => {
58                Some(unsafe { self.tail.offset.get() })
59            }
60            ExtentInlineRefType::SharedDataRef => {
61                Some(unsafe { self.tail.shared_data_tail.offset.get() })
62            }
63            _ => None,
64        }
65    }
66
67    /// Returns the extent data reference if the reference type is
68    /// [`ExtentInlineRefType::ExtentDataRef`].
69    pub fn extent_data_ref(&self) -> Option<ExtentDataRef> {
70        if self.ref_type == ExtentInlineRefType::ExtentDataRef {
71            Some(unsafe { self.tail.extent_data_ref })
72        } else {
73            None
74        }
75    }
76
77    /// Returns the shared data tail if the reference type is
78    /// [`ExtentInlineRefType::SharedDataRef`].
79    pub fn shared_data_tail(&self) -> Option<ExtentInlineRefSharedDataTail> {
80        if self.ref_type == ExtentInlineRefType::SharedDataRef {
81            Some(unsafe { self.tail.shared_data_tail })
82        } else {
83            None
84        }
85    }
86
87    pub fn as_tree_block_ref(&self) -> Option<&ExtentInlineTreeBlockRef> {
88        const_assert!(
89            core::mem::size_of::<ExtentInlineRefFull>()
90                >= core::mem::size_of::<ExtentInlineTreeBlockRef>()
91        );
92
93        if self.ref_type == ExtentInlineRefType::TreeBlockRef {
94            // Safety: We know that the bytes are valid for `ExtentInlineTreeBlockRef` because
95            // `ref_type` is `ExtentInlineRefType::TreeBlockRef`.
96            Some(unsafe {
97                &*(self as *const ExtentInlineRefFull as *const ExtentInlineTreeBlockRef)
98            })
99        } else {
100            None
101        }
102    }
103
104    pub fn as_shared_block_ref(&self) -> Option<&ExtentInlineSharedBlockRef> {
105        const_assert!(
106            core::mem::size_of::<ExtentInlineRefFull>()
107                >= core::mem::size_of::<ExtentInlineSharedBlockRef>()
108        );
109
110        if self.ref_type == ExtentInlineRefType::SharedBlockRef {
111            // Safety: We know that the bytes are valid for `ExtentInlineSharedBlockRef` because
112            // `ref_type` is `ExtentInlineRefType::SharedBlockRef`.
113            Some(unsafe {
114                &*(self as *const ExtentInlineRefFull as *const ExtentInlineSharedBlockRef)
115            })
116        } else {
117            None
118        }
119    }
120
121    pub fn as_extent_data_ref(&self) -> Option<&ExtentInlineExtentDataRef> {
122        const_assert!(
123            core::mem::size_of::<ExtentInlineRefFull>()
124                >= core::mem::size_of::<ExtentInlineExtentDataRef>()
125        );
126
127        if self.ref_type == ExtentInlineRefType::ExtentDataRef {
128            // Safety: We know that the bytes are valid for `ExtentInlineExtentDataRef` because
129            // `ref_type` is `ExtentInlineRefType::ExtentDataRef`.
130            Some(unsafe {
131                &*(self as *const ExtentInlineRefFull as *const ExtentInlineExtentDataRef)
132            })
133        } else {
134            None
135        }
136    }
137
138    pub fn as_shared_data_ref(&self) -> Option<&ExtentInlineSharedDataRef> {
139        const_assert!(
140            core::mem::size_of::<ExtentInlineRefFull>()
141                >= core::mem::size_of::<ExtentInlineSharedDataRef>()
142        );
143
144        if self.ref_type == ExtentInlineRefType::SharedDataRef {
145            // Safety: We know that the bytes are valid for `ExtentInlineSharedDataRef` because
146            // `ref_type` is `ExtentInlineRefType::SharedDataRef`.
147            Some(unsafe {
148                &*(self as *const ExtentInlineRefFull as *const ExtentInlineSharedDataRef)
149            })
150        } else {
151            None
152        }
153    }
154}
155
156/// Union that contains the actual data for the inline extent reference.
157#[derive(Copy, Clone, FromBytes, Unaligned, KnownLayout, Immutable)]
158#[repr(C, packed)]
159pub union ExtentInlineRefTail {
160    pub offset: U64LE,
161    pub extent_data_ref: ExtentDataRef,
162    pub shared_data_tail: ExtentInlineRefSharedDataTail,
163}
164
165/// A variant of [`ExtentInlineRefTail`] for [`ExtentInlineRefType::SharedDataRef`].
166#[derive(Copy, Clone, Debug, Hash, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
167#[repr(C, packed)]
168pub struct ExtentInlineRefSharedDataTail {
169    /// The byte offset of the metadata that contains the extent data item that describes this
170    /// extent.
171    pub offset: U64LE,
172
173    /// The shared data reference count.
174    pub shared_data_ref: SharedDataRef,
175}
176
177/// An [`ExtentInlineRefHeader`] and/or [`ExtentInlineRefFull`] where the value is known to be
178/// [`ExtentInlineRefType::TreeBlockRef`].
179#[derive(Copy, Clone, Debug, Hash, TryFromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
180#[repr(C, packed)]
181pub struct ExtentInlineTreeBlockRef {
182    /// This is here to allow [`TryFromBytes`] to enforce the type of the reference.
183    ref_type: ExtentInlineTreeBlockRefType,
184
185    /// The object ID of the tree root that allocated the block.
186    pub offset: U64LE,
187}
188
189#[derive(Copy, Clone, Debug, Hash, PartialEq, TryFromBytes, IntoBytes, KnownLayout, Immutable)]
190#[repr(u8)]
191enum ExtentInlineTreeBlockRefType {
192    #[allow(dead_code)]
193    TreeBlockRef = ExtentInlineRefType::TreeBlockRef as u8,
194}
195const_assert_eq!(
196    core::mem::size_of::<ExtentInlineTreeBlockRefType>(),
197    core::mem::size_of::<ExtentInlineRefType>()
198);
199
200/// An [`ExtentInlineRefHeader`] and/or [`ExtentInlineRefFull`] where the value is known to be
201/// [`ExtentInlineRefType::SharedBlockRef`].
202#[derive(Copy, Clone, Debug, Hash, TryFromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
203#[repr(C, packed)]
204pub struct ExtentInlineSharedBlockRef {
205    /// This is here to allow [`TryFromBytes`] to enforce the type of the reference.
206    ref_type: ExtentInlineSharedBlockRefType,
207
208    /// The byte offset of the node one level above in the tree where this block is located.
209    pub offset: U64LE,
210}
211
212#[derive(Copy, Clone, Debug, Hash, PartialEq, TryFromBytes, IntoBytes, KnownLayout, Immutable)]
213#[repr(u8)]
214#[allow(dead_code)]
215enum ExtentInlineSharedBlockRefType {
216    SharedBlockRef = ExtentInlineRefType::SharedBlockRef as u8,
217}
218
219/// An [`ExtentInlineRefHeader`] and/or [`ExtentInlineRefFull`] where the value is known to be
220/// [`ExtentInlineRefType::ExtentDataRef`].
221#[derive(Copy, Clone, Debug, Hash, TryFromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
222#[repr(C, packed)]
223pub struct ExtentInlineExtentDataRef {
224    /// This is here to allow [`TryFromBytes`] to enforce the type of the reference.
225    ref_type: ExtentInlineExtentDataRefType,
226
227    /// The extent data reference that overlaps the unused offset field.
228    pub extent_data_ref: ExtentDataRef,
229}
230
231#[derive(Copy, Clone, Debug, Hash, PartialEq, TryFromBytes, IntoBytes, KnownLayout, Immutable)]
232#[repr(u8)]
233enum ExtentInlineExtentDataRefType {
234    #[allow(dead_code)]
235    ExtentDataRef = ExtentInlineRefType::ExtentDataRef as u8,
236}
237const_assert_eq!(
238    core::mem::size_of::<ExtentInlineExtentDataRefType>(),
239    core::mem::size_of::<ExtentInlineRefType>()
240);
241
242/// An [`ExtentInlineRefHeader`] and/or [`ExtentInlineRefFull`] where the value is known to be
243/// [`ExtentInlineRefType::SharedDataRef`].
244#[derive(Copy, Clone, Debug, Hash, TryFromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
245#[repr(C, packed)]
246pub struct ExtentInlineSharedDataRef {
247    /// This is here to allow [`TryFromBytes`] to enforce the type of the reference.
248    ref_type: ExtentInlineSharedDataRefType,
249
250    /// The tail of a shared data reference that contains the byte offset of the metadata that
251    /// contains the extent data item that describes this extent and the shared data reference
252    /// count.
253    pub shared_data_tail: ExtentInlineRefSharedDataTail,
254}
255
256#[derive(Copy, Clone, Debug, Hash, PartialEq, TryFromBytes, IntoBytes, KnownLayout, Immutable)]
257#[repr(u8)]
258enum ExtentInlineSharedDataRefType {
259    #[allow(dead_code)]
260    SharedDataRef = ExtentInlineRefType::SharedDataRef as u8,
261}
262const_assert_eq!(
263    core::mem::size_of::<ExtentInlineSharedDataRefType>(),
264    core::mem::size_of::<ExtentInlineRefType>()
265);
266
267/// The type of [`ExtentInlineRefHeader`] or [`ExtentInlineRefFull`].
268///
269/// [`ExtentInlineRefHeader`]: crate::ExtentInlineRefHeader
270/// [`ExtentInlineRefFull`]: crate::ExtentInlineRefFull
271#[derive(
272    Copy,
273    Clone,
274    Debug,
275    Hash,
276    PartialEq,
277    EnumIter,
278    IntoPrimitive,
279    TryFromPrimitive,
280    TryFromBytes,
281    IntoBytes,
282    KnownLayout,
283    Immutable,
284)]
285#[repr(u8)]
286pub enum ExtentInlineRefType {
287    /// The reference is indirect for a tree block.
288    ///
289    /// [`offset`] contains the object ID of the tree root that allocated the block.
290    ///
291    /// [`offset`]: ExtentInlineRefHeader::offset
292    TreeBlockRef = 176,
293
294    /// The reference is shared for a tree block.
295    ///
296    /// [`offset`] contains the byte offset of the node one level above in the tree where this block
297    /// is located.
298    ///
299    /// [`offset`]: ExtentInlineRefHeader::offset
300    SharedBlockRef = 182,
301
302    /// The reference is indirect for a data extent.
303    ///
304    /// An [`ExtentDataRef`] is located immediately after the [`type`] field and overlaps the unused
305    /// [`offset`] field.
306    ///
307    /// [`type`]: ExtentInlineRef::type
308    /// [`offset`]: ExtentInlineRefHeader::offset
309    ExtentDataRef = 178,
310
311    /// The reference is shared for a data extent.
312    ///
313    /// [`offset`] contains the byte offset of the metadata that contains the extent data item that
314    /// describes this extent.
315    ///
316    /// Immediately following [`offset`] (and the end of [`ExtentInlineRefHeader`] structure) is a
317    /// [`SharedDataRef`] that contains the reference count.
318    ///
319    /// [`offset`]: ExtentInlineRefHeader::offset
320    SharedDataRef = 184,
321}