From 2ef521b0a4fa5ccd806220e5d9ad45f276ef9672 Mon Sep 17 00:00:00 2001 From: AtharvaKulkarniIT <122991006+AtharvaKulkarniIT@users.noreply.github.com> Date: Sun, 1 Nov 2026 16:18:51 +0530 Subject: [PATCH] generator: skip fstab editor for read-only root Fixes #2592 Signed-off-by: AtharvaKulkarniIT <122991006+AtharvaKulkarniIT@users.noreply.github.com> --- crates/lib/src/generator.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/lib/src/generator.rs b/crates/lib/src/generator.rs index 9cdfff7778..dba8485b25 100644 --- a/crates/lib/src/generator.rs +++ b/crates/lib/src/generator.rs @@ -39,6 +39,7 @@ pub(crate) fn fstab_generator_impl(root: &Dir, unit_dir: &Dir) -> Result { .map(std::io::BufReader::new) { let mut from_anaconda = false; + let mut root_is_readonly = false; for line in fd.lines() { let line = line.context("Reading /etc/fstab")?; if line.contains(BOOTC_EDITED_STAMP) { @@ -48,8 +49,17 @@ pub(crate) fn fstab_generator_impl(root: &Dir, unit_dir: &Dir) -> Result { if line.contains(FSTAB_ANACONDA_STAMP) { from_anaconda = true; } + let fields = line.split_ascii_whitespace().collect::>(); + if !line.trim_start().starts_with('#') + && fields.get(1) == Some(&"/") + && fields + .get(3) + .is_some_and(|options| options.split(',').any(|option| option == "ro")) + { + root_is_readonly = true; + } } - if !from_anaconda { + if !from_anaconda || root_is_readonly { return Ok(false); } tracing::debug!("/etc/fstab from anaconda: {from_anaconda}"); @@ -609,6 +619,24 @@ UUID=341c4712-54e8-4839-8020-d94073b1dc8b /boot xfs defaul Ok(()) } + #[test] + fn test_generator_fstab_readonly_root() -> Result<()> { + let anaconda_fstab = indoc::indoc! { " +# Created by anaconda +UUID=715be2b7-c458-49f2-acec-b2fdb53d9089 / xfs defaults,ro 0 0 +" }; + let tempdir = fixture()?; + let unit_dir = &tempdir.open_dir("run/systemd/system")?; + + tempdir.atomic_write("etc/fstab", anaconda_fstab)?; + tempdir.atomic_write(OSTREE_BOOTED, "ostree booted")?; + let updated = fstab_generator_impl(&tempdir, unit_dir)?; + assert!(!updated); + assert_eq!(unit_dir.entries()?.count(), 0); + + Ok(()) + } + #[test] fn test_tmpfiles_ordering_dropin() -> Result<()> { let tempdir = fixture()?;