diff_unix.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. "os"
  11. "path/filepath"
  12. "runtime"
  13. "github.com/containerd/containerd/pkg/userns"
  14. "github.com/docker/docker/pkg/archive"
  15. "github.com/docker/docker/pkg/reexec"
  16. "golang.org/x/sys/unix"
  17. )
  18. type applyLayerResponse struct {
  19. LayerSize int64 `json:"layerSize"`
  20. }
  21. // applyLayer is the entry-point for docker-applylayer on re-exec. This is not
  22. // used on Windows as it does not support chroot, hence no point sandboxing
  23. // through chroot and rexec.
  24. func applyLayer() {
  25. var (
  26. tmpDir string
  27. err error
  28. options *archive.TarOptions
  29. )
  30. runtime.LockOSThread()
  31. flag.Parse()
  32. inUserns := userns.RunningInUserNS()
  33. if err := chroot(flag.Arg(0)); err != nil {
  34. fatal(err)
  35. }
  36. // We need to be able to set any perms
  37. oldmask := unix.Umask(0)
  38. defer unix.Umask(oldmask)
  39. if err := json.Unmarshal([]byte(os.Getenv("OPT")), &options); err != nil {
  40. fatal(err)
  41. }
  42. if inUserns {
  43. options.InUserNS = true
  44. }
  45. if tmpDir, err = os.MkdirTemp("/", "temp-docker-extract"); err != nil {
  46. fatal(err)
  47. }
  48. os.Setenv("TMPDIR", tmpDir)
  49. size, err := archive.UnpackLayer("/", os.Stdin, options)
  50. os.RemoveAll(tmpDir)
  51. if err != nil {
  52. fatal(err)
  53. }
  54. encoder := json.NewEncoder(os.Stdout)
  55. if err := encoder.Encode(applyLayerResponse{size}); err != nil {
  56. fatal(fmt.Errorf("unable to encode layerSize JSON: %s", err))
  57. }
  58. if _, err := flush(os.Stdin); err != nil {
  59. fatal(err)
  60. }
  61. os.Exit(0)
  62. }
  63. // applyLayerHandler parses a diff in the standard layer format from `layer`, and
  64. // applies it to the directory `dest`. Returns the size in bytes of the
  65. // contents of the layer.
  66. func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions, decompress bool) (size int64, err error) {
  67. dest = filepath.Clean(dest)
  68. if decompress {
  69. decompressed, err := archive.DecompressStream(layer)
  70. if err != nil {
  71. return 0, err
  72. }
  73. defer decompressed.Close()
  74. layer = decompressed
  75. }
  76. if options == nil {
  77. options = &archive.TarOptions{}
  78. if userns.RunningInUserNS() {
  79. options.InUserNS = true
  80. }
  81. }
  82. if options.ExcludePatterns == nil {
  83. options.ExcludePatterns = []string{}
  84. }
  85. data, err := json.Marshal(options)
  86. if err != nil {
  87. return 0, fmt.Errorf("ApplyLayer json encode: %v", err)
  88. }
  89. cmd := reexec.Command("docker-applyLayer", dest)
  90. cmd.Stdin = layer
  91. cmd.Env = append(cmd.Env, fmt.Sprintf("OPT=%s", data))
  92. outBuf, errBuf := new(bytes.Buffer), new(bytes.Buffer)
  93. cmd.Stdout, cmd.Stderr = outBuf, errBuf
  94. // reexec.Command() sets cmd.SysProcAttr.Pdeathsig on Linux, which
  95. // causes the started process to be signaled when the creating OS thread
  96. // dies. Ensure that the reexec is not prematurely signaled. See
  97. // https://go.dev/issue/27505 for more information.
  98. runtime.LockOSThread()
  99. defer runtime.UnlockOSThread()
  100. if err = cmd.Run(); err != nil {
  101. return 0, fmt.Errorf("ApplyLayer %s stdout: %s stderr: %s", err, outBuf, errBuf)
  102. }
  103. // Stdout should be a valid JSON struct representing an applyLayerResponse.
  104. response := applyLayerResponse{}
  105. decoder := json.NewDecoder(outBuf)
  106. if err = decoder.Decode(&response); err != nil {
  107. return 0, fmt.Errorf("unable to decode ApplyLayer JSON response: %s", err)
  108. }
  109. return response.LayerSize, nil
  110. }