archive: Extract createTarFile() from ApplyLayer

This way we can reuse it for Untar()

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
Alexander Larsson 2013-12-20 11:30:45 +01:00
parent 07f62f199f
commit 710d5a48fb
2 changed files with 85 additions and 80 deletions

View File

@ -13,6 +13,7 @@ import (
"os/exec"
"path"
"path/filepath"
"syscall"
)
type Archive io.Reader
@ -124,6 +125,84 @@ func (compression *Compression) Extension() string {
return ""
}
func createTarFile(path, extractDir string, hdr *tar.Header, reader *tar.Reader) error {
switch hdr.Typeflag {
case tar.TypeDir:
// Create directory unless it exists as a directory already.
// In that case we just want to merge the two
if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) {
if err := os.Mkdir(path, os.FileMode(hdr.Mode)); err != nil {
return err
}
}
case tar.TypeReg, tar.TypeRegA:
// Source is regular file
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
if err != nil {
return err
}
if _, err := io.Copy(file, reader); err != nil {
file.Close()
return err
}
file.Close()
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
mode := uint32(hdr.Mode & 07777)
switch hdr.Typeflag {
case tar.TypeBlock:
mode |= syscall.S_IFBLK
case tar.TypeChar:
mode |= syscall.S_IFCHR
case tar.TypeFifo:
mode |= syscall.S_IFIFO
}
if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
return err
}
case tar.TypeLink:
if err := os.Link(filepath.Join(extractDir, hdr.Linkname), path); err != nil {
return err
}
case tar.TypeSymlink:
if err := os.Symlink(hdr.Linkname, path); err != nil {
return err
}
default:
return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag)
}
if err := syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
return err
}
// There is no LChmod, so ignore mode for symlink. Also, this
// must happen after chown, as that can modify the file mode
if hdr.Typeflag != tar.TypeSymlink {
if err := syscall.Chmod(path, uint32(hdr.Mode&07777)); err != nil {
return err
}
}
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and
if hdr.Typeflag != tar.TypeSymlink {
if err := syscall.UtimesNano(path, ts); err != nil {
return err
}
} else {
if err := LUtimesNano(path, ts); err != nil {
return err
}
}
return nil
}
// Tar creates an archive from the directory at `path`, and returns it as a
// stream of bytes.
func Tar(path string, compression Compression) (io.Reader, error) {

View File

@ -2,7 +2,6 @@ package archive
import (
"archive/tar"
"github.com/dotcloud/docker/utils"
"io"
"os"
"path/filepath"
@ -89,95 +88,22 @@ func ApplyLayer(dest string, layer Archive) error {
// 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).
hasDir := false
if fi, err := os.Lstat(path); err == nil {
if fi.IsDir() && hdr.Typeflag == tar.TypeDir {
hasDir = true
} else {
if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) {
if err := os.RemoveAll(path); err != nil {
return err
}
}
}
switch hdr.Typeflag {
case tar.TypeDir:
if !hasDir {
err = os.Mkdir(path, os.FileMode(hdr.Mode))
if err != nil {
return err
}
}
dirs = append(dirs, hdr)
case tar.TypeReg, tar.TypeRegA:
// Source is regular file
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode))
if err != nil {
return err
}
if _, err := io.Copy(file, tr); err != nil {
file.Close()
return err
}
file.Close()
case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
mode := uint32(hdr.Mode & 07777)
switch hdr.Typeflag {
case tar.TypeBlock:
mode |= syscall.S_IFBLK
case tar.TypeChar:
mode |= syscall.S_IFCHR
case tar.TypeFifo:
mode |= syscall.S_IFIFO
}
if err := syscall.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor))); err != nil {
return err
}
case tar.TypeLink:
if err := os.Link(filepath.Join(dest, hdr.Linkname), path); err != nil {
return err
}
case tar.TypeSymlink:
if err := os.Symlink(hdr.Linkname, path); err != nil {
return err
}
default:
utils.Debugf("unhandled type %d\n", hdr.Typeflag)
}
if err = syscall.Lchown(path, hdr.Uid, hdr.Gid); err != nil {
if err := createTarFile(path, dest, hdr, tr); err != nil {
return err
}
// There is no LChmod, so ignore mode for symlink. Also, this
// must happen after chown, as that can modify the file mode
if hdr.Typeflag != tar.TypeSymlink {
err = syscall.Chmod(path, uint32(hdr.Mode&07777))
if err != nil {
return err
}
}
// Directories must be handled at the end to avoid further
// file creation in them to modify the mtime
if hdr.Typeflag != tar.TypeDir {
ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)}
// syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and
if hdr.Typeflag != tar.TypeSymlink {
if err := syscall.UtimesNano(path, ts); err != nil {
return err
}
} else {
if err := LUtimesNano(path, ts); 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)
}
}
}