copy.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package container // import "github.com/docker/docker/api/server/router/container"
  2. import (
  3. "compress/flate"
  4. "compress/gzip"
  5. "context"
  6. "encoding/base64"
  7. "encoding/json"
  8. "errors"
  9. "io"
  10. "net/http"
  11. "github.com/docker/docker/api/server/httputils"
  12. "github.com/docker/docker/api/types"
  13. "github.com/docker/docker/api/types/versions"
  14. "github.com/docker/docker/errdefs"
  15. gddohttputil "github.com/golang/gddo/httputil"
  16. )
  17. type pathError struct{}
  18. func (pathError) Error() string {
  19. return "Path cannot be empty"
  20. }
  21. func (pathError) InvalidParameter() {}
  22. // postContainersCopy is deprecated in favor of getContainersArchive.
  23. func (s *containerRouter) postContainersCopy(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  24. // Deprecated since 1.8, Errors out since 1.12
  25. version := httputils.VersionFromContext(ctx)
  26. if versions.GreaterThanOrEqualTo(version, "1.24") {
  27. w.WriteHeader(http.StatusNotFound)
  28. return nil
  29. }
  30. if err := httputils.CheckForJSON(r); err != nil {
  31. return err
  32. }
  33. cfg := types.CopyConfig{}
  34. if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
  35. if err == io.EOF {
  36. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  37. }
  38. return errdefs.InvalidParameter(err)
  39. }
  40. if cfg.Resource == "" {
  41. return pathError{}
  42. }
  43. data, err := s.backend.ContainerCopy(vars["name"], cfg.Resource)
  44. if err != nil {
  45. return err
  46. }
  47. defer data.Close()
  48. w.Header().Set("Content-Type", "application/x-tar")
  49. _, err = io.Copy(w, data)
  50. return err
  51. }
  52. // // Encode the stat to JSON, base64 encode, and place in a header.
  53. func setContainerPathStatHeader(stat *types.ContainerPathStat, header http.Header) error {
  54. statJSON, err := json.Marshal(stat)
  55. if err != nil {
  56. return err
  57. }
  58. header.Set(
  59. "X-Docker-Container-Path-Stat",
  60. base64.StdEncoding.EncodeToString(statJSON),
  61. )
  62. return nil
  63. }
  64. func (s *containerRouter) headContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  65. v, err := httputils.ArchiveFormValues(r, vars)
  66. if err != nil {
  67. return err
  68. }
  69. stat, err := s.backend.ContainerStatPath(v.Name, v.Path)
  70. if err != nil {
  71. return err
  72. }
  73. return setContainerPathStatHeader(stat, w.Header())
  74. }
  75. func writeCompressedResponse(w http.ResponseWriter, r *http.Request, body io.Reader) error {
  76. var cw io.Writer
  77. switch gddohttputil.NegotiateContentEncoding(r, []string{"gzip", "deflate"}) {
  78. case "gzip":
  79. gw := gzip.NewWriter(w)
  80. defer gw.Close()
  81. cw = gw
  82. w.Header().Set("Content-Encoding", "gzip")
  83. case "deflate":
  84. fw, err := flate.NewWriter(w, flate.DefaultCompression)
  85. if err != nil {
  86. return err
  87. }
  88. defer fw.Close()
  89. cw = fw
  90. w.Header().Set("Content-Encoding", "deflate")
  91. default:
  92. cw = w
  93. }
  94. _, err := io.Copy(cw, body)
  95. return err
  96. }
  97. func (s *containerRouter) getContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  98. v, err := httputils.ArchiveFormValues(r, vars)
  99. if err != nil {
  100. return err
  101. }
  102. tarArchive, stat, err := s.backend.ContainerArchivePath(v.Name, v.Path)
  103. if err != nil {
  104. return err
  105. }
  106. defer tarArchive.Close()
  107. if err := setContainerPathStatHeader(stat, w.Header()); err != nil {
  108. return err
  109. }
  110. w.Header().Set("Content-Type", "application/x-tar")
  111. return writeCompressedResponse(w, r, tarArchive)
  112. }
  113. func (s *containerRouter) putContainersArchive(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  114. v, err := httputils.ArchiveFormValues(r, vars)
  115. if err != nil {
  116. return err
  117. }
  118. noOverwriteDirNonDir := httputils.BoolValue(r, "noOverwriteDirNonDir")
  119. copyUIDGID := httputils.BoolValue(r, "copyUIDGID")
  120. return s.backend.ContainerExtractToDir(v.Name, v.Path, copyUIDGID, noOverwriteDirNonDir, r.Body)
  121. }