write_file: Do not overwrite with same content

The `write_file` function will no longer overwrite existing files if the
content has not changed.  This will ensure file metadata timestamps are
accurate, and reduce unnecessary filesystem activity.
This commit is contained in:
2024-01-18 19:46:25 -06:00
parent e5403dbc66
commit 1099fa40c7
3 changed files with 41 additions and 10 deletions

7
Cargo.lock generated
View File

@@ -300,6 +300,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "humansize"
version = "2.1.3"
@@ -923,6 +929,7 @@ dependencies = [
"argparse",
"blake2",
"file-mode",
"hex",
"pwd-grp",
"serde",
"serde_yaml",

View File

@@ -9,6 +9,7 @@ edition = "2021"
argparse = "0.2.2"
blake2 = "0.10.6"
file-mode = { version = "0.1.2", features = ["serde"] }
hex = "0.4.3"
pwd-grp = "0.1.1"
serde = { version = "1.0.195", features = ["derive"] }
serde_yaml = "0.9.30"

View File

@@ -12,7 +12,7 @@ use serde::de::DeserializeOwned;
use serde_yaml::Value;
use shlex::Shlex;
use tera::{Context, Tera};
use tracing::{debug, error, info, warn};
use tracing::{debug, error, info, trace, warn};
use model::Instructions;
@@ -161,13 +161,18 @@ fn process_instructions(
};
let mut dest = PathBuf::from(destdir.as_ref());
dest.push(i.dest.strip_prefix("/").unwrap_or(i.dest.as_path()));
let orig_cksm = checksum(&dest).ok();
if let Err(e) = write_file(&dest, out.as_bytes()) {
error!("Failed to write output file {}: {}", dest.display(), e);
continue;
}
let new_cksm = checksum(&dest).ok();
if orig_cksm != new_cksm {
let changed = match write_file(&dest, out.as_bytes()) {
Ok(c) => c,
Err(e) => {
error!(
"Failed to write output file {}: {}",
dest.display(),
e
);
continue;
}
};
if changed {
info!("File {} was changed", dest.display());
if let Some(hooks) = i.hooks {
if let Some(changed) = hooks.changed {
@@ -219,19 +224,37 @@ fn process_instructions(
fn write_file(
dest: impl AsRef<Path>,
data: &[u8],
) -> Result<(), std::io::Error> {
) -> Result<bool, std::io::Error> {
if let Some(p) = dest.as_ref().parent() {
if !p.exists() {
info!("Creating directory {}", p.display());
std::fs::create_dir_all(p)?;
}
}
if let Ok(orig_cksm) = checksum(&dest) {
trace!(
"Original checksum: {}: {}",
dest.as_ref().display(),
hex::encode(&orig_cksm)
);
let mut blake = Blake2b512::new();
blake.update(data);
let new_cksm = blake.finalize().to_vec();
trace!(
"New checksum: {}: {}",
dest.as_ref().display(),
hex::encode(&new_cksm)
);
if orig_cksm == new_cksm {
return Ok(false);
}
}
debug!("Writing output: {}", dest.as_ref().display());
let mut f = std::fs::File::create(&dest)?;
f.write_all(data)?;
let size = f.stream_position()?;
debug!("Wrote output: {} ({} bytes)", dest.as_ref().display(), size);
Ok(())
Ok(true)
}
fn chown(