github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/api/handlers/compat/containers_archive.go (about) 1 package compat 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "os" 7 "strings" 8 9 "github.com/hanks177/podman/v4/libpod" 10 "github.com/hanks177/podman/v4/libpod/define" 11 "github.com/hanks177/podman/v4/pkg/api/handlers/utils" 12 api "github.com/hanks177/podman/v4/pkg/api/types" 13 "github.com/hanks177/podman/v4/pkg/copy" 14 "github.com/hanks177/podman/v4/pkg/domain/entities" 15 "github.com/hanks177/podman/v4/pkg/domain/infra/abi" 16 "github.com/gorilla/schema" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 ) 20 21 func Archive(w http.ResponseWriter, r *http.Request) { 22 decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) 23 runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) 24 25 switch r.Method { 26 case http.MethodPut: 27 handlePut(w, r, decoder, runtime) 28 case http.MethodHead, http.MethodGet: 29 handleHeadAndGet(w, r, decoder, runtime) 30 default: 31 utils.Error(w, http.StatusNotImplemented, errors.Errorf("unsupported method: %v", r.Method)) 32 } 33 } 34 35 func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { 36 query := struct { 37 Path string `schema:"path"` 38 }{} 39 40 err := decoder.Decode(&query, r.URL.Query()) 41 if err != nil { 42 utils.Error(w, http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) 43 return 44 } 45 46 if query.Path == "" { 47 utils.Error(w, http.StatusBadRequest, errors.New("missing `path` parameter")) 48 return 49 } 50 51 containerName := utils.GetName(r) 52 containerEngine := abi.ContainerEngine{Libpod: runtime} 53 statReport, err := containerEngine.ContainerStat(r.Context(), containerName, query.Path) 54 55 // NOTE 56 // The statReport may actually be set even in case of an error. That's 57 // the case when we're looking at a symlink pointing to nirvana. In 58 // such cases, we really need the FileInfo but we also need the error. 59 if statReport != nil { 60 statHeader, err := copy.EncodeFileInfo(&statReport.FileInfo) 61 if err != nil { 62 utils.Error(w, http.StatusInternalServerError, err) 63 return 64 } 65 w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) 66 } 67 68 if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == copy.ErrENOENT { 69 // 404 is returned for an absent container and path. The 70 // clients must deal with it accordingly. 71 utils.Error(w, http.StatusNotFound, err) 72 return 73 } else if err != nil { 74 utils.Error(w, http.StatusInternalServerError, err) 75 return 76 } 77 78 // Our work is done when the user is interested in the header only. 79 if r.Method == http.MethodHead { 80 w.WriteHeader(http.StatusOK) 81 return 82 } 83 84 copyFunc, err := containerEngine.ContainerCopyToArchive(r.Context(), containerName, query.Path, w) 85 if err != nil { 86 utils.Error(w, http.StatusInternalServerError, err) 87 return 88 } 89 w.Header().Set("Content-Type", "application/x-tar") 90 w.WriteHeader(http.StatusOK) 91 if err := copyFunc(); err != nil { 92 logrus.Error(err.Error()) 93 } 94 } 95 96 func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { 97 query := struct { 98 Path string `schema:"path"` 99 Chown bool `schema:"copyUIDGID"` 100 Rename string `schema:"rename"` 101 NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` 102 }{ 103 Chown: utils.IsLibpodRequest(r), // backward compatibility 104 } 105 106 err := decoder.Decode(&query, r.URL.Query()) 107 if err != nil { 108 utils.Error(w, http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) 109 return 110 } 111 112 var rename map[string]string 113 if query.Rename != "" { 114 if err := json.Unmarshal([]byte(query.Rename), &rename); err != nil { 115 utils.Error(w, http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query field 'rename'")) 116 return 117 } 118 } 119 120 containerName := utils.GetName(r) 121 containerEngine := abi.ContainerEngine{Libpod: runtime} 122 123 copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body, 124 entities.CopyOptions{ 125 Chown: query.Chown, 126 NoOverwriteDirNonDir: query.NoOverwriteDirNonDir, 127 Rename: rename, 128 }) 129 if err != nil { 130 switch { 131 case errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err): 132 // 404 is returned for an absent container and path. The 133 // clients must deal with it accordingly. 134 utils.Error(w, http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) 135 case strings.Contains(err.Error(), "copier: put: error creating file"): 136 // Not the best test but need to break this out for compatibility 137 // See vendor/github.com/containers/buildah/copier/copier.go:1585 138 utils.Error(w, http.StatusBadRequest, err) 139 default: 140 utils.Error(w, http.StatusInternalServerError, err) 141 } 142 return 143 } 144 145 if err := copyFunc(); err != nil { 146 logrus.Error(err.Error()) 147 utils.Error(w, http.StatusInternalServerError, err) 148 return 149 } 150 w.WriteHeader(http.StatusOK) 151 }