diff --git a/Cargo.lock b/Cargo.lock index 931de9b99e..8492c473a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1685,7 +1685,7 @@ dependencies = [ [[package]] name = "redox_installer" -version = "0.2.36" +version = "0.2.37" dependencies = [ "anyhow", "arg_parser", @@ -1694,6 +1694,7 @@ dependencies = [ "fscommon", "gpt", "libc", + "libredox", "pkgar", "pkgar-core", "pkgar-keys", diff --git a/Cargo.toml b/Cargo.toml index 79113a636e..9b501523a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "redox_installer" -version = "0.2.36" +version = "0.2.37" description = "A Redox filesystem builder" license = "MIT" authors = ["Jeremy Soller "] @@ -32,6 +32,7 @@ fatfs = "0.3.0" fscommon = "0.1.1" gpt = "3.0.0" libc = "0.2.70" +libredox = "0.1" pkgar = "0.1.17" pkgar-core = "0.1.17" pkgar-keys = "0.1.17" diff --git a/src/bin/installer_tui.rs b/src/bin/installer_tui.rs index 8d5ef15402..1383c2624a 100644 --- a/src/bin/installer_tui.rs +++ b/src/bin/installer_tui.rs @@ -1,9 +1,10 @@ -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use pkgar::{ext::EntryExt, PackageHead}; use pkgar_core::PackageSrc; use pkgar_keys::PublicKeyFile; -use redox_installer::{with_whole_disk, Config, DiskOption}; +use redox_installer::{try_fast_install, with_redoxfs_mount, with_whole_disk, Config, DiskOption}; use std::{ + env, ffi::OsStr, fs, io::{self, Read, Write}, @@ -279,6 +280,8 @@ fn main() { let password_opt = choose_password(); + let instant = std::time::Instant::now(); + let bootloader_bios = { let path = root_path.join("boot").join("bootloader.bios"); if path.exists() { @@ -316,45 +319,69 @@ fn main() { efi_partition_size: None, skip_partitions: false, // TODO? }; - 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()]; - - // Copy files from locally installed packages - 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))?; - - // Sort and remove duplicates - files.sort(); - files.dedup(); - - // Install files - let mut buf = vec![0; 4 * MIB as usize]; - for (i, name) in files.iter().enumerate() { - eprintln!("copy {} [{}/{}]", name, i, files.len()); - - let src = root_path.join(name); - let dest = mount_path.join(name); - copy_file(&src, &dest, &mut buf)?; + let res = with_whole_disk(&disk_path, &disk_option, |mut fs| { + // Fast install method via filesystem clone + let mut last_percent = 0; + if try_fast_install(&mut fs, move |used, used_old| { + let percent = (used * 100) / used_old; + if percent != last_percent { + eprint!( + "\r{}%: {} MB/{} MB", + percent, + used / 1000 / 1000, + used_old / 1000 / 1000 + ); + last_percent = percent; + } + })? { + eprintln!("\rfinished installing using fast mode"); + return Ok(()); } - eprintln!("finished installing, unmounting filesystem"); + // Slow install method via file copy + with_redoxfs_mount(fs, |mount_path| { + let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?; - Ok(()) + // Copy filesystem.toml, which is not packaged + let mut files = vec!["filesystem.toml".to_string()]; + + // Copy files from locally installed packages + 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))?; + + // Sort and remove duplicates + files.sort(); + files.dedup(); + + // Install files + let mut buf = vec![0; 4 * MIB as usize]; + for (i, name) in files.iter().enumerate() { + eprintln!("copy {} [{}/{}]", name, i, files.len()); + + let src = root_path.join(name); + let dest = mount_path.join(name); + copy_file(&src, &dest, &mut buf)?; + } + + eprintln!("finished installing, unmounting filesystem"); + + Ok(()) + }) }); match res { Ok(()) => { - eprintln!("installer_tui: installed successfully"); + eprintln!( + "installer_tui: installed successfully in {:?}", + instant.elapsed() + ); process::exit(0); } Err(err) => { diff --git a/src/lib.rs b/src/lib.rs index 7c09808e44..c1a329e364 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ pub use crate::config::package::PackageConfig; pub use crate::config::Config; use crate::disk_wrapper::DiskWrapper; -use anyhow::{bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use pkg::Library; use rand::{rngs::OsRng, RngCore}; use redoxfs::{unmount_path, Disk, DiskIo, FileSystem}; @@ -374,6 +374,17 @@ XDG_VIDEOS_DIR="$HOME/Videos" } pub fn with_redoxfs(disk: D, password_opt: Option<&[u8]>, callback: F) -> Result +where + D: Disk + Send + 'static, + F: FnOnce(FileSystem) -> Result, +{ + 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)?; + callback(fs) +} + +pub fn with_redoxfs_mount(fs: FileSystem, callback: F) -> Result where D: Disk + Send + 'static, F: FnOnce(&Path) -> Result, @@ -388,10 +399,6 @@ where 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 (tx, rx) = channel(); let join_handle = { let mount_path = mount_path.clone(); @@ -492,7 +499,7 @@ pub fn fetch_bootloaders( pub fn with_whole_disk(disk_path: P, disk_option: &DiskOption, callback: F) -> Result where P: AsRef, - F: FnOnce(&Path) -> Result, + F: FnOnce(FileSystem>>) -> Result, { let target = get_target(); @@ -677,6 +684,84 @@ where with_redoxfs(disk_redoxfs, disk_option.password_opt, callback) } +/// Try fast install using live disk memory +pub fn try_fast_install( + fs: &mut redoxfs::FileSystem, + mut progress: F, +) -> Result { + use libredox::{call::MmapArgs, flag}; + use std::os::fd::AsRawFd; + use syscall::PAGE_SIZE; + + let phys = env::var("DISK_LIVE_ADDR") + .ok() + .and_then(|x| usize::from_str_radix(&x, 16).ok()) + .unwrap_or(0); + let size = env::var("DISK_LIVE_SIZE") + .ok() + .and_then(|x| usize::from_str_radix(&x, 16).ok()) + .unwrap_or(0); + if phys == 0 || size == 0 { + return Ok(false); + } + + let start = (phys / PAGE_SIZE) * PAGE_SIZE; + let end = phys + .checked_add(size) + .context("phys + size overflow")? + .next_multiple_of(PAGE_SIZE); + let size = end - start; + + let original = unsafe { + //TODO: unmap this memory + let file = fs::File::open("/scheme/memory/physical")?; + let base = libredox::call::mmap(MmapArgs { + fd: file.as_raw_fd() as usize, + addr: core::ptr::null_mut(), + offset: start as u64, + length: size, + prot: flag::PROT_READ, + flags: flag::MAP_SHARED, + }) + .map_err(|err| anyhow!("failed to mmap livedisk: {}", err))?; + + std::slice::from_raw_parts(base as *const u8, size) + }; + + struct DiskLive { + original: &'static [u8], + } + + impl redoxfs::Disk for DiskLive { + unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result { + let offset = (block * redoxfs::BLOCK_SIZE) as usize; + if offset + buffer.len() > self.original.len() { + return Err(syscall::Error::new(syscall::EINVAL)); + } + buffer.copy_from_slice(&self.original[offset..offset + buffer.len()]); + Ok(buffer.len()) + } + + unsafe fn write_at(&mut self, _block: u64, _buffer: &[u8]) -> syscall::Result { + Err(syscall::Error::new(syscall::EINVAL)) + } + + fn size(&mut self) -> syscall::Result { + Ok(self.original.len() as u64) + } + } + + let mut fs_old = redoxfs::FileSystem::open(DiskLive { original }, None, None, false)?; + let size_old = fs_old.header.size(); + let free_old = fs_old.allocator().free() * redoxfs::BLOCK_SIZE; + let used_old = size_old - free_old; + redoxfs::clone(&mut fs_old, fs, move |used| { + progress(used, used_old); + })?; + + Ok(true) +} + fn install_inner( config: Config, output: &Path, @@ -700,8 +785,10 @@ fn install_inner( efi_partition_size: config.general.efi_partition_size, skip_partitions: config.general.skip_partitions.unwrap_or(false), }; - with_whole_disk(output, &disk_option, move |mount_path| { - install_dir(config, mount_path, cookbook) + with_whole_disk(output, &disk_option, move |fs| { + with_redoxfs_mount(fs, move |mount_path| { + install_dir(config, mount_path, cookbook) + }) }) } }