nt_hive/
leaf.rs

1// Copyright 2020-2025 Colin Finck <colin@reactos.org>
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4use core::iter::FusedIterator;
5use core::mem;
6use core::ops::{Deref, Range};
7
8use zerocopy::byteorder::LittleEndian;
9use zerocopy::{
10    FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, SplitByteSliceMut,
11    Unaligned, U32,
12};
13
14use crate::error::{NtHiveError, Result};
15use crate::helpers::byte_subrange;
16use crate::hive::Hive;
17use crate::index_root::IndexRootItemRange;
18use crate::key_node::{KeyNode, KeyNodeMut};
19use crate::subkeys_list::SubkeysList;
20
21/// On-Disk Structure of a Fast Leaf item (On-Disk Signature: `lf`).
22/// They are supported since Windows NT 4.
23#[allow(dead_code)]
24#[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)]
25#[repr(packed)]
26struct FastLeafItem {
27    key_node_offset: U32<LittleEndian>,
28    name_hint: [u8; 4],
29}
30
31/// On-Disk Structure of a Hash Leaf item (On-Disk Signature: `lh`).
32/// They are supported since Windows XP.
33#[allow(dead_code)]
34#[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)]
35#[repr(packed)]
36struct HashLeafItem {
37    key_node_offset: U32<LittleEndian>,
38    name_hash: [u8; 4],
39}
40
41/// On-Disk Structure of an Index Leaf item (On-Disk Signature: `li`).
42/// They are supported in all Windows versions.
43#[derive(FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned)]
44#[repr(packed)]
45struct IndexLeafItem {
46    key_node_offset: U32<LittleEndian>,
47}
48
49/// All known and supported Leaf types.
50///
51/// We first had only Index Leafs, then got Fast Leafs with Windows NT 4 which add a
52/// `name_hint` (first 4 characters of the key name), and finally got Hash Leafs with
53/// Windows XP which come with a `name_hash` (simple hash of the entire key name)
54/// instead.
55/// Both Fast Leafs and Hash Leafs were introduced to speed up key lookups.
56/// However, their performance benefits are marginal to non-existing in 2020
57/// when we assume that the entire registry hive is randomly accessible.
58/// Therefore, the nt-hive crate treats all types equally by only accessing the
59/// `key_node_offset` field and ignoring all other fields.
60#[derive(Clone, Copy)]
61pub(crate) enum LeafType {
62    Fast,
63    Hash,
64    Index,
65}
66
67impl LeafType {
68    pub(crate) fn from_signature(signature: &[u8]) -> Option<Self> {
69        match signature {
70            b"lf" => Some(Self::Fast),
71            b"lh" => Some(Self::Hash),
72            b"li" => Some(Self::Index),
73            _ => None,
74        }
75    }
76
77    fn item_size(&self) -> usize {
78        match self {
79            Self::Fast => mem::size_of::<FastLeafItem>(),
80            Self::Hash => mem::size_of::<HashLeafItem>(),
81            Self::Index => mem::size_of::<IndexLeafItem>(),
82        }
83    }
84}
85
86/// Byte range of a single Leaf item returned by [`LeafItemRanges`].
87pub(crate) struct LeafItemRange(Range<usize>);
88
89impl LeafItemRange {
90    pub fn key_node_offset<B>(&self, hive: &Hive<B>) -> u32
91    where
92        B: SplitByteSlice,
93    {
94        // We make use of the fact that a `FastLeafItem` or `HashLeafItem` is just an
95        // `IndexLeafItem` with additional fields.
96        // As they all have the `key_node_offset` as their first field, treat them equally.
97        let (index_leaf_item, _) =
98            Ref::<&[u8], IndexLeafItem>::from_prefix(&hive.data[self.0.clone()]).unwrap();
99        index_leaf_item.key_node_offset.get()
100    }
101}
102
103impl Deref for LeafItemRange {
104    type Target = Range<usize>;
105
106    fn deref(&self) -> &Self::Target {
107        &self.0
108    }
109}
110
111/// Iterator over
112///   a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index),
113///   returning a [`LeafItemRange`] for each Leaf item.
114///
115/// On-Disk Signatures: `lf`, `lh`, `li`
116#[derive(Clone)]
117pub(crate) struct LeafItemRanges {
118    items_range: Range<usize>,
119    leaf_type: LeafType,
120}
121
122impl LeafItemRanges {
123    pub fn new(
124        count: u16,
125        count_field_offset: usize,
126        data_range: Range<usize>,
127        leaf_type: LeafType,
128    ) -> Result<Self> {
129        let byte_count = count as usize * leaf_type.item_size();
130
131        let items_range = byte_subrange(&data_range, byte_count).ok_or_else(|| {
132            NtHiveError::InvalidSizeField {
133                offset: count_field_offset,
134                expected: byte_count,
135                actual: data_range.len(),
136            }
137        })?;
138
139        Ok(Self {
140            items_range,
141            leaf_type,
142        })
143    }
144
145    pub fn from_index_root_item_range<B>(
146        hive: &Hive<B>,
147        index_root_item_range: IndexRootItemRange,
148    ) -> Result<Self>
149    where
150        B: SplitByteSlice,
151    {
152        let subkeys_list_offset = index_root_item_range.subkeys_list_offset(hive);
153        let cell_range = hive.cell_range_from_data_offset(subkeys_list_offset)?;
154        let subkeys_list = SubkeysList::new_without_index_root(hive, cell_range)?;
155
156        let header = subkeys_list.header();
157        let count = header.count.get();
158        let count_field_offset = hive.offset_of_field(&header.count);
159
160        // Subkeys Lists belonging to Index Root items need to contain at least 1 item.
161        // Otherwise, we can't perform efficient binary search on them, which is the sole reason
162        // Index Roots exist.
163        if count == 0 {
164            return Err(NtHiveError::InvalidSizeField {
165                offset: count_field_offset,
166                expected: 1,
167                actual: 0,
168            });
169        }
170
171        let leaf_type = LeafType::from_signature(&header.signature).unwrap();
172        LeafItemRanges::new(
173            count,
174            count_field_offset,
175            subkeys_list.data_range,
176            leaf_type,
177        )
178    }
179}
180
181impl Iterator for LeafItemRanges {
182    type Item = LeafItemRange;
183
184    fn next(&mut self) -> Option<Self::Item> {
185        let item_size = self.leaf_type.item_size();
186        let item_range = byte_subrange(&self.items_range, item_size)?;
187        self.items_range.start += item_size;
188
189        Some(LeafItemRange(item_range))
190    }
191
192    fn count(self) -> usize {
193        let (size, _) = self.size_hint();
194        size
195    }
196
197    fn last(mut self) -> Option<Self::Item> {
198        let (size, _) = self.size_hint();
199        if size == 0 {
200            return None;
201        }
202
203        self.nth(size - 1)
204    }
205
206    fn nth(&mut self, n: usize) -> Option<Self::Item> {
207        // `n` is arbitrary and usize, so we may hit boundaries here. Check that!
208        let bytes_to_skip = n.checked_mul(self.leaf_type.item_size())?;
209        self.items_range.start = self.items_range.start.checked_add(bytes_to_skip)?;
210        self.next()
211    }
212
213    fn size_hint(&self) -> (usize, Option<usize>) {
214        let size = self.items_range.len() / self.leaf_type.item_size();
215        (size, Some(size))
216    }
217}
218
219impl<B: SplitByteSlice> From<LeafKeyNodes<'_, B>> for LeafItemRanges {
220    fn from(leaf_key_nodes: LeafKeyNodes<'_, B>) -> LeafItemRanges {
221        leaf_key_nodes.leaf_item_ranges
222    }
223}
224
225impl ExactSizeIterator for LeafItemRanges {}
226impl FusedIterator for LeafItemRanges {}
227
228/// Iterator over
229///   a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index),
230///   returning a constant [`KeyNode`] for each Leaf item,
231///   used by [`SubKeyNodes`].
232///
233/// On-Disk Signatures: `lf`, `lh`, `li`
234///
235/// [`SubKeyNodes`]: crate::subkeys_list::SubKeyNodes
236#[derive(Clone)]
237pub struct LeafKeyNodes<'h, B: SplitByteSlice> {
238    hive: &'h Hive<B>,
239    leaf_item_ranges: LeafItemRanges,
240}
241
242impl<'h, B> LeafKeyNodes<'h, B>
243where
244    B: SplitByteSlice,
245{
246    pub(crate) fn new(
247        hive: &'h Hive<B>,
248        count: u16,
249        count_field_offset: usize,
250        data_range: Range<usize>,
251        leaf_type: LeafType,
252    ) -> Result<Self> {
253        let leaf_item_ranges =
254            LeafItemRanges::new(count, count_field_offset, data_range, leaf_type)?;
255
256        Ok(Self {
257            hive,
258            leaf_item_ranges,
259        })
260    }
261}
262
263impl<'h, B> Iterator for LeafKeyNodes<'h, B>
264where
265    B: SplitByteSlice,
266{
267    type Item = Result<KeyNode<'h, B>>;
268
269    fn next(&mut self) -> Option<Self::Item> {
270        let leaf_item_range = self.leaf_item_ranges.next()?;
271        let key_node = iter_try!(KeyNode::from_leaf_item_range(self.hive, leaf_item_range));
272        Some(Ok(key_node))
273    }
274
275    fn count(self) -> usize {
276        self.leaf_item_ranges.count()
277    }
278
279    fn last(mut self) -> Option<Self::Item> {
280        let (size, _) = self.size_hint();
281        if size == 0 {
282            return None;
283        }
284
285        self.nth(size - 1)
286    }
287
288    fn nth(&mut self, n: usize) -> Option<Self::Item> {
289        // `n` is arbitrary and usize, so we may hit boundaries here. Check that!
290        let bytes_to_skip = n.checked_mul(self.leaf_item_ranges.leaf_type.item_size())?;
291        self.leaf_item_ranges.items_range.start = self
292            .leaf_item_ranges
293            .items_range
294            .start
295            .checked_add(bytes_to_skip)?;
296        self.next()
297    }
298
299    fn size_hint(&self) -> (usize, Option<usize>) {
300        self.leaf_item_ranges.size_hint()
301    }
302}
303
304impl<B> ExactSizeIterator for LeafKeyNodes<'_, B> where B: SplitByteSlice {}
305impl<B> FusedIterator for LeafKeyNodes<'_, B> where B: SplitByteSlice {}
306
307/// Iterator over
308///   a contiguous range of data bytes containing Leaf items of any type (Fast/Hash/Index),
309///   returning a mutable [`KeyNode`] for each Leaf item,
310///   used by [`SubKeyNodesMut`].
311///
312/// On-Disk Signatures: `lf`, `lh`, `li`
313///
314/// [`SubKeyNodesMut`]: crate::subkeys_list::SubKeyNodesMut
315pub(crate) struct LeafKeyNodesMut<'h, B: SplitByteSliceMut> {
316    hive: &'h mut Hive<B>,
317    leaf_item_ranges: LeafItemRanges,
318}
319
320impl<'h, B> LeafKeyNodesMut<'h, B>
321where
322    B: SplitByteSliceMut,
323{
324    pub(crate) fn new(
325        hive: &'h mut Hive<B>,
326        count: u16,
327        count_field_offset: usize,
328        data_range: Range<usize>,
329        leaf_type: LeafType,
330    ) -> Result<Self> {
331        let leaf_item_ranges =
332            LeafItemRanges::new(count, count_field_offset, data_range, leaf_type)?;
333
334        Ok(Self {
335            hive,
336            leaf_item_ranges,
337        })
338    }
339
340    pub(crate) fn next<'a>(&'a mut self) -> Option<Result<KeyNodeMut<'a, B>>>
341    where
342        'h: 'a,
343    {
344        let leaf_item_range = self.leaf_item_ranges.next()?;
345        let key_node = iter_try!(KeyNodeMut::from_leaf_item_range(self.hive, leaf_item_range));
346        Some(Ok(key_node))
347    }
348}