diff --git a/go.mod b/go.mod index 75b06af4758..00460ef136a 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/opencontainers/runc v1.0.0 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.8.2 + github.com/opencontainers/selinux v1.8.5 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.14.2 diff --git a/go.sum b/go.sum index 1ced3a1c4c4..68cc0ac6495 100644 --- a/go.sum +++ b/go.sum @@ -699,8 +699,9 @@ github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pK github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc= github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.8.5 h1:OkT6bMHOQ1JQQO4ihjQ49sj0+wciDcjziSVTRn8VeTA= +github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 h1:WaxyNFpmIDu4i6so9r6LVFIbSaXqsj8oitMitt86ae4= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go index b3d142d8c5b..14e1e38c248 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_linux.go @@ -1,12 +1,13 @@ package label import ( + "errors" + "fmt" "os" "os/user" "strings" "github.com/opencontainers/selinux/go-selinux" - "github.com/pkg/errors" ) // Valid Label Options @@ -53,11 +54,11 @@ func InitLabels(options []string) (plabel string, mlabel string, retErr error) { return "", selinux.PrivContainerMountLabel(), nil } if i := strings.Index(opt, ":"); i == -1 { - return "", "", errors.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) } con := strings.SplitN(opt, ":", 2) if !validOptions[con[0]] { - return "", "", errors.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) + return "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) } if con[0] == "filetype" { mcon["type"] = con[1] @@ -151,7 +152,7 @@ func Relabel(path string, fileLabel string, shared bool) error { path = strings.TrimSuffix(path, "/") } if exclude_paths[path] { - return errors.Errorf("SELinux relabeling of %s is not allowed", path) + return fmt.Errorf("SELinux relabeling of %s is not allowed", path) } if shared { diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go new file mode 100644 index 00000000000..897ecbac41c --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon.go @@ -0,0 +1,22 @@ +// +build linux,go1.16 + +package selinux + +import ( + "errors" + "io/fs" + "os" + + "github.com/opencontainers/selinux/pkg/pwalkdir" +) + +func rchcon(fpath, label string) error { + return pwalkdir.Walk(fpath, func(p string, _ fs.DirEntry, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go new file mode 100644 index 00000000000..2c8b033ce05 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go @@ -0,0 +1,21 @@ +// +build linux,!go1.16 + +package selinux + +import ( + "errors" + "os" + + "github.com/opencontainers/selinux/pkg/pwalk" +) + +func rchcon(fpath, label string) error { + return pwalk.Walk(fpath, func(p string, _ os.FileInfo, _ error) error { + e := setFileLabel(p, label) + // Walk a file tree can race with removal, so ignore ENOENT. + if errors.Is(e, os.ErrNotExist) { + return nil + } + return e + }) +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go index b336ebad3ab..0eedcaa780c 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -1,7 +1,7 @@ package selinux import ( - "github.com/pkg/errors" + "errors" ) const ( @@ -38,6 +38,8 @@ var ( // CategoryRange allows the upper bound on the category range to be adjusted CategoryRange = DefaultCategoryRange + + privContainerMountLabel string ) // Context is a representation of the SELinux label broken into 4 parts @@ -280,5 +282,7 @@ func GetDefaultContextWithLevel(user, level, scon string) (string, error) { // PrivContainerMountLabel returns mount label for privileged containers func PrivContainerMountLabel() string { + // Make sure label is initialized. + _ = label("") return privContainerMountLabel } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go index a91a116f848..295b2bc4e06 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -5,20 +5,18 @@ import ( "bytes" "crypto/rand" "encoding/binary" + "errors" "fmt" "io" "io/ioutil" "os" "path" "path/filepath" - "regexp" "strconv" "strings" "sync" "github.com/bits-and-blooms/bitset" - "github.com/opencontainers/selinux/pkg/pwalk" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -35,8 +33,6 @@ const ( xattrNameSelinux = "security.selinux" ) -var policyRoot = filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) - type selinuxState struct { enabledSet bool enabled bool @@ -71,7 +67,6 @@ const ( ) var ( - assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) readOnlyFileLabel string state = selinuxState{ mcsList: make(map[string]bool), @@ -80,8 +75,24 @@ var ( // for attrPath() attrPathOnce sync.Once haveThreadSelf bool + + // for policyRoot() + policyRootOnce sync.Once + policyRootVal string + + // for label() + loadLabelsOnce sync.Once + labels map[string]string ) +func policyRoot() string { + policyRootOnce.Do(func() { + policyRootVal = filepath.Join(selinuxDir, readConfig(selinuxTypeTag)) + }) + + return policyRootVal +} + func (s *selinuxState) setEnable(enabled bool) bool { s.Lock() defer s.Unlock() @@ -120,7 +131,7 @@ func verifySELinuxfsMount(mnt string) bool { if err == nil { break } - if err == unix.EAGAIN || err == unix.EINTR { + if err == unix.EAGAIN || err == unix.EINTR { //nolint:errorlint // unix errors are bare continue } return false @@ -223,7 +234,7 @@ func readConfig(target string) string { scanner := bufio.NewScanner(in) for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) + line := bytes.TrimSpace(scanner.Bytes()) if len(line) == 0 { // Skip blank lines continue @@ -232,11 +243,12 @@ func readConfig(target string) string { // Skip comments continue } - if groups := assignRegex.FindStringSubmatch(line); groups != nil { - key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) - if key == target { - return strings.Trim(val, "\"") - } + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue + } + if bytes.Equal(fields[0], []byte(target)) { + return string(bytes.Trim(fields[1], `"`)) } } return "" @@ -250,12 +262,12 @@ func isProcHandle(fh *os.File) error { if err == nil { break } - if err != unix.EINTR { - return errors.Wrapf(err, "statfs(%q) failed", fh.Name()) + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return &os.PathError{Op: "fstatfs", Path: fh.Name(), Err: err} } } if buf.Type != unix.PROC_SUPER_MAGIC { - return errors.Errorf("file %q is not on procfs", fh.Name()) + return fmt.Errorf("file %q is not on procfs", fh.Name()) } return nil @@ -275,12 +287,15 @@ func readCon(fpath string) (string, error) { if err := isProcHandle(in); err != nil { return "", err } + return readConFd(in) +} - var retval string - if _, err := fmt.Fscanf(in, "%s", &retval); err != nil { +func readConFd(in *os.File) (string, error) { + data, err := ioutil.ReadAll(in) + if err != nil { return "", err } - return strings.Trim(retval, "\x00"), nil + return string(bytes.TrimSuffix(data, []byte{0})), nil } // classIndex returns the int index for an object class in the loaded policy, @@ -311,8 +326,8 @@ func setFileLabel(fpath string, label string) error { if err == nil { break } - if err != unix.EINTR { - return errors.Wrapf(err, "failed to set file label on %s", fpath) + if err != unix.EINTR { //nolint:errorlint // unix errors are bare + return &os.PathError{Op: "lsetxattr", Path: fpath, Err: err} } } @@ -327,7 +342,7 @@ func fileLabel(fpath string) (string, error) { label, err := lgetxattr(fpath, xattrNameSelinux) if err != nil { - return "", err + return "", &os.PathError{Op: "lgetxattr", Path: fpath, Err: err} } // Trim the NUL byte at the end of the byte buffer, if present. if len(label) > 0 && label[len(label)-1] == '\x00' { @@ -390,7 +405,7 @@ func writeCon(fpath, val string) error { _, err = out.Write(nil) } if err != nil { - return errors.Wrapf(err, "failed to set %s on procfs", fpath) + return err } return nil } @@ -489,13 +504,13 @@ func (l *level) parseLevel(levelStr string) error { lvl := strings.SplitN(levelStr, ":", 2) sens, err := parseLevelItem(lvl[0], sensitivity) if err != nil { - return errors.Wrap(err, "failed to parse sensitivity") + return fmt.Errorf("failed to parse sensitivity: %w", err) } l.sens = sens if len(lvl) > 1 { cats, err := catsToBitset(lvl[1]) if err != nil { - return errors.Wrap(err, "failed to parse categories") + return fmt.Errorf("failed to parse categories: %w", err) } l.cats = cats } @@ -513,14 +528,14 @@ func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) { case 2: mlsRange.high = &level{} if err := mlsRange.high.parseLevel(levelSlice[1]); err != nil { - return nil, errors.Wrapf(err, "failed to parse high level %q", levelSlice[1]) + return nil, fmt.Errorf("failed to parse high level %q: %w", levelSlice[1], err) } fallthrough // rangeStr that is single level, e.g. s6:c0,c3,c5,c30.c1023 case 1: mlsRange.low = &level{} if err := mlsRange.low.parseLevel(levelSlice[0]); err != nil { - return nil, errors.Wrapf(err, "failed to parse low level %q", levelSlice[0]) + return nil, fmt.Errorf("failed to parse low level %q: %w", levelSlice[0], err) } } @@ -665,11 +680,7 @@ func readWriteCon(fpath string, val string) (string, error) { return "", err } - var retval string - if _, err := fmt.Fscanf(f, "%s", &retval); err != nil { - return "", err - } - return strings.Trim(retval, "\x00"), nil + return readConFd(f) } // setExecLabel sets the SELinux label that the kernel will use for any programs @@ -697,17 +708,21 @@ func socketLabel() (string, error) { // peerLabel retrieves the label of the client on the other side of a socket func peerLabel(fd uintptr) (string, error) { - return unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC) + label, err := unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC) + if err != nil { + return "", &os.PathError{Op: "getsockopt", Path: "fd " + strconv.Itoa(int(fd)), Err: err} + } + return label, nil } // setKeyLabel takes a process label and tells the kernel to assign the // label to the next kernel keyring that gets created func setKeyLabel(label string) error { err := writeCon("/proc/self/attr/keycreate", label) - if os.IsNotExist(errors.Cause(err)) { + if errors.Is(err, os.ErrNotExist) { return nil } - if label == "" && os.IsPermission(errors.Cause(err)) { + if label == "" && errors.Is(err, os.ErrPermission) { return nil } return err @@ -720,10 +735,10 @@ func keyLabel() (string, error) { // get returns the Context as a string func (c Context) get() string { - if c["level"] != "" { - return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) + if level := c["level"]; level != "" { + return c["user"] + ":" + c["role"] + ":" + c["type"] + ":" + level } - return fmt.Sprintf("%s:%s:%s", c["user"], c["role"], c["type"]) + return c["user"] + ":" + c["role"] + ":" + c["type"] } // newContext creates a new Context struct from the specified label @@ -784,7 +799,7 @@ func enforceMode() int { // setEnforceMode sets the current SELinux mode Enforcing, Permissive. // Disabled is not valid, since this needs to be set at boot time. func setEnforceMode(mode int) error { - return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0644) + return ioutil.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0o644) } // defaultEnforceMode returns the systems default SELinux mode Enforcing, @@ -888,24 +903,21 @@ func openContextFile() (*os.File, error) { if f, err := os.Open(contextFile); err == nil { return f, nil } - lxcPath := filepath.Join(policyRoot, "/contexts/lxc_contexts") - return os.Open(lxcPath) + return os.Open(filepath.Join(policyRoot(), "/contexts/lxc_contexts")) } -var labels, privContainerMountLabel = loadLabels() - -func loadLabels() (map[string]string, string) { - labels := make(map[string]string) +func loadLabels() { + labels = make(map[string]string) in, err := openContextFile() if err != nil { - return labels, "" + return } defer in.Close() scanner := bufio.NewScanner(in) for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) + line := bytes.TrimSpace(scanner.Bytes()) if len(line) == 0 { // Skip blank lines continue @@ -914,38 +926,47 @@ func loadLabels() (map[string]string, string) { // Skip comments continue } - if groups := assignRegex.FindStringSubmatch(line); groups != nil { - key, val := strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2]) - labels[key] = strings.Trim(val, "\"") + fields := bytes.SplitN(line, []byte{'='}, 2) + if len(fields) != 2 { + continue } + key, val := bytes.TrimSpace(fields[0]), bytes.TrimSpace(fields[1]) + labels[string(key)] = string(bytes.Trim(val, `"`)) } con, _ := NewContext(labels["file"]) con["level"] = fmt.Sprintf("s0:c%d,c%d", maxCategory-2, maxCategory-1) - reserveLabel(con.get()) - return labels, con.get() + privContainerMountLabel = con.get() + reserveLabel(privContainerMountLabel) +} + +func label(key string) string { + loadLabelsOnce.Do(func() { + loadLabels() + }) + return labels[key] } // kvmContainerLabels returns the default processLabel and mountLabel to be used // for kvm containers by the calling process. func kvmContainerLabels() (string, string) { - processLabel := labels["kvm_process"] + processLabel := label("kvm_process") if processLabel == "" { - processLabel = labels["process"] + processLabel = label("process") } - return addMcs(processLabel, labels["file"]) + return addMcs(processLabel, label("file")) } // initContainerLabels returns the default processLabel and file labels to be // used for containers running an init system like systemd by the calling process. func initContainerLabels() (string, string) { - processLabel := labels["init_process"] + processLabel := label("init_process") if processLabel == "" { - processLabel = labels["process"] + processLabel = label("process") } - return addMcs(processLabel, labels["file"]) + return addMcs(processLabel, label("file")) } // containerLabels returns an allocated processLabel and fileLabel to be used for @@ -955,9 +976,9 @@ func containerLabels() (processLabel string, fileLabel string) { return "", "" } - processLabel = labels["process"] - fileLabel = labels["file"] - readOnlyFileLabel = labels["ro_file"] + processLabel = label("process") + fileLabel = label("file") + readOnlyFileLabel = label("ro_file") if processLabel == "" || fileLabel == "" { return "", fileLabel @@ -985,7 +1006,7 @@ func addMcs(processLabel, fileLabel string) (string, string) { // securityCheckContext validates that the SELinux label is understood by the kernel func securityCheckContext(val string) error { - return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0644) + return ioutil.WriteFile(path.Join(getSelinuxMountPoint(), "context"), []byte(val), 0o644) } // copyLevel returns a label with the MLS/MCS level from src label replaced on @@ -1023,7 +1044,7 @@ func badPrefix(fpath string) error { badPrefixes := []string{"/usr"} for _, prefix := range badPrefixes { if strings.HasPrefix(fpath, prefix) { - return errors.Errorf("relabeling content in %s is not allowed", prefix) + return fmt.Errorf("relabeling content in %s is not allowed", prefix) } } return nil @@ -1044,17 +1065,10 @@ func chcon(fpath string, label string, recurse bool) error { } if !recurse { - return SetFileLabel(fpath, label) + return setFileLabel(fpath, label) } - return pwalk.Walk(fpath, func(p string, info os.FileInfo, err error) error { - e := SetFileLabel(p, label) - // Walk a file tree can race with removal, so ignore ENOENT - if os.IsNotExist(errors.Cause(e)) { - return nil - } - return e - }) + return rchcon(fpath, label) } // dupSecOpt takes an SELinux process label and returns security options that @@ -1072,7 +1086,8 @@ func dupSecOpt(src string) ([]string, error) { con["type"] == "" { return nil, nil } - dup := []string{"user:" + con["user"], + dup := []string{ + "user:" + con["user"], "role:" + con["role"], "type:" + con["type"], } @@ -1140,9 +1155,8 @@ func findUserInContext(context Context, r io.Reader, verifier func(string) error return outConn, nil } } - if err := scanner.Err(); err != nil { - return "", errors.Wrap(err, "failed to scan for context") + return "", fmt.Errorf("failed to scan for context: %w", err) } return "", nil @@ -1155,7 +1169,7 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) { context, err := newContext(c.scon) if err != nil { - return "", errors.Wrapf(err, "failed to create label for %s", c.scon) + return "", fmt.Errorf("failed to create label for %s: %w", c.scon, err) } // set so the verifier validates the matched context with the provided user and level. @@ -1180,19 +1194,18 @@ func getDefaultContextFromReaders(c *defaultSECtx) (string, error) { return conn, nil } - return "", errors.Wrapf(ErrContextMissing, "context not found: %q", c.scon) + return "", fmt.Errorf("context %q not found: %w", c.scon, ErrContextMissing) } func getDefaultContextWithLevel(user, level, scon string) (string, error) { - userPath := filepath.Join(policyRoot, selinuxUsersDir, user) - defaultPath := filepath.Join(policyRoot, defaultContexts) - + userPath := filepath.Join(policyRoot(), selinuxUsersDir, user) fu, err := os.Open(userPath) if err != nil { return "", err } defer fu.Close() + defaultPath := filepath.Join(policyRoot(), defaultContexts) fd, err := os.Open(defaultPath) if err != nil { return "", err diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go index b7218a0b6a8..42657759c38 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go @@ -2,8 +2,6 @@ package selinux -const privContainerMountLabel = "" - func setDisabled() { } @@ -152,3 +150,7 @@ func disableSecOpt() []string { func getDefaultContextWithLevel(user, level, scon string) (string, error) { return "", nil } + +func label(_ string) string { + return "" +} diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go index 117c255be20..c6b0a7f2655 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go @@ -10,7 +10,7 @@ func lgetxattr(path, attr string) ([]byte, error) { // Start with a 128 length byte array dest := make([]byte, 128) sz, errno := doLgetxattr(path, attr, dest) - for errno == unix.ERANGE { + for errno == unix.ERANGE { //nolint:errorlint // unix errors are bare // Buffer too small, use zero-sized buffer to get the actual size sz, errno = doLgetxattr(path, attr, []byte{}) if errno != nil { @@ -31,7 +31,7 @@ func lgetxattr(path, attr string) ([]byte, error) { func doLgetxattr(path, attr string, dest []byte) (int, error) { for { sz, err := unix.Lgetxattr(path, attr, dest) - if err != unix.EINTR { + if err != unix.EINTR { //nolint:errorlint // unix errors are bare return sz, err } } diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md index 16c4dfd3ecc..7e78dce0156 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md @@ -8,6 +8,12 @@ By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. This can be changed by using WalkN function which has the additional parameter, specifying the number of goroutines (concurrency). +### pwalk vs pwalkdir + +This package is deprecated in favor of +[pwalkdir](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +which is faster, but requires at least Go 1.16. + ### Caveats Please note the following limitations of this code: diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go index 437b12b3e28..011fe862aad 100644 --- a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go @@ -1,12 +1,11 @@ package pwalk import ( + "fmt" "os" "path/filepath" "runtime" "sync" - - "github.com/pkg/errors" ) type WalkFunc = filepath.WalkFunc @@ -20,7 +19,7 @@ type WalkFunc = filepath.WalkFunc // // Note that this implementation only supports primitive error handling: // -// - no errors are ever passed to WalkFn; +// - no errors are ever passed to walkFn; // // - once a walkFn returns any error, all further processing stops // and the error is returned to the caller of Walk; @@ -42,7 +41,7 @@ func Walk(root string, walkFn WalkFunc) error { func WalkN(root string, walkFn WalkFunc, num int) error { // make sure limit is sensible if num < 1 { - return errors.Errorf("walk(%q): num must be > 0", root) + return fmt.Errorf("walk(%q): num must be > 0", root) } files := make(chan *walkArgs, 2*num) @@ -96,7 +95,7 @@ func WalkN(root string, walkFn WalkFunc, num int) error { return err } -// walkArgs holds the arguments that were passed to the Walk or WalkLimit +// walkArgs holds the arguments that were passed to the Walk or WalkN // functions. type walkArgs struct { path string diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md new file mode 100644 index 00000000000..068ac400565 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md @@ -0,0 +1,54 @@ +## pwalkdir: parallel implementation of filepath.WalkDir + +This is a wrapper for [filepath.WalkDir](https://pkg.go.dev/path/filepath#WalkDir) +which may speed it up by calling multiple callback functions (WalkDirFunc) +in parallel, utilizing goroutines. + +By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks. +This can be changed by using WalkN function which has the additional +parameter, specifying the number of goroutines (concurrency). + +### pwalk vs pwalkdir + +This package is very similar to +[pwalk](https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir), +but utilizes `filepath.WalkDir` (added to Go 1.16), which does not call stat(2) +on every entry and is therefore faster (up to 3x, depending on usage scenario). + +Users who are OK with requiring Go 1.16+ should switch to this +implementation. + +### Caveats + +Please note the following limitations of this code: + +* Unlike filepath.WalkDir, the order of calls is non-deterministic; + +* Only primitive error handling is supported: + + * fs.SkipDir is not supported; + + * no errors are ever passed to WalkDirFunc; + + * once any error is returned from any walkDirFunc instance, no more calls + to WalkDirFunc are made, and the error is returned to the caller of WalkDir; + + * if more than one WalkDirFunc instance will return an error, only one + of such errors will be propagated to and returned by WalkDir, others + will be silently discarded. + +### Documentation + +For the official documentation, see +https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir + +### Benchmarks + +For a WalkDirFunc that consists solely of the return statement, this +implementation is about 15% slower than the standard library's +filepath.WalkDir. + +Otherwise (if a WalkDirFunc is actually doing something) this is usually +faster, except when the WalkDirN(..., 1) is used. Run `go test -bench .` +to see how different operations can benefit from it, as well as how the +level of paralellism affects the speed. diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go new file mode 100644 index 00000000000..222820750c3 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalkdir/pwalkdir.go @@ -0,0 +1,103 @@ +// +build go1.16 + +package pwalkdir + +import ( + "fmt" + "io/fs" + "path/filepath" + "runtime" + "sync" +) + +// Walk is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// twice the runtime.NumCPU() walkFn will be called at any one time. +// If you want to change the maximum, use WalkN instead. +// +// The order of calls is non-deterministic. +// +// Note that this implementation only supports primitive error handling: +// +// - no errors are ever passed to walkFn; +// +// - once a walkFn returns any error, all further processing stops +// and the error is returned to the caller of Walk; +// +// - filepath.SkipDir is not supported; +// +// - if more than one walkFn instance will return an error, only one +// of such errors will be propagated and returned by Walk, others +// will be silently discarded. +func Walk(root string, walkFn fs.WalkDirFunc) error { + return WalkN(root, walkFn, runtime.NumCPU()*2) +} + +// WalkN is a wrapper for filepath.WalkDir which can call multiple walkFn +// in parallel, allowing to handle each item concurrently. A maximum of +// num walkFn will be called at any one time. +// +// Please see Walk documentation for caveats of using this function. +func WalkN(root string, walkFn fs.WalkDirFunc, num int) error { + // make sure limit is sensible + if num < 1 { + return fmt.Errorf("walk(%q): num must be > 0", root) + } + + files := make(chan *walkArgs, 2*num) + errCh := make(chan error, 1) // Get the first error, ignore others. + + // Start walking a tree asap. + var ( + err error + wg sync.WaitGroup + ) + wg.Add(1) + go func() { + err = filepath.WalkDir(root, func(p string, entry fs.DirEntry, err error) error { + if err != nil { + close(files) + return err + } + // Add a file to the queue unless a callback sent an error. + select { + case e := <-errCh: + close(files) + return e + default: + files <- &walkArgs{path: p, entry: entry} + return nil + } + }) + if err == nil { + close(files) + } + wg.Done() + }() + + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + for file := range files { + if e := walkFn(file.path, file.entry, nil); e != nil { + select { + case errCh <- e: // sent ok + default: // buffer full + } + } + } + wg.Done() + }() + } + + wg.Wait() + + return err +} + +// walkArgs holds the arguments that were passed to the Walk or WalkN +// functions. +type walkArgs struct { + path string + entry fs.DirEntry +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3017646f0e3..55c31d727bd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -517,10 +517,11 @@ github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate/seccomp github.com/opencontainers/runtime-tools/specerror github.com/opencontainers/runtime-tools/validate -# github.com/opencontainers/selinux v1.8.2 +# github.com/opencontainers/selinux v1.8.5 github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/go-selinux/label github.com/opencontainers/selinux/pkg/pwalk +github.com/opencontainers/selinux/pkg/pwalkdir # github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 github.com/openshift/imagebuilder github.com/openshift/imagebuilder/dockerfile/command