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