From 925da74118697702092b24ff115b64258643f81c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2020 09:17:29 +0000 Subject: Bump github.com/containers/storage from 1.16.2 to 1.16.3 Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.16.2 to 1.16.3. - [Release notes](https://github.com/containers/storage/releases) - [Changelog](https://github.com/containers/storage/blob/master/docs/containers-storage-changes.md) - [Commits](https://github.com/containers/storage/compare/v1.16.2...v1.16.3) Signed-off-by: dependabot-preview[bot] Signed-off-by: Daniel J Walsh --- .../selinux/go-selinux/label/label.go | 134 ++++++++------------- .../selinux/go-selinux/label/label_selinux.go | 122 +++---------------- .../selinux/go-selinux/label/label_stub.go | 54 +++++++++ .../selinux/go-selinux/selinux_linux.go | 78 ++++++++---- .../selinux/go-selinux/selinux_stub.go | 14 ++- .../opencontainers/selinux/go-selinux/xattrs.go | 8 +- .../opencontainers/selinux/pkg/pwalk/README.md | 42 +++++++ .../opencontainers/selinux/pkg/pwalk/pwalk.go | 99 +++++++++++++++ 8 files changed, 333 insertions(+), 218 deletions(-) create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md create mode 100644 vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go (limited to 'vendor/github.com/opencontainers') diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go index e178568fd..6e38d3d32 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go @@ -1,109 +1,77 @@ -// +build !selinux !linux - package label -// InitLabels returns the process label and file labels to be used within -// the container. A list of options can be passed into this function to alter -// the labels. -func InitLabels(options []string) (string, string, error) { - return "", "", nil -} - -func ROMountLabel() string { - return "" -} - -func GenLabels(options string) (string, string, error) { - return "", "", nil -} +import ( + "github.com/opencontainers/selinux/go-selinux" +) -func FormatMountLabel(src string, mountLabel string) string { - return src -} +// Deprecated: use selinux.ROFileLabel +var ROMountLabel = selinux.ROFileLabel -func SetProcessLabel(processLabel string) error { - return nil -} +// SetProcessLabel takes a process label and tells the kernel to assign the +// label to the next program executed by the current process. +// Deprecated: use selinux.SetExecLabel +var SetProcessLabel = selinux.SetExecLabel -func ProcessLabel() (string, error) { - return "", nil -} +// ProcessLabel returns the process label that the kernel will assign +// to the next program executed by the current process. If "" is returned +// this indicates that the default labeling will happen for the process. +// Deprecated: use selinux.ExecLabel +var ProcessLabel = selinux.ExecLabel -func SetSocketLabel(processLabel string) error { - return nil -} +// SetSocketLabel takes a process label and tells the kernel to assign the +// label to the next socket that gets created +// Deprecated: use selinux.SetSocketLabel +var SetSocketLabel = selinux.SetSocketLabel -func SocketLabel() (string, error) { - return "", nil -} +// SocketLabel retrieves the current default socket label setting +// Deprecated: use selinux.SocketLabel +var SocketLabel = selinux.SocketLabel -func SetKeyLabel(processLabel string) error { - return nil -} +// SetKeyLabel takes a process label and tells the kernel to assign the +// label to the next kernel keyring that gets created +// Deprecated: use selinux.SetKeyLabel +var SetKeyLabel = selinux.SetKeyLabel -func KeyLabel() (string, error) { - return "", nil -} +// KeyLabel retrieves the current default kernel keyring label setting +// Deprecated: use selinux.KeyLabel +var KeyLabel = selinux.KeyLabel -func FileLabel(path string) (string, error) { - return "", nil -} +// FileLabel returns the label for specified path +// Deprecated: use selinux.FileLabel +var FileLabel = selinux.FileLabel -func SetFileLabel(path string, fileLabel string) error { - return nil -} - -func SetFileCreateLabel(fileLabel string) error { - return nil -} - -func Relabel(path string, fileLabel string, shared bool) error { - return nil -} - -func PidLabel(pid int) (string, error) { - return "", nil -} +// PidLabel will return the label of the process running with the specified pid +// Deprecated: use selinux.PidLabel +var PidLabel = selinux.PidLabel +// Init initialises the labeling system func Init() { + selinux.GetEnabled() } -// ClearLabels clears all reserved labels -func ClearLabels() { - return -} +// ClearLabels will clear all reserved labels +// Deprecated: use selinux.ClearLabels +var ClearLabels = selinux.ClearLabels +// ReserveLabel will record the fact that the MCS label has already been used. +// This will prevent InitLabels from using the MCS label in a newly created +// container +// Deprecated: use selinux.ReserveLabel func ReserveLabel(label string) error { + selinux.ReserveLabel(label) return nil } +// ReleaseLabel will remove the reservation of the MCS label. +// This will allow InitLabels to use the MCS label in a newly created +// containers +// Deprecated: use selinux.ReleaseLabel func ReleaseLabel(label string) error { + selinux.ReleaseLabel(label) return nil } // DupSecOpt takes a process label and returns security options that // can be used to set duplicate labels on future container processes -func DupSecOpt(src string) ([]string, error) { - return nil, nil -} - -// DisableSecOpt returns a security opt that can disable labeling -// support for future container processes -func DisableSecOpt() []string { - return nil -} - -// Validate checks that the label does not include unexpected options -func Validate(label string) error { - return nil -} - -// RelabelNeeded checks whether the user requested a relabel -func RelabelNeeded(label string) bool { - return false -} - -// IsShared checks that the label includes a "shared" mark -func IsShared(label string) bool { - return false -} +// Deprecated: use selinux.DupSecOpt +var DupSecOpt = selinux.DupSecOpt diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index 2730fcf4a..903829958 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/opencontainers/selinux/go-selinux" + "github.com/pkg/errors" ) // Valid Label Options @@ -21,7 +22,7 @@ var validOptions = map[string]bool{ "level": true, } -var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be used together") +var ErrIncompatibleLabel = errors.New("Bad SELinux option z and Z can not be used together") // InitLabels returns the process label and file labels to be used within // the container. A list of options can be passed into this function to alter @@ -35,7 +36,7 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { if processLabel != "" { defer func() { if Err != nil { - ReleaseLabel(mountLabel) + selinux.ReleaseLabel(mountLabel) } }() pcon, err := selinux.NewContext(processLabel) @@ -52,11 +53,11 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { return "", mountLabel, nil } if i := strings.Index(opt, ":"); i == -1 { - return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type, filetype' followed by ':' and a value", opt) + return "", "", errors.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 "", "", fmt.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) + return "", "", errors.Errorf("Bad label option %q, valid options 'disable, user, role, level, type, filetype'", con[0]) } if con[0] == "filetype" { @@ -67,19 +68,16 @@ func InitLabels(options []string) (plabel string, mlabel string, Err error) { mcon[con[0]] = con[1] } } - _ = ReleaseLabel(processLabel) + selinux.ReleaseLabel(processLabel) processLabel = pcon.Get() mountLabel = mcon.Get() - _ = ReserveLabel(processLabel) + selinux.ReserveLabel(processLabel) } return processLabel, mountLabel, nil } -func ROMountLabel() string { - return selinux.ROFileLabel() -} - -// DEPRECATED: The GenLabels function is only to be used during the transition to the official API. +// Deprecated: The GenLabels function is only to be used during the transition +// to the official API. Use InitLabels(strings.Fields(options)) instead. func GenLabels(options string) (string, string, error) { return InitLabels(strings.Fields(options)) } @@ -102,71 +100,27 @@ func FormatMountLabel(src, mountLabel string) string { return src } -// SetProcessLabel takes a process label and tells the kernel to assign the -// label to the next program executed by the current process. -func SetProcessLabel(processLabel string) error { - return selinux.SetExecLabel(processLabel) -} - -// SetSocketLabel takes a process label and tells the kernel to assign the -// label to the next socket that gets created -func SetSocketLabel(processLabel string) error { - return selinux.SetSocketLabel(processLabel) -} - -// SocketLabel retrieves the current default socket label setting -func SocketLabel() (string, error) { - return selinux.SocketLabel() -} - -// SetKeyLabel takes a process label and tells the kernel to assign the -// label to the next kernel keyring that gets created -func SetKeyLabel(processLabel string) error { - return selinux.SetKeyLabel(processLabel) -} - -// KeyLabel retrieves the current default kernel keyring label setting -func KeyLabel() (string, error) { - return selinux.KeyLabel() -} - -// ProcessLabel returns the process label that the kernel will assign -// to the next program executed by the current process. If "" is returned -// this indicates that the default labeling will happen for the process. -func ProcessLabel() (string, error) { - return selinux.ExecLabel() -} - -// FileLabel returns the label for specified path -func FileLabel(path string) (string, error) { - return selinux.FileLabel(path) -} - // SetFileLabel modifies the "path" label to the specified file label func SetFileLabel(path string, fileLabel string) error { - if selinux.GetEnabled() && fileLabel != "" { - return selinux.SetFileLabel(path, fileLabel) + if !selinux.GetEnabled() || fileLabel == "" { + return nil } - return nil + return selinux.SetFileLabel(path, fileLabel) } // SetFileCreateLabel tells the kernel the label for all files to be created func SetFileCreateLabel(fileLabel string) error { - if selinux.GetEnabled() { - return selinux.SetFSCreateLabel(fileLabel) + if !selinux.GetEnabled() { + return nil } - return nil + return selinux.SetFSCreateLabel(fileLabel) } // Relabel changes the label of path to the filelabel string. // It changes the MCS label to s0 if shared is true. // This will allow all containers to share the content. func Relabel(path string, fileLabel string, shared bool) error { - if !selinux.GetEnabled() { - return nil - } - - if fileLabel == "" { + if !selinux.GetEnabled() || fileLabel == "" { return nil } @@ -211,7 +165,7 @@ func Relabel(path string, fileLabel string, shared bool) error { path = strings.TrimSuffix(path, "/") } if exclude_paths[path] { - return fmt.Errorf("SELinux relabeling of %s is not allowed", path) + return errors.Errorf("SELinux relabeling of %s is not allowed", path) } if shared { @@ -229,48 +183,10 @@ func Relabel(path string, fileLabel string, shared bool) error { return nil } -// PidLabel will return the label of the process running with the specified pid -func PidLabel(pid int) (string, error) { - return selinux.PidLabel(pid) -} - -// Init initialises the labeling system -func Init() { - selinux.GetEnabled() -} - -// ClearLabels will clear all reserved labels -func ClearLabels() { - selinux.ClearLabels() -} - -// ReserveLabel will record the fact that the MCS label has already been used. -// This will prevent InitLabels from using the MCS label in a newly created -// container -func ReserveLabel(label string) error { - selinux.ReserveLabel(label) - return nil -} - -// ReleaseLabel will remove the reservation of the MCS label. -// This will allow InitLabels to use the MCS label in a newly created -// containers -func ReleaseLabel(label string) error { - selinux.ReleaseLabel(label) - return nil -} - -// DupSecOpt takes a process label and returns security options that -// can be used to set duplicate labels on future container processes -func DupSecOpt(src string) ([]string, error) { - return selinux.DupSecOpt(src) -} - // DisableSecOpt returns a security opt that can disable labeling // support for future container processes -func DisableSecOpt() []string { - return selinux.DisableSecOpt() -} +// Deprecated: use selinux.DisableSecOpt +var DisableSecOpt = selinux.DisableSecOpt // Validate checks that the label does not include unexpected options func Validate(label string) error { diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go new file mode 100644 index 000000000..cda59d671 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_stub.go @@ -0,0 +1,54 @@ +// +build !selinux !linux + +package label + +// InitLabels returns the process label and file labels to be used within +// the container. A list of options can be passed into this function to alter +// the labels. +func InitLabels(options []string) (string, string, error) { + return "", "", nil +} + +// Deprecated: The GenLabels function is only to be used during the transition +// to the official API. Use InitLabels(strings.Fields(options)) instead. +func GenLabels(options string) (string, string, error) { + return "", "", nil +} + +func FormatMountLabel(src string, mountLabel string) string { + return src +} + +func SetFileLabel(path string, fileLabel string) error { + return nil +} + +func SetFileCreateLabel(fileLabel string) error { + return nil +} + +func Relabel(path string, fileLabel string, shared bool) error { + return nil +} + +// DisableSecOpt returns a security opt that can disable labeling +// support for future container processes +func DisableSecOpt() []string { + // TODO the selinux.DisableSecOpt stub returns []string{"disable"} instead of "nil" + return nil +} + +// Validate checks that the label does not include unexpected options +func Validate(label string) error { + return nil +} + +// RelabelNeeded checks whether the user requested a relabel +func RelabelNeeded(label string) bool { + return false +} + +// IsShared checks that the label includes a "shared" mark +func IsShared(label string) bool { + return false +} 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 c51ddfda2..599bdb6e2 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go @@ -17,8 +17,8 @@ import ( "strconv" "strings" "sync" - "syscall" + "github.com/opencontainers/selinux/pkg/pwalk" "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -37,7 +37,6 @@ const ( selinuxTypeTag = "SELINUXTYPE" selinuxTag = "SELINUX" xattrNameSelinux = "security.selinux" - stRdOnly = 0x01 ) type selinuxState struct { @@ -103,13 +102,13 @@ func SetDisabled() { } func verifySELinuxfsMount(mnt string) bool { - var buf syscall.Statfs_t + var buf unix.Statfs_t for { - err := syscall.Statfs(mnt, &buf) + err := unix.Statfs(mnt, &buf) if err == nil { break } - if err == syscall.EAGAIN { + if err == unix.EAGAIN { continue } return false @@ -118,7 +117,7 @@ func verifySELinuxfsMount(mnt string) bool { if uint32(buf.Type) != uint32(unix.SELINUX_MAGIC) { return false } - if (buf.Flags & stRdOnly) != 0 { + if (buf.Flags & unix.ST_RDONLY) != 0 { return false } @@ -251,10 +250,10 @@ func isProcHandle(fh *os.File) error { var buf unix.Statfs_t err := unix.Fstatfs(int(fh.Fd()), &buf) if err != nil { - return fmt.Errorf("statfs(%q) failed: %v", fh.Name(), err) + return errors.Wrapf(err, "statfs(%q) failed", fh.Name()) } if buf.Type != unix.PROC_SUPER_MAGIC { - return fmt.Errorf("file %q is not on procfs", fh.Name()) + return errors.Errorf("file %q is not on procfs", fh.Name()) } return nil @@ -282,12 +281,29 @@ func readCon(fpath string) (string, error) { return strings.Trim(retval, "\x00"), nil } +// ClassIndex returns the int index for an object class in the loaded policy, or -1 and an error +func ClassIndex(class string) (int, error) { + permpath := fmt.Sprintf("class/%s/index", class) + indexpath := filepath.Join(getSelinuxMountPoint(), permpath) + + indexB, err := ioutil.ReadFile(indexpath) + if err != nil { + return -1, err + } + index, err := strconv.Atoi(string(indexB)) + if err != nil { + return -1, err + } + + return index, nil +} + // SetFileLabel sets the SELinux label for this path or returns an error. func SetFileLabel(fpath string, label string) error { if fpath == "" { return ErrEmptyPath } - if err := lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil { + if err := unix.Lsetxattr(fpath, xattrNameSelinux, []byte(label), 0); err != nil { return errors.Wrapf(err, "failed to set file label on %s", fpath) } return nil @@ -390,7 +406,7 @@ func attrPath(attr string) string { return path.Join(threadSelfPrefix, attr) } - return path.Join("/proc/self/task/", strconv.Itoa(syscall.Gettid()), "/attr/", attr) + return path.Join("/proc/self/task/", strconv.Itoa(unix.Gettid()), "/attr/", attr) } func readAttr(attr string) (string, error) { @@ -410,6 +426,18 @@ func CanonicalizeContext(val string) (string, error) { return readWriteCon(filepath.Join(getSelinuxMountPoint(), "context"), val) } +/* +ComputeCreateContext requests the type transition from source to target for class from the kernel. +*/ +func ComputeCreateContext(source string, target string, class string) (string, error) { + classidx, err := ClassIndex(class) + if err != nil { + return "", err + } + + return readWriteCon(filepath.Join(getSelinuxMountPoint(), "create"), fmt.Sprintf("%s %s %d", source, target, classidx)) +} + func readWriteCon(fpath string, val string) (string, error) { if fpath == "" { return "", ErrEmptyPath @@ -461,17 +489,17 @@ 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), syscall.SOL_SOCKET, syscall.SO_PEERSEC) + return unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC) } // 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(err) { + if os.IsNotExist(errors.Cause(err)) { return nil } - if label == "" && os.IsPermission(err) { + if label == "" && os.IsPermission(errors.Cause(err)) { return nil } return err @@ -772,14 +800,14 @@ func badPrefix(fpath string) error { badPrefixes := []string{"/usr"} for _, prefix := range badPrefixes { if strings.HasPrefix(fpath, prefix) { - return fmt.Errorf("relabeling content in %s is not allowed", prefix) + return errors.Errorf("relabeling content in %s is not allowed", prefix) } } return nil } -// Chcon changes the `fpath` file object to the SELinux label `label`. -// If `fpath` is a directory and `recurse`` is true, Chcon will walk the +// Chcon changes the fpath file object to the SELinux label label. +// If fpath is a directory and recurse is true, Chcon will walk the // directory tree setting the label. func Chcon(fpath string, label string, recurse bool) error { if fpath == "" { @@ -791,19 +819,19 @@ func Chcon(fpath string, label string, recurse bool) error { if err := badPrefix(fpath); err != nil { return err } - callback := func(p string, info os.FileInfo, err error) error { + + if !recurse { + return SetFileLabel(fpath, label) + } + + return pwalk.Walk(fpath, func(p string, info os.FileInfo, err error) error { e := SetFileLabel(p, label) - if os.IsNotExist(e) { + // Walk a file tree can race with removal, so ignore ENOENT + if os.IsNotExist(errors.Cause(e)) { return nil } return e - } - - if recurse { - return filepath.Walk(fpath, callback) - } - - return SetFileLabel(fpath, label) + }) } // DupSecOpt takes an SELinux process label and returns security options that 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 0c2e1cd38..f349513d9 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go @@ -1,4 +1,4 @@ -// +build !selinux +// +build !selinux !linux package selinux @@ -35,6 +35,11 @@ func GetEnabled() bool { return false } +// ClassIndex returns the int index for an object class in the loaded policy, or -1 and an error +func ClassIndex(class string) (int, error) { + return -1, nil +} + // SetFileLabel sets the SELinux label for this path or returns an error. func SetFileLabel(fpath string, label string) error { return nil @@ -88,6 +93,13 @@ func CanonicalizeContext(val string) (string, error) { return "", nil } +/* +ComputeCreateContext requests the type transition from source to target for class from the kernel. +*/ +func ComputeCreateContext(source string, target string, class string) (string, error) { + return "", nil +} + /* SetExecLabel sets the SELinux label that the kernel will use for any programs that are executed by the current process thread, or an error. diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go index 4e711a9f8..de5c80ef3 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go @@ -12,8 +12,8 @@ func lgetxattr(path string, attr string) ([]byte, error) { // Start with a 128 length byte array dest := make([]byte, 128) sz, errno := unix.Lgetxattr(path, attr, dest) - if errno == unix.ERANGE { - // Buffer too small, get the real size first + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size sz, errno = unix.Lgetxattr(path, attr, []byte{}) if errno != nil { return nil, errno @@ -28,7 +28,3 @@ func lgetxattr(path string, attr string) ([]byte, error) { return dest[:sz], nil } - -func lsetxattr(path string, attr string, data []byte, flags int) error { - return unix.Lsetxattr(path, attr, data, flags) -} diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md new file mode 100644 index 000000000..16c4dfd3e --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md @@ -0,0 +1,42 @@ +## pwalk: parallel implementation of filepath.Walk + +This is a wrapper for [filepath.Walk](https://pkg.go.dev/path/filepath?tab=doc#Walk) +which may speed it up by calling multiple callback functions (WalkFunc) 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). + +### Caveats + +Please note the following limitations of this code: + +* Unlike filepath.Walk, the order of calls is non-deterministic; + +* Only primitive error handling is supported: + + * filepath.SkipDir is not supported; + + * no errors are ever passed to WalkFunc; + + * once any error is returned from any WalkFunc instance, no more new calls + to WalkFunc are made, and the error is returned to the caller of Walk; + + * if more than one walkFunc instance will return an error, only one + of such errors will be propagated and returned by Walk, others + will be silently discarded. + +### Documentation + +For the official documentation, see +https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalk?tab=doc + +### Benchmarks + +For a WalkFunc that consists solely of the return statement, this +implementation is about 10% slower than the standard library's +filepath.Walk. + +Otherwise (if a WalkFunc is doing something) this is usually faster, +except when the WalkN(..., 1) is used. diff --git a/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go new file mode 100644 index 000000000..2ee0d0150 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go @@ -0,0 +1,99 @@ +package pwalk + +import ( + "os" + "path/filepath" + "runtime" + "sync" + + "github.com/pkg/errors" +) + +type WalkFunc = filepath.WalkFunc + +// Walk is a wrapper for filepath.Walk 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 WalkFunc) error { + return WalkN(root, walkFn, runtime.NumCPU()*2) +} + +// WalkN is a wrapper for filepath.Walk 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. +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) + } + + 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 + go func() { + err = filepath.Walk(root, func(p string, info os.FileInfo, 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, info: &info} + return nil + } + }) + if err == nil { + close(files) + } + }() + + var wg sync.WaitGroup + wg.Add(num) + for i := 0; i < num; i++ { + go func() { + for file := range files { + if e := walkFn(file.path, *file.info, 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 WalkLimit +// functions. +type walkArgs struct { + path string + info *os.FileInfo +} -- cgit v1.2.3-54-g00ecf