Merge branch 'ext' into 'master'
Modernize code and add support for local binary packages See merge request redox-os/installer!38
This commit is contained in:
Generated
+13
-58
@@ -1,6 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
@@ -68,6 +68,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
|
||||
|
||||
[[package]]
|
||||
name = "arg_parser"
|
||||
version = "0.1.0"
|
||||
@@ -502,28 +508,6 @@ dependencies = [
|
||||
"version_check 0.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fatfs"
|
||||
version = "0.3.6"
|
||||
@@ -1165,9 +1149,9 @@ dependencies = [
|
||||
name = "redox_installer"
|
||||
version = "0.2.24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arg_parser",
|
||||
"cc",
|
||||
"failure",
|
||||
"fatfs",
|
||||
"fscommon",
|
||||
"gpt",
|
||||
@@ -1443,7 +1427,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1511,17 +1495,6 @@ version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
@@ -1533,18 +1506,6 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.41"
|
||||
@@ -1627,7 +1588,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1753,12 +1714,6 @@ version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.6.2"
|
||||
@@ -1901,7 +1856,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -1923,7 +1878,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -2171,5 +2126,5 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.68",
|
||||
"syn",
|
||||
]
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ license = "MIT"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
repository = "https://gitlab.redox-os.org/redox-os/installer"
|
||||
default-run = "redox_installer"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "redox_installer"
|
||||
@@ -24,9 +25,9 @@ name = "redox_installer"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.89"
|
||||
arg_parser = "0.1.0"
|
||||
cc = "=1.0.99" # Hack for ring 0.13.5 not building
|
||||
failure = "0.1.8"
|
||||
fatfs = "0.3.0"
|
||||
fscommon = "0.1.1"
|
||||
gpt = "3.0.0"
|
||||
|
||||
@@ -114,6 +114,7 @@ fn main() {
|
||||
version: None,
|
||||
git: None,
|
||||
path: None,
|
||||
pkg_path: None,
|
||||
} => false,
|
||||
_ => true,
|
||||
})
|
||||
@@ -143,7 +144,8 @@ fn main() {
|
||||
};
|
||||
|
||||
if let Some(path) = parser.args.get(0) {
|
||||
if let Err(err) = redox_installer::install(config, path, cookbook, parser.found("live"))
|
||||
if let Err(err) =
|
||||
redox_installer::install(config, path, cookbook.as_deref(), parser.found("live"))
|
||||
{
|
||||
writeln!(stderr, "installer: failed to install: {}", err).unwrap();
|
||||
process::exit(1);
|
||||
|
||||
+81
-82
@@ -1,24 +1,15 @@
|
||||
extern crate arg_parser;
|
||||
#[macro_use] extern crate failure;
|
||||
extern crate pkgar;
|
||||
extern crate pkgar_core;
|
||||
extern crate pkgar_keys;
|
||||
extern crate redox_installer;
|
||||
extern crate serde;
|
||||
extern crate termion;
|
||||
extern crate toml;
|
||||
|
||||
use pkgar::{PackageHead, ext::EntryExt};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use pkgar::{ext::EntryExt, PackageHead};
|
||||
use pkgar_core::PackageSrc;
|
||||
use pkgar_keys::PublicKeyFile;
|
||||
use redox_installer::{Config, with_whole_disk, DiskOption};
|
||||
use redox_installer::{with_whole_disk, Config, DiskOption};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
os::unix::fs::{MetadataExt, OpenOptionsExt, symlink},
|
||||
os::unix::fs::{symlink, MetadataExt, OpenOptionsExt},
|
||||
path::Path,
|
||||
process
|
||||
process,
|
||||
};
|
||||
use termion::input::TermRead;
|
||||
|
||||
@@ -29,52 +20,54 @@ fn disk_paths(_paths: &mut Vec<(String, u64)>) {}
|
||||
fn disk_paths(paths: &mut Vec<(String, u64)>) {
|
||||
let mut schemes = Vec::new();
|
||||
match fs::read_dir("/scheme") {
|
||||
Ok(entries) => for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
let path = entry.path();
|
||||
if let Ok(path_str) = path.into_os_string().into_string() {
|
||||
let scheme = path_str.trim_start_matches(':').trim_matches('/');
|
||||
if scheme.starts_with("disk") {
|
||||
if scheme == "disk/live" {
|
||||
// Skip live disks
|
||||
continue;
|
||||
}
|
||||
Ok(entries) => {
|
||||
for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
let path = entry.path();
|
||||
if let Ok(path_str) = path.into_os_string().into_string() {
|
||||
let scheme = path_str.trim_start_matches(':').trim_matches('/');
|
||||
if scheme.starts_with("disk") {
|
||||
if scheme == "disk/live" {
|
||||
// Skip live disks
|
||||
continue;
|
||||
}
|
||||
|
||||
schemes.push(format!("{}:", scheme));
|
||||
schemes.push(format!("{}:", scheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("installer_tui: failed to list schemes: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
for scheme in schemes {
|
||||
let is_dir = fs::metadata(&scheme)
|
||||
.map(|x| x.is_dir())
|
||||
.unwrap_or(false);
|
||||
let is_dir = fs::metadata(&scheme).map(|x| x.is_dir()).unwrap_or(false);
|
||||
if is_dir {
|
||||
match fs::read_dir(&scheme) {
|
||||
Ok(entries) => for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
if let Ok(file_name) = entry.file_name().into_string() {
|
||||
if file_name.contains('p') {
|
||||
// Skip partitions
|
||||
continue;
|
||||
}
|
||||
Ok(entries) => {
|
||||
for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
if let Ok(file_name) = entry.file_name().into_string() {
|
||||
if file_name.contains('p') {
|
||||
// Skip partitions
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(path) = entry.path().into_os_string().into_string() {
|
||||
if let Ok(metadata) = entry.metadata() {
|
||||
let size = metadata.len();
|
||||
if size > 0 {
|
||||
paths.push((path, size));
|
||||
if let Ok(path) = entry.path().into_os_string().into_string() {
|
||||
if let Ok(metadata) = entry.metadata() {
|
||||
let size = metadata.len();
|
||||
if size > 0 {
|
||||
paths.push((path, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("installer_tui: failed to list '{}': {}", scheme, err);
|
||||
}
|
||||
@@ -102,14 +95,14 @@ fn format_size(size: u64) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Error> {
|
||||
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<()> {
|
||||
if let Some(parent) = dest.parent() {
|
||||
// Parent may be a symlink
|
||||
if ! parent.is_symlink() {
|
||||
if !parent.is_symlink() {
|
||||
match fs::create_dir_all(&parent) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to create directory {}: {}", parent.display(), err));
|
||||
bail!("failed to create directory {}: {}", parent.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,36 +111,47 @@ fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Err
|
||||
let metadata = match fs::symlink_metadata(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to read metadata of {}: {}", src.display(), err));
|
||||
},
|
||||
bail!("failed to read metadata of {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
if metadata.file_type().is_symlink() {
|
||||
let real_src = match fs::read_link(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to read link {}: {}", src.display(), err));
|
||||
bail!("failed to read link {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
match symlink(&real_src, &dest) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to copy link {} ({}) to {}: {}", src.display(), real_src.display(), dest.display(), err));
|
||||
},
|
||||
bail!(
|
||||
"failed to copy link {} ({}) to {}: {}",
|
||||
src.display(),
|
||||
real_src.display(),
|
||||
dest.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut src_file = match fs::File::open(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to open file {}: {}", src.display(), err));
|
||||
bail!("failed to open file {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
let mut dest_file = match fs::OpenOptions::new().write(true).create_new(true).mode(metadata.mode()).open(&dest) {
|
||||
let mut dest_file = match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.mode(metadata.mode())
|
||||
.open(&dest)
|
||||
{
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to create file {}: {}", dest.display(), err));
|
||||
bail!("failed to create file {}: {}", dest.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,7 +159,7 @@ fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Err
|
||||
let count = match src_file.read(buf) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to read file {}: {}", src.display(), err));
|
||||
bail!("failed to read file {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,7 +170,7 @@ fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Err
|
||||
match dest_file.write_all(&buf[..count]) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
return Err(format_err!("failed to write file {}: {}", dest.display(), err));
|
||||
bail!("failed to write file {}: {}", dest.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,7 +179,11 @@ fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<(), failure::Err
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn package_files(root_path: &Path, config: &mut Config, files: &mut Vec<String>) -> Result<(), pkgar::Error> {
|
||||
fn package_files(
|
||||
root_path: &Path,
|
||||
config: &mut Config,
|
||||
files: &mut Vec<String>,
|
||||
) -> Result<(), pkgar::Error> {
|
||||
//TODO: Remove packages from config where all files are located (and have valid shasum?)
|
||||
config.packages.clear();
|
||||
|
||||
@@ -189,18 +197,15 @@ fn package_files(root_path: &Path, config: &mut Config, files: &mut Vec<String>)
|
||||
if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
|
||||
let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
|
||||
for entry in pkg.read_entries()? {
|
||||
files.push(
|
||||
entry
|
||||
.check_path()?
|
||||
.to_str().unwrap()
|
||||
.to_string()
|
||||
);
|
||||
files.push(entry.check_path()?.to_str().unwrap().to_string());
|
||||
}
|
||||
files.push(
|
||||
pkg_path
|
||||
.strip_prefix(root_path).unwrap()
|
||||
.to_str().unwrap()
|
||||
.to_string()
|
||||
.strip_prefix(root_path)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -227,7 +232,7 @@ fn choose_disk() -> String {
|
||||
Ok(0) => {
|
||||
eprintln!("installer_tui: failed to read line: end of input");
|
||||
process::exit(1);
|
||||
},
|
||||
}
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
eprintln!("installer_tui: failed to read line: {}", err);
|
||||
@@ -242,7 +247,7 @@ fn choose_disk() -> String {
|
||||
} else {
|
||||
eprintln!("{} not from 1 to {}", i, paths.len());
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("invalid input: {}", err);
|
||||
}
|
||||
@@ -262,7 +267,7 @@ fn choose_password() -> Option<String> {
|
||||
eprintln!();
|
||||
|
||||
if password.is_empty() {
|
||||
return None;
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(password)
|
||||
@@ -311,28 +316,22 @@ fn main() {
|
||||
password_opt: password_opt.as_ref().map(|x| x.as_bytes()),
|
||||
efi_partition_size: None,
|
||||
};
|
||||
let res = with_whole_disk(&disk_path, &disk_option, |mount_path| -> Result<(), failure::Error> {
|
||||
let res = with_whole_disk(&disk_path, &disk_option, |mount_path| -> Result<()> {
|
||||
let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?;
|
||||
|
||||
// Copy filesystem.toml, which is not packaged
|
||||
let mut files = vec![
|
||||
"filesystem.toml".to_string(),
|
||||
];
|
||||
let mut files = vec!["filesystem.toml".to_string()];
|
||||
|
||||
// Copy files from locally installed packages
|
||||
if let Err(err) = package_files(&root_path, &mut config, &mut files) {
|
||||
return Err(format_err!("failed to read package files: {}", err));
|
||||
}
|
||||
package_files(&root_path, &mut config, &mut files)
|
||||
// TODO: implement Error trait
|
||||
.map_err(|err| anyhow!("failed to read package files: {err}"))?;
|
||||
|
||||
// Perform config install (after packages have been converted to files)
|
||||
eprintln!("configuring system");
|
||||
let cookbook: Option<&'static str> = None;
|
||||
redox_installer::install_dir(config, mount_path, cookbook).map_err(|err| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
redox_installer::install_dir(config, mount_path, cookbook)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
|
||||
// Sort and remove duplicates
|
||||
files.sort();
|
||||
@@ -357,7 +356,7 @@ fn main() {
|
||||
Ok(()) => {
|
||||
eprintln!("installer_tui: installed successfully");
|
||||
process::exit(0);
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("installer_tui: failed to install: {}", err);
|
||||
process::exit(1);
|
||||
|
||||
+7
-11
@@ -1,12 +1,11 @@
|
||||
|
||||
use Result;
|
||||
use crate::Result;
|
||||
use libc::{gid_t, uid_t};
|
||||
|
||||
use std::io::{Error, Write};
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Error, Write};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{PermissionsExt, symlink};
|
||||
use std::os::unix::fs::{symlink, PermissionsExt};
|
||||
use std::path::Path;
|
||||
|
||||
//type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -48,8 +47,7 @@ pub struct FileConfig {
|
||||
impl FileConfig {
|
||||
pub(crate) fn create<P: AsRef<Path>>(&self, prefix: P) -> Result<()> {
|
||||
let path = self.path.trim_start_matches('/');
|
||||
let target_file = prefix.as_ref()
|
||||
.join(path);
|
||||
let target_file = prefix.as_ref().join(path);
|
||||
|
||||
if self.directory {
|
||||
println!("Create directory {}", target_file.display());
|
||||
@@ -76,11 +74,9 @@ impl FileConfig {
|
||||
|
||||
fn apply_perms<P: AsRef<Path>>(&self, target: P) -> Result<()> {
|
||||
let path = target.as_ref();
|
||||
let mode = self.mode.unwrap_or_else(|| if self.directory {
|
||||
0o0755
|
||||
} else {
|
||||
0o0644
|
||||
});
|
||||
let mode = self
|
||||
.mode
|
||||
.unwrap_or_else(|| if self.directory { 0o0755 } else { 0o0644 });
|
||||
let uid = self.uid.unwrap_or(!0);
|
||||
let gid = self.gid.unwrap_or(!0);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ pub struct GeneralConfig {
|
||||
pub prompt: Option<bool>,
|
||||
// Allow config to specify cookbook recipe or binary package as default
|
||||
pub repo_binary: Option<bool>,
|
||||
pub filesystem_size: Option<u32>, //MiB
|
||||
pub filesystem_size: Option<u32>, //MiB
|
||||
pub efi_partition_size: Option<u32>, //MiB
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -3,6 +3,9 @@ use std::fs;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::Result;
|
||||
|
||||
pub mod file;
|
||||
pub mod general;
|
||||
pub mod package;
|
||||
@@ -25,16 +28,16 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_file(path: &Path) -> Result<Self, failure::Error> {
|
||||
pub fn from_file(path: &Path) -> Result<Self> {
|
||||
let mut config: Config = match fs::read_to_string(&path) {
|
||||
Ok(config_data) => match toml::from_str(&config_data) {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
return Err(format_err!("{}: failed to decode: {}", path.display(), err));
|
||||
bail!("{}: failed to decode: {}", path.display(), err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(format_err!("{}: failed to read: {}", path.display(), err));
|
||||
bail!("{}: failed to read: {}", path.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +46,7 @@ impl Config {
|
||||
let mut configs = mem::take(&mut config.include)
|
||||
.into_iter()
|
||||
.map(|path| Config::from_file(&config_dir.join(path)))
|
||||
.collect::<Result<Vec<Config>, failure::Error>>()?;
|
||||
.collect::<Result<Vec<Config>>>()?;
|
||||
configs.push(config); // Put ourself last to ensure that it overwrites anything else.
|
||||
|
||||
config = configs.remove(0);
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
pub enum PackageConfig {
|
||||
Empty,
|
||||
Build(String),
|
||||
|
||||
// TODO: Sum type
|
||||
Spec {
|
||||
version: Option<String>,
|
||||
git: Option<String>,
|
||||
path: Option<String>,
|
||||
}
|
||||
pkg_path: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for PackageConfig {
|
||||
|
||||
+16
-20
@@ -21,10 +21,7 @@ enum Buffer<'a> {
|
||||
|
||||
impl DiskWrapper {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let disk = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(path)?;
|
||||
let disk = OpenOptions::new().read(true).write(true).open(path)?;
|
||||
let metadata = disk.metadata()?;
|
||||
let size = metadata.len();
|
||||
// TODO: get real block size: disk_metadata.blksize() works on disks but not image files
|
||||
@@ -71,24 +68,27 @@ impl DiskWrapper {
|
||||
let remaining = buf_len.checked_sub(i).unwrap();
|
||||
let len = cmp::min(
|
||||
remaining,
|
||||
self.block.len().checked_sub(offset.try_into().unwrap()).unwrap()
|
||||
self.block
|
||||
.len()
|
||||
.checked_sub(offset.try_into().unwrap())
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
self.disk.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk
|
||||
.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk.read_exact(&mut self.block)?;
|
||||
|
||||
match buf {
|
||||
Buffer::Read(read) => {
|
||||
read[i..i.checked_add(len).unwrap()].copy_from_slice(
|
||||
&self.block[offset..offset.checked_add(len).unwrap()]
|
||||
);
|
||||
},
|
||||
read[i..i.checked_add(len).unwrap()]
|
||||
.copy_from_slice(&self.block[offset..offset.checked_add(len).unwrap()]);
|
||||
}
|
||||
Buffer::Write(write) => {
|
||||
self.block[offset..offset.checked_add(len).unwrap()].copy_from_slice(
|
||||
&write[i..i.checked_add(len).unwrap()]
|
||||
);
|
||||
self.block[offset..offset.checked_add(len).unwrap()]
|
||||
.copy_from_slice(&write[i..i.checked_add(len).unwrap()]);
|
||||
|
||||
self.disk.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk
|
||||
.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk.write_all(&mut self.block)?;
|
||||
}
|
||||
}
|
||||
@@ -112,12 +112,8 @@ impl Seek for DiskWrapper {
|
||||
let current: i64 = self.seek.try_into().unwrap();
|
||||
let end: i64 = self.size.try_into().unwrap();
|
||||
self.seek = match pos {
|
||||
SeekFrom::Start(offset) => {
|
||||
cmp::min(self.size, offset)
|
||||
},
|
||||
SeekFrom::End(offset) => {
|
||||
cmp::max(0, cmp::min(end, end.wrapping_add(offset))) as u64
|
||||
},
|
||||
SeekFrom::Start(offset) => cmp::min(self.size, offset),
|
||||
SeekFrom::End(offset) => cmp::max(0, cmp::min(end, end.wrapping_add(offset))) as u64,
|
||||
SeekFrom::Current(offset) => {
|
||||
cmp::max(0, cmp::min(end, current.wrapping_add(offset))) as u64
|
||||
}
|
||||
|
||||
+279
-190
@@ -1,44 +1,31 @@
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate argon2;
|
||||
extern crate libc;
|
||||
extern crate liner;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
extern crate pkgutils;
|
||||
extern crate rand;
|
||||
extern crate redoxfs;
|
||||
extern crate syscall;
|
||||
extern crate termion;
|
||||
|
||||
mod config;
|
||||
mod disk_wrapper;
|
||||
|
||||
pub use config::Config;
|
||||
pub use config::file::FileConfig;
|
||||
pub use config::package::PackageConfig;
|
||||
use disk_wrapper::DiskWrapper;
|
||||
pub use crate::config::file::FileConfig;
|
||||
pub use crate::config::package::PackageConfig;
|
||||
pub use crate::config::Config;
|
||||
use crate::disk_wrapper::DiskWrapper;
|
||||
|
||||
use failure::{Error, err_msg};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use anyhow::{bail, Result};
|
||||
use pkgutils::{Package, Repo};
|
||||
use rand::{rngs::OsRng, RngCore};
|
||||
use redoxfs::{unmount_path, Disk, DiskIo, FileSystem};
|
||||
use termion::input::TermRead;
|
||||
use pkgutils::{Repo, Package};
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
env,
|
||||
fs,
|
||||
env, fs,
|
||||
io::{self, Seek, SeekFrom, Write},
|
||||
path::Path,
|
||||
process,
|
||||
sync::mpsc::channel,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
thread,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub struct DiskOption<'a> {
|
||||
pub bootloader_bios: &'a [u8],
|
||||
pub bootloader_efi: &'a [u8],
|
||||
@@ -50,10 +37,7 @@ const REMOTE: &'static str = "https://static.redox-os.org/pkg";
|
||||
|
||||
fn get_target() -> String {
|
||||
env::var("TARGET").unwrap_or(
|
||||
option_env!("TARGET").map_or(
|
||||
"x86_64-unknown-redox".to_string(),
|
||||
|x| x.to_string()
|
||||
)
|
||||
option_env!("TARGET").map_or("x86_64-unknown-redox".to_string(), |x| x.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,7 +50,7 @@ fn hash_password(password: &str) -> Result<String> {
|
||||
let hash = argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &config)?;
|
||||
Ok(hash)
|
||||
} else {
|
||||
Ok("".to_string())
|
||||
Ok("".into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,15 +72,14 @@ fn prompt_password(prompt: &str, confirm_prompt: &str) -> Result<String> {
|
||||
let confirm_password = stdin.read_passwd(&mut stdout)?;
|
||||
|
||||
// Note: Actually comparing two Option<String> values
|
||||
if confirm_password == password {
|
||||
Ok(password.unwrap_or("".to_string()))
|
||||
} else {
|
||||
Err(err_msg("passwords do not match"))
|
||||
if confirm_password != password {
|
||||
bail!("passwords do not match");
|
||||
}
|
||||
Ok(password.unwrap_or("".to_string()))
|
||||
}
|
||||
|
||||
//TODO: error handling
|
||||
fn install_packages<S: AsRef<str>>(config: &Config, dest: &str, cookbook: Option<S>) {
|
||||
fn install_packages(config: &Config, dest: &str, cookbook: Option<&str>) {
|
||||
let target = &get_target();
|
||||
|
||||
let mut repo = Repo::new(target);
|
||||
@@ -104,68 +87,127 @@ fn install_packages<S: AsRef<str>>(config: &Config, dest: &str, cookbook: Option
|
||||
|
||||
if let Some(cookbook) = cookbook {
|
||||
let dest_pkg = format!("{}/pkg", dest);
|
||||
if ! Path::new(&dest_pkg).exists() {
|
||||
if !Path::new(&dest_pkg).exists() {
|
||||
fs::create_dir(&dest_pkg).unwrap();
|
||||
}
|
||||
|
||||
for (packagename, package) in &config.packages {
|
||||
let pkgar_path = format!("{}/{}/repo/{}/{}.pkgar",
|
||||
env::current_dir().unwrap().to_string_lossy(),
|
||||
cookbook.as_ref(), target, packagename);
|
||||
let from_remote = match (config.general.repo_binary, package) {
|
||||
(Some(true), PackageConfig::Empty) => true,
|
||||
(Some(true), PackageConfig::Spec { version: None, git: None, path: None }) => true,
|
||||
(_, PackageConfig::Build(rule)) if rule == "binary" => true,
|
||||
_ => false
|
||||
};
|
||||
if from_remote {
|
||||
println!("Installing package from remote: {}", packagename);
|
||||
repo.fetch(&packagename).unwrap().install(dest).unwrap();
|
||||
} else if Path::new(&pkgar_path).exists() {
|
||||
println!("Installing package from local repo: {}", packagename);
|
||||
let public_path = format!("{}/{}/build/id_ed25519.pub.toml",
|
||||
env::current_dir().unwrap().to_string_lossy(),
|
||||
cookbook.as_ref());
|
||||
pkgar::extract(&public_path, &pkgar_path, dest).unwrap();
|
||||
let pkgar_path = format!(
|
||||
"{cwd}/{cookbook}/repo/{target}/{packagename}.pkgar",
|
||||
cwd = env::current_dir().unwrap().to_string_lossy(),
|
||||
);
|
||||
|
||||
let head_path = format!("{}/{}.pkgar_head", dest_pkg, packagename);
|
||||
pkgar::split(&public_path, &pkgar_path, &head_path, Option::<&str>::None).unwrap();
|
||||
} else {
|
||||
println!("Installing package tar.gz from local repo: {}", packagename);
|
||||
let path = format!("{}/{}/repo/{}/{}.tar.gz",
|
||||
env::current_dir().unwrap().to_string_lossy(),
|
||||
cookbook.as_ref(), target, packagename);
|
||||
Package::from_path(&path).unwrap().install(dest).unwrap();
|
||||
enum Rule<'a> {
|
||||
RemotePrebuilt,
|
||||
LocalPrebuilt { pkg_path: &'a str },
|
||||
Build,
|
||||
}
|
||||
|
||||
let rule = match (config.general.repo_binary, package) {
|
||||
(
|
||||
Some(true),
|
||||
PackageConfig::Empty
|
||||
| PackageConfig::Spec {
|
||||
version: None,
|
||||
git: None,
|
||||
path: None,
|
||||
pkg_path: None,
|
||||
},
|
||||
) => {
|
||||
// prebuilt
|
||||
Rule::RemotePrebuilt
|
||||
}
|
||||
(_, PackageConfig::Build(rule)) if rule == "binary" => Rule::RemotePrebuilt,
|
||||
(
|
||||
_,
|
||||
PackageConfig::Spec {
|
||||
pkg_path: Some(pkg_path),
|
||||
..
|
||||
},
|
||||
) => Rule::LocalPrebuilt {
|
||||
pkg_path: &*pkg_path,
|
||||
},
|
||||
|
||||
_ => Rule::Build,
|
||||
};
|
||||
|
||||
match rule {
|
||||
Rule::LocalPrebuilt { pkg_path } => {
|
||||
println!(
|
||||
"Installing package from local pkgar file: {packagename} <- `{pkg_path}`"
|
||||
);
|
||||
Package::from_path(pkg_path).unwrap().install(dest).unwrap();
|
||||
}
|
||||
Rule::RemotePrebuilt => {
|
||||
println!("Installing package from remote: {packagename}");
|
||||
repo.fetch(&packagename).unwrap().install(dest).unwrap();
|
||||
}
|
||||
Rule::Build if Path::new(&pkgar_path).exists() => {
|
||||
println!("Installing package from local repo: {}", packagename);
|
||||
let public_path = format!(
|
||||
"{cwd}/{cookbook}/build/id_ed25519.pub.toml",
|
||||
cwd = env::current_dir().unwrap().to_string_lossy(),
|
||||
);
|
||||
pkgar::extract(&public_path, &pkgar_path, dest).unwrap();
|
||||
|
||||
let head_path = format!("{dest_pkg}/{packagename}.pkgar_head");
|
||||
pkgar::split(&public_path, &pkgar_path, &head_path, Option::<&str>::None)
|
||||
.unwrap();
|
||||
}
|
||||
Rule::Build => {
|
||||
println!("Installing package tar.gz from local repo: {packagename}");
|
||||
let path = format!(
|
||||
"{cwd}/{cookbook}/repo/{target}/{packagename}.tar.gz",
|
||||
cwd = env::current_dir().unwrap().to_string_lossy(),
|
||||
);
|
||||
Package::from_path(&path).unwrap().install(dest).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (packagename, _package) in &config.packages {
|
||||
println!("Installing package from remote: {}", packagename);
|
||||
repo.fetch(&packagename).unwrap().install(dest).unwrap();
|
||||
for (packagename, package) in &config.packages {
|
||||
let mut package = if let PackageConfig::Spec {
|
||||
pkg_path: Some(override_path),
|
||||
..
|
||||
} = package
|
||||
{
|
||||
println!("Installing package from local file: {}", packagename);
|
||||
Package::from_path(override_path).unwrap()
|
||||
} else {
|
||||
println!("Installing package from remote: {}", packagename);
|
||||
repo.fetch(&packagename).unwrap()
|
||||
};
|
||||
package.install(dest).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_dir<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P, cookbook: Option<S>) -> Result<()> {
|
||||
pub fn install_dir(
|
||||
config: Config,
|
||||
output_dir: impl AsRef<Path>,
|
||||
cookbook: Option<&str>,
|
||||
) -> Result<()> {
|
||||
//let mut context = liner::Context::new();
|
||||
|
||||
macro_rules! prompt {
|
||||
($dst:expr, $def:expr, $($arg:tt)*) => (if config.general.prompt.unwrap_or(true) {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"prompt not currently supported"
|
||||
))
|
||||
// match unwrap_or_prompt($dst, &mut context, &format!($($arg)*)) {
|
||||
// Ok(res) => if res.is_empty() {
|
||||
// Ok($def)
|
||||
// } else {
|
||||
// Ok(res)
|
||||
// },
|
||||
// Err(err) => Err(err)
|
||||
// }
|
||||
} else {
|
||||
Ok($dst.unwrap_or($def))
|
||||
})
|
||||
($dst:expr, $def:expr, $($arg:tt)*) => {
|
||||
if config.general.prompt.unwrap_or(true) {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"prompt not currently supported",
|
||||
))
|
||||
// match unwrap_or_prompt($dst, &mut context, &format!($($arg)*)) {
|
||||
// Ok(res) => if res.is_empty() {
|
||||
// Ok($def)
|
||||
// } else {
|
||||
// Ok(res)
|
||||
// },
|
||||
// Err(err) => Err(err)
|
||||
// }
|
||||
} else {
|
||||
Ok($dst.unwrap_or($def))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let output_dir = output_dir.as_ref();
|
||||
@@ -192,7 +234,8 @@ pub fn install_dir<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P,
|
||||
} else if config.general.prompt.unwrap_or(true) {
|
||||
prompt_password(
|
||||
&format!("{}: enter password: ", username),
|
||||
&format!("{}: confirm password: ", username))?
|
||||
&format!("{}: confirm password: ", username),
|
||||
)?
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@@ -209,17 +252,34 @@ pub fn install_dir<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P,
|
||||
next_gid = gid + 1;
|
||||
}
|
||||
|
||||
let name = prompt!(user.name, username.clone(), "{}: name (GECOS) [{}]: ", username, username)?;
|
||||
let home = prompt!(user.home, format!("/home/{}", username), "{}: home [/home/{}]: ", username, username)?;
|
||||
let shell = prompt!(user.shell, "/bin/ion".to_string(), "{}: shell [/bin/ion]: ", username)?;
|
||||
let name = prompt!(
|
||||
user.name,
|
||||
username.clone(),
|
||||
"{}: name (GECOS) [{}]: ",
|
||||
username,
|
||||
username
|
||||
)?;
|
||||
let home = prompt!(
|
||||
user.home,
|
||||
format!("/home/{}", username),
|
||||
"{}: home [/home/{}]: ",
|
||||
username,
|
||||
username
|
||||
)?;
|
||||
let shell = prompt!(
|
||||
user.shell,
|
||||
"/bin/ion".to_string(),
|
||||
"{}: shell [/bin/ion]: ",
|
||||
username
|
||||
)?;
|
||||
|
||||
println!("Adding user {}:", username);
|
||||
println!("\tPassword: {}", password);
|
||||
println!("\tUID: {}", uid);
|
||||
println!("\tGID: {}", gid);
|
||||
println!("\tName: {}", name);
|
||||
println!("\tHome: {}", home);
|
||||
println!("\tShell: {}", shell);
|
||||
println!("Adding user {username}:");
|
||||
println!("\tPassword: {password}");
|
||||
println!("\tUID: {uid}");
|
||||
println!("\tGID: {gid}");
|
||||
println!("\tName: {name}");
|
||||
println!("\tHome: {home}");
|
||||
println!("\tShell: {shell}");
|
||||
|
||||
FileConfig {
|
||||
path: home.clone(),
|
||||
@@ -230,7 +290,8 @@ pub fn install_dir<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P,
|
||||
uid: Some(uid),
|
||||
gid: Some(gid),
|
||||
recursive_chown: true,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
|
||||
if uid >= 1000 {
|
||||
// Create XDG user dirs
|
||||
@@ -259,7 +320,8 @@ pub fn install_dir<P: AsRef<Path>, S: AsRef<str>>(config: Config, output_dir: P,
|
||||
uid: Some(uid),
|
||||
gid: Some(gid),
|
||||
recursive_chown: false,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
FileConfig {
|
||||
path: format!("{}/.config/user-dirs.dirs", home),
|
||||
@@ -272,20 +334,22 @@ XDG_PICTURES_DIR="$HOME/Pictures"
|
||||
XDG_PUBLICSHARE_DIR="$HOME/Public"
|
||||
XDG_TEMPLATES_DIR="$HOME/Templates"
|
||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
"#.to_string(),
|
||||
"#
|
||||
.to_string(),
|
||||
symlink: false,
|
||||
directory: false,
|
||||
mode: Some(0o0600),
|
||||
uid: Some(uid),
|
||||
gid: Some(gid),
|
||||
recursive_chown: false,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
let password = hash_password(&password)?;
|
||||
|
||||
passwd.push_str(&format!("{};{};{};{};{};{}\n", username, uid, gid, name, home, shell));
|
||||
shadow.push_str(&format!("{};{}\n", username, password));
|
||||
passwd.push_str(&format!("{username};{uid};{gid};{name};{home};{shell}\n",));
|
||||
shadow.push_str(&format!("{username};{password}\n"));
|
||||
groups.push((username.clone(), gid, vec![username]));
|
||||
}
|
||||
|
||||
@@ -312,7 +376,8 @@ XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
uid: None,
|
||||
gid: None,
|
||||
recursive_chown: false,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
if !shadow.is_empty() {
|
||||
@@ -325,7 +390,8 @@ XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
uid: Some(0),
|
||||
gid: Some(0),
|
||||
recursive_chown: false,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
if !groups.is_empty() {
|
||||
@@ -335,8 +401,8 @@ XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
use std::fmt::Write;
|
||||
writeln!(groups_data, "{name};x;{gid};{}", members.join(",")).unwrap();
|
||||
|
||||
println!("Adding group {}:", name);
|
||||
println!("\tGID: {}", gid);
|
||||
println!("Adding group {name}:");
|
||||
println!("\tGID: {gid}");
|
||||
println!("\tMembers: {}", members.join(", "));
|
||||
}
|
||||
|
||||
@@ -350,16 +416,17 @@ XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
uid: None,
|
||||
gid: None,
|
||||
recursive_chown: false,
|
||||
}.create(&output_dir)?;
|
||||
}
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F)
|
||||
-> Result<T> where
|
||||
D: Disk + Send + 'static,
|
||||
F: FnOnce(&Path) -> Result<T>
|
||||
pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F) -> Result<T>
|
||||
where
|
||||
D: Disk + Send + 'static,
|
||||
F: FnOnce(&Path) -> Result<T>,
|
||||
{
|
||||
let mount_path = if cfg!(target_os = "redox") {
|
||||
format!("file.redox_installer_{}", process::id())
|
||||
@@ -368,35 +435,27 @@ pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F)
|
||||
};
|
||||
|
||||
if cfg!(not(target_os = "redox")) {
|
||||
if ! Path::new(&mount_path).exists() {
|
||||
if !Path::new(&mount_path).exists() {
|
||||
fs::create_dir(&mount_path)?;
|
||||
}
|
||||
}
|
||||
|
||||
let ctime = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
let fs = FileSystem::create(
|
||||
disk,
|
||||
password_opt,
|
||||
ctime.as_secs(),
|
||||
ctime.subsec_nanos()
|
||||
).map_err(syscall_error)?;
|
||||
let fs = FileSystem::create(disk, password_opt, ctime.as_secs(), ctime.subsec_nanos())
|
||||
.map_err(syscall_error)?;
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let join_handle = {
|
||||
let mount_path = mount_path.clone();
|
||||
thread::spawn(move || {
|
||||
let res = redoxfs::mount(
|
||||
fs,
|
||||
&mount_path,
|
||||
|real_path| {
|
||||
tx.send(Ok(real_path.to_owned())).unwrap();
|
||||
}
|
||||
);
|
||||
let res = redoxfs::mount(fs, &mount_path, |real_path| {
|
||||
tx.send(Ok(real_path.to_owned())).unwrap();
|
||||
});
|
||||
match res {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
tx.send(Err(err)).unwrap();
|
||||
},
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
@@ -406,10 +465,13 @@ pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F)
|
||||
Ok(real_path) => callback(&real_path),
|
||||
Err(err) => return Err(err.into()),
|
||||
},
|
||||
Err(_) => return Err(io::Error::new(
|
||||
io::ErrorKind::NotConnected,
|
||||
"redoxfs thread did not send a result"
|
||||
).into()),
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotConnected,
|
||||
"redoxfs thread did not send a result",
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
unmount_path(&mount_path)?;
|
||||
@@ -423,7 +485,11 @@ pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F)
|
||||
res
|
||||
}
|
||||
|
||||
pub fn fetch_bootloaders<S: AsRef<str>>(config: &Config, cookbook: Option<S>, live: bool) -> Result<(Vec<u8>, Vec<u8>)> {
|
||||
pub fn fetch_bootloaders(
|
||||
config: &Config,
|
||||
cookbook: Option<&str>,
|
||||
live: bool,
|
||||
) -> Result<(Vec<u8>, Vec<u8>)> {
|
||||
let bootloader_dir = format!("/tmp/redox_installer_bootloader_{}", process::id());
|
||||
|
||||
if Path::new(&bootloader_dir).exists() {
|
||||
@@ -434,8 +500,10 @@ pub fn fetch_bootloaders<S: AsRef<str>>(config: &Config, cookbook: Option<S>, li
|
||||
|
||||
let mut bootloader_config = Config::default();
|
||||
bootloader_config.general = config.general.clone();
|
||||
bootloader_config.packages.insert("bootloader".to_string(), PackageConfig::default());
|
||||
install_packages(&bootloader_config, &bootloader_dir, cookbook.as_ref());
|
||||
bootloader_config
|
||||
.packages
|
||||
.insert("bootloader".to_string(), PackageConfig::default());
|
||||
install_packages(&bootloader_config, &bootloader_dir, cookbook);
|
||||
|
||||
let boot_dir = Path::new(&bootloader_dir).join("boot");
|
||||
let bios_path = boot_dir.join(if live {
|
||||
@@ -466,10 +534,10 @@ pub fn fetch_bootloaders<S: AsRef<str>>(config: &Config, cookbook: Option<S>, li
|
||||
}
|
||||
|
||||
//TODO: make bootloaders use Option, dynamically create BIOS and EFI partitions
|
||||
pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback: F)
|
||||
-> Result<T> where
|
||||
P: AsRef<Path>,
|
||||
F: FnOnce(&Path) -> Result<T>
|
||||
pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback: F) -> Result<T>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: FnOnce(&Path) -> Result<T>,
|
||||
{
|
||||
let target = get_target();
|
||||
|
||||
@@ -478,7 +546,7 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
"i686-unknown-redox" => "BOOTIA32.EFI",
|
||||
"x86_64-unknown-redox" => "BOOTX64.EFI",
|
||||
_ => {
|
||||
return Err(format_err!("target '{}' not supported", target));
|
||||
bail!("target '{target}' not supported");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -491,11 +559,10 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
512 => gpt::disk::LogicalBlockSize::Lb512,
|
||||
_ => {
|
||||
// TODO: support (and test) other block sizes
|
||||
return Err(format_err!("block size {} not supported", block_size));
|
||||
bail!("block size {block_size} not supported");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Calculate partition offsets
|
||||
let gpt_reserved = 34 * 512; // GPT always reserves 34 512-byte sectors
|
||||
let mibi = 1024 * 1024;
|
||||
@@ -506,7 +573,11 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
|
||||
// Second megabyte of the disk is reserved for EFI partition
|
||||
let efi_start = bios_end + 1;
|
||||
let efi_size = if let Some(size) = disk_option.efi_partition_size { size as u64 } else { 1 };
|
||||
let efi_size = if let Some(size) = disk_option.efi_partition_size {
|
||||
size as u64
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let efi_end = efi_start + (efi_size * mibi / block_size) - 1;
|
||||
|
||||
// The rest of the disk is RedoxFS, reserving the GPT table mirror at the end of disk
|
||||
@@ -516,13 +587,17 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
// Format and install BIOS partition
|
||||
{
|
||||
// Write BIOS bootloader to disk
|
||||
eprintln!("Write bootloader with size {:#x}", disk_option.bootloader_bios.len());
|
||||
eprintln!(
|
||||
"Write bootloader with size {:#x}",
|
||||
disk_option.bootloader_bios.len()
|
||||
);
|
||||
disk_file.seek(SeekFrom::Start(0))?;
|
||||
disk_file.write_all(&disk_option.bootloader_bios)?;
|
||||
|
||||
// Replace MBR tables with protective MBR
|
||||
// TODO: div_ceil
|
||||
let mbr_blocks = ((disk_size + block_size - 1) / block_size) - 1;
|
||||
eprintln!("Writing protective MBR with disk blocks {:#x}", mbr_blocks);
|
||||
eprintln!("Writing protective MBR with disk blocks {mbr_blocks:#x}");
|
||||
gpt::mbr::ProtectiveMBR::with_lb_size(mbr_blocks as u32)
|
||||
.update_conservative(&mut disk_file)?;
|
||||
|
||||
@@ -536,39 +611,48 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
// Add BIOS boot partition
|
||||
let mut partitions = BTreeMap::new();
|
||||
let mut partition_id = 1;
|
||||
partitions.insert(partition_id, gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::BIOS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: bios_start,
|
||||
last_lba: bios_end,
|
||||
flags: 0, // TODO
|
||||
name: "BIOS".to_string(),
|
||||
});
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::BIOS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: bios_start,
|
||||
last_lba: bios_end,
|
||||
flags: 0, // TODO
|
||||
name: "BIOS".to_string(),
|
||||
},
|
||||
);
|
||||
partition_id += 1;
|
||||
|
||||
// Add EFI boot partition
|
||||
partitions.insert(partition_id, gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::EFI,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: efi_start,
|
||||
last_lba: efi_end,
|
||||
flags: 0, // TODO
|
||||
name: "EFI".to_string(),
|
||||
});
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::EFI,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: efi_start,
|
||||
last_lba: efi_end,
|
||||
flags: 0, // TODO
|
||||
name: "EFI".to_string(),
|
||||
},
|
||||
);
|
||||
partition_id += 1;
|
||||
|
||||
// Add RedoxFS partition
|
||||
partitions.insert(partition_id, gpt::partition::Partition {
|
||||
//TODO: Use REDOX_REDOXFS type (needs GPT crate changes)
|
||||
part_type_guid: gpt::partition_types::LINUX_FS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: redoxfs_start,
|
||||
last_lba: redoxfs_end,
|
||||
flags: 0,
|
||||
name: "REDOX".to_string(),
|
||||
});
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
//TODO: Use REDOX_REDOXFS type (needs GPT crate changes)
|
||||
part_type_guid: gpt::partition_types::LINUX_FS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: redoxfs_start,
|
||||
last_lba: redoxfs_end,
|
||||
flags: 0,
|
||||
name: "REDOX".to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
eprintln!("Writing GPT tables: {:#?}", partitions);
|
||||
eprintln!("Writing GPT tables: {partitions:#?}");
|
||||
|
||||
// Initialize GPT table
|
||||
gpt_disk.update_partitions(partitions)?;
|
||||
@@ -581,13 +665,13 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
{
|
||||
let disk_efi_start = efi_start * block_size;
|
||||
let disk_efi_end = (efi_end + 1) * block_size;
|
||||
let mut disk_efi = fscommon::StreamSlice::new(
|
||||
&mut disk_file,
|
||||
disk_efi_start,
|
||||
disk_efi_end,
|
||||
)?;
|
||||
let mut disk_efi =
|
||||
fscommon::StreamSlice::new(&mut disk_file, disk_efi_start, disk_efi_end)?;
|
||||
|
||||
eprintln!("Formatting EFI partition with size {:#x}", disk_efi_end - disk_efi_start);
|
||||
eprintln!(
|
||||
"Formatting EFI partition with size {:#x}",
|
||||
disk_efi_end - disk_efi_start
|
||||
);
|
||||
fatfs::format_volume(&mut disk_efi, fatfs::FormatVolumeOptions::new())?;
|
||||
|
||||
eprintln!("Opening EFI partition");
|
||||
@@ -601,7 +685,11 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
let efi_dir = root_dir.open_dir("EFI")?;
|
||||
efi_dir.create_dir("BOOT")?;
|
||||
|
||||
eprintln!("Writing EFI/BOOT/{} file with size {:#x}", bootloader_efi_name, disk_option.bootloader_efi.len());
|
||||
eprintln!(
|
||||
"Writing EFI/BOOT/{} file with size {:#x}",
|
||||
bootloader_efi_name,
|
||||
disk_option.bootloader_efi.len()
|
||||
);
|
||||
let boot_dir = efi_dir.open_dir("BOOT")?;
|
||||
let mut file = boot_dir.create_file(bootloader_efi_name)?;
|
||||
file.truncate()?;
|
||||
@@ -609,40 +697,41 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback
|
||||
}
|
||||
|
||||
// Format and install RedoxFS partition
|
||||
eprintln!("Installing to RedoxFS partition with size {:#x}", (redoxfs_end - redoxfs_start) * block_size);
|
||||
eprintln!(
|
||||
"Installing to RedoxFS partition with size {:#x}",
|
||||
(redoxfs_end - redoxfs_start) * block_size
|
||||
);
|
||||
let disk_redoxfs = DiskIo(fscommon::StreamSlice::new(
|
||||
disk_file,
|
||||
redoxfs_start * block_size,
|
||||
(redoxfs_end + 1) * block_size
|
||||
(redoxfs_end + 1) * block_size,
|
||||
)?);
|
||||
with_redoxfs(
|
||||
disk_redoxfs,
|
||||
disk_option.password_opt,
|
||||
callback
|
||||
)
|
||||
with_redoxfs(disk_redoxfs, disk_option.password_opt, callback)
|
||||
}
|
||||
|
||||
pub fn install<P, S>(config: Config, output: P, cookbook: Option<S>, live: bool)
|
||||
-> Result<()> where
|
||||
P: AsRef<Path>,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
println!("Install {:#?} to {}", config, output.as_ref().display());
|
||||
fn install_inner(config: Config, output: &Path, cookbook: Option<&str>, live: bool) -> Result<()> {
|
||||
println!("Install {config:#?} to {}", output.display());
|
||||
|
||||
if output.as_ref().is_dir() {
|
||||
if output.is_dir() {
|
||||
install_dir(config, output, cookbook)
|
||||
} else {
|
||||
let (bootloader_bios, bootloader_efi) = fetch_bootloaders(&config, cookbook.as_ref(), live)?;
|
||||
let (bootloader_bios, bootloader_efi) = fetch_bootloaders(&config, cookbook, live)?;
|
||||
let disk_option = DiskOption {
|
||||
bootloader_bios: &bootloader_bios,
|
||||
bootloader_efi: &bootloader_efi,
|
||||
password_opt: None,
|
||||
efi_partition_size: config.general.efi_partition_size,
|
||||
};
|
||||
with_whole_disk(output, &disk_option,
|
||||
move |mount_path| {
|
||||
install_dir(config, mount_path, cookbook)
|
||||
}
|
||||
)
|
||||
with_whole_disk(output, &disk_option, move |mount_path| {
|
||||
install_dir(config, mount_path, cookbook)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn install(
|
||||
config: Config,
|
||||
output: impl AsRef<Path>,
|
||||
cookbook: Option<&str>,
|
||||
live: bool,
|
||||
) -> Result<()> {
|
||||
install_inner(config, output.as_ref(), cookbook, live)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user