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}