Skip to main content

efivar_fix/boot/parse/
boot_entry.rs

1//! This module contains parsing code for a boot entry
2
3use std::{fmt::Display, io::Read};
4
5use byteorder::{LittleEndian, ReadBytesExt};
6
7use super::FilePathList;
8use crate::{efi::Variable, push::PushVecU8, utils::read_nt_utf16_string, Error, VarReader};
9use std::convert::TryFrom;
10
11bitflags::bitflags! {
12    /// Possible attributes of a boot entry as a bitfield
13    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14    pub struct BootEntryAttributes : u32 {
15        const LOAD_OPTION_ACTIVE = 0x1;
16        const LOAD_OPTION_FORCE_RECONNECT = 0x2;
17        const LOAD_OPTION_HIDDEN = 0x8;
18        const LOAD_OPTION_CATEGORY_APP = 0x100;
19    }
20}
21
22impl Display for BootEntryAttributes {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        Display::fmt(&self.0, f)
25    }
26}
27
28#[derive(Debug, PartialEq, Clone)]
29pub struct BootEntry {
30    pub attributes: BootEntryAttributes,
31    pub description: String,
32    pub file_path_list: Option<FilePathList>,
33    pub optional_data: Vec<u8>,
34}
35
36impl BootEntry {
37    pub fn read(manager: &(impl ?Sized + VarReader), variable: &Variable) -> crate::Result<Self> {
38        let (value, _flags) = manager.read(variable)?;
39        Self::parse(value)
40    }
41
42    pub fn parse(value: Vec<u8>) -> crate::Result<Self> {
43        // slice of the buffer
44        // Used so we can move the offset in it with ReadBytesExt functions
45        let mut buf = &value[..];
46
47        let attributes = buf
48            .read_u32::<LittleEndian>()
49            .map_err(|_| Error::VarParseError)?;
50
51        let file_path_list_length = buf
52            .read_u16::<LittleEndian>()
53            .map_err(|_| Error::VarParseError)?;
54
55        let description = read_nt_utf16_string(&mut buf).map_err(crate::Error::StringParseError)?;
56
57        let mut file_path_list_buf = vec![0u8; file_path_list_length.into()];
58        buf.read_exact(&mut file_path_list_buf)
59            .map_err(|_| Error::VarParseError)?;
60
61        let file_path_list = FilePathList::parse(&mut &file_path_list_buf[..])?.into();
62
63        Ok(BootEntry {
64            attributes: BootEntryAttributes::from_bits(attributes).ok_or(Error::VarParseError)?,
65            description,
66            file_path_list,
67            optional_data: buf.to_vec(),
68        })
69    }
70
71    pub fn to_bytes(&self) -> Vec<u8> {
72        let mut bytes: Vec<u8> = vec![];
73
74        // append attribute bytes
75        bytes.push_u32(self.attributes.bits());
76
77        // append file path list length
78        let mut fpl_bytes: Vec<u8> = if let Some(fpl) = &self.file_path_list {
79            fpl.to_bytes()
80        } else {
81            vec![]
82        };
83        bytes.append(
84            &mut u16::try_from(fpl_bytes.len())
85                .expect("length should fit in u16")
86                .to_le_bytes()
87                .to_vec(),
88        );
89
90        // append description bytes
91        let mut desc_bytes = self
92            .description
93            .encode_utf16()
94            .flat_map(|var| var.to_le_bytes())
95            .collect();
96        bytes.append(&mut desc_bytes);
97        // write description null termination
98        bytes.append(&mut vec![0x00, 0x00]);
99
100        // append file path list bytes
101        bytes.append(&mut fpl_bytes);
102
103        // append optional data
104        bytes.append(&mut self.optional_data.clone());
105
106        bytes
107    }
108}