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

Commit

Permalink
Merge branch 'fix-hardlinks'
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt committed Nov 22, 2019
2 parents 6041b49 + 9ea51e7 commit 14b2737
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 11 deletions.
24 changes: 13 additions & 11 deletions tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func (t *Tar) addTopLevelFolder(sourceArchive, destination string) (string, erro
return destination, nil
}

func (t *Tar) untarNext(to string) error {
func (t *Tar) untarNext(destination string) error {
f, err := t.Read()
if err != nil {
return err // don't wrap error; calling loop must break on io.EOF
Expand All @@ -215,20 +215,17 @@ func (t *Tar) untarNext(to string) error {
if !ok {
return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
}
return t.untarFile(f, filepath.Join(to, header.Name))
return t.untarFile(f, destination, header)
}

func (t *Tar) untarFile(f File, to string) error {
func (t *Tar) untarFile(f File, destination string, hdr *tar.Header) error {
to := filepath.Join(destination, hdr.Name)

// do not overwrite existing files, if configured
if !f.IsDir() && !t.OverwriteExisting && fileExists(to) {
return fmt.Errorf("file already exists: %s", to)
}

hdr, ok := f.Header.(*tar.Header)
if !ok {
return fmt.Errorf("expected header to be *tar.Header but was %T", f.Header)
}

switch hdr.Typeflag {
case tar.TypeDir:
return mkdir(to, f.Mode())
Expand All @@ -237,7 +234,7 @@ func (t *Tar) untarFile(f File, to string) error {
case tar.TypeSymlink:
return writeNewSymbolicLink(to, hdr.Linkname)
case tar.TypeLink:
return writeNewHardLink(to, filepath.Join(to, hdr.Linkname))
return writeNewHardLink(to, filepath.Join(destination, hdr.Linkname))
case tar.TypeXGlobalHeader:
return nil // ignore the pax global header from git-generated tarballs
default:
Expand Down Expand Up @@ -513,9 +510,14 @@ func (t *Tar) Extract(source, target, destination string) error {
if err != nil {
return fmt.Errorf("relativizing paths: %v", err)
}
joined := filepath.Join(destination, end)
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, joined)
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 14b2737

Please sign in to comment.