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