github.com/grange74/docker@v1.6.0-rc3/pkg/chrootarchive/archive.go (about)

     1  package chrootarchive
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"syscall"
    12  
    13  	"github.com/docker/docker/pkg/archive"
    14  	"github.com/docker/docker/pkg/reexec"
    15  )
    16  
    17  var chrootArchiver = &archive.Archiver{Untar: Untar}
    18  
    19  func chroot(path string) error {
    20  	if err := syscall.Chroot(path); err != nil {
    21  		return err
    22  	}
    23  	return syscall.Chdir("/")
    24  }
    25  
    26  func untar() {
    27  	runtime.LockOSThread()
    28  	flag.Parse()
    29  
    30  	var options *archive.TarOptions
    31  
    32  	if err := json.Unmarshal([]byte(os.Getenv("OPT")), &options); err != nil {
    33  		fatal(err)
    34  	}
    35  
    36  	if err := chroot(flag.Arg(0)); err != nil {
    37  		fatal(err)
    38  	}
    39  	if err := archive.Unpack(os.Stdin, "/", options); err != nil {
    40  		fatal(err)
    41  	}
    42  	// fully consume stdin in case it is zero padded
    43  	flush(os.Stdin)
    44  	os.Exit(0)
    45  }
    46  
    47  func Untar(tarArchive io.Reader, dest string, options *archive.TarOptions) error {
    48  	if tarArchive == nil {
    49  		return fmt.Errorf("Empty archive")
    50  	}
    51  	if options == nil {
    52  		options = &archive.TarOptions{}
    53  	}
    54  	if options.ExcludePatterns == nil {
    55  		options.ExcludePatterns = []string{}
    56  	}
    57  
    58  	dest = filepath.Clean(dest)
    59  	if _, err := os.Stat(dest); os.IsNotExist(err) {
    60  		if err := os.MkdirAll(dest, 0777); err != nil {
    61  			return err
    62  		}
    63  	}
    64  
    65  	// We can't pass the exclude list directly via cmd line
    66  	// because we easily overrun the shell max argument list length
    67  	// when the full image list is passed (e.g. when this is used
    68  	// by `docker load`). Instead we will add the JSON marshalled
    69  	// and placed in the env, which has significantly larger
    70  	// max size
    71  	data, err := json.Marshal(options)
    72  	if err != nil {
    73  		return fmt.Errorf("Untar json encode: %v", err)
    74  	}
    75  	decompressedArchive, err := archive.DecompressStream(tarArchive)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	defer decompressedArchive.Close()
    80  
    81  	cmd := reexec.Command("docker-untar", dest)
    82  	cmd.Stdin = decompressedArchive
    83  	cmd.Env = append(cmd.Env, fmt.Sprintf("OPT=%s", data))
    84  	out, err := cmd.CombinedOutput()
    85  	if err != nil {
    86  		return fmt.Errorf("Untar %s %s", err, out)
    87  	}
    88  	return nil
    89  }
    90  
    91  func TarUntar(src, dst string) error {
    92  	return chrootArchiver.TarUntar(src, dst)
    93  }
    94  
    95  // CopyWithTar creates a tar archive of filesystem path `src`, and
    96  // unpacks it at filesystem path `dst`.
    97  // The archive is streamed directly with fixed buffering and no
    98  // intermediary disk IO.
    99  func CopyWithTar(src, dst string) error {
   100  	return chrootArchiver.CopyWithTar(src, dst)
   101  }
   102  
   103  // CopyFileWithTar emulates the behavior of the 'cp' command-line
   104  // for a single file. It copies a regular file from path `src` to
   105  // path `dst`, and preserves all its metadata.
   106  //
   107  // If `dst` ends with a trailing slash '/', the final destination path
   108  // will be `dst/base(src)`.
   109  func CopyFileWithTar(src, dst string) (err error) {
   110  	return chrootArchiver.CopyFileWithTar(src, dst)
   111  }
   112  
   113  // UntarPath is a convenience function which looks for an archive
   114  // at filesystem path `src`, and unpacks it at `dst`.
   115  func UntarPath(src, dst string) error {
   116  	return chrootArchiver.UntarPath(src, dst)
   117  }