Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Commit

Permalink
Fixes Tar.Extract when extracting hardlinked files
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
gus committed Mar 8, 2019
1 parent 11f2ffb commit f426e42
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
5 changes: 5 additions & 0 deletions tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions tar_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
Binary file added testdata/gnu-hardlinks.tar
Binary file not shown.

0 comments on commit f426e42

Please sign in to comment.