summaryrefslogtreecommitdiff
path: root/src/manifest.rs
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 03:00:45 +0000
committerHermes Agent <hermes@localhost>2026-08-12 03:00:45 +0000
commita60cf70253fb1e22dfcc480385c0499272b75477 (patch)
treefc662e61c1c76109c3e23142c35422908f24397c /src/manifest.rs
parent5384232eec4dff669cd2bb68ee93c34b6b63c78c (diff)
Collect hardlink facts from archive artifacts
Diffstat (limited to 'src/manifest.rs')
-rw-r--r--src/manifest.rs39
1 files changed, 31 insertions, 8 deletions
diff --git a/src/manifest.rs b/src/manifest.rs
index 3c34ade..5b7ae77 100644
--- a/src/manifest.rs
+++ b/src/manifest.rs
@@ -131,7 +131,13 @@ impl ArtifactManifest {
}
}
EntryType::Directory => {}
- EntryType::Link => {}
+ EntryType::Link => {
+ let target = entry
+ .link_name()
+ .with_context(|| format!("read archive hardlink target for {path}"))?
+ .ok_or_else(|| anyhow::anyhow!("archive hardlink has no target: {path}"))?;
+ files.push(FileRecord::hardlink(path, portable_path(&target)?));
+ }
_ => unreachable!("entry type was checked above"),
}
}
@@ -247,16 +253,29 @@ fn portable_path(path: &Path) -> Result<String> {
bail!("absolute path is not valid in an artifact manifest: {}", path.display());
}
let value = path.to_str().ok_or_else(|| anyhow::anyhow!("non-UTF-8 path is not valid in an artifact manifest: {}", path.display()))?;
- if value.is_empty() || value == "." {
+ if value.is_empty() {
bail!("empty path is not valid in an artifact manifest");
}
- if path
- .components()
- .any(|component| matches!(component, std::path::Component::ParentDir))
- {
- bail!("parent path is not valid in an artifact manifest: {}", path.display());
+ let mut normalized = PathBuf::new();
+ for component in path.components() {
+ match component {
+ std::path::Component::Normal(part) => normalized.push(part),
+ std::path::Component::CurDir => {}
+ std::path::Component::ParentDir => {
+ bail!("parent path is not valid in an artifact manifest: {}", path.display());
+ }
+ std::path::Component::RootDir | std::path::Component::Prefix(_) => {
+ bail!("absolute path is not valid in an artifact manifest: {}", path.display());
+ }
+ }
}
- Ok(value.to_owned())
+ if normalized.as_os_str().is_empty() {
+ return Ok(".".into());
+ }
+ let normalized = normalized
+ .to_str()
+ .ok_or_else(|| anyhow::anyhow!("non-UTF-8 path is not valid in an artifact manifest: {}", path.display()))?;
+ Ok(normalized.to_owned())
}
fn is_enabled_service(path: &str) -> bool {
@@ -326,6 +345,10 @@ impl FileRecord {
pub fn symlink(path: impl Into<String>, target: impl Into<String>) -> Self {
Self { path: path.into(), kind: "symlink".into(), digest: None, target: Some(target.into()) }
}
+
+ pub fn hardlink(path: impl Into<String>, target: impl Into<String>) -> Self {
+ Self { path: path.into(), kind: "hardlink".into(), digest: None, target: Some(target.into()) }
+ }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]