Merge pull request #674 from dmcgowan/fs-hardlink-unmodified

Fix hardlinks in tars for unmodified files
This commit is contained in:
Stephen Day
2017-04-26 18:50:12 -05:00
committed by GitHub
9 changed files with 94 additions and 47 deletions

View File

@@ -65,7 +65,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error {
}
continue
case (fi.Mode() & os.ModeType) == 0:
link, err := GetLinkSource(target, fi, inodes)
link, err := getLinkSource(target, fi, inodes)
if err != nil {
return errors.Wrap(err, "failed to get hardlink")
}

View File

@@ -16,9 +16,13 @@ import (
type ChangeKind int
const (
// ChangeKindUnmodified represents an unmodified
// file
ChangeKindUnmodified = iota
// ChangeKindAdd represents an addition of
// a file
ChangeKindAdd = iota
ChangeKindAdd
// ChangeKindModify represents a change to
// an existing file
@@ -31,6 +35,8 @@ const (
func (k ChangeKind) String() string {
switch k {
case ChangeKindUnmodified:
return "unmodified"
case ChangeKindAdd:
return "add"
case ChangeKindModify:
@@ -287,7 +293,10 @@ func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err
f1 = nil
f2 = nil
if same {
continue
if !isLinked(f) {
continue
}
k = ChangeKindUnmodified
}
}
if err := changeFn(k, p, f, nil); err != nil {

View File

@@ -90,3 +90,11 @@ func compareCapabilities(p1, p2 string) (bool, error) {
}
return bytes.Equal(c1, c2), nil
}
func isLinked(f os.FileInfo) bool {
s, ok := f.Sys().(*syscall.Stat_t)
if !ok {
return false
}
return !f.IsDir() && s.Nlink > 1
}

View File

@@ -1,5 +1,7 @@
package fs
import "os"
func detectDirDiff(upper, lower string) *diffDirOptions {
return nil
}
@@ -13,3 +15,7 @@ func compareCapabilities(p1, p2 string) (bool, error) {
// TODO: Use windows equivalent
return true, nil
}
func isLinked(os.FileInfo) bool {
return false
}

View File

@@ -2,11 +2,26 @@ package fs
import "os"
// GetLinkSource returns a path for the given name and
// GetLinkID returns an identifier representing the node a hardlink is pointing
// to. If the file is not hard linked then 0 will be returned.
func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
return getLinkInfo(fi)
}
// getLinkSource returns a path for the given name and
// file info to its link source in the provided inode
// map. If the given file name is not in the map and
// has other links, it is added to the inode map
// to be a source for other link locations.
func GetLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
return getHardLink(name, fi, inodes)
func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
inode, isHardlink := getLinkInfo(fi)
if !isHardlink {
return "", nil
}
path, ok := inodes[inode]
if !ok {
inodes[inode] = name
}
return path, nil
}

View File

@@ -3,31 +3,15 @@
package fs
import (
"errors"
"os"
"syscall"
)
func getHardLink(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
if fi.IsDir() {
return "", nil
}
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
s, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return "", errors.New("unsupported stat type")
return 0, false
}
// If inode is not hardlinked, no reason to lookup or save inode
if s.Nlink == 1 {
return "", nil
}
inode := uint64(s.Ino)
path, ok := inodes[inode]
if !ok {
inodes[inode] = name
}
return path, nil
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1
}

View File

@@ -2,6 +2,6 @@ package fs
import "os"
func getHardLink(string, os.FileInfo, map[uint64]string) (string, error) {
return "", nil
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
return 0, false
}