From 50722fa3d33dc862d8223a9d2d432ea00fcdc8e5 Mon Sep 17 00:00:00 2001 From: Johan-Liebert1 Date: Thu, 28 Aug 2025 14:43:39 +0530 Subject: [PATCH 1/2] initramfs: Better error handling Add error contexts to functions. Create wrappers around intrenal functions with error context added Signed-off-by: Johan-Liebert1 --- Cargo.lock | 1 + crates/initramfs/Cargo.toml | 1 + crates/initramfs/src/mount.rs | 65 ++++++++++++++++++++++++----------- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65d88810d..f53fa62c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,6 +194,7 @@ dependencies = [ "clap", "composefs", "composefs-boot", + "fn-error-context", "rustix 1.0.8", "serde", "toml 0.9.5", diff --git a/crates/initramfs/Cargo.toml b/crates/initramfs/Cargo.toml index 82950f93e..2617cbbf4 100644 --- a/crates/initramfs/Cargo.toml +++ b/crates/initramfs/Cargo.toml @@ -13,6 +13,7 @@ serde = { workspace = true, features = ["derive"] } composefs.workspace = true composefs-boot.workspace = true toml.workspace = true +fn-error-context.workspace = true [lints] workspace = true diff --git a/crates/initramfs/src/mount.rs b/crates/initramfs/src/mount.rs index e1e2516ee..b4f2a4d82 100644 --- a/crates/initramfs/src/mount.rs +++ b/crates/initramfs/src/mount.rs @@ -17,17 +17,20 @@ use rustix::{ fsconfig_create, fsconfig_set_string, fsmount, open_tree, unmount, FsMountFlags, MountAttrFlags, OpenTreeFlags, UnmountFlags, }, + path, }; use serde::Deserialize; use composefs::{ fsverity::{FsVerityHashValue, Sha256HashValue}, - mount::{mount_at, FsHandle}, + mount::FsHandle, mountcompat::{overlayfs_set_fd, overlayfs_set_lower_and_data_fds, prepare_mount}, repository::Repository, }; use composefs_boot::cmdline::get_cmdline_composefs; +use fn_error_context::context; + // Config file #[derive(Clone, Copy, Debug, Deserialize)] #[serde(rename_all = "lowercase")] @@ -100,39 +103,51 @@ pub struct Args { } // Helpers -fn open_dir(dirfd: impl AsFd, name: impl AsRef + Debug) -> rustix::io::Result { - openat( +fn mount_at_wrapper( + fs_fd: impl AsFd, + dirfd: impl AsFd, + path: impl path::Arg + Debug + Clone, +) -> Result<()> { + composefs::mount::mount_at(fs_fd, dirfd, path.clone()) + .with_context(|| format!("Mounting at path {path:?}")) +} + +#[context("Opening dir {name:?}")] +fn open_dir(dirfd: impl AsFd, name: impl AsRef + Debug) -> Result { + let res = openat( dirfd, name.as_ref(), OFlags::PATH | OFlags::DIRECTORY | OFlags::CLOEXEC, Mode::empty(), - ) - .inspect_err(|_| { - eprintln!("Failed to open dir {name:?}"); - }) + ); + + Ok(res?) } -fn ensure_dir(dirfd: impl AsFd, name: &str) -> rustix::io::Result { +#[context("Ensure dir")] +fn ensure_dir(dirfd: impl AsFd, name: &str) -> Result { match mkdirat(dirfd.as_fd(), name, 0o700.into()) { Ok(()) | Err(Errno::EXIST) => {} - Err(err) => Err(err)?, + Err(err) => Err(err).with_context(|| format!("Creating dir {name}"))?, } + open_dir(dirfd, name) } -fn bind_mount(fd: impl AsFd, path: &str) -> rustix::io::Result { - open_tree( +#[context("Bind mounting to path {path}")] +fn bind_mount(fd: impl AsFd, path: &str) -> Result { + let res = open_tree( fd.as_fd(), path, OpenTreeFlags::OPEN_TREE_CLONE | OpenTreeFlags::OPEN_TREE_CLOEXEC | OpenTreeFlags::AT_EMPTY_PATH, - ) - .inspect_err(|_| { - eprintln!("Open tree failed for {path}"); - }) + ); + + Ok(res?) } +#[context("Mounting tmpfs")] fn mount_tmpfs() -> Result { let tmpfs = FsHandle::open("tmpfs")?; fsconfig_create(tmpfs.as_fd())?; @@ -143,6 +158,7 @@ fn mount_tmpfs() -> Result { )?) } +#[context("Mounting state as overlay")] fn overlay_state(base: impl AsFd, state: impl AsFd, source: &str) -> Result<()> { let upper = ensure_dir(state.as_fd(), "upper")?; let work = ensure_dir(state.as_fd(), "work")?; @@ -159,13 +175,14 @@ fn overlay_state(base: impl AsFd, state: impl AsFd, source: &str) -> Result<()> MountAttrFlags::empty(), )?; - Ok(mount_at(fs, base, ".")?) + mount_at_wrapper(fs, base, ".").context("Moving mount") } fn overlay_transient(base: impl AsFd) -> Result<()> { overlay_state(base, prepare_mount(mount_tmpfs()?)?, "transient") } +#[context("Opening rootfs")] fn open_root_fs(path: &Path) -> Result { let rootfs = open_tree( CWD, @@ -185,12 +202,14 @@ fn open_root_fs(path: &Path) -> Result { /// * sysroot - fd for /sysroot /// * name - Name of the EROFS image to be mounted /// * insecure - Whether fsverity is optional or not +#[context("Mounting composefs image")] pub fn mount_composefs_image(sysroot: &OwnedFd, name: &str, insecure: bool) -> Result { let mut repo = Repository::::open_path(sysroot, "composefs")?; repo.set_insecure(insecure); repo.mount(name).context("Failed to mount composefs image") } +#[context("Mounting subdirectory")] fn mount_subdir( new_root: impl AsFd, state: impl AsFd, @@ -208,7 +227,11 @@ fn mount_subdir( match mount_type { MountType::None => Ok(()), - MountType::Bind => Ok(mount_at(bind_mount(&state, subdir)?, &new_root, subdir)?), + MountType::Bind => Ok(mount_at_wrapper( + bind_mount(&state, subdir)?, + &new_root, + subdir, + )?), MountType::Overlay => overlay_state( open_dir(&new_root, subdir)?, open_dir(&state, subdir)?, @@ -218,6 +241,7 @@ fn mount_subdir( } } +#[context("GPT workaround")] pub(crate) fn gpt_workaround() -> Result<()> { // https://github.com/systemd/systemd/issues/35017 let rootdev = stat("/dev/gpt-auto-root"); @@ -238,6 +262,7 @@ pub(crate) fn gpt_workaround() -> Result<()> { } /// Sets up /sysroot for switch-root +#[context("Setting up /sysroot")] pub fn setup_root(args: Args) -> Result<()> { let config = match std::fs::read_to_string(args.config) { Ok(text) => toml::from_str(&text)?, @@ -267,14 +292,14 @@ pub fn setup_root(args: Args) -> Result<()> { // 6.15 and later. Before 6.15 we can't mount into a floating tree, so mount it first. This // will leave an abandoned clone of the sysroot mounted under it, but that's OK for now. if cfg!(feature = "pre-6.15") { - mount_at(&new_root, CWD, &args.sysroot)?; + mount_at_wrapper(&new_root, CWD, &args.sysroot)?; } if config.root.transient { overlay_transient(&new_root)?; } - match mount_at(&sysroot_clone, &new_root, "sysroot") { + match composefs::mount::mount_at(&sysroot_clone, &new_root, "sysroot") { Ok(()) | Err(Errno::NOENT) => {} Err(err) => Err(err)?, } @@ -287,7 +312,7 @@ pub fn setup_root(args: Args) -> Result<()> { if cfg!(not(feature = "pre-6.15")) { // Replace the /sysroot with the new composed root filesystem unmount(&args.sysroot, UnmountFlags::DETACH)?; - mount_at(&new_root, CWD, &args.sysroot)?; + mount_at_wrapper(&new_root, CWD, &args.sysroot)?; } Ok(()) From 5cbc0be7a6fa4de5ec006661382fefc3f66d77ad Mon Sep 17 00:00:00 2001 From: Johan-Liebert1 Date: Thu, 28 Aug 2025 17:53:57 +0530 Subject: [PATCH 2/2] initramfs: Bind mount /etc This is prep work for eventual integration with composefs-native backend Signed-off-by: Johan-Liebert1 --- crates/initramfs/src/mount.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/initramfs/src/mount.rs b/crates/initramfs/src/mount.rs index b4f2a4d82..5dea8c9d5 100644 --- a/crates/initramfs/src/mount.rs +++ b/crates/initramfs/src/mount.rs @@ -306,7 +306,7 @@ pub fn setup_root(args: Args) -> Result<()> { // etc + var let state = open_dir(open_dir(&sysroot, "state/deploy")?, image.to_hex())?; - mount_subdir(&new_root, &state, "etc", config.etc, MountType::Overlay)?; + mount_subdir(&new_root, &state, "etc", config.etc, MountType::Bind)?; mount_subdir(&new_root, &state, "var", config.var, MountType::Bind)?; if cfg!(not(feature = "pre-6.15")) {