62f648b0 |
// +build !windows
|
4f0d95fa |
package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" |
62f648b0 |
import ( |
3c177dc8 |
"bytes"
"encoding/json"
"flag"
"fmt"
"io" |
b48f4bf5 |
"io/ioutil" |
3c177dc8 |
"os" |
d089b639 |
"path/filepath" |
3c177dc8 |
"runtime" |
3029e765 |
"strings" |
3c177dc8 |
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/reexec" |
3029e765 |
"github.com/pkg/errors" |
62f648b0 |
)
|
3c177dc8 |
// untar is the entry-point for docker-untar on re-exec. This is not used on
// Windows as it does not support chroot, hence no point sandboxing through
// chroot and rexec.
func untar() {
runtime.LockOSThread()
flag.Parse()
|
3029e765 |
var options archive.TarOptions |
3c177dc8 |
|
9de5d3da |
// read the options from the pipe "ExtraFiles" |
3c177dc8 |
if err := json.NewDecoder(os.NewFile(3, "options")).Decode(&options); err != nil {
fatal(err)
}
|
d089b639 |
dst := flag.Arg(0)
var root string
if len(flag.Args()) > 1 {
root = flag.Arg(1)
}
if root == "" {
root = dst
}
if err := chroot(root); err != nil { |
3c177dc8 |
fatal(err)
}
|
3029e765 |
if err := archive.Unpack(os.Stdin, dst, &options); err != nil { |
3c177dc8 |
fatal(err)
}
// fully consume stdin in case it is zero padded |
a9c61691 |
if _, err := flush(os.Stdin); err != nil {
fatal(err)
}
|
3c177dc8 |
os.Exit(0)
}
|
d089b639 |
func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions, root string) error { |
3029e765 |
if root == "" {
return errors.New("must specify a root to chroot to")
} |
3c177dc8 |
// We can't pass a potentially large exclude list directly via cmd line
// because we easily overrun the kernel's max argument/environment size
// when the full image list is passed (e.g. when this is used by
// `docker load`). We will marshall the options via a pipe to the
// child
r, w, err := os.Pipe()
if err != nil {
return fmt.Errorf("Untar pipe failure: %v", err)
}
|
d089b639 |
if root != "" {
relDest, err := filepath.Rel(root, dest)
if err != nil {
return err
}
if relDest == "." {
relDest = "/"
}
if relDest[0] != '/' {
relDest = "/" + relDest
}
dest = relDest
}
cmd := reexec.Command("docker-untar", dest, root) |
3c177dc8 |
cmd.Stdin = decompressedArchive
cmd.ExtraFiles = append(cmd.ExtraFiles, r)
output := bytes.NewBuffer(nil)
cmd.Stdout = output
cmd.Stderr = output
if err := cmd.Start(); err != nil { |
f5f8f008 |
w.Close() |
3c177dc8 |
return fmt.Errorf("Untar error on re-exec cmd: %v", err)
} |
d089b639 |
|
9de5d3da |
// write the options to the pipe for the untar exec to read |
3c177dc8 |
if err := json.NewEncoder(w).Encode(options); err != nil { |
f5f8f008 |
w.Close() |
3c177dc8 |
return fmt.Errorf("Untar json encode to pipe failed: %v", err)
}
w.Close()
if err := cmd.Wait(); err != nil { |
b48f4bf5 |
// when `xz -d -c -q | docker-untar ...` failed on docker-untar side,
// we need to exhaust `xz`'s output, otherwise the `xz` side will be
// pending on write pipe forever
io.Copy(ioutil.Discard, decompressedArchive)
|
ac043c7d |
return fmt.Errorf("Error processing tar file(%v): %s", err, output) |
3c177dc8 |
}
return nil
} |
3029e765 |
func tar() {
runtime.LockOSThread()
flag.Parse()
src := flag.Arg(0)
var root string
if len(flag.Args()) > 1 {
root = flag.Arg(1)
}
if root == "" {
root = src
}
if err := realChroot(root); err != nil {
fatal(err)
}
var options archive.TarOptions
if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil {
fatal(err)
}
rdr, err := archive.TarWithOptions(src, &options)
if err != nil {
fatal(err)
}
defer rdr.Close()
if _, err := io.Copy(os.Stdout, rdr); err != nil {
fatal(err)
}
os.Exit(0)
}
func invokePack(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) {
if root == "" {
return nil, errors.New("root path must not be empty")
}
relSrc, err := filepath.Rel(root, srcPath)
if err != nil {
return nil, err
}
if relSrc == "." {
relSrc = "/"
}
if relSrc[0] != '/' {
relSrc = "/" + relSrc
}
// make sure we didn't trim a trailing slash with the call to `Rel`
if strings.HasSuffix(srcPath, "/") && !strings.HasSuffix(relSrc, "/") {
relSrc += "/"
}
cmd := reexec.Command("docker-tar", relSrc, root)
errBuff := bytes.NewBuffer(nil)
cmd.Stderr = errBuff
tarR, tarW := io.Pipe()
cmd.Stdout = tarW
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, errors.Wrap(err, "error getting options pipe for tar process")
}
if err := cmd.Start(); err != nil {
return nil, errors.Wrap(err, "tar error on re-exec cmd")
}
go func() {
err := cmd.Wait()
err = errors.Wrapf(err, "error processing tar file: %s", errBuff)
tarW.CloseWithError(err)
}()
if err := json.NewEncoder(stdin).Encode(options); err != nil {
stdin.Close()
return nil, errors.Wrap(err, "tar json encode to pipe failed")
}
stdin.Close()
return tarR, nil
} |