|
@@ -153,7 +153,15 @@ func (compression *Compression) Extension() string {
|
|
|
return ""
|
|
|
}
|
|
|
|
|
|
-func addTarFile(path, name string, tw *tar.Writer, twBuf *bufio.Writer) error {
|
|
|
+type tarAppender struct {
|
|
|
+ TarWriter *tar.Writer
|
|
|
+ Buffer *bufio.Writer
|
|
|
+
|
|
|
+ // for hardlink mapping
|
|
|
+ SeenFiles map[uint64]string
|
|
|
+}
|
|
|
+
|
|
|
+func (ta *tarAppender) addTarFile(path, name string) error {
|
|
|
fi, err := os.Lstat(path)
|
|
|
if err != nil {
|
|
|
return err
|
|
@@ -188,13 +196,28 @@ func addTarFile(path, name string, tw *tar.Writer, twBuf *bufio.Writer) error {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // if it's a regular file and has more than 1 link,
|
|
|
+ // it's hardlinked, so set the type flag accordingly
|
|
|
+ if fi.Mode().IsRegular() && stat.Nlink > 1 {
|
|
|
+ // a link should have a name that it links too
|
|
|
+ // and that linked name should be first in the tar archive
|
|
|
+ ino := uint64(stat.Ino)
|
|
|
+ if oldpath, ok := ta.SeenFiles[ino]; ok {
|
|
|
+ hdr.Typeflag = tar.TypeLink
|
|
|
+ hdr.Linkname = oldpath
|
|
|
+ hdr.Size = 0 // This Must be here for the writer math to add up!
|
|
|
+ } else {
|
|
|
+ ta.SeenFiles[ino] = name
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
capability, _ := system.Lgetxattr(path, "security.capability")
|
|
|
if capability != nil {
|
|
|
hdr.Xattrs = make(map[string]string)
|
|
|
hdr.Xattrs["security.capability"] = string(capability)
|
|
|
}
|
|
|
|
|
|
- if err := tw.WriteHeader(hdr); err != nil {
|
|
|
+ if err := ta.TarWriter.WriteHeader(hdr); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
@@ -204,17 +227,17 @@ func addTarFile(path, name string, tw *tar.Writer, twBuf *bufio.Writer) error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- twBuf.Reset(tw)
|
|
|
- _, err = io.Copy(twBuf, file)
|
|
|
+ ta.Buffer.Reset(ta.TarWriter)
|
|
|
+ _, err = io.Copy(ta.Buffer, file)
|
|
|
file.Close()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- err = twBuf.Flush()
|
|
|
+ err = ta.Buffer.Flush()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- twBuf.Reset(nil)
|
|
|
+ ta.Buffer.Reset(nil)
|
|
|
}
|
|
|
|
|
|
return nil
|
|
@@ -345,9 +368,15 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- tw := tar.NewWriter(compressWriter)
|
|
|
-
|
|
|
go func() {
|
|
|
+ ta := &tarAppender{
|
|
|
+ TarWriter: tar.NewWriter(compressWriter),
|
|
|
+ Buffer: pools.BufioWriter32KPool.Get(nil),
|
|
|
+ SeenFiles: make(map[uint64]string),
|
|
|
+ }
|
|
|
+ // this buffer is needed for the duration of this piped stream
|
|
|
+ defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
|
|
+
|
|
|
// In general we log errors here but ignore them because
|
|
|
// during e.g. a diff operation the container can continue
|
|
|
// mutating the filesystem and we can see transient errors
|
|
@@ -357,9 +386,6 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|
|
options.Includes = []string{"."}
|
|
|
}
|
|
|
|
|
|
- twBuf := pools.BufioWriter32KPool.Get(nil)
|
|
|
- defer pools.BufioWriter32KPool.Put(twBuf)
|
|
|
-
|
|
|
var renamedRelFilePath string // For when tar.Options.Name is set
|
|
|
for _, include := range options.Includes {
|
|
|
filepath.Walk(filepath.Join(srcPath, include), func(filePath string, f os.FileInfo, err error) error {
|
|
@@ -395,7 +421,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|
|
relFilePath = strings.Replace(relFilePath, renamedRelFilePath, options.Name, 1)
|
|
|
}
|
|
|
|
|
|
- if err := addTarFile(filePath, relFilePath, tw, twBuf); err != nil {
|
|
|
+ if err := ta.addTarFile(filePath, relFilePath); err != nil {
|
|
|
log.Debugf("Can't add file %s to tar: %s", srcPath, err)
|
|
|
}
|
|
|
return nil
|
|
@@ -403,7 +429,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|
|
}
|
|
|
|
|
|
// Make sure to check the error on Close.
|
|
|
- if err := tw.Close(); err != nil {
|
|
|
+ if err := ta.TarWriter.Close(); err != nil {
|
|
|
log.Debugf("Can't close tar writer: %s", err)
|
|
|
}
|
|
|
if err := compressWriter.Close(); err != nil {
|