From 36854dc6e78014234ad6e06a423ede529d7b4d37 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 18 Dec 2024 17:26:01 -0500 Subject: [PATCH 1/2] tests: Verify mtime changes This could be its own whole new test, but it's simple enough to add one here. Signed-off-by: Colin Walters --- tests/booted/test-image-pushpull-upgrade.nu | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/booted/test-image-pushpull-upgrade.nu b/tests/booted/test-image-pushpull-upgrade.nu index 68af3c332..6e3e812da 100644 --- a/tests/booted/test-image-pushpull-upgrade.nu +++ b/tests/booted/test-image-pushpull-upgrade.nu @@ -46,8 +46,16 @@ RUN echo test content > /usr/share/blah.txt # Just sanity check it let v = podman run --rm localhost/bootc-derived cat /usr/share/blah.txt | str trim assert equal $v "test content" + + let orig_root_mtime = ls -Dl /ostree/bootc | get modified + # Now, fetch it back into the bootc storage! bootc switch --transport containers-storage localhost/bootc-derived + + # Also test that the mtime changes on modification + let new_root_mtime = ls -Dl /ostree/bootc | get modified + assert ($new_root_mtime > $orig_root_mtime) + # And reboot into it tmt-reboot } From e6223aab73cac9ead0eefec3927ed5804c3203e7 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 20 Dec 2024 08:37:11 -0500 Subject: [PATCH 2/2] rhsm: Use atomic_write Just noticed this when reading the code; our cap-std-ext crate has a handy `atomic_write` API which streamlines exactly this use case. This avoids having a corrupted/half-written facts file if our process is interrupted, and also avoids a case where a reading process may temporarily see a half written file. Signed-off-by: Colin Walters --- lib/src/rhsm.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/src/rhsm.rs b/lib/src/rhsm.rs index 2ba477b13..fbb850a86 100644 --- a/lib/src/rhsm.rs +++ b/lib/src/rhsm.rs @@ -1,8 +1,8 @@ //! Integration with Red Hat Subscription Manager -use anyhow::Result; -use cap_std::fs::{Dir, OpenOptions}; -use cap_std_ext::cap_std; +use anyhow::{Context, Result}; +use cap_std::fs::Dir; +use cap_std_ext::{cap_std, dirext::CapStdExtDirExt}; use fn_error_context::context; use serde::Serialize; @@ -96,11 +96,11 @@ pub(crate) async fn publish_facts(root: &Dir) -> Result<()> { let (_deployments, host) = crate::status::get_status(&sysroot, booted_deployment.as_ref())?; let facts = RhsmFacts::from(host.status); - let mut bootc_facts_file = root.open_with( - FACTS_PATH, - OpenOptions::new().write(true).create(true).truncate(true), - )?; - serde_json::to_writer_pretty(&mut bootc_facts_file, &facts)?; + root.atomic_replace_with(FACTS_PATH, |w| { + serde_json::to_writer_pretty(w, &facts)?; + anyhow::Ok(()) + }) + .with_context(|| format!("Writing {FACTS_PATH}"))?; Ok(()) }