podman
1package copy2
3import (4"encoding/base64"5"encoding/json"6"errors"7"fmt"8"net/http"9"os"10"path/filepath"11"strings"12
13"github.com/containers/podman/v5/libpod/define"14)
15
16// XDockerContainerPathStatHeader is the *key* in http headers pointing to the
17// base64 encoded JSON payload of stating a path in a container.
18const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat"19
20// ErrENOENT mimics the stdlib's ErrENOENT and can be used to implement custom logic
21// while preserving the user-visible error message.
22var ErrENOENT = errors.New("no such file or directory")23
24// FileInfo describes a file or directory and is returned by
25// (*CopyItem).Stat().
26type FileInfo = define.FileInfo27
28// EncodeFileInfo serializes the specified FileInfo as a base64 encoded JSON
29// payload. Intended for Docker compat.
30func EncodeFileInfo(info *FileInfo) (string, error) {31buf, err := json.Marshal(&info)32if err != nil {33return "", fmt.Errorf("failed to serialize file stats: %w", err)34}35return base64.URLEncoding.EncodeToString(buf), nil36}
37
38// ExtractFileInfoFromHeader extracts a base64 encoded JSON payload of a
39// FileInfo in the http header. If no such header entry is found, nil is
40// returned. Intended for Docker compat.
41func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) {42rawData := header.Get(XDockerContainerPathStatHeader)43if len(rawData) == 0 {44return nil, nil45}46
47info := FileInfo{}48base64Decoder := base64.NewDecoder(base64.URLEncoding, strings.NewReader(rawData))49if err := json.NewDecoder(base64Decoder).Decode(&info); err != nil {50return nil, err51}52
53return &info, nil54}
55
56// ResolveHostPath resolves the specified, possibly relative, path on the host.
57func ResolveHostPath(path string) (*FileInfo, error) {58resolvedHostPath, err := filepath.Abs(path)59if err != nil {60return nil, err61}62resolvedHostPath = PreserveBasePath(path, resolvedHostPath)63
64statInfo, err := os.Stat(resolvedHostPath)65if err != nil {66if os.IsNotExist(err) {67return nil, ErrENOENT68}69return nil, err70}71
72return &FileInfo{73Name: statInfo.Name(),74Size: statInfo.Size(),75Mode: statInfo.Mode(),76ModTime: statInfo.ModTime(),77IsDir: statInfo.IsDir(),78LinkTarget: resolvedHostPath,79}, nil80}
81
82// PreserveBasePath makes sure that the original base path (e.g., "/" or "./")
83// is preserved. The filepath API among tends to clean up a bit too much but
84// we *must* preserve this data by all means.
85func PreserveBasePath(original, resolved string) string {86// Handle "/"87if strings.HasSuffix(original, "/") {88if !strings.HasSuffix(resolved, "/") {89resolved += "/"90}91return resolved92}93
94// Handle "/."95if strings.HasSuffix(original, "/.") {96if strings.HasSuffix(resolved, "/") { // could be root!97resolved += "."98} else if !strings.HasSuffix(resolved, "/.") {99resolved += "/."100}101return resolved102}103
104return resolved105}
106