ovmf_prebuilt/
lib.rs

1//! Download, cache, and access [OVMF] prebuilts.
2//!
3//! [OVMF]: https://github.com/tianocore/edk2/tree/master/OvmfPkg#readme
4//!
5//! # Example
6//!
7//! ```
8//! use ovmf_prebuilt::{Arch, FileType, Source, Prebuilt};
9//! use std::path::Path;
10//!
11//! let prebuilt = Prebuilt::fetch(Source::LATEST, "target/ovmf")
12//!     .expect("failed to update prebuilt");
13//! assert_eq!(
14//!     prebuilt.get_file(Arch::X64, FileType::Code),
15//!     Path::new("target/ovmf/x64/code.fd")
16//! );
17//! ```
18
19#![warn(missing_docs)]
20
21mod error;
22mod fetch;
23mod source_constants;
24
25use fetch::update_cache;
26use std::path::{Path, PathBuf};
27
28pub use error::Error;
29
30/// Which prebuilt to download.
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct Source {
33    /// Release tag name, e.g. "edk2-stable202408-r1".
34    pub tag: &'static str,
35
36    /// SHA-256 hash of the compressed tarball.
37    pub sha256: &'static str,
38}
39
40/// UEFI architecture.
41#[allow(missing_docs)]
42#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
43pub enum Arch {
44    Aarch64,
45    Ia32,
46    LoongArch64,
47    Riscv64,
48    X64,
49}
50
51impl Arch {
52    /// Convert to a string.
53    pub fn as_str(self) -> &'static str {
54        match self {
55            Self::Aarch64 => "aarch64",
56            Self::Ia32 => "ia32",
57            Self::LoongArch64 => "loongarch64",
58            Self::Riscv64 => "riscv64",
59            Self::X64 => "x64",
60        }
61    }
62}
63
64/// Type of file within the prebuilt archive.
65#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
66#[allow(missing_docs)]
67pub enum FileType {
68    Code,
69    Vars,
70    Shell,
71}
72
73impl FileType {
74    /// Convert to a string.
75    pub fn as_str(self) -> &'static str {
76        match self {
77            Self::Code => "code.fd",
78            Self::Vars => "vars.fd",
79            Self::Shell => "shell.efi",
80        }
81    }
82}
83
84/// Cached prebuilt.
85pub struct Prebuilt {
86    dir: PathBuf,
87}
88
89impl Prebuilt {
90    /// Fetch a prebuilt from a local cache. If the cache is out of
91    /// date, the prebuilt is downloaded and the cache is updated.
92    ///
93    /// The SHA-256 hash of the original prebuilt is stored in
94    /// `<prebuilt_dir>/sha256`. This is used to determine whether the
95    /// cache is up-to-date. Note that if some external process modifies
96    /// the cached files but leaves the `sha256` file unmodified, this
97    /// code will not detect that the cache is invalid.
98    ///
99    /// If the cache is updated, the downloaded prebuilt's hash will be
100    /// checked against [`source.sha256`]. An error will be
101    /// returned if the hash does not match, and the filesystem will not
102    /// be modified. This ensures that if you pin this library in
103    /// `Cargo.lock`, and use one of the [`Source`] associated
104    /// constants, the library will never unpack unverified files. This
105    /// provides some protection against a malicious attack modifying
106    /// the release tarballs on Github.
107    ///
108    /// [`source.sha256`]: Source::sha256
109    pub fn fetch<P: AsRef<Path>>(source: Source, prebuilt_dir: P) -> Result<Self, Error> {
110        let prebuilt_dir = prebuilt_dir.as_ref();
111
112        update_cache(source, prebuilt_dir)?;
113
114        Ok(Self {
115            dir: prebuilt_dir.to_owned(),
116        })
117    }
118
119    /// Get the path of a specific file within the cache.
120    pub fn get_file(&self, arch: Arch, file_type: FileType) -> PathBuf {
121        self.dir.join(arch.as_str()).join(file_type.as_str())
122    }
123}