mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
710d5a48fb
This way we can reuse it for Untar() Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
120 lines
3.1 KiB
Go
120 lines
3.1 KiB
Go
package archive
|
|
|
|
import (
|
|
"archive/tar"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
// Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes.
|
|
// They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major,
|
|
// then the top 12 bits of the minor
|
|
func mkdev(major int64, minor int64) uint32 {
|
|
return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
|
|
}
|
|
func timeToTimespec(time time.Time) (ts syscall.Timespec) {
|
|
if time.IsZero() {
|
|
// Return UTIME_OMIT special value
|
|
ts.Sec = 0
|
|
ts.Nsec = ((1 << 30) - 2)
|
|
return
|
|
}
|
|
return syscall.NsecToTimespec(time.UnixNano())
|
|
}
|
|
|
|
// ApplyLayer parses a diff in the standard layer format from `layer`, and
|
|
// applies it to the directory `dest`.
|
|
func ApplyLayer(dest string, layer Archive) error {
|
|
// We need to be able to set any perms
|
|
oldmask := syscall.Umask(0)
|
|
defer syscall.Umask(oldmask)
|
|
|
|
layer, err := DecompressStream(layer)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tr := tar.NewReader(layer)
|
|
|
|
var dirs []*tar.Header
|
|
|
|
// Iterate through the files in the archive.
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if err == io.EOF {
|
|
// end of tar archive
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Normalize name, for safety and for a simple is-root check
|
|
hdr.Name = filepath.Clean(hdr.Name)
|
|
|
|
if !strings.HasSuffix(hdr.Name, "/") {
|
|
// Not the root directory, ensure that the parent directory exists.
|
|
// This happened in some tests where an image had a tarfile without any
|
|
// parent directories.
|
|
parent := filepath.Dir(hdr.Name)
|
|
parentPath := filepath.Join(dest, parent)
|
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
|
err = os.MkdirAll(parentPath, 600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Skip AUFS metadata dirs
|
|
if strings.HasPrefix(hdr.Name, ".wh..wh.") {
|
|
continue
|
|
}
|
|
|
|
path := filepath.Join(dest, hdr.Name)
|
|
base := filepath.Base(path)
|
|
if strings.HasPrefix(base, ".wh.") {
|
|
originalBase := base[len(".wh."):]
|
|
originalPath := filepath.Join(filepath.Dir(path), originalBase)
|
|
if err := os.RemoveAll(originalPath); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// If path exits we almost always just want to remove and replace it.
|
|
// The only exception is when it is a directory *and* the file from
|
|
// the layer is also a directory. Then we want to merge them (i.e.
|
|
// just apply the metadata from the layer).
|
|
if fi, err := os.Lstat(path); err == nil {
|
|
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
|
|
if err := os.RemoveAll(path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := createTarFile(path, dest, hdr, tr); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Directory mtimes must be handled at the end to avoid further
|
|
// file creation in them to modify the directory mtime
|
|
if hdr.Typeflag == tar.TypeDir {
|
|
dirs = append(dirs, hdr)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, hdr := range dirs {
|
|
path := filepath.Join(dest, hdr.Name)
|
|
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
|
|
if err := syscall.UtimesNano(path, ts); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|