gopkg.in/docker/docker.v23@v23.0.11/pkg/chrootarchive/diff_unix.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"flag"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"runtime"
    15  
    16  	"github.com/containerd/containerd/pkg/userns"
    17  	"github.com/docker/docker/pkg/archive"
    18  	"github.com/docker/docker/pkg/reexec"
    19  	"golang.org/x/sys/unix"
    20  )
    21  
    22  type applyLayerResponse struct {
    23  	LayerSize int64 `json:"layerSize"`
    24  }
    25  
    26  // applyLayer is the entry-point for docker-applylayer on re-exec. This is not
    27  // used on Windows as it does not support chroot, hence no point sandboxing
    28  // through chroot and rexec.
    29  func applyLayer() {
    30  
    31  	var (
    32  		tmpDir  string
    33  		err     error
    34  		options *archive.TarOptions
    35  	)
    36  	runtime.LockOSThread()
    37  	flag.Parse()
    38  
    39  	inUserns := userns.RunningInUserNS()
    40  	if err := chroot(flag.Arg(0)); err != nil {
    41  		fatal(err)
    42  	}
    43  
    44  	// We need to be able to set any perms
    45  	oldmask := unix.Umask(0)
    46  	defer unix.Umask(oldmask)
    47  
    48  	if err := json.Unmarshal([]byte(os.Getenv("OPT")), &options); err != nil {
    49  		fatal(err)
    50  	}
    51  
    52  	if inUserns {
    53  		options.InUserNS = true
    54  	}
    55  
    56  	if tmpDir, err = os.MkdirTemp("/", "temp-docker-extract"); err != nil {
    57  		fatal(err)
    58  	}
    59  
    60  	os.Setenv("TMPDIR", tmpDir)
    61  	size, err := archive.UnpackLayer("/", os.Stdin, options)
    62  	os.RemoveAll(tmpDir)
    63  	if err != nil {
    64  		fatal(err)
    65  	}
    66  
    67  	encoder := json.NewEncoder(os.Stdout)
    68  	if err := encoder.Encode(applyLayerResponse{size}); err != nil {
    69  		fatal(fmt.Errorf("unable to encode layerSize JSON: %s", err))
    70  	}
    71  
    72  	if _, err := flush(os.Stdin); err != nil {
    73  		fatal(err)
    74  	}
    75  
    76  	os.Exit(0)
    77  }
    78  
    79  // applyLayerHandler parses a diff in the standard layer format from `layer`, and
    80  // applies it to the directory `dest`. Returns the size in bytes of the
    81  // contents of the layer.
    82  func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, err error) {
    83  	dest = filepath.Clean(dest)
    84  	if decompress {
    85  		decompressed, err := archive.DecompressStream(layer)
    86  		if err != nil {
    87  			return 0, err
    88  		}
    89  		defer decompressed.Close()
    90  
    91  		layer = decompressed
    92  	}
    93  	if options == nil {
    94  		options = &archive.TarOptions{}
    95  		if userns.RunningInUserNS() {
    96  			options.InUserNS = true
    97  		}
    98  	}
    99  	if options.ExcludePatterns == nil {
   100  		options.ExcludePatterns = []string{}
   101  	}
   102  
   103  	data, err := json.Marshal(options)
   104  	if err != nil {
   105  		return 0, fmt.Errorf("ApplyLayer json encode: %v", err)
   106  	}
   107  
   108  	cmd := reexec.Command("docker-applyLayer", dest)
   109  	cmd.Stdin = layer
   110  	cmd.Env = append(cmd.Env, fmt.Sprintf("OPT=%s", data))
   111  
   112  	outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
   113  	cmd.Stdout, cmd.Stderr = outBuf, errBuf
   114  
   115  	if err = cmd.Run(); err != nil {
   116  		return 0, fmt.Errorf("ApplyLayer %s stdout: %s stderr: %s", err, outBuf, errBuf)
   117  	}
   118  
   119  	// Stdout should be a valid JSON struct representing an applyLayerResponse.
   120  	response := applyLayerResponse{}
   121  	decoder := json.NewDecoder(outBuf)
   122  	if err = decoder.Decode(&response); err != nil {
   123  		return 0, fmt.Errorf("unable to decode ApplyLayer JSON response: %s", err)
   124  	}
   125  
   126  	return response.LayerSize, nil
   127  }