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