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