Merge pull request #3292 from alexlarsson/export-changes-manual
Don't shell out to tar for ExportChanges
This commit is contained in:
commit
b563c0c02b
|
@ -1,7 +1,10 @@
|
||||||
package archive
|
package archive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -310,24 +313,109 @@ func ChangesSize(newDir string, changes []Change) int64 {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExportChanges(dir string, changes []Change) (Archive, error) {
|
func major(device uint64) uint64 {
|
||||||
files := make([]string, 0)
|
return (device >> 8) & 0xfff
|
||||||
deletions := make([]string, 0)
|
}
|
||||||
for _, change := range changes {
|
|
||||||
if change.Kind == ChangeModify || change.Kind == ChangeAdd {
|
func minor(device uint64) uint64 {
|
||||||
files = append(files, change.Path)
|
return (device & 0xff) | ((device >> 12) & 0xfff00)
|
||||||
}
|
}
|
||||||
if change.Kind == ChangeDelete {
|
|
||||||
base := filepath.Base(change.Path)
|
func ExportChanges(dir string, changes []Change) (Archive, error) {
|
||||||
dir := filepath.Dir(change.Path)
|
reader, writer := io.Pipe()
|
||||||
deletions = append(deletions, filepath.Join(dir, ".wh."+base))
|
tw := tar.NewWriter(writer)
|
||||||
}
|
|
||||||
}
|
go func() {
|
||||||
// FIXME: Why do we create whiteout files inside Tar code ?
|
// In general we log errors here but ignore them because
|
||||||
return TarFilter(dir, &TarOptions{
|
// during e.g. a diff operation the container can continue
|
||||||
Compression: Uncompressed,
|
// mutating the filesystem and we can see transient errors
|
||||||
Includes: files,
|
// from this
|
||||||
Recursive: false,
|
for _, change := range changes {
|
||||||
CreateFiles: deletions,
|
if change.Kind == ChangeDelete {
|
||||||
})
|
whiteOutDir := filepath.Dir(change.Path)
|
||||||
|
whiteOutBase := filepath.Base(change.Path)
|
||||||
|
whiteOut := filepath.Join(whiteOutDir, ".wh."+whiteOutBase)
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Name: whiteOut[1:],
|
||||||
|
Size: 0,
|
||||||
|
ModTime: time.Now(),
|
||||||
|
AccessTime: time.Now(),
|
||||||
|
ChangeTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
|
utils.Debugf("Can't write whiteout header: %s\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path := filepath.Join(dir, change.Path)
|
||||||
|
|
||||||
|
var stat syscall.Stat_t
|
||||||
|
if err := syscall.Lstat(path, &stat); err != nil {
|
||||||
|
utils.Debugf("Can't stat source file: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mtim := getLastModification(&stat)
|
||||||
|
atim := getLastAccess(&stat)
|
||||||
|
hdr := &tar.Header{
|
||||||
|
Name: change.Path[1:],
|
||||||
|
Mode: int64(stat.Mode & 07777),
|
||||||
|
Uid: int(stat.Uid),
|
||||||
|
Gid: int(stat.Gid),
|
||||||
|
ModTime: time.Unix(int64(mtim.Sec), int64(mtim.Nsec)),
|
||||||
|
AccessTime: time.Unix(int64(atim.Sec), int64(atim.Nsec)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if stat.Mode&syscall.S_IFDIR == syscall.S_IFDIR {
|
||||||
|
hdr.Typeflag = tar.TypeDir
|
||||||
|
} else if stat.Mode&syscall.S_IFLNK == syscall.S_IFLNK {
|
||||||
|
hdr.Typeflag = tar.TypeSymlink
|
||||||
|
if link, err := os.Readlink(path); err != nil {
|
||||||
|
utils.Debugf("Can't readlink source file: %s\n", err)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
hdr.Linkname = link
|
||||||
|
}
|
||||||
|
} else if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK ||
|
||||||
|
stat.Mode&syscall.S_IFCHR == syscall.S_IFCHR {
|
||||||
|
if stat.Mode&syscall.S_IFBLK == syscall.S_IFBLK {
|
||||||
|
hdr.Typeflag = tar.TypeBlock
|
||||||
|
} else {
|
||||||
|
hdr.Typeflag = tar.TypeChar
|
||||||
|
}
|
||||||
|
hdr.Devmajor = int64(major(uint64(stat.Rdev)))
|
||||||
|
hdr.Devminor = int64(minor(uint64(stat.Rdev)))
|
||||||
|
} else if stat.Mode&syscall.S_IFIFO == syscall.S_IFIFO ||
|
||||||
|
stat.Mode&syscall.S_IFSOCK == syscall.S_IFSOCK {
|
||||||
|
hdr.Typeflag = tar.TypeFifo
|
||||||
|
} else if stat.Mode&syscall.S_IFREG == syscall.S_IFREG {
|
||||||
|
hdr.Typeflag = tar.TypeReg
|
||||||
|
hdr.Size = stat.Size
|
||||||
|
} else {
|
||||||
|
utils.Debugf("Unknown file type: %s\n", path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tw.WriteHeader(hdr); err != nil {
|
||||||
|
utils.Debugf("Can't write tar header: %s\n", err)
|
||||||
|
}
|
||||||
|
if hdr.Typeflag == tar.TypeReg {
|
||||||
|
if file, err := os.Open(path); err != nil {
|
||||||
|
utils.Debugf("Can't open file: %s\n", err)
|
||||||
|
} else {
|
||||||
|
_, err := io.Copy(tw, file)
|
||||||
|
if err != nil {
|
||||||
|
utils.Debugf("Can't copy file: %s\n", err)
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure to check the error on Close.
|
||||||
|
if err := tw.Close(); err != nil {
|
||||||
|
utils.Debugf("Can't close layer: %s\n", err)
|
||||||
|
}
|
||||||
|
writer.Close()
|
||||||
|
}()
|
||||||
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue