gopkg.in/docker/docker.v23@v23.0.11/pkg/chrootarchive/archive_unix.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive" 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "flag" 10 "fmt" 11 "io" 12 "net" 13 "os" 14 "os/user" 15 "path/filepath" 16 "runtime" 17 "strings" 18 19 "github.com/docker/docker/pkg/archive" 20 "github.com/docker/docker/pkg/reexec" 21 "github.com/pkg/errors" 22 ) 23 24 func init() { 25 // initialize nss libraries in Glibc so that the dynamic libraries are loaded in the host 26 // environment not in the chroot from untrusted files. 27 _, _ = user.Lookup("docker") 28 _, _ = net.LookupHost("localhost") 29 } 30 31 // untar is the entry-point for docker-untar on re-exec. This is not used on 32 // Windows as it does not support chroot, hence no point sandboxing through 33 // chroot and rexec. 34 func untar() { 35 runtime.LockOSThread() 36 flag.Parse() 37 38 var options archive.TarOptions 39 40 // read the options from the pipe "ExtraFiles" 41 if err := json.NewDecoder(os.NewFile(3, "options")).Decode(&options); err != nil { 42 fatal(err) 43 } 44 45 dst := flag.Arg(0) 46 var root string 47 if len(flag.Args()) > 1 { 48 root = flag.Arg(1) 49 } 50 51 if root == "" { 52 root = dst 53 } 54 55 if err := chroot(root); err != nil { 56 fatal(err) 57 } 58 59 if err := archive.Unpack(os.Stdin, dst, &options); err != nil { 60 fatal(err) 61 } 62 // fully consume stdin in case it is zero padded 63 if _, err := flush(os.Stdin); err != nil { 64 fatal(err) 65 } 66 67 os.Exit(0) 68 } 69 70 func invokeUnpack(decompressedArchive io.Reader, dest string, options *archive.TarOptions, root string) error { 71 if root == "" { 72 return errors.New("must specify a root to chroot to") 73 } 74 75 // We can't pass a potentially large exclude list directly via cmd line 76 // because we easily overrun the kernel's max argument/environment size 77 // when the full image list is passed (e.g. when this is used by 78 // `docker load`). We will marshall the options via a pipe to the 79 // child 80 r, w, err := os.Pipe() 81 if err != nil { 82 return fmt.Errorf("Untar pipe failure: %v", err) 83 } 84 85 if root != "" { 86 relDest, err := filepath.Rel(root, dest) 87 if err != nil { 88 return err 89 } 90 if relDest == "." { 91 relDest = "/" 92 } 93 if relDest[0] != '/' { 94 relDest = "/" + relDest 95 } 96 dest = relDest 97 } 98 99 cmd := reexec.Command("docker-untar", dest, root) 100 cmd.Stdin = decompressedArchive 101 102 cmd.ExtraFiles = append(cmd.ExtraFiles, r) 103 output := bytes.NewBuffer(nil) 104 cmd.Stdout = output 105 cmd.Stderr = output 106 107 if err := cmd.Start(); err != nil { 108 w.Close() 109 return fmt.Errorf("Untar error on re-exec cmd: %v", err) 110 } 111 112 // write the options to the pipe for the untar exec to read 113 if err := json.NewEncoder(w).Encode(options); err != nil { 114 w.Close() 115 return fmt.Errorf("Untar json encode to pipe failed: %v", err) 116 } 117 w.Close() 118 119 if err := cmd.Wait(); err != nil { 120 // when `xz -d -c -q | docker-untar ...` failed on docker-untar side, 121 // we need to exhaust `xz`'s output, otherwise the `xz` side will be 122 // pending on write pipe forever 123 io.Copy(io.Discard, decompressedArchive) 124 125 return fmt.Errorf("Error processing tar file(%v): %s", err, output) 126 } 127 return nil 128 } 129 130 func tar() { 131 runtime.LockOSThread() 132 flag.Parse() 133 134 src := flag.Arg(0) 135 var root string 136 if len(flag.Args()) > 1 { 137 root = flag.Arg(1) 138 } 139 140 if root == "" { 141 root = src 142 } 143 144 if err := realChroot(root); err != nil { 145 fatal(err) 146 } 147 148 var options archive.TarOptions 149 if err := json.NewDecoder(os.Stdin).Decode(&options); err != nil { 150 fatal(err) 151 } 152 153 rdr, err := archive.TarWithOptions(src, &options) 154 if err != nil { 155 fatal(err) 156 } 157 defer rdr.Close() 158 159 if _, err := io.Copy(os.Stdout, rdr); err != nil { 160 fatal(err) 161 } 162 163 os.Exit(0) 164 } 165 166 func invokePack(srcPath string, options *archive.TarOptions, root string) (io.ReadCloser, error) { 167 if root == "" { 168 return nil, errors.New("root path must not be empty") 169 } 170 171 relSrc, err := filepath.Rel(root, srcPath) 172 if err != nil { 173 return nil, err 174 } 175 if relSrc == "." { 176 relSrc = "/" 177 } 178 if relSrc[0] != '/' { 179 relSrc = "/" + relSrc 180 } 181 182 // make sure we didn't trim a trailing slash with the call to `Rel` 183 if strings.HasSuffix(srcPath, "/") && !strings.HasSuffix(relSrc, "/") { 184 relSrc += "/" 185 } 186 187 cmd := reexec.Command("docker-tar", relSrc, root) 188 189 errBuff := bytes.NewBuffer(nil) 190 cmd.Stderr = errBuff 191 192 tarR, tarW := io.Pipe() 193 cmd.Stdout = tarW 194 195 stdin, err := cmd.StdinPipe() 196 if err != nil { 197 return nil, errors.Wrap(err, "error getting options pipe for tar process") 198 } 199 200 if err := cmd.Start(); err != nil { 201 return nil, errors.Wrap(err, "tar error on re-exec cmd") 202 } 203 204 go func() { 205 err := cmd.Wait() 206 err = errors.Wrapf(err, "error processing tar file: %s", errBuff) 207 tarW.CloseWithError(err) 208 }() 209 210 if err := json.NewEncoder(stdin).Encode(options); err != nil { 211 stdin.Close() 212 return nil, errors.Wrap(err, "tar json encode to pipe failed") 213 } 214 stdin.Close() 215 216 return tarR, nil 217 }