diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 596e65499c..15f60e63bf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,4 +22,4 @@ cargo-test: - cargo build --locked - cargo test - fallocate -l 256MiB test.bin - - ./target/debug/redox_installer -c res/test.toml test.bin + - ./target/debug/redox_installer -c res/test.toml test.bin --no-mount diff --git a/res/test.toml b/res/test.toml index e85ef380dc..ed52bd5e7b 100644 --- a/res/test.toml +++ b/res/test.toml @@ -16,8 +16,6 @@ filesystem_size = 256 [packages.coreutils] -[packages.drivers] - [packages.extrautils] [packages.findutils] @@ -26,6 +24,8 @@ filesystem_size = 256 [packages.kernel] +[packages.kibi] + [packages.libgcc] [packages.libstdcxx] @@ -38,8 +38,6 @@ filesystem_size = 256 [packages.relibc] -[packages.smith] - [packages.userutils] [packages.uutils] @@ -48,16 +46,17 @@ filesystem_size = 256 path = "/usr/lib/init.d/00_base" data = """ # clear and recreate tmpdir with 0o1777 permission -rm -r /tmp +rm -rf /tmp mkdir -m a=rwxt /tmp ipcd ptyd -sudo --daemon +nowait sudo --daemon """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/lib/init.d/00_drivers" @@ -67,6 +66,7 @@ pcid-spawner /etc/pcid.d/ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/hostname" @@ -74,6 +74,7 @@ data = "redox" symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/lib/os-release" @@ -91,6 +92,7 @@ SUPPORT_URL="https://redox-os.org/community/" symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/os-release" @@ -98,6 +100,7 @@ data = "../usr/lib/os-release" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/pkg.d/50_redox" @@ -105,6 +108,7 @@ data = "https://static.redox-os.org/pkg" symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr" @@ -113,6 +117,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/usr/bin" @@ -121,6 +126,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/bin" @@ -128,6 +134,7 @@ data = "usr/bin" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/include" @@ -136,6 +143,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/include" @@ -143,6 +151,7 @@ data = "usr/include" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/lib" @@ -151,6 +160,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/lib" @@ -158,6 +168,7 @@ data = "usr/lib" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/libexec" @@ -166,6 +177,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/usr/share" @@ -174,6 +186,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/share" @@ -181,6 +194,7 @@ data = "usr/share" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/share/fonts" @@ -188,6 +202,7 @@ data = "../../ui/fonts" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/var" @@ -196,6 +211,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/var/cache" @@ -204,6 +220,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/var/lib" @@ -212,6 +229,7 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false [[files]] path = "/var/lock" @@ -220,6 +238,7 @@ symlink = false directory = true mode = 1023 recursive_chown = false +postinstall = false [[files]] path = "/var/log" @@ -228,6 +247,16 @@ symlink = false directory = true mode = 493 recursive_chown = false +postinstall = false + +[[files]] +path = "/var/run" +data = "" +symlink = false +directory = true +mode = 493 +recursive_chown = false +postinstall = false [[files]] path = "/var/tmp" @@ -236,6 +265,7 @@ symlink = false directory = true mode = 1023 recursive_chown = false +postinstall = false [[files]] path = "/dev/null" @@ -243,6 +273,7 @@ data = "/scheme/null" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/random" @@ -250,6 +281,7 @@ data = "/scheme/rand" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/urandom" @@ -257,6 +289,7 @@ data = "/scheme/rand" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/zero" @@ -264,6 +297,7 @@ data = "/scheme/zero" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/tty" @@ -271,6 +305,7 @@ data = "libc:tty" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/stdin" @@ -278,6 +313,7 @@ data = "libc:stdin" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/stdout" @@ -285,6 +321,7 @@ data = "libc:stdout" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/dev/stderr" @@ -292,26 +329,28 @@ data = "libc:stderr" symlink = true directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/lib/init.d/10_net" data = """ smolnetd -dnsd -dhcpd -b +nowait dhcpd """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/net/dns" data = """ -208.67.222.222 +9.9.9.9 """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/net/ip" @@ -321,6 +360,7 @@ data = """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/net/ip_router" @@ -330,6 +370,7 @@ data = """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/etc/net/ip_subnet" @@ -339,17 +380,19 @@ data = """ symlink = false directory = false recursive_chown = false +postinstall = false [[files]] path = "/usr/lib/init.d/30_console" data = """ inputd -A 2 -getty 2 -getty /scheme/debug -J +nowait getty 2 +nowait getty /scheme/debug -J """ symlink = false directory = false recursive_chown = false +postinstall = false [users.root] password = "password" diff --git a/res/update.sh b/res/update.sh index ff5515866b..77964bf1aa 100755 --- a/res/update.sh +++ b/res/update.sh @@ -15,7 +15,7 @@ fi set -x # Update res/test.toml from the redoxer.toml template -cargo run --release --manifest-path "${RES_PATH}/../Cargo.toml" -- \ +"${REDOX_PATH}/build/fstools/bin/redox_installer" \ --config="${REDOX_PATH}/config/x86_64/minimal-net.toml" \ --output-config="${RES_PATH}/test.toml" sed -i '1s/^/# Automatically generated by update.sh\n\n/' "${RES_PATH}/test.toml" diff --git a/src/bin/installer.rs b/src/bin/installer.rs index bf14f10303..c2443a1ee6 100644 --- a/src/bin/installer.rs +++ b/src/bin/installer.rs @@ -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); } diff --git a/src/config/general.rs b/src/config/general.rs index b5c736b805..c2a2c6c713 100644 --- a/src/config/general.rs +++ b/src/config/general.rs @@ -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, + /// Allow prompts for missing information such as user password, true by default pub prompt: Option, - // 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, - pub filesystem_size: Option, //MiB - pub efi_partition_size: Option, //MiB + /// Total filesystem size in MB + pub filesystem_size: Option, + /// EFI partition size in MB, default to 2MB + pub efi_partition_size: Option, + /// Skip disk partitioning, assume whole disk is a partition pub skip_partitions: Option, + /// Set a plain text password to encrypt the disk, or empty string to prompt + pub encrypt_disk: Option, + /// Use live disk for bootloader config, default is false + pub live_disk: Option, + /// If set, write bootloader disk into this path + pub write_bootloader: Option, + /// 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, } 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); } } diff --git a/src/installer.rs b/src/installer.rs index 12a4d9b240..28d449d72d 100644 --- a/src/installer.rs +++ b/src/installer.rs @@ -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(mut fs: FileSystem, callback: F) -> Result +where + D: Disk + Send + 'static, + F: FnOnce(&Path) -> Result, +{ + 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( 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, - 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) -> Result<()> { + install_inner(config, output.as_ref()) }