github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/utils/utils.go (about) 1 package utils 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 "io" 7 "os" 8 "path/filepath" 9 "strings" 10 "unsafe" 11 12 "golang.org/x/sys/unix" 13 ) 14 15 const ( 16 exitSignalOffset = 128 17 ) 18 19 // NativeEndian is the native byte order of the host system. 20 var NativeEndian binary.ByteOrder 21 22 func init() { 23 // Copied from <golang.org/x/net/internal/socket/sys.go>. 24 i := uint32(1) 25 b := (*[4]byte)(unsafe.Pointer(&i)) 26 if b[0] == 1 { 27 NativeEndian = binary.LittleEndian 28 } else { 29 NativeEndian = binary.BigEndian 30 } 31 } 32 33 // ExitStatus returns the correct exit status for a process based on if it 34 // was signaled or exited cleanly 35 func ExitStatus(status unix.WaitStatus) int { 36 if status.Signaled() { 37 return exitSignalOffset + int(status.Signal()) 38 } 39 return status.ExitStatus() 40 } 41 42 // WriteJSON writes the provided struct v to w using standard json marshaling 43 // without a trailing newline. This is used instead of json.Encoder because 44 // there might be a problem in json decoder in some cases, see: 45 // https://github.com/docker/docker/issues/14203#issuecomment-174177790 46 func WriteJSON(w io.Writer, v interface{}) error { 47 data, err := json.Marshal(v) 48 if err != nil { 49 return err 50 } 51 _, err = w.Write(data) 52 return err 53 } 54 55 // CleanPath makes a path safe for use with filepath.Join. This is done by not 56 // only cleaning the path, but also (if the path is relative) adding a leading 57 // '/' and cleaning it (then removing the leading '/'). This ensures that a 58 // path resulting from prepending another path will always resolve to lexically 59 // be a subdirectory of the prefixed path. This is all done lexically, so paths 60 // that include symlinks won't be safe as a result of using CleanPath. 61 func CleanPath(path string) string { 62 // Deal with empty strings nicely. 63 if path == "" { 64 return "" 65 } 66 67 // Ensure that all paths are cleaned (especially problematic ones like 68 // "/../../../../../" which can cause lots of issues). 69 path = filepath.Clean(path) 70 71 // If the path isn't absolute, we need to do more processing to fix paths 72 // such as "../../../../<etc>/some/path". We also shouldn't convert absolute 73 // paths to relative ones. 74 if !filepath.IsAbs(path) { 75 path = filepath.Clean(string(os.PathSeparator) + path) 76 // This can't fail, as (by definition) all paths are relative to root. 77 path, _ = filepath.Rel(string(os.PathSeparator), path) 78 } 79 80 // Clean the path again for good measure. 81 return filepath.Clean(path) 82 } 83 84 // stripRoot returns the passed path, stripping the root path if it was 85 // (lexicially) inside it. Note that both passed paths will always be treated 86 // as absolute, and the returned path will also always be absolute. In 87 // addition, the paths are cleaned before stripping the root. 88 func stripRoot(root, path string) string { 89 // Make the paths clean and absolute. 90 root, path = CleanPath("/"+root), CleanPath("/"+path) 91 switch { 92 case path == root: 93 path = "/" 94 case root == "/": 95 // do nothing 96 case strings.HasPrefix(path, root+"/"): 97 path = strings.TrimPrefix(path, root+"/") 98 } 99 return CleanPath("/" + path) 100 } 101 102 // SearchLabels searches through a list of key=value pairs for a given key, 103 // returning its value, and the binary flag telling whether the key exist. 104 func SearchLabels(labels []string, key string) (string, bool) { 105 key += "=" 106 for _, s := range labels { 107 if strings.HasPrefix(s, key) { 108 return s[len(key):], true 109 } 110 } 111 return "", false 112 } 113 114 // Annotations returns the bundle path and user defined annotations from the 115 // libcontainer state. We need to remove the bundle because that is a label 116 // added by libcontainer. 117 func Annotations(labels []string) (bundle string, userAnnotations map[string]string) { 118 userAnnotations = make(map[string]string) 119 for _, l := range labels { 120 parts := strings.SplitN(l, "=", 2) 121 if len(parts) < 2 { 122 continue 123 } 124 if parts[0] == "bundle" { 125 bundle = parts[1] 126 } else { 127 userAnnotations[parts[0]] = parts[1] 128 } 129 } 130 return 131 }