@@ -6,17 +6,15 @@ use crate::tpm::Swtpm;
66use crate :: util:: command_to_string;
77use crate :: { net, platform} ;
88use anyhow:: { bail, Context , Result } ;
9+ use ovmf_prebuilt:: { FileType , Prebuilt , Source } ;
910use regex:: bytes:: Regex ;
1011use serde_json:: { json, Value } ;
11- use sha2:: { Digest , Sha256 } ;
1212use std:: env;
1313use std:: ffi:: OsString ;
14- use std:: io:: { BufRead , BufReader , Cursor , Read , Write } ;
14+ use std:: io:: { BufRead , BufReader , Read , Write } ;
1515use std:: path:: { Path , PathBuf } ;
1616use std:: process:: { Child , Command , Stdio } ;
17- use tar:: Archive ;
1817use tempfile:: TempDir ;
19- use ureq:: Agent ;
2018#[ cfg( target_os = "linux" ) ]
2119use { std:: fs:: Permissions , std:: os:: unix:: fs:: PermissionsExt } ;
2220
@@ -38,161 +36,41 @@ const ENV_VAR_OVMF_VARS: &str = "OVMF_VARS";
3836/// Environment variable for overriding the path of the OVMF shell file.
3937const ENV_VAR_OVMF_SHELL : & str = "OVMF_SHELL" ;
4038
41- /// Download `url` and return the raw data.
42- fn download_url ( url : & str ) -> Result < Vec < u8 > > {
43- let agent: Agent = ureq:: AgentBuilder :: new ( )
44- . user_agent ( "uefi-rs-ovmf-downloader" )
45- . build ( ) ;
46-
47- // Limit the size of the download.
48- let max_size_in_bytes = 5 * 1024 * 1024 ;
49-
50- // Download the file.
51- println ! ( "downloading {url}" ) ;
52- let resp = agent. get ( url) . call ( ) ?;
53- let mut data = Vec :: with_capacity ( max_size_in_bytes) ;
54- resp. into_reader ( )
55- . take ( max_size_in_bytes. try_into ( ) . unwrap ( ) )
56- . read_to_end ( & mut data) ?;
57- println ! ( "received {} bytes" , data. len( ) ) ;
58-
59- Ok ( data)
60- }
61-
62- // Extract the tarball's files into `prebuilt_dir`.
63- //
64- // `tarball_data` is raw decompressed tar data.
65- fn extract_prebuilt ( tarball_data : & [ u8 ] , prebuilt_dir : & Path ) -> Result < ( ) > {
66- let cursor = Cursor :: new ( tarball_data) ;
67- let mut archive = Archive :: new ( cursor) ;
68-
69- // Extract each file entry.
70- for entry in archive. entries ( ) ? {
71- let mut entry = entry?;
72-
73- // Skip directories.
74- if entry. size ( ) == 0 {
75- continue ;
39+ impl From < UefiArch > for ovmf_prebuilt:: Arch {
40+ fn from ( arch : UefiArch ) -> Self {
41+ match arch {
42+ UefiArch :: AArch64 => Self :: Aarch64 ,
43+ UefiArch :: IA32 => Self :: Ia32 ,
44+ UefiArch :: X86_64 => Self :: X64 ,
7645 }
77-
78- let path = entry. path ( ) ?;
79- // Strip the leading directory, which is the release name.
80- let path: PathBuf = path. components ( ) . skip ( 1 ) . collect ( ) ;
81-
82- let dir = path. parent ( ) . unwrap ( ) ;
83- let dst_dir = prebuilt_dir. join ( dir) ;
84- let dst_path = prebuilt_dir. join ( path) ;
85- println ! ( "unpacking to {}" , dst_path. display( ) ) ;
86- fs_err:: create_dir_all ( dst_dir) ?;
87- entry. unpack ( dst_path) ?;
8846 }
89-
90- Ok ( ( ) )
9147}
9248
93- /// Update the local copy of the prebuilt OVMF files. Does nothing if the local
94- /// copy is already up to date.
95- fn update_prebuilt ( ) -> Result < PathBuf > {
96- let prebuilt_dir = Path :: new ( OVMF_PREBUILT_DIR ) ;
97- let hash_path = prebuilt_dir . join ( "sha256" ) ;
98-
99- // Check if the hash file already has the expected hash in it. If so, assume
100- // that we've already got the correct prebuilt downloaded and unpacked.
101- if let Ok ( current_hash ) = fs_err :: read_to_string ( & hash_path ) {
102- if current_hash == OVMF_PREBUILT_HASH {
103- return Ok ( prebuilt_dir . to_path_buf ( ) ) ;
49+ /// Get a user-provided path for the given OVMF file type.
50+ ///
51+ /// This uses the command-line arg if present, otherwise it falls back to an
52+ /// environment variable. If neither is present, returns `None`.
53+ fn get_user_provided_path ( file_type : FileType , opt : & QemuOpt ) -> Option < PathBuf > {
54+ let opt_path ;
55+ let var_name ;
56+ match file_type {
57+ FileType :: Code => {
58+ opt_path = & opt . ovmf_code ;
59+ var_name = ENV_VAR_OVMF_CODE ;
10460 }
105- }
106-
107- let base_url = "https://github.com/rust-osdev/ovmf-prebuilt/releases/download" ;
108- let url = format ! (
109- "{base_url}/{release}/{release}-bin.tar.xz" ,
110- release = OVMF_PREBUILT_TAG
111- ) ;
112-
113- let data = download_url ( & url) ?;
114-
115- // Validate the hash.
116- let actual_hash = format ! ( "{:x}" , Sha256 :: digest( & data) ) ;
117- if actual_hash != OVMF_PREBUILT_HASH {
118- bail ! (
119- "file hash {actual_hash} does not match {}" ,
120- OVMF_PREBUILT_HASH
121- ) ;
122- }
123-
124- // Unpack the tarball.
125- println ! ( "decompressing tarball" ) ;
126- let mut decompressed = Vec :: new ( ) ;
127- let mut compressed = Cursor :: new ( data) ;
128- lzma_rs:: xz_decompress ( & mut compressed, & mut decompressed) ?;
129-
130- // Clear out the existing prebuilt dir, if present.
131- let _ = fs_err:: remove_dir_all ( prebuilt_dir) ;
132-
133- // Extract the files.
134- extract_prebuilt ( & decompressed, prebuilt_dir) ?;
135-
136- // Rename the x64 directory to x86_64, to match `Arch::as_str`.
137- fs_err:: rename ( prebuilt_dir. join ( "x64" ) , prebuilt_dir. join ( "x86_64" ) ) ?;
138-
139- // Write out the hash file. When we upgrade to a new release of
140- // ovmf-prebuilt, the hash will no longer match, triggering a fresh
141- // download.
142- fs_err:: write ( & hash_path, actual_hash) ?;
143-
144- Ok ( prebuilt_dir. to_path_buf ( ) )
145- }
146-
147- #[ derive( Clone , Copy , Debug ) ]
148- enum OvmfFileType {
149- Code ,
150- Vars ,
151- Shell ,
152- }
153-
154- impl OvmfFileType {
155- fn as_str ( & self ) -> & ' static str {
156- match self {
157- Self :: Code => "code" ,
158- Self :: Vars => "vars" ,
159- Self :: Shell => "shell" ,
61+ FileType :: Vars => {
62+ opt_path = & opt. ovmf_vars ;
63+ var_name = ENV_VAR_OVMF_VARS ;
16064 }
161- }
162-
163- fn extension ( & self ) -> & ' static str {
164- match self {
165- Self :: Code | Self :: Vars => "fd" ,
166- Self :: Shell => "efi" ,
65+ FileType :: Shell => {
66+ opt_path = & None ;
67+ var_name = ENV_VAR_OVMF_SHELL ;
16768 }
16869 }
169-
170- /// Get a user-provided path for the given OVMF file type.
171- ///
172- /// This uses the command-line arg if present, otherwise it falls back to an
173- /// environment variable. If neither is present, returns `None`.
174- fn get_user_provided_path ( self , opt : & QemuOpt ) -> Option < PathBuf > {
175- let opt_path;
176- let var_name;
177- match self {
178- Self :: Code => {
179- opt_path = & opt. ovmf_code ;
180- var_name = ENV_VAR_OVMF_CODE ;
181- }
182- Self :: Vars => {
183- opt_path = & opt. ovmf_vars ;
184- var_name = ENV_VAR_OVMF_VARS ;
185- }
186- Self :: Shell => {
187- opt_path = & None ;
188- var_name = ENV_VAR_OVMF_SHELL ;
189- }
190- }
191- if let Some ( path) = opt_path {
192- Some ( path. clone ( ) )
193- } else {
194- env:: var_os ( var_name) . map ( PathBuf :: from)
195- }
70+ if let Some ( path) = opt_path {
71+ Some ( path. clone ( ) )
72+ } else {
73+ env:: var_os ( var_name) . map ( PathBuf :: from)
19674 }
19775}
19876
@@ -210,8 +88,8 @@ impl OvmfPaths {
21088 /// 1. Command-line arg
21189 /// 2. Environment variable
21290 /// 3. Prebuilt file (automatically downloaded)
213- fn find_ovmf_file ( file_type : OvmfFileType , opt : & QemuOpt , arch : UefiArch ) -> Result < PathBuf > {
214- if let Some ( path) = file_type . get_user_provided_path ( opt) {
91+ fn find_ovmf_file ( file_type : FileType , opt : & QemuOpt , arch : UefiArch ) -> Result < PathBuf > {
92+ if let Some ( path) = get_user_provided_path ( file_type , opt) {
21593 // The user provided an exact path to use; verify that it
21694 // exists.
21795 if path. exists ( ) {
@@ -224,22 +102,21 @@ impl OvmfPaths {
224102 ) ;
225103 }
226104 } else {
227- let prebuilt_dir = update_prebuilt ( ) ?;
105+ let prebuilt = Prebuilt :: fetch ( Source {
106+ tag : OVMF_PREBUILT_TAG ,
107+ sha256 : OVMF_PREBUILT_HASH ,
108+ } , OVMF_PREBUILT_DIR ) ?;
228109
229- Ok ( prebuilt_dir. join ( format ! (
230- "{arch}/{}.{}" ,
231- file_type. as_str( ) ,
232- file_type. extension( )
233- ) ) )
110+ Ok ( prebuilt. get_file ( arch. into ( ) , file_type) )
234111 }
235112 }
236113
237114 /// Find path to OVMF files by the strategy documented for
238115 /// [`Self::find_ovmf_file`].
239116 fn find ( opt : & QemuOpt , arch : UefiArch ) -> Result < Self > {
240- let code = Self :: find_ovmf_file ( OvmfFileType :: Code , opt, arch) ?;
241- let vars = Self :: find_ovmf_file ( OvmfFileType :: Vars , opt, arch) ?;
242- let shell = Self :: find_ovmf_file ( OvmfFileType :: Shell , opt, arch) ?;
117+ let code = Self :: find_ovmf_file ( FileType :: Code , opt, arch) ?;
118+ let vars = Self :: find_ovmf_file ( FileType :: Vars , opt, arch) ?;
119+ let shell = Self :: find_ovmf_file ( FileType :: Shell , opt, arch) ?;
243120
244121 Ok ( Self { code, vars, shell } )
245122 }
0 commit comments