archive_unix.go 4.7 KB

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