github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/spec/parse.go (about) 1 package createconfig 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "github.com/docker/go-units" 10 "github.com/pkg/errors" 11 ) 12 13 // deviceCgroupRulegex defines the valid format of device-cgroup-rule 14 var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`) 15 16 // Pod signifies a kernel namespace is being shared 17 // by a container with the pod it is associated with 18 const Pod = "pod" 19 20 // weightDevice is a structure that holds device:weight pair 21 type weightDevice struct { 22 Path string 23 Weight uint16 24 } 25 26 func (w *weightDevice) String() string { 27 return fmt.Sprintf("%s:%d", w.Path, w.Weight) 28 } 29 30 // LinuxNS is a struct that contains namespace information 31 // It implemented Valid to show it is a valid namespace 32 type LinuxNS interface { 33 Valid() bool 34 } 35 36 // IsNS returns if the specified string has a ns: prefix 37 func IsNS(s string) bool { 38 parts := strings.SplitN(s, ":", 2) 39 return len(parts) > 1 && parts[0] == "ns" 40 } 41 42 // IsPod returns if the specified string is pod 43 func IsPod(s string) bool { 44 return s == Pod 45 } 46 47 // Valid checks the validity of a linux namespace 48 // s should be the string representation of ns 49 func Valid(s string, ns LinuxNS) bool { 50 return IsPod(s) || IsNS(s) || ns.Valid() 51 } 52 53 // NS is the path to the namespace to join. 54 func NS(s string) string { 55 parts := strings.SplitN(s, ":", 2) 56 if len(parts) > 1 { 57 return parts[1] 58 } 59 return "" 60 } 61 62 // ValidateweightDevice validates that the specified string has a valid device-weight format 63 // for blkio-weight-device flag 64 func ValidateweightDevice(val string) (*weightDevice, error) { 65 split := strings.SplitN(val, ":", 2) 66 if len(split) != 2 { 67 return nil, fmt.Errorf("bad format: %s", val) 68 } 69 if !strings.HasPrefix(split[0], "/dev/") { 70 return nil, fmt.Errorf("bad format for device path: %s", val) 71 } 72 weight, err := strconv.ParseUint(split[1], 10, 0) 73 if err != nil { 74 return nil, fmt.Errorf("invalid weight for device: %s", val) 75 } 76 if weight > 0 && (weight < 10 || weight > 1000) { 77 return nil, fmt.Errorf("invalid weight for device: %s", val) 78 } 79 80 return &weightDevice{ 81 Path: split[0], 82 Weight: uint16(weight), 83 }, nil 84 } 85 86 // throttleDevice is a structure that holds device:rate_per_second pair 87 type throttleDevice struct { 88 path string 89 rate uint64 90 } 91 92 func (t *throttleDevice) String() string { 93 return fmt.Sprintf("%s:%d", t.path, t.rate) 94 } 95 96 // validateBpsDevice validates that the specified string has a valid device-rate format 97 // for device-read-bps and device-write-bps flags 98 func validateBpsDevice(val string) (*throttleDevice, error) { 99 split := strings.SplitN(val, ":", 2) 100 if len(split) != 2 { 101 return nil, fmt.Errorf("bad format: %s", val) 102 } 103 if !strings.HasPrefix(split[0], "/dev/") { 104 return nil, fmt.Errorf("bad format for device path: %s", val) 105 } 106 rate, err := units.RAMInBytes(split[1]) 107 if err != nil { 108 return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) 109 } 110 if rate < 0 { 111 return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) 112 } 113 114 return &throttleDevice{ 115 path: split[0], 116 rate: uint64(rate), 117 }, nil 118 } 119 120 // validateIOpsDevice validates that the specified string has a valid device-rate format 121 // for device-write-iops and device-read-iops flags 122 func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint 123 split := strings.SplitN(val, ":", 2) 124 if len(split) != 2 { 125 return nil, fmt.Errorf("bad format: %s", val) 126 } 127 if !strings.HasPrefix(split[0], "/dev/") { 128 return nil, fmt.Errorf("bad format for device path: %s", val) 129 } 130 rate, err := strconv.ParseUint(split[1], 10, 64) 131 if err != nil { 132 return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val) 133 } 134 return &throttleDevice{ 135 path: split[0], 136 rate: rate, 137 }, nil 138 } 139 140 // getLoggingOpts splits the path= and tag= options provided to --log-opt. 141 func getLoggingOpts(opts []string) (string, string) { 142 var path, tag string 143 for _, opt := range opts { 144 arr := strings.SplitN(opt, "=", 2) 145 if len(arr) == 2 { 146 if strings.TrimSpace(arr[0]) == "path" { 147 path = strings.TrimSpace(arr[1]) 148 } else if strings.TrimSpace(arr[0]) == "tag" { 149 tag = strings.TrimSpace(arr[1]) 150 } 151 } 152 if path != "" && tag != "" { 153 break 154 } 155 } 156 return path, tag 157 } 158 159 // ParseDevice parses device mapping string to a src, dest & permissions string 160 func ParseDevice(device string) (string, string, string, error) { //nolint 161 src := "" 162 dst := "" 163 permissions := "rwm" 164 arr := strings.Split(device, ":") 165 switch len(arr) { 166 case 3: 167 if !IsValidDeviceMode(arr[2]) { 168 return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2]) 169 } 170 permissions = arr[2] 171 fallthrough 172 case 2: 173 if IsValidDeviceMode(arr[1]) { 174 permissions = arr[1] 175 } else { 176 if len(arr[1]) == 0 || arr[1][0] != '/' { 177 return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1]) 178 } 179 dst = arr[1] 180 } 181 fallthrough 182 case 1: 183 src = arr[0] 184 default: 185 return "", "", "", fmt.Errorf("invalid device specification: %s", device) 186 } 187 188 if dst == "" { 189 dst = src 190 } 191 return src, dst, permissions, nil 192 } 193 194 // IsValidDeviceMode checks if the mode for device is valid or not. 195 // IsValid mode is a composition of r (read), w (write), and m (mknod). 196 func IsValidDeviceMode(mode string) bool { 197 var legalDeviceMode = map[rune]bool{ 198 'r': true, 199 'w': true, 200 'm': true, 201 } 202 if mode == "" { 203 return false 204 } 205 for _, c := range mode { 206 if !legalDeviceMode[c] { 207 return false 208 } 209 legalDeviceMode[c] = false 210 } 211 return true 212 } 213 214 // validateDeviceCgroupRule validates the format of deviceCgroupRule 215 func validateDeviceCgroupRule(deviceCgroupRule string) error { 216 if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) { 217 return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule) 218 } 219 return nil 220 } 221 222 // parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice 223 func parseDeviceCgroupRule(deviceCgroupRule string) [][]string { 224 return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1) 225 }