mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Don't shell out to tar for ExportChanges
This changes ExportChanges to use the go tar support so we can directly create tar layer files. This has several advantages: * We don't have to create the whiteout files on disk to get them added to the layer * We can later guarantee specific features (such as xattrs) being supported by the tar implementation. Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
parent
07f62f199f
commit
d54ce8087a
1 changed files with 108 additions and 20 deletions
|
@ -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(mtim.Sec, mtim.Nsec),
|
||||||
|
AccessTime: time.Unix(atim.Sec, 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(stat.Rdev))
|
||||||
|
hdr.Devminor = int64(minor(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…
Add table
Reference in a new issue