Skip to content

Commit

Permalink
Handle hard links in remote builds
Browse files Browse the repository at this point in the history
Fixes: containers#9893

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan authored and mheon committed May 25, 2021
1 parent c53638e commit ba88486
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 12 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ bin/podman.cross.%: .gopathok
.PHONY: local-cross
local-cross: $(CROSS_BUILD_TARGETS) ## Cross compile podman binary for multiple architectures

.PHONY: cross
cross: local-cross

# Update nix/nixpkgs.json its latest stable commit
.PHONY: nixpkgs
nixpkgs:
Expand Down
44 changes: 32 additions & 12 deletions pkg/bindings/images/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ import (
"github.com/sirupsen/logrus"
)

type devino struct {
Dev uint64
Ino uint64
}

var (
iidRegex = regexp.MustCompile(`^[0-9a-f]{12}`)
)
Expand Down Expand Up @@ -402,7 +407,7 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
defer pw.Close()
defer gw.Close()
defer tw.Close()

seen := make(map[devino]string)
for _, src := range sources {
s, err := filepath.Abs(src)
if err != nil {
Expand Down Expand Up @@ -431,25 +436,40 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {
}

if info.Mode().IsRegular() { // add file item
f, lerr := os.Open(path)
if lerr != nil {
return lerr
di, isHardLink := checkHardLink(info)
if err != nil {
return err
}

hdr, lerr := tar.FileInfoHeader(info, name)
if lerr != nil {
f.Close()
return lerr
hdr, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
orig, ok := seen[di]
if ok {
hdr.Typeflag = tar.TypeLink
hdr.Linkname = orig
hdr.Size = 0

return tw.WriteHeader(hdr)
}
f, err := os.Open(path)
if err != nil {
return err
}

hdr.Name = name
if lerr := tw.WriteHeader(hdr); lerr != nil {
if err := tw.WriteHeader(hdr); err != nil {
f.Close()
return lerr
return err
}

_, cerr := io.Copy(tw, f)
_, err = io.Copy(tw, f)
f.Close()
return cerr
if err == nil && isHardLink {
seen[di] = name
}
return err
} else if info.Mode().IsDir() { // add folders
hdr, lerr := tar.FileInfoHeader(info, name)
if lerr != nil {
Expand Down
16 changes: 16 additions & 0 deletions pkg/bindings/images/build_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// +build !windows

package images

import (
"os"
"syscall"
)

func checkHardLink(fi os.FileInfo) (devino, bool) {
st := fi.Sys().(*syscall.Stat_t)
return devino{
Dev: uint64(st.Dev),
Ino: uint64(st.Ino),
}, st.Nlink > 1
}
9 changes: 9 additions & 0 deletions pkg/bindings/images/build_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package images

import (
"os"
)

func checkHardLink(fi os.FileInfo) (devino, bool) {
return devino{}, false
}
20 changes: 20 additions & 0 deletions test/system/070-build.bats
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,26 @@ EOF
is "$output" ".*/tmp/bogus: no such file or directory"
}

@test "podman build COPY hardlinks " {
tmpdir=$PODMAN_TMPDIR/build-test
mkdir -p $tmpdir

dockerfile=$tmpdir/Dockerfile
cat >$dockerfile <<EOF
FROM $IMAGE
COPY . /test
EOF
ln $dockerfile $tmpdir/hardlink

run_podman build -t build_test $tmpdir
run_podman run --rm build_test stat -c '%i' /test/Dockerfile
dinode=$output
run_podman run --rm build_test stat -c '%i' /test/hardlink
is "$output" "$dinode" "COPY hardlinks work"

run_podman rmi -f build_test
}

function teardown() {
# A timeout or other error in 'build' can leave behind stale images
# that podman can't even see and which will cascade into subsequent
Expand Down

0 comments on commit ba88486

Please sign in to comment.