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 }