 eaedadbed0
			
		
	
	eaedadbed0
	
	
	
		
			
			Go 1.18 and up now provides a strings.Cut() which is better suited for
splitting key/value pairs (and similar constructs), and performs better:
```go
func BenchmarkSplit(b *testing.B) {
        b.ReportAllocs()
        data := []string{"12hello=world", "12hello=", "12=hello", "12hello"}
        for i := 0; i < b.N; i++ {
                for _, s := range data {
                        _ = strings.SplitN(s, "=", 2)[0]
                }
        }
}
func BenchmarkCut(b *testing.B) {
        b.ReportAllocs()
        data := []string{"12hello=world", "12hello=", "12=hello", "12hello"}
        for i := 0; i < b.N; i++ {
                for _, s := range data {
                        _, _, _ = strings.Cut(s, "=")
                }
        }
}
```
    BenchmarkSplit
    BenchmarkSplit-10            8244206               128.0 ns/op           128 B/op          4 allocs/op
    BenchmarkCut
    BenchmarkCut-10             54411998                21.80 ns/op            0 B/op          0 allocs/op
While looking at occurrences of `strings.Split()`, I also updated some for alternatives,
or added some constraints; for cases where an specific number of items is expected, I used `strings.SplitN()`
with a suitable limit. This prevents (theoretical) unlimited splits.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|    Copyright The containerd Authors.
 | |
| 
 | |
|    Licensed under the Apache License, Version 2.0 (the "License");
 | |
|    you may not use this file except in compliance with the License.
 | |
|    You may obtain a copy of the License at
 | |
| 
 | |
|        http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
|    Unless required by applicable law or agreed to in writing, software
 | |
|    distributed under the License is distributed on an "AS IS" BASIS,
 | |
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|    See the License for the specific language governing permissions and
 | |
|    limitations under the License.
 | |
| */
 | |
| 
 | |
| // Package cap provides Linux capability utility
 | |
| package cap
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| // FromNumber returns a cap string like "CAP_SYS_ADMIN"
 | |
| // that corresponds to the given number like 21.
 | |
| //
 | |
| // FromNumber returns an empty string for unknown cap number.
 | |
| func FromNumber(num int) string {
 | |
| 	if num < 0 || num > len(capsLatest)-1 {
 | |
| 		return ""
 | |
| 	}
 | |
| 	return capsLatest[num]
 | |
| }
 | |
| 
 | |
| // FromBitmap parses an uint64 bitmap into string slice like
 | |
| // []{"CAP_SYS_ADMIN", ...}.
 | |
| //
 | |
| // Unknown cap numbers are returned as []int.
 | |
| func FromBitmap(v uint64) ([]string, []int) {
 | |
| 	var (
 | |
| 		res     []string
 | |
| 		unknown []int
 | |
| 	)
 | |
| 	for i := 0; i <= 63; i++ {
 | |
| 		if b := (v >> i) & 0x1; b == 0x1 {
 | |
| 			if s := FromNumber(i); s != "" {
 | |
| 				res = append(res, s)
 | |
| 			} else {
 | |
| 				unknown = append(unknown, i)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return res, unknown
 | |
| }
 | |
| 
 | |
| // Type is the type of capability
 | |
| type Type int
 | |
| 
 | |
| const (
 | |
| 	// Effective is CapEff
 | |
| 	Effective Type = 1 << iota
 | |
| 	// Permitted is CapPrm
 | |
| 	Permitted
 | |
| 	// Inheritable is CapInh
 | |
| 	Inheritable
 | |
| 	// Bounding is CapBnd
 | |
| 	Bounding
 | |
| 	// Ambient is CapAmb
 | |
| 	Ambient
 | |
| )
 | |
| 
 | |
| // ParseProcPIDStatus returns uint64 bitmap value from /proc/<PID>/status file
 | |
| func ParseProcPIDStatus(r io.Reader) (map[Type]uint64, error) {
 | |
| 	res := make(map[Type]uint64)
 | |
| 	scanner := bufio.NewScanner(r)
 | |
| 	for scanner.Scan() {
 | |
| 		line := scanner.Text()
 | |
| 		k, v, ok := strings.Cut(line, ":")
 | |
| 		if !ok {
 | |
| 			continue
 | |
| 		}
 | |
| 		k = strings.TrimSpace(k)
 | |
| 		switch k {
 | |
| 		case "CapInh", "CapPrm", "CapEff", "CapBnd", "CapAmb":
 | |
| 			ui64, err := strconv.ParseUint(strings.TrimSpace(v), 16, 64)
 | |
| 			if err != nil {
 | |
| 				return nil, fmt.Errorf("failed to parse line %q", line)
 | |
| 			}
 | |
| 			switch k {
 | |
| 			case "CapInh":
 | |
| 				res[Inheritable] = ui64
 | |
| 			case "CapPrm":
 | |
| 				res[Permitted] = ui64
 | |
| 			case "CapEff":
 | |
| 				res[Effective] = ui64
 | |
| 			case "CapBnd":
 | |
| 				res[Bounding] = ui64
 | |
| 			case "CapAmb":
 | |
| 				res[Ambient] = ui64
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	if err := scanner.Err(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| // Current returns the list of the effective and the known caps of
 | |
| // the current process.
 | |
| //
 | |
| // The result is like []string{"CAP_SYS_ADMIN", ...}.
 | |
| func Current() ([]string, error) {
 | |
| 	f, err := os.Open("/proc/self/status")
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer f.Close()
 | |
| 	caps, err := ParseProcPIDStatus(f)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	capEff := caps[Effective]
 | |
| 	names, _ := FromBitmap(capEff)
 | |
| 	return names, nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// caps35 is the caps of kernel 3.5 (37 entries)
 | |
| 	caps35 = []string{
 | |
| 		"CAP_CHOWN",            // 2.2
 | |
| 		"CAP_DAC_OVERRIDE",     // 2.2
 | |
| 		"CAP_DAC_READ_SEARCH",  // 2.2
 | |
| 		"CAP_FOWNER",           // 2.2
 | |
| 		"CAP_FSETID",           // 2.2
 | |
| 		"CAP_KILL",             // 2.2
 | |
| 		"CAP_SETGID",           // 2.2
 | |
| 		"CAP_SETUID",           // 2.2
 | |
| 		"CAP_SETPCAP",          // 2.2
 | |
| 		"CAP_LINUX_IMMUTABLE",  // 2.2
 | |
| 		"CAP_NET_BIND_SERVICE", // 2.2
 | |
| 		"CAP_NET_BROADCAST",    // 2.2
 | |
| 		"CAP_NET_ADMIN",        // 2.2
 | |
| 		"CAP_NET_RAW",          // 2.2
 | |
| 		"CAP_IPC_LOCK",         // 2.2
 | |
| 		"CAP_IPC_OWNER",        // 2.2
 | |
| 		"CAP_SYS_MODULE",       // 2.2
 | |
| 		"CAP_SYS_RAWIO",        // 2.2
 | |
| 		"CAP_SYS_CHROOT",       // 2.2
 | |
| 		"CAP_SYS_PTRACE",       // 2.2
 | |
| 		"CAP_SYS_PACCT",        // 2.2
 | |
| 		"CAP_SYS_ADMIN",        // 2.2
 | |
| 		"CAP_SYS_BOOT",         // 2.2
 | |
| 		"CAP_SYS_NICE",         // 2.2
 | |
| 		"CAP_SYS_RESOURCE",     // 2.2
 | |
| 		"CAP_SYS_TIME",         // 2.2
 | |
| 		"CAP_SYS_TTY_CONFIG",   // 2.2
 | |
| 		"CAP_MKNOD",            // 2.4
 | |
| 		"CAP_LEASE",            // 2.4
 | |
| 		"CAP_AUDIT_WRITE",      // 2.6.11
 | |
| 		"CAP_AUDIT_CONTROL",    // 2.6.11
 | |
| 		"CAP_SETFCAP",          // 2.6.24
 | |
| 		"CAP_MAC_OVERRIDE",     // 2.6.25
 | |
| 		"CAP_MAC_ADMIN",        // 2.6.25
 | |
| 		"CAP_SYSLOG",           // 2.6.37
 | |
| 		"CAP_WAKE_ALARM",       // 3.0
 | |
| 		"CAP_BLOCK_SUSPEND",    // 3.5
 | |
| 	}
 | |
| 	// caps316 is the caps of kernel 3.16 (38 entries)
 | |
| 	caps316 = append(caps35, "CAP_AUDIT_READ")
 | |
| 	// caps58 is the caps of kernel 5.8 (40 entries)
 | |
| 	caps58 = append(caps316, []string{"CAP_PERFMON", "CAP_BPF"}...)
 | |
| 	// caps59 is the caps of kernel 5.9 (41 entries)
 | |
| 	caps59     = append(caps58, "CAP_CHECKPOINT_RESTORE")
 | |
| 	capsLatest = caps59
 | |
| )
 | |
| 
 | |
| // Known returns the known cap strings of the latest kernel.
 | |
| // The current latest kernel is 5.9.
 | |
| func Known() []string {
 | |
| 	return capsLatest
 | |
| }
 |