Skip to content

fix: hard links whose target is not in the manifest - #335

Open
lczyk wants to merge 3 commits into
canonical:mainfrom
lczyk:fix/hard-link-until-mutate-target
Open

lczyk wants to merge 3 commits into
canonical:mainfrom
lczyk:fix/hard-link-until-mutate-target

Conversation

@lczyk

@lczyk lczyk commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor
  • Have you signed the CLA?

did some formal verification (fizzbee, my specs are here and here) of the cut path and found 7 minor bugs:

test-package ships /file and /hardlink-to-file. cut the following slice:

# slices/test-package.yaml
package: test-package
slices:
  myslice:
    contents:
      /file: {until: mutate}
      /hardlink-to-file:
$ chisel cut --release ./ --arch amd64 --root $rootfs test-package_myslice
Extracting files from package "test-package"...
error: cannot extract from package "test-package": cannot add hard link /hardlink-to-file to report: target /file not previously added

the release validates but does not cut. same with the target covered by a glob with until: mutate.

repro using chisel-proxy, with a prebuilt deb (its directory packing does not keep hard links):

$ alias chisel-proxy='go run github.com/lczyk/chisel-proxy/cmd/chisel-proxy@v0.3.0'
$ chisel-proxy cut --arch amd64 ./test-package_0.0.0_amd64.deb ./test-package.yaml \
    -- --root ./rootfs test-package_myslice
Extracting files from package "test-package"...
error: cannot extract from package "test-package": cannot add hard link /hardlink-to-file to report: target /file not previously added

fix

builds on #334: with the extraction on main a hard link can still be made against another package's file, and this fallback would then record that content. merge #334 first.

paths with until: mutate are kept out of the manifest, so the link's target is never there. hard link entries now carry the hash and size of their content (fsutil.Create hashes it from disk), and a link whose target is not in the report is recorded as a regular file with that content. links whose target is reported keep sharing an inode as before.

Adding a hard link to the report required its target to be there
already, so a hard link to a path listed with "until: mutate", directly
or through a glob, failed the cut with "cannot add hard link ... target
not previously added" although the release validates.

Hard link entries now carry the hash and size of their content, and a
link whose target is not reported is recorded as a regular file with
that content.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Address the hard-link symlink handling and add the missing hash/size test coverage.

1 open finding
What changed in this PR

Fixes extraction and manifest reporting for hard links whose targets are omitted by until: mutate.

Changes:

  • Hashes hard-link content and records standalone metadata.
  • Handles unreported hard-link targets.
  • Adds manifest and slicer regression coverage.
File Summary
internal/​slicer/​slicer_test.go Adds end-to-end hard-link coverage.
internal/​manifestutil/​report.go Handles omitted targets; symlink metadata must be preserved.
internal/​manifestutil/​report_test.go Tests standalone hard-link reporting.
internal/​fsutil/​create.go Adds hard-link hashing; hashing must avoid incorrectly following symlinks, with additional assertions needed.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/fsutil/create.go
@lczyk lczyk added the Bug An undesired feature ;-) label Oct 10, 2026
A hard link to a symlink has no content of its own and the symlink may
be dangling, so only links to regular files are hashed.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate issues remain in hard-link ownership, symlink handling, and permission-sensitive extraction.

1 open finding
1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Fallback converts unreported symlink hard links into empty regular files

internal/​manifestutil/​report.go:80

This fallback misreports hard links to symlinks when the symlink target is omitted from the report (for example, until: mutate). fsutil.Create intentionally returns regular mode with an empty hash for that case, so clearing Link makes the manifest describe the actual symlink inode as an empty regular file. Preserve the symlink type/target or reject this unsupported case instead of converting every unreported hard link to a regular file.

🧠 Review effort: Lite

Comment thread internal/manifestutil/report.go Outdated
Comment on lines +76 to +80
} else {
// The target is not in the report, for instance because it is
// listed with "until: mutate", so the link is reported as a
// regular file with its own content.
fsEntryCpy.Link = ""
A hard link to a symlink has no content of its own, so when its target
is not in the report the error stays instead of recording an empty
regular file. A link whose content cannot be read gets no hash either,
which only matters when its target is not reported.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug An undesired feature ;-)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants