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  }