archive_unix_nolinux.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. //go:build unix && !linux
  2. package chrootarchive // import "github.com/docker/docker/pkg/chrootarchive"
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "os"
  9. "syscall"
  10. "github.com/docker/docker/pkg/archive"
  11. "github.com/docker/docker/pkg/reexec"
  12. "github.com/pkg/errors"
  13. "golang.org/x/sys/unix"
  14. )
  15. const (
  16. packCmd = "chrootarchive-pack-in-chroot"
  17. unpackCmd = "chrootarchive-unpack-in-chroot"
  18. unpackLayerCmd = "chrootarchive-unpack-layer-in-chroot"
  19. )
  20. func init() {
  21. reexec.Register(packCmd, reexecMain(packInChroot))
  22. reexec.Register(unpackCmd, reexecMain(unpackInChroot))
  23. reexec.Register(unpackLayerCmd, reexecMain(unpackLayerInChroot))
  24. }
  25. func reexecMain(f func(options archive.TarOptions, args ...string) error) func() {
  26. return func() {
  27. if len(os.Args) < 2 {
  28. fmt.Fprintln(os.Stderr, "root parameter is required")
  29. os.Exit(1)
  30. }
  31. options, err := recvOptions()
  32. root := os.Args[1]
  33. if err != nil {
  34. fmt.Fprintln(os.Stderr, err)
  35. os.Exit(1)
  36. }
  37. if err := syscall.Chroot(root); err != nil {
  38. fmt.Fprintln(
  39. os.Stderr,
  40. os.PathError{Op: "chroot", Path: root, Err: err},
  41. )
  42. os.Exit(2)
  43. }
  44. if err := f(*options, os.Args[2:]...); err != nil {
  45. fmt.Fprintln(os.Stderr, err)
  46. os.Exit(3)
  47. }
  48. }
  49. }
  50. func doUnpack(decompressedArchive io.Reader, relDest, root string, options *archive.TarOptions) error {
  51. optionsR, optionsW, err := os.Pipe()
  52. if err != nil {
  53. return err
  54. }
  55. defer optionsW.Close()
  56. defer optionsR.Close()
  57. stderr := bytes.NewBuffer(nil)
  58. cmd := reexec.Command(unpackCmd, root, relDest)
  59. cmd.Stdin = decompressedArchive
  60. cmd.Stderr = stderr
  61. cmd.ExtraFiles = []*os.File{
  62. optionsR,
  63. }
  64. if err = cmd.Start(); err != nil {
  65. return errors.Wrap(err, "re-exec error")
  66. }
  67. if err = json.NewEncoder(optionsW).Encode(options); err != nil {
  68. return errors.Wrap(err, "tar options encoding failed")
  69. }
  70. if err = cmd.Wait(); err != nil {
  71. return errors.Wrap(err, stderr.String())
  72. }
  73. return nil
  74. }
  75. func doPack(relSrc, root string, options *archive.TarOptions) (io.ReadCloser, error) {
  76. optionsR, optionsW, err := os.Pipe()
  77. if err != nil {
  78. return nil, err
  79. }
  80. defer optionsW.Close()
  81. defer optionsR.Close()
  82. stderr := bytes.NewBuffer(nil)
  83. cmd := reexec.Command(packCmd, root, relSrc)
  84. cmd.ExtraFiles = []*os.File{
  85. optionsR,
  86. }
  87. cmd.Stderr = stderr
  88. stdout, err := cmd.StdoutPipe()
  89. if err != nil {
  90. return nil, err
  91. }
  92. r, w := io.Pipe()
  93. if err = cmd.Start(); err != nil {
  94. return nil, errors.Wrap(err, "re-exec error")
  95. }
  96. go func() {
  97. _, _ = io.Copy(w, stdout)
  98. // Cleanup once stdout pipe is closed.
  99. if err = cmd.Wait(); err != nil {
  100. r.CloseWithError(errors.Wrap(err, stderr.String()))
  101. } else {
  102. r.Close()
  103. }
  104. }()
  105. if err = json.NewEncoder(optionsW).Encode(options); err != nil {
  106. return nil, errors.Wrap(err, "tar options encoding failed")
  107. }
  108. return r, nil
  109. }
  110. func doUnpackLayer(root string, layer io.Reader, options *archive.TarOptions) (int64, error) {
  111. var result int64
  112. optionsR, optionsW, err := os.Pipe()
  113. if err != nil {
  114. return 0, err
  115. }
  116. defer optionsW.Close()
  117. defer optionsR.Close()
  118. buffer := bytes.NewBuffer(nil)
  119. cmd := reexec.Command(unpackLayerCmd, root)
  120. cmd.Stdin = layer
  121. cmd.Stdout = buffer
  122. cmd.Stderr = buffer
  123. cmd.ExtraFiles = []*os.File{
  124. optionsR,
  125. }
  126. if err = cmd.Start(); err != nil {
  127. return 0, errors.Wrap(err, "re-exec error")
  128. }
  129. if err = json.NewEncoder(optionsW).Encode(options); err != nil {
  130. return 0, errors.Wrap(err, "tar options encoding failed")
  131. }
  132. if err = cmd.Wait(); err != nil {
  133. return 0, errors.Wrap(err, buffer.String())
  134. }
  135. if err = json.NewDecoder(buffer).Decode(&result); err != nil {
  136. return 0, errors.Wrap(err, "json decoding error")
  137. }
  138. return result, nil
  139. }
  140. func unpackInChroot(options archive.TarOptions, args ...string) error {
  141. if len(args) < 1 {
  142. return fmt.Errorf("destination parameter is required")
  143. }
  144. relDest := args[0]
  145. return archive.Unpack(os.Stdin, relDest, &options)
  146. }
  147. func packInChroot(options archive.TarOptions, args ...string) error {
  148. if len(args) < 1 {
  149. return fmt.Errorf("source parameter is required")
  150. }
  151. relSrc := args[0]
  152. tb, err := archive.NewTarballer(relSrc, &options)
  153. if err != nil {
  154. return err
  155. }
  156. go tb.Do()
  157. _, err = io.Copy(os.Stdout, tb.Reader())
  158. return err
  159. }
  160. func unpackLayerInChroot(options archive.TarOptions, _args ...string) error {
  161. // We need to be able to set any perms
  162. _ = unix.Umask(0)
  163. size, err := archive.UnpackLayer("/", os.Stdin, &options)
  164. if err != nil {
  165. return err
  166. }
  167. return json.NewEncoder(os.Stdout).Encode(size)
  168. }
  169. func recvOptions() (*archive.TarOptions, error) {
  170. var options archive.TarOptions
  171. optionsPipe := os.NewFile(3, "tar-options")
  172. if optionsPipe == nil {
  173. return nil, fmt.Errorf("could not read tar options from the pipe")
  174. }
  175. defer optionsPipe.Close()
  176. err := json.NewDecoder(optionsPipe).Decode(&options)
  177. if err != nil {
  178. return &options, err
  179. }
  180. return &options, nil
  181. }