Add no_mount option and tidy up flags

This commit is contained in:
Wildan M
2025-12-16 01:41:55 +07:00
parent 990d9d343e
commit 433aa99660
3 changed files with 133 additions and 33 deletions
+17 -8
View File
@@ -19,7 +19,8 @@ fn main() {
.add_flag(&["filesystem-size"])
.add_flag(&["r", "repo-binary"])
.add_flag(&["l", "list-packages"])
.add_flag(&["live"]);
.add_flag(&["live"])
.add_flag(&["no-mount"]);
parser.parse(env::args());
// Use pre-built binaries for packages as the default.
@@ -127,14 +128,22 @@ fn main() {
None
};
if cookbook.is_some() {
config.general.cookbook = cookbook;
}
if parser.found("live") {
config.general.live_disk = Some(true);
}
if parser.found("no-mount") {
config.general.no_mount = Some(true);
}
let write_bootloader = parser.get_opt("write-bootloader");
if write_bootloader.is_some() {
config.general.write_bootloader = write_bootloader;
}
if let Some(path) = parser.args.first() {
if let Err(err) = redox_installer::install(
config,
path,
cookbook.as_deref(),
parser.found("live"),
parser.get_opt("write-bootloader").as_deref(),
) {
if let Err(err) = redox_installer::install(config, path) {
eprintln!("installer: failed to install: {}", err);
process::exit(1);
}
+31 -3
View File
@@ -1,19 +1,47 @@
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct GeneralConfig {
/// Specify a path where cookbook exists, all packages will be installed locally
pub cookbook: Option<String>,
/// Allow prompts for missing information such as user password, true by default
pub prompt: Option<bool>,
// Allow config to specify cookbook recipe or binary package as default
/// Allow config to specify cookbook recipe or binary package as default
/// note: Not read by installer anymore, exists only for legacy reasons
pub repo_binary: Option<bool>,
pub filesystem_size: Option<u32>, //MiB
pub efi_partition_size: Option<u32>, //MiB
/// Total filesystem size in MB
pub filesystem_size: Option<u32>,
/// EFI partition size in MB, default to 2MB
pub efi_partition_size: Option<u32>,
/// Skip disk partitioning, assume whole disk is a partition
pub skip_partitions: Option<bool>,
/// Set a plain text password to encrypt the disk, or empty string to prompt
pub encrypt_disk: Option<String>,
/// Use live disk for bootloader config, default is false
pub live_disk: Option<bool>,
/// If set, write bootloader disk into this path
pub write_bootloader: Option<String>,
/// Use AR to write files instead of FUSE-based mount
/// (bypasses FUSE, but slower and requires namespaced context such as "podman unshare")
pub no_mount: Option<bool>,
}
impl GeneralConfig {
/// Merge two config, "other" is more dominant
pub(super) fn merge(&mut self, other: GeneralConfig) {
if let Some(cookbook) = other.cookbook {
self.cookbook = Some(cookbook);
}
self.prompt = other.prompt.or(self.prompt);
self.repo_binary = other.repo_binary.or(self.repo_binary);
self.filesystem_size = other.filesystem_size.or(self.filesystem_size);
self.efi_partition_size = other.efi_partition_size.or(self.efi_partition_size);
self.skip_partitions = other.skip_partitions.or(self.skip_partitions);
if let Some(encrypt_disk) = other.encrypt_disk {
self.encrypt_disk = Some(encrypt_disk);
}
self.live_disk = other.live_disk.or(self.live_disk);
if let Some(write_bootloader) = other.write_bootloader {
self.write_bootloader = Some(write_bootloader);
}
self.no_mount = other.no_mount.or(self.no_mount);
}
}
+85 -22
View File
@@ -3,7 +3,7 @@ use anyhow::{anyhow, Context};
use anyhow::{bail, Result};
use pkg::Library;
use rand::{rngs::OsRng, TryRngCore};
use redoxfs::{unmount_path, Disk, DiskIo, FileSystem};
use redoxfs::{unmount_path, Disk, DiskIo, FileSystem, BLOCK_SIZE};
use termion::input::TermRead;
use crate::config::file::FileConfig;
@@ -412,6 +412,67 @@ where
res
}
pub fn with_redoxfs_ar<D, T, F>(mut fs: FileSystem<D>, 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())
} else {
format!("/tmp/redox_installer_{}", process::id())
};
let res = callback(Path::new(&mount_path));
if res.is_ok() {
let _end_block = fs
.tx(|tx| {
// Archive_at root node
redoxfs::archive_at(tx, Path::new(&mount_path), redoxfs::TreePtr::root())
.map_err(|err| syscall::Error::new(err.raw_os_error().unwrap()))?;
// Squash alloc log
tx.sync(true)?;
let end_block = tx.header.size() / BLOCK_SIZE;
/* TODO: Cut off any free blocks at the end of the filesystem
let mut end_changed = true;
while end_changed {
end_changed = false;
let allocator = fs.allocator();
let levels = allocator.levels();
for level in 0..levels.len() {
let level_size = 1 << level;
for &block in levels[level].iter() {
if block < end_block && block + level_size >= end_block {
end_block = block;
end_changed = true;
}
}
}
}
*/
// Update header
tx.header.size = (end_block * BLOCK_SIZE).into();
tx.header_changed = true;
tx.sync(false)?;
Ok(end_block)
})
.map_err(syscall_error)?;
// let size = (fs.block + end_block) * BLOCK_SIZE;
// fs.disk.file.set_len(size)?;
}
fs::remove_dir_all(&mount_path)?;
res
}
pub fn fetch_bootloaders(
config: &Config,
cookbook: Option<&str>,
@@ -744,42 +805,44 @@ pub fn try_fast_install<D: redoxfs::Disk, F: FnMut(u64, u64)>(
Ok(true)
}
fn install_inner(
config: Config,
output: &Path,
cookbook: Option<&str>,
live: bool,
write_bootloader: Option<&str>,
) -> Result<()> {
fn install_inner(config: Config, output: &Path) -> Result<()> {
println!("Install {config:#?} to {}", output.display());
let cookbook = config.general.cookbook.clone();
let cookbook = cookbook.as_ref().map(|p| p.as_str());
if output.is_dir() {
install_dir(config, output, cookbook)
} else {
let live = config.general.live_disk.unwrap_or(false);
let password_opt = config.general.encrypt_disk.clone();
let password_opt = password_opt.as_ref().map(|p| p.as_bytes());
let (bootloader_bios, bootloader_efi) = fetch_bootloaders(&config, cookbook, live)?;
if let Some(write_bootloader) = write_bootloader {
if let Some(write_bootloader) = &config.general.write_bootloader {
std::fs::write(write_bootloader, &bootloader_efi)?;
}
let disk_option = DiskOption {
bootloader_bios: &bootloader_bios,
bootloader_efi: &bootloader_efi,
password_opt: None,
password_opt: password_opt,
efi_partition_size: config.general.efi_partition_size,
skip_partitions: config.general.skip_partitions.unwrap_or(false),
};
with_whole_disk(output, &disk_option, move |fs| {
with_redoxfs_mount(fs, move |mount_path| {
install_dir(config, mount_path, cookbook)
})
if config.general.no_mount.unwrap_or(false) {
with_redoxfs_ar(fs, move |mount_path| {
install_dir(config, mount_path, cookbook)
})
} else {
with_redoxfs_mount(fs, move |mount_path| {
install_dir(config, mount_path, cookbook)
})
}
})
}
}
pub fn install(
config: Config,
output: impl AsRef<Path>,
cookbook: Option<&str>,
live: bool,
write_bootloader: Option<&str>,
) -> Result<()> {
install_inner(config, output.as_ref(), cookbook, live, write_bootloader)
/// Install RedoxFS into a new disk file, or a sysroot directory.
/// This function assumes all interactive prompts resolved by the caller,
/// so "prompt" option is ignored from this function onward.
pub fn install(config: Config, output: impl AsRef<Path>) -> Result<()> {
install_inner(config, output.as_ref())
}