github.com/rish1988/moby@v25.0.2+incompatible/pkg/chrootarchive/archive_unix_nolinux.go (about)

     1  //go:build unix && !linux
     2  
     3  package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"syscall"
    12  
    13  	"github.com/docker/docker/pkg/archive"
    14  	"github.com/docker/docker/pkg/reexec"
    15  	"github.com/pkg/errors"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  const (
    20  	packCmd        = "chrootarchive-pack-in-chroot"
    21  	unpackCmd      = "chrootarchive-unpack-in-chroot"
    22  	unpackLayerCmd = "chrootarchive-unpack-layer-in-chroot"
    23  )
    24  
    25  func init() {
    26  	reexec.Register(packCmd, reexecMain(packInChroot))
    27  	reexec.Register(unpackCmd, reexecMain(unpackInChroot))
    28  	reexec.Register(unpackLayerCmd, reexecMain(unpackLayerInChroot))
    29  }
    30  
    31  func reexecMain(f func(options archive.TarOptions, args ...string) error) func() {
    32  	return func() {
    33  		if len(os.Args) < 2 {
    34  			fmt.Fprintln(os.Stderr, "root parameter is required")
    35  			os.Exit(1)
    36  		}
    37  
    38  		options, err := recvOptions()
    39  		root := os.Args[1]
    40  
    41  		if err != nil {
    42  			fmt.Fprintln(os.Stderr, err)
    43  			os.Exit(1)
    44  		}
    45  
    46  		if err := syscall.Chroot(root); err != nil {
    47  			fmt.Fprintln(
    48  				os.Stderr,
    49  				os.PathError{Op: "chroot", Path: root, Err: err},
    50  			)
    51  			os.Exit(2)
    52  		}
    53  
    54  		if err := f(*options, os.Args[2:]...); err != nil {
    55  			fmt.Fprintln(os.Stderr, err)
    56  			os.Exit(3)
    57  		}
    58  	}
    59  }
    60  
    61  func doUnpack(decompressedArchive io.Reader, relDest, root string, options *archive.TarOptions) error {
    62  	optionsR, optionsW, err := os.Pipe()
    63  	if err != nil {
    64  		return err
    65  	}
    66  	defer optionsW.Close()
    67  	defer optionsR.Close()
    68  
    69  	stderr := bytes.NewBuffer(nil)
    70  
    71  	cmd := reexec.Command(unpackCmd, root, relDest)
    72  	cmd.Stdin = decompressedArchive
    73  	cmd.Stderr = stderr
    74  	cmd.ExtraFiles = []*os.File{
    75  		optionsR,
    76  	}
    77  
    78  	if err = cmd.Start(); err != nil {
    79  		return errors.Wrap(err, "re-exec error")
    80  	}
    81  
    82  	if err = json.NewEncoder(optionsW).Encode(options); err != nil {
    83  		return errors.Wrap(err, "tar options encoding failed")
    84  	}
    85  
    86  	if err = cmd.Wait(); err != nil {
    87  		return errors.Wrap(err, stderr.String())
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func doPack(relSrc, root string, options *archive.TarOptions) (io.ReadCloser, error) {
    94  	optionsR, optionsW, err := os.Pipe()
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	defer optionsW.Close()
    99  	defer optionsR.Close()
   100  
   101  	stderr := bytes.NewBuffer(nil)
   102  	cmd := reexec.Command(packCmd, root, relSrc)
   103  	cmd.ExtraFiles = []*os.File{
   104  		optionsR,
   105  	}
   106  	cmd.Stderr = stderr
   107  	stdout, err := cmd.StdoutPipe()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	r, w := io.Pipe()
   113  
   114  	if err = cmd.Start(); err != nil {
   115  		return nil, errors.Wrap(err, "re-exec error")
   116  	}
   117  
   118  	go func() {
   119  		_, _ = io.Copy(w, stdout)
   120  		// Cleanup once stdout pipe is closed.
   121  		if err = cmd.Wait(); err != nil {
   122  			r.CloseWithError(errors.Wrap(err, stderr.String()))
   123  		} else {
   124  			r.Close()
   125  		}
   126  	}()
   127  
   128  	if err = json.NewEncoder(optionsW).Encode(options); err != nil {
   129  		return nil, errors.Wrap(err, "tar options encoding failed")
   130  	}
   131  
   132  	return r, nil
   133  }
   134  
   135  func doUnpackLayer(root string, layer io.Reader, options *archive.TarOptions) (int64, error) {
   136  	var result int64
   137  	optionsR, optionsW, err := os.Pipe()
   138  	if err != nil {
   139  		return 0, err
   140  	}
   141  	defer optionsW.Close()
   142  	defer optionsR.Close()
   143  	buffer := bytes.NewBuffer(nil)
   144  
   145  	cmd := reexec.Command(unpackLayerCmd, root)
   146  	cmd.Stdin = layer
   147  	cmd.Stdout = buffer
   148  	cmd.Stderr = buffer
   149  	cmd.ExtraFiles = []*os.File{
   150  		optionsR,
   151  	}
   152  
   153  	if err = cmd.Start(); err != nil {
   154  		return 0, errors.Wrap(err, "re-exec error")
   155  	}
   156  
   157  	if err = json.NewEncoder(optionsW).Encode(options); err != nil {
   158  		return 0, errors.Wrap(err, "tar options encoding failed")
   159  	}
   160  
   161  	if err = cmd.Wait(); err != nil {
   162  		return 0, errors.Wrap(err, buffer.String())
   163  	}
   164  
   165  	if err = json.NewDecoder(buffer).Decode(&result); err != nil {
   166  		return 0, errors.Wrap(err, "json decoding error")
   167  	}
   168  
   169  	return result, nil
   170  }
   171  
   172  func unpackInChroot(options archive.TarOptions, args ...string) error {
   173  	if len(args) < 1 {
   174  		return fmt.Errorf("destination parameter is required")
   175  	}
   176  
   177  	relDest := args[0]
   178  
   179  	return archive.Unpack(os.Stdin, relDest, &options)
   180  }
   181  
   182  func packInChroot(options archive.TarOptions, args ...string) error {
   183  	if len(args) < 1 {
   184  		return fmt.Errorf("source parameter is required")
   185  	}
   186  
   187  	relSrc := args[0]
   188  
   189  	tb, err := archive.NewTarballer(relSrc, &options)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	go tb.Do()
   195  
   196  	_, err = io.Copy(os.Stdout, tb.Reader())
   197  
   198  	return err
   199  }
   200  
   201  func unpackLayerInChroot(options archive.TarOptions, _args ...string) error {
   202  	// We need to be able to set any perms
   203  	_ = unix.Umask(0)
   204  
   205  	size, err := archive.UnpackLayer("/", os.Stdin, &options)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	return json.NewEncoder(os.Stdout).Encode(size)
   211  }
   212  
   213  func recvOptions() (*archive.TarOptions, error) {
   214  	var options archive.TarOptions
   215  	optionsPipe := os.NewFile(3, "tar-options")
   216  	if optionsPipe == nil {
   217  		return nil, fmt.Errorf("could not read tar options from the pipe")
   218  	}
   219  	defer optionsPipe.Close()
   220  	err := json.NewDecoder(optionsPipe).Decode(&options)
   221  	if err != nil {
   222  		return &options, err
   223  	}
   224  
   225  	return &options, nil
   226  }