diff options
| author | Hermes Agent <hermes@localhost> | 2026-08-12 03:36:47 +0000 |
|---|---|---|
| committer | Hermes Agent <hermes@localhost> | 2026-08-12 03:36:47 +0000 |
| commit | 754972b95ba2fabc082aed387c246e632a8d7434 (patch) | |
| tree | 9a86dc5c927ea6dfea5d381ad1d9ccdcf882c2bd /src | |
| parent | 434fc29ab867d630185b632a43e82513ac918fe5 (diff) | |
Preserve rootfs hardlink facts in manifests
Diffstat (limited to 'src')
| -rw-r--r-- | src/manifest.rs | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/src/manifest.rs b/src/manifest.rs index 307913e..6168706 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,6 +1,7 @@ -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; use std::fs; use std::io::Read; +use std::os::unix::fs::MetadataExt; use std::path::{Path, PathBuf}; use anyhow::{bail, Context, Result}; @@ -154,8 +155,15 @@ impl ArtifactManifest { fn collect_files(rootfs: &Path) -> Result<Vec<FileRecord>> { let mut files = Vec::new(); - for entry in WalkDir::new(rootfs).follow_links(false).min_depth(1) { - let entry = entry.with_context(|| format!("walk rootfs {}", rootfs.display()))?; + let mut entries = WalkDir::new(rootfs) + .follow_links(false) + .min_depth(1) + .into_iter() + .collect::<std::result::Result<Vec<_>, _>>() + .with_context(|| format!("walk rootfs {}", rootfs.display()))?; + entries.sort_by_key(|entry| entry.path().to_path_buf()); + let mut hardlink_targets: BTreeMap<(u64, u64), String> = BTreeMap::new(); + for entry in entries { let relative = entry.path().strip_prefix(rootfs).expect("walk entry is below rootfs"); if is_internal_metadata_path(&portable_path(relative)?) { continue; @@ -163,7 +171,14 @@ fn collect_files(rootfs: &Path) -> Result<Vec<FileRecord>> { let path = portable_path(relative)?; let file_type = entry.file_type(); if file_type.is_file() { - files.push(FileRecord::file(path, sha256_file(entry.path())?)); + let metadata = entry.metadata().with_context(|| format!("inspect rootfs file {}", entry.path().display()))?; + let key = (metadata.dev(), metadata.ino()); + if let Some(target) = hardlink_targets.get(&key) { + files.push(FileRecord::hardlink(path, target.clone())); + } else { + hardlink_targets.insert(key, path.clone()); + files.push(FileRecord::file(path, sha256_file(entry.path())?)); + } } else if file_type.is_symlink() { let target = fs::read_link(entry.path()) .with_context(|| format!("read rootfs symlink {}", entry.path().display()))?; |