diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index 36425f70a5..7082cd9088 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -350,7 +350,13 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode - if hdr.Typeflag != tar.TypeSymlink { + if hdr.Typeflag == tar.TypeLink { + if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { + if err := os.Chmod(path, hdrInfo.Mode()); err != nil { + return err + } + } + } else if hdr.Typeflag != tar.TypeSymlink { if err := os.Chmod(path, hdrInfo.Mode()); err != nil { return err } @@ -358,7 +364,13 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and - if hdr.Typeflag != tar.TypeSymlink { + if hdr.Typeflag == tar.TypeLink { + if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { + if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { + return err + } + } + } else if hdr.Typeflag != tar.TypeSymlink { if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { return err } diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index 6cd95d5ad5..c127b307e2 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -435,6 +435,34 @@ func TestUntarInvalidFilenames(t *testing.T) { } } +func TestUntarHardlinkToSymlink(t *testing.T) { + for i, headers := range [][]*tar.Header{ + { + { + Name: "symlink1", + Typeflag: tar.TypeSymlink, + Linkname: "regfile", + Mode: 0644, + }, + { + Name: "symlink2", + Typeflag: tar.TypeLink, + Linkname: "symlink1", + Mode: 0644, + }, + { + Name: "regfile", + Typeflag: tar.TypeReg, + Mode: 0644, + }, + }, + } { + if err := testBreakout("untar", "docker-TestUntarHardlinkToSymlink", headers); err != nil { + t.Fatalf("i=%d. %v", i, err) + } + } +} + func TestUntarInvalidHardlink(t *testing.T) { for i, headers := range [][]*tar.Header{ { // try reading victim/hello (../)