Skip to content

Commit e1cf2e3

Browse files
committed
wip: continue liveusb command implementation
1 parent 2ce2374 commit e1cf2e3

File tree

7 files changed

+151
-22
lines changed

7 files changed

+151
-22
lines changed

goldboot-image/src/lib.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ pub enum ImageArch {
3737
S390x,
3838
}
3939

40+
impl ImageArch {
41+
pub fn as_github_string(&self) -> String {
42+
match self {
43+
ImageArch::Amd64 => "x86_64",
44+
ImageArch::Arm64 => todo!(),
45+
ImageArch::I386 => todo!(),
46+
ImageArch::Mips => todo!(),
47+
ImageArch::Mips64 => todo!(),
48+
ImageArch::S390x => todo!(),
49+
}
50+
.to_string()
51+
}
52+
}
53+
4054
impl Default for ImageArch {
4155
fn default() -> Self {
4256
match std::env::consts::ARCH {
@@ -387,11 +401,11 @@ impl ImageHandle {
387401
let path = path.as_ref();
388402
let mut file = File::open(path)?;
389403

390-
debug!("Opening image from: {}", path.display());
404+
debug!(path = ?path, "Opening image");
391405

392406
// Read primary header (always plaintext)
393407
let primary_header: PrimaryHeader = file.read_be()?;
394-
trace!("Read: {:?}", &primary_header);
408+
debug!(primary_header = ?primary_header, "Primary header");
395409

396410
// Get image ID
397411
let id = if let Some(stem) = path.file_stem() {
@@ -753,9 +767,9 @@ impl ImageHandle {
753767
let mut cluster: Cluster = cluster_table.read_be()?;
754768

755769
trace!(
756-
"Read cluster of size {} from offset {}",
757-
cluster.size,
758-
entry.cluster_offset
770+
cluster_size = cluster.size,
771+
cluster_offset = entry.cluster_offset,
772+
"Read cluster",
759773
);
760774

761775
// Reverse encryption
@@ -777,7 +791,7 @@ impl ImageHandle {
777791

778792
// Write the cluster to the block
779793
dest.seek(SeekFrom::Start(entry.block_offset))?;
780-
dest.write_all(&cluster.data)?;
794+
// dest.write_all(&cluster.data)?;
781795
}
782796

783797
progress(

goldboot/src/cli/cmd/image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub fn run(cmd: super::Commands) -> ExitCode {
99
match cmd {
1010
super::Commands::Image { command } => match &command {
1111
super::ImageCommands::List {} => {
12-
let images = ImageLibrary::load().unwrap();
12+
let images = ImageLibrary::find_all().unwrap();
1313

1414
println!("Image Name Image Size Build Date Image ID Description");
1515
for image in images {

goldboot/src/cli/cmd/liveusb.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,34 @@ pub fn run(cmd: super::Commands) -> ExitCode {
2222
return ExitCode::FAILURE;
2323
}
2424

25+
// Load from library or download
26+
let mut image_handles = match ImageLibrary::find_by_name("Test") {
27+
Ok(image_handles) => image_handles,
28+
Err(_) => return ExitCode::FAILURE,
29+
};
30+
31+
// TODO prompt password
32+
if image_handles[0].load(None).is_err() {
33+
return ExitCode::FAILURE;
34+
}
35+
2536
if !confirm {
2637
if !Confirm::with_theme(&theme)
27-
.with_prompt("Do you want to continue?")
38+
.with_prompt(format!("Do you want to overwrite: {}?", dest))
2839
.interact()
2940
.unwrap()
3041
{
3142
std::process::exit(0);
3243
}
3344
}
3445

35-
ExitCode::SUCCESS
46+
match image_handles[0].write(dest, ProgressBar::Write.new_empty()) {
47+
Err(err) => {
48+
error!(error = %err, "Failed to write image");
49+
ExitCode::FAILURE
50+
}
51+
_ => ExitCode::SUCCESS,
52+
}
3653
}
3754
_ => panic!(),
3855
}

goldboot/src/foundry/molds/goldboot/mod.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ use anyhow::{bail, Result};
22
use dialoguer::theme::Theme;
33
use goldboot_image::ImageArch;
44
use serde::{Deserialize, Serialize};
5-
use std::io::{BufRead, BufReader};
5+
use serde_json::{Map, Value};
6+
use std::{
7+
collections::HashMap,
8+
io::{BufRead, BufReader},
9+
};
10+
use tracing::debug;
611
use validator::Validate;
712

813
use crate::{
@@ -54,6 +59,10 @@ impl CastImage for Goldboot {
5459
let mut qemu = QemuBuilder::new(&worker, OsCategory::Linux)
5560
.vga("cirrus")
5661
.source(&worker.element.source)?
62+
.drive_files(HashMap::from([(
63+
"goldboot".to_string(),
64+
get_latest_release(OsCategory::Linux, worker.arch)?,
65+
)]))?
5766
.start()?;
5867

5968
// Start HTTP
@@ -77,7 +86,8 @@ impl CastImage for Goldboot {
7786
enter!("root"),
7887
enter!("r00tme"),
7988
// Install goldboot
80-
enter!(format!("curl -o /usr/bin/goldboot https://github.com/fossable/goldboot/releases/download/goldboot-v0.0.7/goldboot_{}-unknown-linux-gnu", worker.arch)),
89+
enter!("mount /dev/vdb /mnt"),
90+
enter!("cp /mnt/goldboot /usr/bin/goldboot"),
8191
enter!("chmod +x /usr/bin/goldboot"),
8292
// Skip getty login
8393
enter!("sed -i 's|ExecStart=.*$|ExecStart=/usr/bin/goldboot|' /usr/lib/systemd/system/[email protected]"),
@@ -89,3 +99,80 @@ impl CastImage for Goldboot {
8999
Ok(())
90100
}
91101
}
102+
103+
/// Download the latest goldboot release.
104+
fn get_latest_release(os: OsCategory, arch: ImageArch) -> Result<Vec<u8>> {
105+
// List releases
106+
let releases: Vec<Value> = reqwest::blocking::Client::new()
107+
.get("https://api.github.com/repos/fossable/goldboot/releases")
108+
.header("Accept", "application/vnd.github+json")
109+
.header("X-GitHub-Api-Version", "2022-11-28")
110+
.header("User-Agent", "goldboot")
111+
.send()?
112+
.json()?;
113+
debug!(count = releases.len(), "Total releases");
114+
115+
// Match the major and minor versions against what we're currently running
116+
let mut releases: Vec<Map<String, Value>> = releases
117+
.into_iter()
118+
.filter_map(|r| match r {
119+
Value::Object(release) => match release.get("tag_name") {
120+
Some(Value::String(name)) => {
121+
if name.starts_with(&format!(
122+
"goldboot-v{}.{}.",
123+
crate::built_info::PKG_VERSION_MAJOR,
124+
crate::built_info::PKG_VERSION_MINOR
125+
)) {
126+
Some(release)
127+
} else {
128+
None
129+
}
130+
}
131+
_ => None,
132+
},
133+
_ => None,
134+
})
135+
.collect();
136+
137+
debug!(count = releases.len(), "Matched releases");
138+
139+
// Sort by patch version
140+
releases.sort_by_key(|release| match release.get("tag_name") {
141+
Some(Value::String(name)) => name.split(".").last().unwrap().parse::<i64>().unwrap(),
142+
_ => todo!(),
143+
});
144+
145+
// Find asset for the given arch
146+
let asset = match releases.last().unwrap().get("assets") {
147+
Some(Value::Array(assets)) => assets
148+
.iter()
149+
.filter_map(|a| match a {
150+
Value::Object(asset) => match asset.get("name") {
151+
Some(Value::String(name)) => {
152+
if name.contains(&arch.as_github_string())
153+
&& name.contains(&os.as_github_string())
154+
{
155+
Some(asset)
156+
} else {
157+
None
158+
}
159+
}
160+
_ => None,
161+
},
162+
_ => None,
163+
})
164+
.last(),
165+
_ => None,
166+
};
167+
168+
// Download the asset
169+
if let Some(asset) = asset {
170+
debug!(asset = ?asset, "Found asset for download");
171+
match asset.get("browser_download_url") {
172+
Some(Value::String(url)) => Ok(reqwest::blocking::get(url)?.bytes()?.into()),
173+
_ => todo!(),
174+
}
175+
} else {
176+
bail!("No release asset found for OS/Arch combination");
177+
}
178+
}

goldboot/src/foundry/qemu.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@ pub enum OsCategory {
2525
Windows,
2626
}
2727

28+
impl OsCategory {
29+
/// Convert to String representation for use in a Github release asset
30+
pub fn as_github_string(&self) -> String {
31+
match self {
32+
OsCategory::Darwin => "apple",
33+
OsCategory::Linux => "linux",
34+
OsCategory::Windows => "windows",
35+
}
36+
.to_string()
37+
}
38+
}
39+
2840
/// Supported VM hardware acceleration.
2941
pub enum Accel {
42+
/// "Kernel VM" which requires Intel VT or AMD-V
3043
Kvm,
3144
/// Basically means no acceleration
3245
Tcg,
@@ -363,7 +376,7 @@ impl QemuBuilder {
363376

364377
// Add a buffer of extra space
365378
let mut fs_size: u64 = files.values().map(|c| c.len() as u64).sum();
366-
fs_size += 32000;
379+
fs_size += 320000;
367380

368381
debug!(
369382
fs_path = ?fs_path,
@@ -391,6 +404,7 @@ impl QemuBuilder {
391404
let root_dir = fs.root_dir();
392405

393406
for (path, content) in &files {
407+
debug!(path = ?path, size = content.len(), "Copying file to temporary filesystem");
394408
let mut file = root_dir.create_file(path)?;
395409
file.write_all(content)?;
396410
}

goldboot/src/gui/select_image.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn init(window: &'static gtk::ApplicationWindow) {
4343

4444
let mut images = Vec::new();
4545

46-
for image in ImageLibrary::load().unwrap() {
46+
for image in ImageLibrary::find_all().unwrap() {
4747
images.push(image.id.clone());
4848
image_box.append(&create_image_row(&image));
4949
}

goldboot/src/library.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{
1010
fs::File,
1111
path::{Path, PathBuf},
1212
};
13-
use tracing::{debug, info};
13+
use tracing::{debug, error, info};
1414

1515
/// Represents the local image library.
1616
///
@@ -119,33 +119,30 @@ impl ImageLibrary {
119119

120120
/// Find images in the library by ID.
121121
pub fn find_by_id(image_id: &str) -> Result<ImageHandle> {
122-
Ok(Self::load()?
122+
Ok(Self::find_all()?
123123
.into_iter()
124124
.find(|image| image.id == image_id || image.id[0..12] == image_id[0..12])
125125
.ok_or_else(|| anyhow!("Image not found"))?)
126126
}
127127

128128
/// Find images in the library by name.
129129
pub fn find_by_name(image_name: &str) -> Result<Vec<ImageHandle>> {
130-
Ok(Self::load()?
130+
Ok(Self::find_all()?
131131
.into_iter()
132132
.filter(|image| image.primary_header.name() == image_name)
133133
.collect())
134134
}
135135

136-
/// Load images present in the local image library.
137-
pub fn load() -> Result<Vec<ImageHandle>> {
136+
/// Find all images present in the local image library.
137+
pub fn find_all() -> Result<Vec<ImageHandle>> {
138138
let mut images = Vec::new();
139139

140140
for p in Self::open().directory.read_dir()? {
141141
let path = p?.path();
142142

143143
if let Some(ext) = path.extension() {
144144
if ext == "gb" {
145-
match ImageHandle::open(&path) {
146-
Ok(image) => images.push(image),
147-
Err(error) => debug!("Failed to load image: {:?}", error),
148-
}
145+
images.push(ImageHandle::open(&path)?);
149146
}
150147
}
151148
}

0 commit comments

Comments
 (0)