nt_apiset/
namespace_entry.rs

1// Copyright 2023 Colin Finck <colin@reactos.org>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use core::iter::FusedIterator;
5use core::mem;
6use core::ops::Range;
7
8use bitflags::bitflags;
9use nt_string::u16strle::U16StrLe;
10use zerocopy::{FromBytes, LayoutVerified, LittleEndian, Unaligned, U32};
11
12use crate::error::{NtApiSetError, Result};
13use crate::value_entry::{ApiSetValueEntries, ApiSetValueEntryHeader};
14
15#[allow(dead_code)]
16#[derive(Debug, FromBytes, Unaligned)]
17#[repr(packed)]
18pub(crate) struct ApiSetNamespaceEntryHeader {
19    /// See [`ApiSetNamespaceEntryFlags`]
20    flags: U32<LittleEndian>,
21    name_offset: U32<LittleEndian>,
22    name_length: U32<LittleEndian>,
23    hashed_length: U32<LittleEndian>,
24    array_offset: U32<LittleEndian>,
25    array_count: U32<LittleEndian>,
26}
27
28bitflags! {
29    /// Flags returned by [`ApiSetNamespaceEntry::flags`].
30    pub struct ApiSetNamespaceEntryFlags: u32 {
31        /// This API Set Namespace Entry is sealed, meaning the loader shall not look for a schema extension.
32        const SEALED = 1 << 0;
33        /// This API Set Namespace Entry is an extension (begins with "ext-" and not with "api-").
34        const IS_EXTENSION = 1 << 1;
35    }
36}
37
38/// Iterator over the [`ApiSetNamespaceEntry`]s of an [`ApiSetMap`].
39///
40/// This iterator is returned by [`ApiSetMap::namespace_entries`].
41///
42/// Namespace Entries are sorted case-insensitively by the name of the API Set.
43///
44/// [`ApiSetMap`]: crate::map::ApiSetMap
45/// [`ApiSetMap::namespace_entries`]: crate::map::ApiSetMap::namespace_entries
46#[derive(Clone, Debug)]
47pub struct ApiSetNamespaceEntries<'a> {
48    section_bytes: &'a [u8],
49    range: Range<usize>,
50}
51
52impl<'a> ApiSetNamespaceEntries<'a> {
53    pub(crate) const fn new(section_bytes: &'a [u8], range: Range<usize>) -> Self {
54        Self {
55            section_bytes,
56            range,
57        }
58    }
59}
60
61impl<'a> Iterator for ApiSetNamespaceEntries<'a> {
62    type Item = ApiSetNamespaceEntry<'a>;
63
64    fn next(&mut self) -> Option<Self::Item> {
65        let (header, _) =
66            LayoutVerified::<_, ApiSetNamespaceEntryHeader>::new_unaligned_from_prefix(
67                self.section_bytes.get(self.range.clone())?,
68            )?;
69        let entry = ApiSetNamespaceEntry {
70            section_bytes: self.section_bytes,
71            position: self.range.start,
72            header,
73        };
74        self.range.start += mem::size_of::<ApiSetNamespaceEntryHeader>();
75
76        Some(entry)
77    }
78
79    fn size_hint(&self) -> (usize, Option<usize>) {
80        let size = self.range.len() / mem::size_of::<ApiSetNamespaceEntryHeader>();
81        (size, Some(size))
82    }
83
84    fn nth(&mut self, n: usize) -> Option<Self::Item> {
85        // `n` is arbitrary and usize, so we may hit boundaries here. Check that!
86        let bytes_to_skip = n.checked_mul(mem::size_of::<ApiSetNamespaceEntryHeader>())?;
87        self.range.start = self.range.start.checked_add(bytes_to_skip)?;
88        self.next()
89    }
90}
91
92impl<'a> ExactSizeIterator for ApiSetNamespaceEntries<'a> {}
93impl<'a> FusedIterator for ApiSetNamespaceEntries<'a> {}
94
95/// A single Namespace Entry in an [`ApiSetMap`].
96///
97/// Such entries are returned by the [`ApiSetNamespaceEntries`] iterator as well as the [`ApiSetMap::find_namespace_entry`] function.
98///
99/// [`ApiSetMap`]: crate::map::ApiSetMap
100/// [`ApiSetMap::find_namespace_entry`]: crate::map::ApiSetMap::find_namespace_entry
101#[derive(Debug)]
102pub struct ApiSetNamespaceEntry<'a> {
103    section_bytes: &'a [u8],
104    position: usize,
105    header: LayoutVerified<&'a [u8], ApiSetNamespaceEntryHeader>,
106}
107
108impl<'a> ApiSetNamespaceEntry<'a> {
109    /// Returns flags set for this [`ApiSetNamespaceEntry`] as specified by [`ApiSetNamespaceEntryFlags`].
110    pub fn flags(&self) -> ApiSetNamespaceEntryFlags {
111        ApiSetNamespaceEntryFlags::from_bits_truncate(self.header.flags.get())
112    }
113
114    /// Returns the name of this API Set Namespace Entry.
115    ///
116    /// This name should begin with either "api-" or "ext-".
117    /// It does not end with a file extension.
118    pub fn name(&self) -> Result<U16StrLe<'a>> {
119        let start = self.header.name_offset.get() as usize;
120        let length = self.header.name_length.get() as usize;
121        let end = start + length;
122        let range = start..end;
123
124        let name_bytes =
125            self.section_bytes
126                .get(range.clone())
127                .ok_or(NtApiSetError::EntryNameOutOfBounds {
128                    name_range: range,
129                    entry_offset: self.position,
130                    actual: self.section_bytes.len(),
131                })?;
132
133        Ok(U16StrLe(name_bytes))
134    }
135
136    /// Returns an iterator over the [`ApiSetValueEntry`]s of this [`ApiSetNamespaceEntry`].
137    ///
138    /// These entries describe the mapping destination of an API Set Namespace Entry.
139    ///
140    /// [`ApiSetValueEntry`]: crate::value_entry::ApiSetValueEntry
141    pub fn value_entries(&self) -> Result<ApiSetValueEntries<'a>> {
142        let start = self.header.array_offset.get() as usize;
143        let count = self.header.array_count.get() as usize;
144        let end = start + mem::size_of::<ApiSetValueEntryHeader>() * count;
145        let range = start..end;
146
147        self.section_bytes
148            .get(range.clone())
149            .ok_or(NtApiSetError::ValueEntriesOutOfBounds {
150                range: start..end,
151                actual: self.section_bytes.len(),
152            })?;
153
154        Ok(ApiSetValueEntries::new(self.section_bytes, range))
155    }
156}