// applyLayer is the entry-point for docker-applylayer on re-exec. This is not // used on Windows as it does not support chroot, hence no point sandboxing // through chroot and rexec. func applyLayer() { var ( tmpDir string err error options *archive.TarOptions ) runtime.LockOSThread() flag.Parse() inUserns := rsystem.RunningInUserNS() if err := chroot(flag.Arg(0)); err != nil { fatal(err) } // We need to be able to set any perms oldmask, err := system.Umask(0) defer system.Umask(oldmask) if err != nil { fatal(err) } if err := json.Unmarshal([]byte(os.Getenv("OPT")), &options); err != nil { fatal(err) } if inUserns { options.InUserNS = true } if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil { fatal(err) } os.Setenv("TMPDIR", tmpDir) size, err := archive.UnpackLayer("/", os.Stdin, options) os.RemoveAll(tmpDir) if err != nil { fatal(err) } encoder := json.NewEncoder(os.Stdout) if err := encoder.Encode(applyLayerResponse{size}); err != nil { fatal(fmt.Errorf("unable to encode layerSize JSON: %s", err)) } if _, err := flush(os.Stdin); err != nil { fatal(err) } os.Exit(0) }
func applyLayer() { var ( root = "/" tmpDir = "" err error ) runtime.LockOSThread() flag.Parse() if runtime.GOOS != "windows" { if err := chroot(flag.Arg(0)); err != nil { fatal(err) } // We need to be able to set any perms oldmask, err := system.Umask(0) defer system.Umask(oldmask) if err != nil { fatal(err) } } else { // As Windows does not support chroot or umask, we use the directory // passed in which will be <pathto>\docker-buildnnnnnnnn instead of // the 'chroot-root', "/" root = flag.Arg(0) } if tmpDir, err = ioutil.TempDir(root, "temp-docker-extract"); err != nil { fatal(err) } os.Setenv("TMPDIR", tmpDir) size, err := archive.UnpackLayer(root, os.Stdin) os.RemoveAll(tmpDir) if err != nil { fatal(err) } encoder := json.NewEncoder(os.Stdout) if err := encoder.Encode(applyLayerResponse{size}); err != nil { fatal(fmt.Errorf("unable to encode layerSize JSON: %s", err)) } flush(os.Stdout) flush(os.Stdin) os.Exit(0) }
// ApplyLayer parses a diff in the standard layer format from `layer`, and // applies it to the directory `dest`. func ApplyLayer(dest string, layer ArchiveReader) error { dest = filepath.Clean(dest) // We need to be able to set any perms oldmask, err := system.Umask(0) if err != nil { return err } defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform layer, err = DecompressStream(layer) if err != nil { return err } return UnpackLayer(dest, layer) }
func TestOverlayTarUntar(t *testing.T) { oldmask, err := system.Umask(0) if err != nil { t.Fatal(err) } defer system.Umask(oldmask) src, err := ioutil.TempDir("", "docker-test-overlay-tar-src") if err != nil { t.Fatal(err) } defer os.RemoveAll(src) setupOverlayTestDir(t, src) dst, err := ioutil.TempDir("", "docker-test-overlay-tar-dst") if err != nil { t.Fatal(err) } defer os.RemoveAll(dst) options := &TarOptions{ Compression: Uncompressed, WhiteoutFormat: OverlayWhiteoutFormat, } archive, err := TarWithOptions(src, options) if err != nil { t.Fatal(err) } defer archive.Close() if err := Untar(archive, dst, options); err != nil { t.Fatal(err) } checkFileMode(t, filepath.Join(dst, "d1"), 0700|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d2"), 0750|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d3"), 0700|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d1", "f1"), 0600) checkFileMode(t, filepath.Join(dst, "d2", "f1"), 0660) checkFileMode(t, filepath.Join(dst, "d3", "f1"), os.ModeCharDevice|os.ModeDevice) checkOpaqueness(t, filepath.Join(dst, "d1"), "y") checkOpaqueness(t, filepath.Join(dst, "d2"), "y") checkOpaqueness(t, filepath.Join(dst, "d3"), "") checkOverlayWhiteout(t, filepath.Join(dst, "d3", "f1")) }
func TestOverlayTarAUFSUntar(t *testing.T) { oldmask, err := system.Umask(0) if err != nil { t.Fatal(err) } defer system.Umask(oldmask) src, err := ioutil.TempDir("", "docker-test-overlay-tar-src") if err != nil { t.Fatal(err) } defer os.RemoveAll(src) setupOverlayTestDir(t, src) dst, err := ioutil.TempDir("", "docker-test-overlay-tar-dst") if err != nil { t.Fatal(err) } defer os.RemoveAll(dst) archive, err := TarWithOptions(src, &TarOptions{ Compression: Uncompressed, WhiteoutFormat: OverlayWhiteoutFormat, }) if err != nil { t.Fatal(err) } defer archive.Close() if err := Untar(archive, dst, &TarOptions{ Compression: Uncompressed, WhiteoutFormat: AUFSWhiteoutFormat, }); err != nil { t.Fatal(err) } checkFileMode(t, filepath.Join(dst, "d1"), 0700|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d1", WhiteoutOpaqueDir), 0700) checkFileMode(t, filepath.Join(dst, "d2"), 0750|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d2", WhiteoutOpaqueDir), 0750) checkFileMode(t, filepath.Join(dst, "d3"), 0700|os.ModeDir) checkFileMode(t, filepath.Join(dst, "d1", "f1"), 0600) checkFileMode(t, filepath.Join(dst, "d2", "f1"), 0660) checkFileMode(t, filepath.Join(dst, "d3", WhiteoutPrefix+"f1"), 0600) }
// do the bulk load of ApplyLayer, but allow for not calling DecompressStream func applyLayerHandler(dest string, layer Reader, decompress bool) (int64, error) { dest = filepath.Clean(dest) // We need to be able to set any perms oldmask, err := system.Umask(0) if err != nil { return 0, err } defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform if decompress { layer, err = DecompressStream(layer) if err != nil { return 0, err } } return UnpackLayer(dest, layer) }
// applyLayer is the entry-point for docker-applylayer on re-exec. This is not // used on Windows as it does not support chroot, hence no point sandboxing // through chroot and rexec. func applyLayer() { var ( tmpDir = "" err error ) runtime.LockOSThread() flag.Parse() if err := chroot(flag.Arg(0)); err != nil { fatal(err) } // We need to be able to set any perms oldmask, err := system.Umask(0) defer system.Umask(oldmask) if err != nil { fatal(err) } if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil { fatal(err) } os.Setenv("TMPDIR", tmpDir) size, err := archive.UnpackLayer("/", os.Stdin) os.RemoveAll(tmpDir) if err != nil { fatal(err) } encoder := json.NewEncoder(os.Stdout) if err := encoder.Encode(applyLayerResponse{size}); err != nil { fatal(fmt.Errorf("unable to encode layerSize JSON: %s", err)) } flush(os.Stdout) flush(os.Stdin) os.Exit(0) }
// ApplyLayer parses a diff in the standard layer format from `layer`, and // applies it to the directory `dest`. func ApplyLayer(dest string, layer ArchiveReader) error { dest = filepath.Clean(dest) // We need to be able to set any perms oldmask, err := system.Umask(0) if err != nil { return err } defer system.Umask(oldmask) // ignore err, ErrNotSupportedPlatform layer, err = DecompressStream(layer) if err != nil { return err } tr := tar.NewReader(layer) trBuf := pools.BufioReader32KPool.Get(tr) defer pools.BufioReader32KPool.Put(trBuf) var dirs []*tar.Header aufsTempdir := "" aufsHardlinks := make(map[string]*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, 0600) if err != nil { return err } } } // Skip AUFS metadata dirs if strings.HasPrefix(hdr.Name, ".wh..wh.") { // Regular files inside /.wh..wh.plnk can be used as hardlink targets // We don't want this directory, but we need the files in them so that // such hardlinks can be resolved. if strings.HasPrefix(hdr.Name, ".wh..wh.plnk") && hdr.Typeflag == tar.TypeReg { basename := filepath.Base(hdr.Name) aufsHardlinks[basename] = hdr if aufsTempdir == "" { if aufsTempdir, err = ioutil.TempDir("", "dockerplnk"); err != nil { return err } defer os.RemoveAll(aufsTempdir) } if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true); err != nil { return err } } continue } path := filepath.Join(dest, hdr.Name) base := filepath.Base(path) // Prevent symlink breakout if !strings.HasPrefix(path, dest) { return breakoutError(fmt.Errorf("%q is outside of %q", path, dest)) } 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 } } } trBuf.Reset(tr) srcData := io.Reader(trBuf) srcHdr := hdr // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so // we manually retarget these into the temporary files we extracted them into if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") { linkBasename := filepath.Base(hdr.Linkname) srcHdr = aufsHardlinks[linkBasename] if srcHdr == nil { return fmt.Errorf("Invalid aufs hardlink") } tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) if err != nil { return err } defer tmpFile.Close() srcData = tmpFile } if err := createTarFile(path, dest, srcHdr, srcData, true); 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 }