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  }