You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix: hard links against content from another package - #334
$ chisel cut --release ./ --arch amd64 --root $rootfs test-package1_myslice test-package2_myslice
Extracting files from package "test-package2"...
Extracting files from package "test-package1"...
$ cat $rootfs/hardlink
bar
$ stat -c '%i'$rootfs/file $rootfs/hardlink
214420890
214420890
/hardlink carries test-package2's content and shares its inode. test-package2 is extracted first (the essential), so its /file is on disk when test-package1's link entry is reached, and the link is made against it. with the packages the other way round the link is a copy of test-package1's own content.
repro using chisel-proxy, with two prebuilt debs (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-package1_0.0.0_amd64.deb ./test-package2_0.0.0_amd64.deb ./test-package1.yaml ./test-package2.yaml \
-- --root ./rootfs test-package1_myslice test-package2_myslice
Extracting files from package "test-package2"...
Extracting files from package "test-package1"...
$ cat ./rootfs/hardlink
bar
with the fix cat prints foo and the inodes differ.
fix
a hard link is only created in the first pass when its target was extracted by the same package in the same pass. otherwise the entry goes through the existing second pass, which writes the package's own content. a deferred link to a link waits for the content at the end of the chain, a deferred link to a symlink becomes that symlink, and link targets may be spelled with or without the leading ./.
A hard link entry was created with os.Link against whatever sat at the
link target path when the entry was reached, so when another package
extracted earlier provided that path, the link carried the other
package's content and shared its inode. The second pass that copies the
content of the entry's own package only ran when the target path was
missing.
A hard link is now only created when the target was extracted by the
same package in the same pass; otherwise the entry goes through the
second pass as before.
Any entry extracted at its own path is a valid first pass link target,
not only regular files, so a hard link to a symlink or to another hard
link of the same package is created in the first pass as before.
Resolve deferred hard-link chains to content-bearing entries
internal/tarball/extract.go:240
When a selected hard link points to another hard-link entry that this package did not extract, deferring it here does not recover its content. For example, package A contains ./file (foo), ./a (link to ./file), and ./b (link to ./a); select only /b while another package already provides /a. This change correctly avoids linking /b to that other package, but the second pass reads A's ./a header, which has no payload, and writes an empty /b instead of foo. Resolve deferred link chains back to their content-bearing entry before writing the selected link.
Normalize hard-link targets before validating extracted paths
internal/tarball/extract.go:244
This now validates every selected hard-link target with sanitizeTarPath, which accepts only names starting with ./. A valid tar entry can have Name: "./hardlink" and Linkname: "file"; if this package already extracted ./file, the previous code could link to it, but this change returns invalid link target file instead. Normalize safe relative link names to the same path format as tar members before checking extracted, while still rejecting targets outside the root.
A deferred hard link whose target is itself a hard link waits for the
content at the end of the chain, resolved once the whole tarball has
been seen, and links against that content when this package extracted
it. A deferred link to a symlink becomes that symlink. Link targets are
cleaned and accepted with or without the leading "./", and the second
pass creates missing parent directories as the first one does.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
did some formal verification (fizzbee, my specs are here and here) of the
cutpath and found 7 minor bugs:preferignores x-arch paths #330mutable: true, until: mutate#331until: mutate#333test-package1ships/fileand/hardlink, a hard link to it.test-package2ships its own/file. cut the following slices:/hardlinkcarriestest-package2's content and shares its inode.test-package2is extracted first (the essential), so its/fileis on disk whentest-package1's link entry is reached, and the link is made against it. with the packages the other way round the link is a copy oftest-package1's own content.repro using
chisel-proxy, with two prebuilt debs (its directory packing does not keep hard links):with the fix
catprintsfooand the inodes differ.fix
a hard link is only created in the first pass when its target was extracted by the same package in the same pass. otherwise the entry goes through the existing second pass, which writes the package's own content. a deferred link to a link waits for the content at the end of the chain, a deferred link to a symlink becomes that symlink, and link targets may be spelled with or without the leading
./.