From 710d5a48fb751623fbf77a51b89f2dfbf0edac68 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 20 Dec 2013 11:30:45 +0100 Subject: [PATCH] archive: Extract createTarFile() from ApplyLayer This way we can reuse it for Untar() Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- archive/archive.go | 79 ++++++++++++++++++++++++++++++++++++++++++ archive/diff.go | 86 ++++------------------------------------------ 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/archive/archive.go b/archive/archive.go index 4dd5f006ef..8fa6f55333 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -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) { diff --git a/archive/diff.go b/archive/diff.go index 464d57a742..cdf06dd055 100644 --- a/archive/diff.go +++ b/archive/diff.go @@ -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) } } }