From f426e4246a0ca99631906e94a3ec803254c99935 Mon Sep 17 00:00:00 2001 From: Gus Knowlden Date: Fri, 8 Mar 2019 13:52:05 -0500 Subject: [PATCH] Fixes Tar.Extract when extracting hardlinked files This chance relativizes the Linkname on tar headers when extracting hardlinked files. This change also adds a couple of tests around the Tar.Unarchive and Tar.Extract functions. --- tar.go | 5 +++ tar_test.go | 67 +++++++++++++++++++++++++++++++++++++ testdata/gnu-hardlinks.tar | Bin 0 -> 10240 bytes 3 files changed, 72 insertions(+) create mode 100644 tar_test.go create mode 100644 testdata/gnu-hardlinks.tar diff --git a/tar.go b/tar.go index 2a1ff4dc..6af36921 100644 --- a/tar.go +++ b/tar.go @@ -512,6 +512,11 @@ func (t *Tar) Extract(source, target, destination string) error { } th.Name = end + // relativize any hardlink names + if th.Typeflag == tar.TypeLink { + th.Linkname = filepath.Join(filepath.Base(filepath.Dir(th.Linkname)), filepath.Base(th.Linkname)) + } + err = t.untarFile(f, destination, th) if err != nil { return fmt.Errorf("extracting file %s: %v", th.Name, err) diff --git a/tar_test.go b/tar_test.go new file mode 100644 index 00000000..fba48edd --- /dev/null +++ b/tar_test.go @@ -0,0 +1,67 @@ +package archiver_test + +import ( + "io/ioutil" + "os" + "path" + "testing" + + "github.com/mholt/archiver" +) + +func requireRegularFile(t *testing.T, path string) os.FileInfo { + fileInfo, err := os.Stat(path) + if err != nil { + t.Fatalf("fileInfo on '%s': %v", path, err) + } + + if !fileInfo.Mode().IsRegular() { + t.Fatalf("'%s' expected to be a regular file", path) + } + + return fileInfo +} + +func assertSameFile(t *testing.T, f1, f2 os.FileInfo) { + if !os.SameFile(f1, f2) { + t.Errorf("expected '%s' and '%s' to be the same file", f1.Name(), f2.Name()) + } +} + +func TestDefaultTar_Unarchive_HardlinkSuccess(t *testing.T) { + source := "testdata/gnu-hardlinks.tar" + + destination, err := ioutil.TempDir("", "archiver_tar_test") + if err != nil { + t.Fatalf("creating temp dir: %v", err) + } + defer os.RemoveAll(destination) + + err = archiver.DefaultTar.Unarchive(source, destination) + if err != nil { + t.Fatalf("unarchiving '%s' to '%s': %v", source, destination, err) + } + + fileaInfo := requireRegularFile(t, path.Join(destination, "dir-1", "dir-2", "file-a")) + filebInfo := requireRegularFile(t, path.Join(destination, "dir-1", "dir-2", "file-b")) + assertSameFile(t, fileaInfo, filebInfo) +} + +func TestDefaultTar_Extract_HardlinkSuccess(t *testing.T) { + source := "testdata/gnu-hardlinks.tar" + + destination, err := ioutil.TempDir("", "archiver_tar_test") + if err != nil { + t.Fatalf("creating temp dir: %v", err) + } + defer os.RemoveAll(destination) + + err = archiver.DefaultTar.Extract(source, path.Join("dir-1", "dir-2"), destination) + if err != nil { + t.Fatalf("unarchiving '%s' to '%s': %v", source, destination, err) + } + + fileaInfo := requireRegularFile(t, path.Join(destination, "dir-2", "file-a")) + filebInfo := requireRegularFile(t, path.Join(destination, "dir-2", "file-b")) + assertSameFile(t, fileaInfo, filebInfo) +} diff --git a/testdata/gnu-hardlinks.tar b/testdata/gnu-hardlinks.tar new file mode 100644 index 0000000000000000000000000000000000000000..25fffda3ac83dacd831421982447c8bcd2bd7ad2 GIT binary patch literal 10240 zcmeIzT?&IR3?~IB2{&^0Bln?Xjf9KB!X+>*NTz)C(h&%89 zp`Q;a3ZCpmAnE_1{FjFg&-Z`y|KIxGvSCt^EUv=gwDbNiUu}#C0SG_<0uX=z1Rwwb h2tWV=5P$##AOHafKmY;|fB*y_009U<00O@dxC63)Q-lBj literal 0 HcmV?d00001