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 00000000..25fffda3 Binary files /dev/null and b/testdata/gnu-hardlinks.tar differ