New Upstream Release - golang-github-opencontainers-selinux

Ready changes

Summary

Merged new upstream version: 1.11.0+ds1 (was: 1.10.2+ds1).

Resulting package

Built on 2023-05-26T11:38 (took 4m33s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-opencontainers-selinux-dev

Lintian Result

Diff

diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index cd1e6a8..7331776 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -30,40 +30,52 @@ jobs:
   lint:
     runs-on: ubuntu-20.04
     steps:
-      - uses: actions/checkout@v2
-      - uses: golangci/golangci-lint-action@v2
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
         with:
-          # must be specified without patch version
-          version: v1.36
-          # Only show new issues for a pull request.
-          only-new-issues: true
+          go-version: 1.20.x
+      - uses: golangci/golangci-lint-action@v3
+        with:
+          version: v1.51
 
   cross:
     runs-on: ubuntu-20.04
     steps:
-    - uses: actions/checkout@v2
-    - name: cross
-      run: make build-cross
+      - uses: actions/checkout@v3
+      - name: cross
+        run: make build-cross
 
+  test-stubs:
+    runs-on: macos-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: 1.20.x
+      - uses: golangci/golangci-lint-action@v3
+        with:
+          version: v1.51
+      - name: test-stubs
+        run: make test
 
   test:
     strategy:
       fail-fast: false
       matrix:
-        go-version: [1.15.x, 1.16.x, 1.17.x]
+        go-version: [1.19.x, 1.20.x]
         race: ["-race", ""]
     runs-on: ubuntu-20.04
     steps:
-    - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
 
-    - name: install go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        stable: '!contains(${{ matrix.go-version }}, "beta") && !contains(${{ matrix.go-version }}, "rc")'
-        go-version: ${{ matrix.go-version }}
+      - name: install go ${{ matrix.go-version }}
+        uses: actions/setup-go@v3
+        with:
+          stable: '!contains(${{ matrix.go-version }}, "beta") && !contains(${{ matrix.go-version }}, "rc")'
+          go-version: ${{ matrix.go-version }}
 
-    - name: build
-      run: make BUILDFLAGS="${{ matrix.race }}" build
+      - name: build
+        run: make BUILDFLAGS="${{ matrix.race }}" build
 
-    - name: test
-      run: make TESTFLAGS="${{ matrix.race }}" test
+      - name: test
+        run: make TESTFLAGS="${{ matrix.race }}" test
diff --git a/.golangci.yml b/.golangci.yml
index b7bec81..a570a2e 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -4,7 +4,33 @@ run:
   deadline: 5m
 linters:
   enable:
-    - whitespace
-    - gocritic
-    - errorlint
-    - gofumpt
+    - dupword       # Detects duplicate words.
+    - errorlint     # Detects code that may cause problems with Go 1.13 error wrapping.
+    - exportloopref # Detects pointers to enclosing loop variables.
+    - gocritic      # Metalinter; detects bugs, performance, and styling issues.
+    - gofumpt       # Detects whether code was gofumpt-ed.
+    - gosec         # Detects security problems.
+    - misspell      # Detects commonly misspelled English words in comments.
+    - nilerr        # Detects code that returns nil even if it checks that the error is not nil.
+    - nolintlint    # Detects ill-formed or insufficient nolint directives.
+    - prealloc      # Detects slice declarations that could potentially be pre-allocated.
+    - predeclared   # Detects code that shadows one of Go's predeclared identifiers
+    - revive        # Metalinter; drop-in replacement for golint.
+    - tenv          # Detects using os.Setenv instead of t.Setenv.
+    - thelper       # Detects test helpers without t.Helper().
+    - tparallel     # Detects inappropriate usage of t.Parallel().
+    - unconvert     # Detects unnecessary type conversions.
+linters-settings:
+  govet:
+    check-shadowing: true
+    enable-all: true
+    settings:
+      shadow:
+        strict: true
+issues:
+  max-issues-per-linter: 0
+  max-same-issues: 0
+  exclude-rules:
+    - text: '^shadow: declaration of "err" shadows declaration'
+      linters:
+        - govet
diff --git a/Makefile b/Makefile
index c19a9e6..c39d6e0 100644
--- a/Makefile
+++ b/Makefile
@@ -33,5 +33,4 @@ lint:
 .PHONY: vendor
 vendor:
 	$(GO) mod tidy
-	$(GO) mod vendor
 	$(GO) mod verify
diff --git a/VERSION b/VERSION
index 81c871d..1cac385 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.10.0
+1.11.0
diff --git a/debian/changelog b/debian/changelog
index c3089b9..5455be0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+golang-github-opencontainers-selinux (1.11.0+ds1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Fri, 26 May 2023 11:34:47 -0000
+
 golang-github-opencontainers-selinux (1.10.0+ds1-1) unstable; urgency=medium
 
   * Team upload
diff --git a/go-selinux/doc.go b/go-selinux/doc.go
index 0ac7d81..57a15c9 100644
--- a/go-selinux/doc.go
+++ b/go-selinux/doc.go
@@ -9,6 +9,5 @@ Usage:
 	if selinux.EnforceMode() != selinux.Enforcing {
 		selinux.SetEnforceMode(selinux.Enforcing)
 	}
-
 */
 package selinux
diff --git a/go-selinux/label/label.go b/go-selinux/label/label.go
index fea096c..07e0f77 100644
--- a/go-selinux/label/label.go
+++ b/go-selinux/label/label.go
@@ -78,6 +78,9 @@ func ReleaseLabel(label string) error {
 // Deprecated: use selinux.DupSecOpt
 var DupSecOpt = selinux.DupSecOpt
 
+// FormatMountLabel returns a string to be used by the mount command. Using
+// the SELinux `context` mount option. Changing labels of files on mount
+// points with this option can never be changed.
 // FormatMountLabel returns a string to be used by the mount command.
 // The format of this string will be used to alter the labeling of the mountpoint.
 // The string returned is suitable to be used as the options field of the mount command.
@@ -85,12 +88,27 @@ var DupSecOpt = selinux.DupSecOpt
 // the first parameter.  Second parameter is the label that you wish to apply
 // to all content in the mount point.
 func FormatMountLabel(src, mountLabel string) string {
+	return FormatMountLabelByType(src, mountLabel, "context")
+}
+
+// FormatMountLabelByType returns a string to be used by the mount command.
+// Allow caller to specify the mount options. For example using the SELinux
+// `fscontext` mount option would allow certain container processes to change
+// labels of files created on the mount points, where as `context` option does
+// not.
+// FormatMountLabelByType returns a string to be used by the mount command.
+// The format of this string will be used to alter the labeling of the mountpoint.
+// The string returned is suitable to be used as the options field of the mount command.
+// If you need to have additional mount point options, you can pass them in as
+// the first parameter.  Second parameter is the label that you wish to apply
+// to all content in the mount point.
+func FormatMountLabelByType(src, mountLabel, contextType string) string {
 	if mountLabel != "" {
 		switch src {
 		case "":
-			src = fmt.Sprintf("context=%q", mountLabel)
+			src = fmt.Sprintf("%s=%q", contextType, mountLabel)
 		default:
-			src = fmt.Sprintf("%s,context=%q", src, mountLabel)
+			src = fmt.Sprintf("%s,%s=%q", src, contextType, mountLabel)
 		}
 	}
 	return src
diff --git a/go-selinux/label/label_linux.go b/go-selinux/label/label_linux.go
index 12de0ae..f61a560 100644
--- a/go-selinux/label/label_linux.go
+++ b/go-selinux/label/label_linux.go
@@ -3,8 +3,6 @@ package label
 import (
 	"errors"
 	"fmt"
-	"os"
-	"os/user"
 	"strings"
 
 	"github.com/opencontainers/selinux/go-selinux"
@@ -113,50 +111,6 @@ func Relabel(path string, fileLabel string, shared bool) error {
 		return nil
 	}
 
-	exclude_paths := map[string]bool{
-		"/":           true,
-		"/bin":        true,
-		"/boot":       true,
-		"/dev":        true,
-		"/etc":        true,
-		"/etc/passwd": true,
-		"/etc/pki":    true,
-		"/etc/shadow": true,
-		"/home":       true,
-		"/lib":        true,
-		"/lib64":      true,
-		"/media":      true,
-		"/opt":        true,
-		"/proc":       true,
-		"/root":       true,
-		"/run":        true,
-		"/sbin":       true,
-		"/srv":        true,
-		"/sys":        true,
-		"/tmp":        true,
-		"/usr":        true,
-		"/var":        true,
-		"/var/lib":    true,
-		"/var/log":    true,
-	}
-
-	if home := os.Getenv("HOME"); home != "" {
-		exclude_paths[home] = true
-	}
-
-	if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
-		if usr, err := user.Lookup(sudoUser); err == nil {
-			exclude_paths[usr.HomeDir] = true
-		}
-	}
-
-	if path != "/" {
-		path = strings.TrimSuffix(path, "/")
-	}
-	if exclude_paths[path] {
-		return fmt.Errorf("SELinux relabeling of %s is not allowed", path)
-	}
-
 	if shared {
 		c, err := selinux.NewContext(fileLabel)
 		if err != nil {
diff --git a/go-selinux/label/label_linux_test.go b/go-selinux/label/label_linux_test.go
index f36469d..0200810 100644
--- a/go-selinux/label/label_linux_test.go
+++ b/go-selinux/label/label_linux_test.go
@@ -2,7 +2,6 @@ package label
 
 import (
 	"errors"
-	"io/ioutil"
 	"os"
 	"strings"
 	"testing"
@@ -98,11 +97,7 @@ func TestDuplicateLabel(t *testing.T) {
 func TestRelabel(t *testing.T) {
 	needSELinux(t)
 
-	testdir, err := ioutil.TempDir("/tmp", "")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(testdir)
+	testdir := t.TempDir()
 	label := "system_u:object_r:container_file_t:s0:c1,c2"
 	if err := Relabel(testdir, "", true); err != nil {
 		t.Fatalf("Relabel with no label failed: %v", err)
diff --git a/go-selinux/label/label_stub.go b/go-selinux/label/label_stub.go
index 02d2062..f21c80c 100644
--- a/go-selinux/label/label_stub.go
+++ b/go-selinux/label/label_stub.go
@@ -1,3 +1,4 @@
+//go:build !linux
 // +build !linux
 
 package label
diff --git a/go-selinux/label/label_stub_test.go b/go-selinux/label/label_stub_test.go
index 08137dd..9742e6e 100644
--- a/go-selinux/label/label_stub_test.go
+++ b/go-selinux/label/label_stub_test.go
@@ -1,12 +1,9 @@
+//go:build !linux
 // +build !linux
 
 package label
 
-import (
-	"io/ioutil"
-	"os"
-	"testing"
-)
+import "testing"
 
 const testLabel = "system_u:object_r:container_file_t:s0:c1,c2"
 
@@ -42,13 +39,7 @@ func TestInit(t *testing.T) {
 }
 
 func TestRelabel(t *testing.T) {
-	testdir, err := ioutil.TempDir("/tmp", "")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(testdir)
-	label := testLabel
-	if err := Relabel("/etc", label, false); err != nil {
+	if err := Relabel("/etc", testLabel, false); err != nil {
 		t.Fatalf("Relabel /etc succeeded")
 	}
 }
@@ -88,11 +79,12 @@ func TestCheckLabelCompile(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if _, err := FileLabel("/etc"); err != nil {
+	tmpDir := t.TempDir()
+	if _, err := FileLabel(tmpDir); err != nil {
 		t.Fatal(err)
 	}
 
-	if err := SetFileLabel("/etc", "foobar"); err != nil {
+	if err := SetFileLabel(tmpDir, "foobar"); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/go-selinux/label/label_test.go b/go-selinux/label/label_test.go
index ae5c048..fb172f3 100644
--- a/go-selinux/label/label_test.go
+++ b/go-selinux/label/label_test.go
@@ -17,4 +17,19 @@ func TestFormatMountLabel(t *testing.T) {
 	if test := FormatMountLabel("src", ""); test != expected {
 		t.Fatalf("Format failed. Expected %s, got %s", expected, test)
 	}
+
+	expected = `fscontext="foobar"`
+	if test := FormatMountLabelByType("", "foobar", "fscontext"); test != expected {
+		t.Fatalf("Format failed. Expected %s, got %s", expected, test)
+	}
+
+	expected = `src,fscontext="foobar"`
+	if test := FormatMountLabelByType("src", "foobar", "fscontext"); test != expected {
+		t.Fatalf("Format failed. Expected %s, got %s", expected, test)
+	}
+
+	expected = `src`
+	if test := FormatMountLabelByType("src", "", "rootcontext"); test != expected {
+		t.Fatalf("Format failed. Expected %s, got %s", expected, test)
+	}
 }
diff --git a/go-selinux/rchcon.go b/go-selinux/rchcon.go
deleted file mode 100644
index 897ecba..0000000
--- a/go-selinux/rchcon.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// +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/go-selinux/rchcon_go115.go b/go-selinux/rchcon_go115.go
deleted file mode 100644
index 2c8b033..0000000
--- a/go-selinux/rchcon_go115.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// +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/go-selinux/selinux.go b/go-selinux/selinux.go
index 5a59d15..af058b8 100644
--- a/go-selinux/selinux.go
+++ b/go-selinux/selinux.go
@@ -23,8 +23,13 @@ var (
 	// ErrEmptyPath is returned when an empty path has been specified.
 	ErrEmptyPath = errors.New("empty path")
 
+	// ErrInvalidLabel is returned when an invalid label is specified.
+	ErrInvalidLabel = errors.New("invalid Label")
+
 	// InvalidLabel is returned when an invalid label is specified.
-	InvalidLabel = errors.New("Invalid Label")
+	//
+	// Deprecated: use [ErrInvalidLabel].
+	InvalidLabel = ErrInvalidLabel
 
 	// ErrIncomparable is returned two levels are not comparable
 	ErrIncomparable = errors.New("incomparable levels")
@@ -144,7 +149,7 @@ func CalculateGlbLub(sourceRange, targetRange string) (string, error) {
 // of the program is finished to guarantee another goroutine does not migrate to the current
 // thread before execution is complete.
 func SetExecLabel(label string) error {
-	return setExecLabel(label)
+	return writeCon(attrPath("exec"), label)
 }
 
 // SetTaskLabel sets the SELinux label for the current thread, or an error.
@@ -152,21 +157,21 @@ func SetExecLabel(label string) error {
 // be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee
 // the current thread does not run in a new mislabeled thread.
 func SetTaskLabel(label string) error {
-	return setTaskLabel(label)
+	return writeCon(attrPath("current"), label)
 }
 
 // SetSocketLabel takes a process label and tells the kernel to assign the
 // label to the next socket that gets created. Calls to SetSocketLabel
 // should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
-// the the socket is created to guarantee another goroutine does not migrate
+// the socket is created to guarantee another goroutine does not migrate
 // to the current thread before execution is complete.
 func SetSocketLabel(label string) error {
-	return setSocketLabel(label)
+	return writeCon(attrPath("sockcreate"), label)
 }
 
 // SocketLabel retrieves the current socket label setting
 func SocketLabel() (string, error) {
-	return socketLabel()
+	return readCon(attrPath("sockcreate"))
 }
 
 // PeerLabel retrieves the label of the client on the other side of a socket
@@ -185,7 +190,7 @@ func SetKeyLabel(label string) error {
 
 // KeyLabel retrieves the current kernel keyring label setting
 func KeyLabel() (string, error) {
-	return keyLabel()
+	return readCon("/proc/self/attr/keycreate")
 }
 
 // Get returns the Context as a string
@@ -208,6 +213,11 @@ func ReserveLabel(label string) {
 	reserveLabel(label)
 }
 
+// MLSEnabled checks if MLS is enabled.
+func MLSEnabled() bool {
+	return isMLSEnabled()
+}
+
 // EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
 func EnforceMode() int {
 	return enforceMode()
@@ -220,7 +230,7 @@ func SetEnforceMode(mode int) error {
 }
 
 // DefaultEnforceMode returns the systems default SELinux mode Enforcing,
-// Permissive or Disabled. Note this is is just the default at boot time.
+// Permissive or Disabled. Note this is just the default at boot time.
 // EnforceMode tells you the systems current mode.
 func DefaultEnforceMode() int {
 	return defaultEnforceMode()
@@ -266,7 +276,7 @@ func CopyLevel(src, dest string) (string, error) {
 	return copyLevel(src, dest)
 }
 
-// Chcon changes the fpath file object to the SELinux label label.
+// Chcon changes the fpath file object to the SELinux label.
 // If fpath is a directory and recurse is true, then Chcon walks the
 // directory tree setting the label.
 //
@@ -284,7 +294,7 @@ func DupSecOpt(src string) ([]string, error) {
 // DisableSecOpt returns a security opt that can be used to disable SELinux
 // labeling support for future container processes.
 func DisableSecOpt() []string {
-	return disableSecOpt()
+	return []string{"disable"}
 }
 
 // GetDefaultContextWithLevel gets a single context for the specified SELinux user
diff --git a/go-selinux/selinux_linux.go b/go-selinux/selinux_linux.go
index ee602ab..f1e9597 100644
--- a/go-selinux/selinux_linux.go
+++ b/go-selinux/selinux_linux.go
@@ -8,15 +8,16 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
+	"io/fs"
 	"math/big"
 	"os"
-	"path"
+	"os/user"
 	"path/filepath"
 	"strconv"
 	"strings"
 	"sync"
 
+	"github.com/opencontainers/selinux/pkg/pwalkdir"
 	"golang.org/x/sys/unix"
 )
 
@@ -34,17 +35,17 @@ const (
 )
 
 type selinuxState struct {
+	mcsList       map[string]bool
+	selinuxfs     string
+	selinuxfsOnce sync.Once
 	enabledSet    bool
 	enabled       bool
-	selinuxfsOnce sync.Once
-	selinuxfs     string
-	mcsList       map[string]bool
 	sync.Mutex
 }
 
 type level struct {
-	sens uint
 	cats *big.Int
+	sens uint
 }
 
 type mlsRange struct {
@@ -53,10 +54,10 @@ type mlsRange struct {
 }
 
 type defaultSECtx struct {
-	user, level, scon   string
-	userRdr, defaultRdr io.Reader
-
-	verifier func(string) error
+	userRdr           io.Reader
+	verifier          func(string) error
+	defaultRdr        io.Reader
+	user, level, scon string
 }
 
 type levelItem byte
@@ -154,7 +155,7 @@ func findSELinuxfs() string {
 	}
 
 	// check if selinuxfs is available before going the slow path
-	fs, err := ioutil.ReadFile("/proc/filesystems")
+	fs, err := os.ReadFile("/proc/filesystems")
 	if err != nil {
 		return ""
 	}
@@ -291,7 +292,7 @@ func readCon(fpath string) (string, error) {
 }
 
 func readConFd(in *os.File) (string, error) {
-	data, err := ioutil.ReadAll(in)
+	data, err := io.ReadAll(in)
 	if err != nil {
 		return "", err
 	}
@@ -304,7 +305,7 @@ func classIndex(class string) (int, error) {
 	permpath := fmt.Sprintf("class/%s/index", class)
 	indexpath := filepath.Join(getSelinuxMountPoint(), permpath)
 
-	indexB, err := ioutil.ReadFile(indexpath)
+	indexB, err := os.ReadFile(indexpath)
 	if err != nil {
 		return -1, err
 	}
@@ -390,21 +391,19 @@ func lFileLabel(fpath string) (string, error) {
 	return string(label), nil
 }
 
-// setFSCreateLabel tells kernel the label to create all file system objects
-// created by this task. Setting label="" to return to default.
 func setFSCreateLabel(label string) error {
-	return writeAttr("fscreate", label)
+	return writeCon(attrPath("fscreate"), label)
 }
 
 // fsCreateLabel returns the default label the kernel which the kernel is using
 // for file system objects created by this task. "" indicates default.
 func fsCreateLabel() (string, error) {
-	return readAttr("fscreate")
+	return readCon(attrPath("fscreate"))
 }
 
 // currentLabel returns the SELinux label of the current process thread, or an error.
 func currentLabel() (string, error) {
-	return readAttr("current")
+	return readCon(attrPath("current"))
 }
 
 // pidLabel returns the SELinux label of the given pid, or an error.
@@ -415,7 +414,7 @@ func pidLabel(pid int) (string, error) {
 // ExecLabel returns the SELinux label that the kernel will use for any programs
 // that are executed by the current process thread, or an error.
 func execLabel() (string, error) {
-	return readAttr("exec")
+	return readCon(attrPath("exec"))
 }
 
 func writeCon(fpath, val string) error {
@@ -461,18 +460,10 @@ func attrPath(attr string) string {
 	})
 
 	if haveThreadSelf {
-		return path.Join(threadSelfPrefix, attr)
+		return filepath.Join(threadSelfPrefix, attr)
 	}
 
-	return path.Join("/proc/self/task/", strconv.Itoa(unix.Gettid()), "/attr/", attr)
-}
-
-func readAttr(attr string) (string, error) {
-	return readCon(attrPath(attr))
-}
-
-func writeAttr(attr, val string) error {
-	return writeCon(attrPath(attr), val)
+	return filepath.Join("/proc/self/task", strconv.Itoa(unix.Gettid()), "attr", attr)
 }
 
 // canonicalizeContext takes a context string and writes it to the kernel
@@ -559,30 +550,30 @@ func (l *level) parseLevel(levelStr string) error {
 
 // rangeStrToMLSRange marshals a string representation of a range.
 func rangeStrToMLSRange(rangeStr string) (*mlsRange, error) {
-	mlsRange := &mlsRange{}
-	levelSlice := strings.SplitN(rangeStr, "-", 2)
+	r := &mlsRange{}
+	l := strings.SplitN(rangeStr, "-", 2)
 
-	switch len(levelSlice) {
+	switch len(l) {
 	// rangeStr that has a low and a high level, e.g. s4:c0.c1023-s6:c0.c1023
 	case 2:
-		mlsRange.high = &level{}
-		if err := mlsRange.high.parseLevel(levelSlice[1]); err != nil {
-			return nil, fmt.Errorf("failed to parse high level %q: %w", levelSlice[1], err)
+		r.high = &level{}
+		if err := r.high.parseLevel(l[1]); err != nil {
+			return nil, fmt.Errorf("failed to parse high level %q: %w", l[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, fmt.Errorf("failed to parse low level %q: %w", levelSlice[0], err)
+		r.low = &level{}
+		if err := r.low.parseLevel(l[0]); err != nil {
+			return nil, fmt.Errorf("failed to parse low level %q: %w", l[0], err)
 		}
 	}
 
-	if mlsRange.high == nil {
-		mlsRange.high = mlsRange.low
+	if r.high == nil {
+		r.high = r.low
 	}
 
-	return mlsRange, nil
+	return r, nil
 }
 
 // bitsetToStr takes a category bitset and returns it in the
@@ -616,17 +607,17 @@ func bitsetToStr(c *big.Int) string {
 	return str
 }
 
-func (l1 *level) equal(l2 *level) bool {
-	if l2 == nil || l1 == nil {
-		return l1 == l2
+func (l *level) equal(l2 *level) bool {
+	if l2 == nil || l == nil {
+		return l == l2
 	}
-	if l1.sens != l2.sens {
+	if l2.sens != l.sens {
 		return false
 	}
-	if l2.cats == nil || l1.cats == nil {
-		return l2.cats == l1.cats
+	if l2.cats == nil || l.cats == nil {
+		return l2.cats == l.cats
 	}
-	return l1.cats.Cmp(l2.cats) == 0
+	return l.cats.Cmp(l2.cats) == 0
 }
 
 // String returns an mlsRange as a string.
@@ -720,36 +711,13 @@ func readWriteCon(fpath string, val string) (string, error) {
 	return readConFd(f)
 }
 
-// setExecLabel sets the SELinux label that the kernel will use for any programs
-// that are executed by the current process thread, or an error.
-func setExecLabel(label string) error {
-	return writeAttr("exec", label)
-}
-
-// setTaskLabel sets the SELinux label for the current thread, or an error.
-// This requires the dyntransition permission.
-func setTaskLabel(label string) error {
-	return writeAttr("current", label)
-}
-
-// setSocketLabel takes a process label and tells the kernel to assign the
-// label to the next socket that gets created
-func setSocketLabel(label string) error {
-	return writeAttr("sockcreate", label)
-}
-
-// socketLabel retrieves the current socket label setting
-func socketLabel() (string, error) {
-	return readAttr("sockcreate")
-}
-
 // peerLabel retrieves the label of the client on the other side of a socket
 func peerLabel(fd uintptr) (string, error) {
-	label, err := unix.GetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_PEERSEC)
+	l, 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
+	return l, nil
 }
 
 // setKeyLabel takes a process label and tells the kernel to assign the
@@ -765,15 +733,10 @@ func setKeyLabel(label string) error {
 	return err
 }
 
-// keyLabel retrieves the current kernel keyring label setting
-func keyLabel() (string, error) {
-	return readCon("/proc/self/attr/keycreate")
-}
-
 // get returns the Context as a string
 func (c Context) get() string {
-	if level := c["level"]; level != "" {
-		return c["user"] + ":" + c["role"] + ":" + c["type"] + ":" + level
+	if l := c["level"]; l != "" {
+		return c["user"] + ":" + c["role"] + ":" + c["type"] + ":" + l
 	}
 	return c["user"] + ":" + c["role"] + ":" + c["type"]
 }
@@ -785,7 +748,7 @@ func newContext(label string) (Context, error) {
 	if len(label) != 0 {
 		con := strings.SplitN(label, ":", 4)
 		if len(con) < 3 {
-			return c, InvalidLabel
+			return c, ErrInvalidLabel
 		}
 		c["user"] = con[0]
 		c["role"] = con[1]
@@ -815,14 +778,23 @@ func reserveLabel(label string) {
 }
 
 func selinuxEnforcePath() string {
-	return path.Join(getSelinuxMountPoint(), "enforce")
+	return filepath.Join(getSelinuxMountPoint(), "enforce")
+}
+
+// isMLSEnabled checks if MLS is enabled.
+func isMLSEnabled() bool {
+	enabledB, err := os.ReadFile(filepath.Join(getSelinuxMountPoint(), "mls"))
+	if err != nil {
+		return false
+	}
+	return bytes.Equal(enabledB, []byte{'1'})
 }
 
 // enforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
 func enforceMode() int {
 	var enforce int
 
-	enforceB, err := ioutil.ReadFile(selinuxEnforcePath())
+	enforceB, err := os.ReadFile(selinuxEnforcePath())
 	if err != nil {
 		return -1
 	}
@@ -836,11 +808,12 @@ 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)), 0o644)
+	//nolint:gosec // ignore G306: permissions to be 0600 or less.
+	return os.WriteFile(selinuxEnforcePath(), []byte(strconv.Itoa(mode)), 0o644)
 }
 
 // defaultEnforceMode returns the systems default SELinux mode Enforcing,
-// Permissive or Disabled. Note this is is just the default at boot time.
+// Permissive or Disabled. Note this is just the default at boot time.
 // EnforceMode tells you the systems current mode.
 func defaultEnforceMode() int {
 	switch readConfig(selinuxTag) {
@@ -940,7 +913,7 @@ func openContextFile() (*os.File, error) {
 	if f, err := os.Open(contextFile); err == nil {
 		return f, nil
 	}
-	return os.Open(filepath.Join(policyRoot(), "/contexts/lxc_contexts"))
+	return os.Open(filepath.Join(policyRoot(), "contexts", "lxc_contexts"))
 }
 
 func loadLabels() {
@@ -1043,7 +1016,8 @@ 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), 0o644)
+	//nolint:gosec // ignore G306: permissions to be 0600 or less.
+	return os.WriteFile(filepath.Join(getSelinuxMountPoint(), "context"), []byte(val), 0o644)
 }
 
 // copyLevel returns a label with the MLS/MCS level from src label replaced on
@@ -1072,22 +1046,7 @@ func copyLevel(src, dest string) (string, error) {
 	return tcon.Get(), nil
 }
 
-// Prevent users from relabeling system files
-func badPrefix(fpath string) error {
-	if fpath == "" {
-		return ErrEmptyPath
-	}
-
-	badPrefixes := []string{"/usr"}
-	for _, prefix := range badPrefixes {
-		if strings.HasPrefix(fpath, prefix) {
-			return fmt.Errorf("relabeling content in %s is not allowed", prefix)
-		}
-	}
-	return nil
-}
-
-// chcon changes the fpath file object to the SELinux label label.
+// chcon changes the fpath file object to the SELinux label.
 // If fpath is a directory and recurse is true, then chcon walks the
 // directory tree setting the label.
 func chcon(fpath string, label string, recurse bool) error {
@@ -1097,17 +1056,97 @@ func chcon(fpath string, label string, recurse bool) error {
 	if label == "" {
 		return nil
 	}
-	if err := badPrefix(fpath); err != nil {
-		return err
+
+	excludePaths := map[string]bool{
+		"/":           true,
+		"/bin":        true,
+		"/boot":       true,
+		"/dev":        true,
+		"/etc":        true,
+		"/etc/passwd": true,
+		"/etc/pki":    true,
+		"/etc/shadow": true,
+		"/home":       true,
+		"/lib":        true,
+		"/lib64":      true,
+		"/media":      true,
+		"/opt":        true,
+		"/proc":       true,
+		"/root":       true,
+		"/run":        true,
+		"/sbin":       true,
+		"/srv":        true,
+		"/sys":        true,
+		"/tmp":        true,
+		"/usr":        true,
+		"/var":        true,
+		"/var/lib":    true,
+		"/var/log":    true,
+	}
+
+	if home := os.Getenv("HOME"); home != "" {
+		excludePaths[home] = true
+	}
+
+	if sudoUser := os.Getenv("SUDO_USER"); sudoUser != "" {
+		if usr, err := user.Lookup(sudoUser); err == nil {
+			excludePaths[usr.HomeDir] = true
+		}
+	}
+
+	if fpath != "/" {
+		fpath = strings.TrimSuffix(fpath, "/")
+	}
+	if excludePaths[fpath] {
+		return fmt.Errorf("SELinux relabeling of %s is not allowed", fpath)
 	}
 
 	if !recurse {
-		return setFileLabel(fpath, label)
+		err := lSetFileLabel(fpath, label)
+		if err != nil {
+			// Check if file doesn't exist, must have been removed
+			if errors.Is(err, os.ErrNotExist) {
+				return nil
+			}
+			// Check if current label is correct on disk
+			flabel, nerr := lFileLabel(fpath)
+			if nerr == nil && flabel == label {
+				return nil
+			}
+			// Check if file doesn't exist, must have been removed
+			if errors.Is(nerr, os.ErrNotExist) {
+				return nil
+			}
+			return err
+		}
+		return nil
 	}
 
 	return rchcon(fpath, label)
 }
 
+func rchcon(fpath, label string) error { //revive:disable:cognitive-complexity
+	fastMode := false
+	// If the current label matches the new label, assume
+	// other labels are correct.
+	if cLabel, err := lFileLabel(fpath); err == nil && cLabel == label {
+		fastMode = true
+	}
+	return pwalkdir.Walk(fpath, func(p string, _ fs.DirEntry, _ error) error {
+		if fastMode {
+			if cLabel, err := lFileLabel(fpath); err == nil && cLabel == label {
+				return nil
+			}
+		}
+		err := lSetFileLabel(p, label)
+		// Walk a file tree can race with removal, so ignore ENOENT.
+		if errors.Is(err, os.ErrNotExist) {
+			return nil
+		}
+		return err
+	})
+}
+
 // dupSecOpt takes an SELinux process label and returns security options that
 // can be used to set the SELinux Type and Level for future container processes.
 func dupSecOpt(src string) ([]string, error) {
@@ -1136,12 +1175,6 @@ func dupSecOpt(src string) ([]string, error) {
 	return dup, nil
 }
 
-// disableSecOpt returns a security opt that can be used to disable SELinux
-// labeling support for future container processes.
-func disableSecOpt() []string {
-	return []string{"disable"}
-}
-
 // findUserInContext scans the reader for a valid SELinux context
 // match that is verified with the verifier. Invalid contexts are
 // skipped. It returns a matched context or an empty string if no
diff --git a/go-selinux/selinux_linux_test.go b/go-selinux/selinux_linux_test.go
index 7dc1fe6..c49e2bf 100644
--- a/go-selinux/selinux_linux_test.go
+++ b/go-selinux/selinux_linux_test.go
@@ -16,25 +16,66 @@ func TestSetFileLabel(t *testing.T) {
 		t.Skip("SELinux not enabled, skipping.")
 	}
 
-	tmp := "selinux_test"
-	con := "system_u:object_r:bin_t:s0"
-	out, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE, 0)
+	const (
+		tmpFile = "selinux_test"
+		tmpLink = "selinux_test_link"
+		con     = "system_u:object_r:bin_t:s0:c1,c2"
+		con2    = "system_u:object_r:bin_t:s0:c3,c4"
+	)
+
+	_ = os.Remove(tmpFile)
+	out, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0)
 	if err != nil {
-		t.Fatalf("unable to open %s: %s", tmp, err)
+		t.Fatal(err)
 	}
 	out.Close()
-	defer os.Remove(tmp)
+	defer os.Remove(tmpFile)
+
+	_ = os.Remove(tmpLink)
+	if err := os.Symlink(tmpFile, tmpLink); err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpLink)
 
-	if err := SetFileLabel(tmp, con); err != nil {
+	if err := SetFileLabel(tmpLink, con); err != nil {
 		t.Fatalf("SetFileLabel failed: %s", err)
 	}
-	filelabel, err := FileLabel(tmp)
+	filelabel, err := FileLabel(tmpLink)
 	if err != nil {
 		t.Fatalf("FileLabel failed: %s", err)
 	}
-	if con != filelabel {
+	if filelabel != con {
 		t.Fatalf("FileLabel failed, returned %s expected %s", filelabel, con)
 	}
+
+	// Using LfileLabel to verify that the symlink itself is not labeled.
+	linkLabel, err := LfileLabel(tmpLink)
+	if err != nil {
+		t.Fatalf("LfileLabel failed: %s", err)
+	}
+	if linkLabel == con {
+		t.Fatalf("Label on symlink should not be set, got: %q", linkLabel)
+	}
+
+	// Use LsetFileLabel to set a label on the symlink itself.
+	if err := LsetFileLabel(tmpLink, con2); err != nil {
+		t.Fatalf("LsetFileLabel failed: %s", err)
+	}
+	filelabel, err = FileLabel(tmpFile)
+	if err != nil {
+		t.Fatalf("FileLabel failed: %s", err)
+	}
+	if filelabel != con {
+		t.Fatalf("FileLabel was updated, returned %s expected %s", filelabel, con)
+	}
+
+	linkLabel, err = LfileLabel(tmpLink)
+	if err != nil {
+		t.Fatalf("LfileLabel failed: %s", err)
+	}
+	if linkLabel != con2 {
+		t.Fatalf("LfileLabel failed: returned %s expected %s", linkLabel, con2)
+	}
 }
 
 func TestKVMLabels(t *testing.T) {
@@ -186,6 +227,7 @@ func TestCanonicalizeContext(t *testing.T) {
 }
 
 func TestFindSELinuxfsInMountinfo(t *testing.T) {
+	//nolint:dupword // ignore duplicate words (sysfs sysfs)
 	const mountinfo = `18 62 0:17 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
 19 62 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
 20 62 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=3995472k,nr_inodes=998868,mode=755
@@ -307,10 +349,10 @@ func TestComputeCreateContext(t *testing.T) {
 
 func TestGlbLub(t *testing.T) {
 	tests := []struct {
+		expectedErr   error
 		sourceRange   string
 		targetRange   string
 		expectedRange string
-		expectedErr   error
 	}{
 		{
 			sourceRange:   "s0:c0.c100-s10:c0.c150",
diff --git a/go-selinux/selinux_stub.go b/go-selinux/selinux_stub.go
index 78743b0..bc3fd3b 100644
--- a/go-selinux/selinux_stub.go
+++ b/go-selinux/selinux_stub.go
@@ -1,10 +1,22 @@
+//go:build !linux
 // +build !linux
 
 package selinux
 
-func setDisabled() {
+func attrPath(string) string {
+	return ""
+}
+
+func readCon(fpath string) (string, error) {
+	return "", nil
+}
+
+func writeCon(string, string) error {
+	return nil
 }
 
+func setDisabled() {}
+
 func getEnabled() bool {
 	return false
 }
@@ -61,22 +73,6 @@ func calculateGlbLub(sourceRange, targetRange string) (string, error) {
 	return "", nil
 }
 
-func setExecLabel(label string) error {
-	return nil
-}
-
-func setTaskLabel(label string) error {
-	return nil
-}
-
-func setSocketLabel(label string) error {
-	return nil
-}
-
-func socketLabel() (string, error) {
-	return "", nil
-}
-
 func peerLabel(fd uintptr) (string, error) {
 	return "", nil
 }
@@ -85,17 +81,12 @@ func setKeyLabel(label string) error {
 	return nil
 }
 
-func keyLabel() (string, error) {
-	return "", nil
-}
-
 func (c Context) get() string {
 	return ""
 }
 
 func newContext(label string) (Context, error) {
-	c := make(Context)
-	return c, nil
+	return Context{}, nil
 }
 
 func clearLabels() {
@@ -104,6 +95,10 @@ func clearLabels() {
 func reserveLabel(label string) {
 }
 
+func isMLSEnabled() bool {
+	return false
+}
+
 func enforceMode() int {
 	return Disabled
 }
@@ -151,10 +146,6 @@ func dupSecOpt(src string) ([]string, error) {
 	return nil, nil
 }
 
-func disableSecOpt() []string {
-	return []string{"disable"}
-}
-
 func getDefaultContextWithLevel(user, level, scon string) (string, error) {
 	return "", nil
 }
diff --git a/go-selinux/selinux_stub_test.go b/go-selinux/selinux_stub_test.go
index 20b5b21..19ea636 100644
--- a/go-selinux/selinux_stub_test.go
+++ b/go-selinux/selinux_stub_test.go
@@ -1,3 +1,4 @@
+//go:build !linux
 // +build !linux
 
 package selinux
@@ -6,93 +7,121 @@ import (
 	"testing"
 )
 
-func TestSELinux(t *testing.T) {
+const testLabel = "foobar"
+
+func TestSELinuxStubs(t *testing.T) {
 	if GetEnabled() {
-		t.Fatal("SELinux enabled on non-linux.")
+		t.Error("SELinux enabled on non-linux.")
+	}
+
+	tmpDir := t.TempDir()
+	if _, err := FileLabel(tmpDir); err != nil {
+		t.Error(err)
 	}
 
-	if _, err := FileLabel("/etc"); err != nil {
-		t.Fatal(err)
+	if err := SetFileLabel(tmpDir, testLabel); err != nil {
+		t.Error(err)
 	}
 
-	if err := SetFileLabel("/etc", "foobar"); err != nil {
-		t.Fatal(err)
+	if _, err := LfileLabel(tmpDir); err != nil {
+		t.Error(err)
+	}
+	if err := LsetFileLabel(tmpDir, testLabel); err != nil {
+		t.Error(err)
 	}
 
-	if err := SetFSCreateLabel("foobar"); err != nil {
-		t.Fatal(err)
+	if err := SetFSCreateLabel(testLabel); err != nil {
+		t.Error(err)
 	}
 
 	if _, err := FSCreateLabel(); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
 	if _, err := CurrentLabel(); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
 
 	if _, err := PidLabel(0); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
 
 	ClearLabels()
 
-	ReserveLabel("foobar")
-	ReleaseLabel("foobar")
-	_, _ = DupSecOpt("foobar")
-	DisableSecOpt()
+	ReserveLabel(testLabel)
+	ReleaseLabel(testLabel)
+	if _, err := DupSecOpt(testLabel); err != nil {
+		t.Error(err)
+	}
+	if v := DisableSecOpt(); len(v) != 1 || v[0] != "disable" {
+		t.Errorf(`expected "disabled", got %v`, v)
+	}
 	SetDisabled()
 	if enabled := GetEnabled(); enabled {
-		t.Fatal("Should not be enabled")
+		t.Error("Should not be enabled")
 	}
-	if err := SetExecLabel("foobar"); err != nil {
-		t.Fatal(err)
+	if err := SetExecLabel(testLabel); err != nil {
+		t.Error(err)
 	}
-	if err := SetTaskLabel("foobar"); err != nil {
-		t.Fatal(err)
+	if err := SetTaskLabel(testLabel); err != nil {
+		t.Error(err)
 	}
 	if _, err := ExecLabel(); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
-	if _, err := CanonicalizeContext("foobar"); err != nil {
-		t.Fatal(err)
+	if _, err := CanonicalizeContext(testLabel); err != nil {
+		t.Error(err)
 	}
-	if _, err := ComputeCreateContext("foo", "bar", "foobar"); err != nil {
-		t.Fatal(err)
+	if _, err := ComputeCreateContext("foo", "bar", testLabel); err != nil {
+		t.Error(err)
 	}
-	if err := SetSocketLabel("foobar"); err != nil {
-		t.Fatal(err)
+	if err := SetSocketLabel(testLabel); err != nil {
+		t.Error(err)
 	}
-	if _, err := ClassIndex("foobar"); err != nil {
-		t.Fatal(err)
+	if _, err := ClassIndex(testLabel); err != nil {
+		t.Error(err)
 	}
 	if _, err := SocketLabel(); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
 	if _, err := PeerLabel(0); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
-	if err := SetKeyLabel("foobar"); err != nil {
-		t.Fatal(err)
+	if err := SetKeyLabel(testLabel); err != nil {
+		t.Error(err)
 	}
 	if _, err := KeyLabel(); err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
-	con, err := NewContext("foobar")
+	if err := SetExecLabel(testLabel); err != nil {
+		t.Error(err)
+	}
+	if _, err := ExecLabel(); err != nil {
+		t.Error(err)
+	}
+	con, err := NewContext(testLabel)
 	if err != nil {
-		t.Fatal(err)
+		t.Error(err)
 	}
 	con.Get()
-	if err := SetEnforceMode(1); err != nil {
-		t.Fatal(err)
-	}
-	DefaultEnforceMode()
-	EnforceMode()
-	ROFileLabel()
-	ContainerLabels()
-	if err := SecurityCheckContext("foobar"); err != nil {
-		t.Fatal(err)
-	}
-	if _, err := CopyLevel("foo", "bar"); err != nil {
-		t.Fatal(err)
+	if err = SetEnforceMode(1); err != nil {
+		t.Error(err)
+	}
+	if v := DefaultEnforceMode(); v != Disabled {
+		t.Errorf("expected %d, got %d", Disabled, v)
+	}
+	if v := EnforceMode(); v != Disabled {
+		t.Errorf("expected %d, got %d", Disabled, v)
+	}
+	if v := ROFileLabel(); v != "" {
+		t.Errorf(`expected "", got %q`, v)
+	}
+	if processLbl, fileLbl := ContainerLabels(); processLbl != "" || fileLbl != "" {
+		t.Errorf(`expected fileLbl="", fileLbl="" got processLbl=%q, fileLbl=%q`, processLbl, fileLbl)
+	}
+	if err = SecurityCheckContext(testLabel); err != nil {
+		t.Error(err)
+	}
+	if _, err = CopyLevel("foo", "bar"); err != nil {
+		t.Error(err)
 	}
 }
diff --git a/go.mod b/go.mod
index 93ee78e..56328f1 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,5 @@
 module github.com/opencontainers/selinux
 
-go 1.13
+go 1.19
 
-require golang.org/x/sys v0.0.0-20191115151921-52ab43148777
+require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8
diff --git a/go.sum b/go.sum
index 9a749d7..28ab7f7 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=
-golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/pkg/pwalk/pwalk.go b/pkg/pwalk/pwalk.go
index 202c80d..a28b4c4 100644
--- a/pkg/pwalk/pwalk.go
+++ b/pkg/pwalk/pwalk.go
@@ -8,6 +8,10 @@ import (
 	"sync"
 )
 
+// WalkFunc is the type of the function called by Walk to visit each
+// file or directory. It is an alias for [filepath.WalkFunc].
+//
+// Deprecated: use [github.com/opencontainers/selinux/pkg/pwalkdir] and [fs.WalkDirFunc].
 type WalkFunc = filepath.WalkFunc
 
 // Walk is a wrapper for filepath.Walk which can call multiple walkFn
@@ -29,6 +33,8 @@ type WalkFunc = filepath.WalkFunc
 // - 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.
+//
+// Deprecated: use [github.com/opencontainers/selinux/pkg/pwalkdir.Walk]
 func Walk(root string, walkFn WalkFunc) error {
 	return WalkN(root, walkFn, runtime.NumCPU()*2)
 }
@@ -38,6 +44,8 @@ func Walk(root string, walkFn WalkFunc) error {
 // num walkFn will be called at any one time.
 //
 // Please see Walk documentation for caveats of using this function.
+//
+// Deprecated: use [github.com/opencontainers/selinux/pkg/pwalkdir.WalkN]
 func WalkN(root string, walkFn WalkFunc, num int) error {
 	// make sure limit is sensible
 	if num < 1 {
@@ -110,6 +118,6 @@ func WalkN(root string, walkFn WalkFunc, num int) error {
 // walkArgs holds the arguments that were passed to the Walk or WalkN
 // functions.
 type walkArgs struct {
-	path string
 	info *os.FileInfo
+	path string
 }
diff --git a/pkg/pwalk/pwalk_test.go b/pkg/pwalk/pwalk_test.go
index 374df73..bf50613 100644
--- a/pkg/pwalk/pwalk_test.go
+++ b/pkg/pwalk/pwalk_test.go
@@ -2,7 +2,6 @@ package pwalk
 
 import (
 	"errors"
-	"io/ioutil"
 	"math/rand"
 	"os"
 	"path/filepath"
@@ -70,17 +69,18 @@ func TestWalkManyErrors(t *testing.T) {
 func makeManyDirs(prefix string, levels, dirs, files int) (count int, err error) {
 	for d := 0; d < dirs; d++ {
 		var dir string
-		dir, err = ioutil.TempDir(prefix, "d-")
+		dir, err = os.MkdirTemp(prefix, "d-")
 		if err != nil {
 			return
 		}
 		count++
 		for f := 0; f < files; f++ {
-			fi, err := ioutil.TempFile(dir, "f-")
+			var fi *os.File
+			fi, err = os.CreateTemp(dir, "f-")
 			if err != nil {
 				return count, err
 			}
-			fi.Close()
+			_ = fi.Close()
 			count++
 		}
 		if levels == 0 {
@@ -102,7 +102,7 @@ func makeManyDirs(prefix string, levels, dirs, files int) (count int, err error)
 // Total dirs: dirs^levels + dirs^(levels-1) + ... + dirs^1
 // Total files: total_dirs * files
 func prepareTestSet(levels, dirs, files int) (dir string, total int, err error) {
-	dir, err = ioutil.TempDir(".", "pwalk-test-")
+	dir, err = os.MkdirTemp(".", "pwalk-test-")
 	if err != nil {
 		return
 	}
@@ -134,31 +134,31 @@ func BenchmarkWalk(b *testing.B) {
 	)
 
 	benchmarks := []struct {
-		name string
 		walk filepath.WalkFunc
+		name string
 	}{
-		{"Empty", cbEmpty},
-		{"ReadFile", cbReadFile},
-		{"ChownChmod", cbChownChmod},
-		{"RandomSleep", cbRandomSleep},
+		{name: "Empty", walk: cbEmpty},
+		{name: "ReadFile", walk: cbReadFile},
+		{name: "ChownChmod", walk: cbChownChmod},
+		{name: "RandomSleep", walk: cbRandomSleep},
 	}
 
 	walkers := []struct {
-		name   string
 		walker walkerFunc
+		name   string
 	}{
-		{"filepath.Walk", filepath.Walk},
-		{"pwalk.Walk", Walk},
+		{name: "filepath.Walk", walker: filepath.Walk},
+		{name: "pwalk.Walk", walker: Walk},
 		// test WalkN with various values of N
-		{"pwalk.Walk1", genWalkN(1)},
-		{"pwalk.Walk2", genWalkN(2)},
-		{"pwalk.Walk4", genWalkN(4)},
-		{"pwalk.Walk8", genWalkN(8)},
-		{"pwalk.Walk16", genWalkN(16)},
-		{"pwalk.Walk32", genWalkN(32)},
-		{"pwalk.Walk64", genWalkN(64)},
-		{"pwalk.Walk128", genWalkN(128)},
-		{"pwalk.Walk256", genWalkN(256)},
+		{name: "pwalk.Walk1", walker: genWalkN(1)},
+		{name: "pwalk.Walk2", walker: genWalkN(2)},
+		{name: "pwalk.Walk4", walker: genWalkN(4)},
+		{name: "pwalk.Walk8", walker: genWalkN(8)},
+		{name: "pwalk.Walk16", walker: genWalkN(16)},
+		{name: "pwalk.Walk32", walker: genWalkN(32)},
+		{name: "pwalk.Walk64", walker: genWalkN(64)},
+		{name: "pwalk.Walk128", walker: genWalkN(128)},
+		{name: "pwalk.Walk256", walker: genWalkN(256)},
 	}
 
 	dir, total, err := prepareTestSet(levels, dirs, files)
@@ -173,15 +173,13 @@ func BenchmarkWalk(b *testing.B) {
 			walker := w.walker
 			walkFn := bm.walk
 			// preheat
-			err := w.walker(dir, bm.walk)
-			if err != nil {
+			if err := w.walker(dir, bm.walk); err != nil {
 				b.Errorf("walk failed: %v", err)
 			}
 			// benchmark
 			b.Run(bm.name+"/"+w.name, func(b *testing.B) {
 				for i := 0; i < b.N; i++ {
-					err := walker(dir, walkFn)
-					if err != nil {
+					if err := walker(dir, walkFn); err != nil {
 						b.Errorf("walk failed: %v", err)
 					}
 				}
@@ -208,12 +206,12 @@ func cbChownChmod(path string, info os.FileInfo, _ error) error {
 func cbReadFile(path string, info os.FileInfo, _ error) error {
 	var err error
 	if info.Mode().IsRegular() {
-		_, err = ioutil.ReadFile(path)
+		_, err = os.ReadFile(path)
 	}
 	return err
 }
 
 func cbRandomSleep(_ string, _ os.FileInfo, _ error) error {
-	time.Sleep(time.Duration(rand.Intn(500)) * time.Microsecond)
+	time.Sleep(time.Duration(rand.Intn(500)) * time.Microsecond) //nolint:gosec // ignore G404: Use of weak random number generator
 	return nil
 }
diff --git a/pkg/pwalkdir/pwalkdir.go b/pkg/pwalkdir/pwalkdir.go
index a5796b2..0f5d9f5 100644
--- a/pkg/pwalkdir/pwalkdir.go
+++ b/pkg/pwalkdir/pwalkdir.go
@@ -111,6 +111,6 @@ func WalkN(root string, walkFn fs.WalkDirFunc, num int) error {
 // walkArgs holds the arguments that were passed to the Walk or WalkN
 // functions.
 type walkArgs struct {
-	path  string
 	entry fs.DirEntry
+	path  string
 }
diff --git a/pkg/pwalkdir/pwalkdir_test.go b/pkg/pwalkdir/pwalkdir_test.go
index 9204179..c173001 100644
--- a/pkg/pwalkdir/pwalkdir_test.go
+++ b/pkg/pwalkdir/pwalkdir_test.go
@@ -6,7 +6,6 @@ package pwalkdir
 import (
 	"errors"
 	"io/fs"
-	"io/ioutil"
 	"math/rand"
 	"os"
 	"path/filepath"
@@ -74,13 +73,14 @@ func TestWalkDirManyErrors(t *testing.T) {
 func makeManyDirs(prefix string, levels, dirs, files int) (count int, err error) {
 	for d := 0; d < dirs; d++ {
 		var dir string
-		dir, err = ioutil.TempDir(prefix, "d-")
+		dir, err = os.MkdirTemp(prefix, "d-")
 		if err != nil {
 			return
 		}
 		count++
 		for f := 0; f < files; f++ {
-			fi, err := ioutil.TempFile(dir, "f-")
+			var fi *os.File
+			fi, err = os.CreateTemp(dir, "f-")
 			if err != nil {
 				return count, err
 			}
@@ -106,7 +106,7 @@ func makeManyDirs(prefix string, levels, dirs, files int) (count int, err error)
 // Total dirs: dirs^levels + dirs^(levels-1) + ... + dirs^1
 // Total files: total_dirs * files
 func prepareTestSet(levels, dirs, files int) (dir string, total int, err error) {
-	dir, err = ioutil.TempDir(".", "pwalk-test-")
+	dir, err = os.MkdirTemp(".", "pwalk-test-")
 	if err != nil {
 		return
 	}
@@ -138,31 +138,31 @@ func BenchmarkWalk(b *testing.B) {
 	)
 
 	benchmarks := []struct {
-		name string
 		walk fs.WalkDirFunc
+		name string
 	}{
-		{"Empty", cbEmpty},
-		{"ReadFile", cbReadFile},
-		{"ChownChmod", cbChownChmod},
-		{"RandomSleep", cbRandomSleep},
+		{name: "Empty", walk: cbEmpty},
+		{name: "ReadFile", walk: cbReadFile},
+		{name: "ChownChmod", walk: cbChownChmod},
+		{name: "RandomSleep", walk: cbRandomSleep},
 	}
 
 	walkers := []struct {
-		name   string
 		walker walkerFunc
+		name   string
 	}{
-		{"filepath.WalkDir", filepath.WalkDir},
-		{"pwalkdir.Walk", Walk},
+		{name: "filepath.WalkDir", walker: filepath.WalkDir},
+		{name: "pwalkdir.Walk", walker: Walk},
 		// test WalkN with various values of N
-		{"pwalkdir.Walk1", genWalkN(1)},
-		{"pwalkdir.Walk2", genWalkN(2)},
-		{"pwalkdir.Walk4", genWalkN(4)},
-		{"pwalkdir.Walk8", genWalkN(8)},
-		{"pwalkdir.Walk16", genWalkN(16)},
-		{"pwalkdir.Walk32", genWalkN(32)},
-		{"pwalkdir.Walk64", genWalkN(64)},
-		{"pwalkdir.Walk128", genWalkN(128)},
-		{"pwalkdir.Walk256", genWalkN(256)},
+		{name: "pwalkdir.Walk1", walker: genWalkN(1)},
+		{name: "pwalkdir.Walk2", walker: genWalkN(2)},
+		{name: "pwalkdir.Walk4", walker: genWalkN(4)},
+		{name: "pwalkdir.Walk8", walker: genWalkN(8)},
+		{name: "pwalkdir.Walk16", walker: genWalkN(16)},
+		{name: "pwalkdir.Walk32", walker: genWalkN(32)},
+		{name: "pwalkdir.Walk64", walker: genWalkN(64)},
+		{name: "pwalkdir.Walk128", walker: genWalkN(128)},
+		{name: "pwalkdir.Walk256", walker: genWalkN(256)},
 	}
 
 	dir, total, err := prepareTestSet(levels, dirs, files)
@@ -177,15 +177,13 @@ func BenchmarkWalk(b *testing.B) {
 			walker := w.walker
 			walkFn := bm.walk
 			// preheat
-			err := w.walker(dir, bm.walk)
-			if err != nil {
+			if err := w.walker(dir, bm.walk); err != nil {
 				b.Errorf("walk failed: %v", err)
 			}
 			// benchmark
 			b.Run(bm.name+"/"+w.name, func(b *testing.B) {
 				for i := 0; i < b.N; i++ {
-					err := walker(dir, walkFn)
-					if err != nil {
+					if err := walker(dir, walkFn); err != nil {
 						b.Errorf("walk failed: %v", err)
 					}
 				}
@@ -212,12 +210,12 @@ func cbChownChmod(path string, e fs.DirEntry, _ error) error {
 func cbReadFile(path string, e fs.DirEntry, _ error) error {
 	var err error
 	if e.Type().IsRegular() {
-		_, err = ioutil.ReadFile(path)
+		_, err = os.ReadFile(path)
 	}
 	return err
 }
 
 func cbRandomSleep(_ string, _ fs.DirEntry, _ error) error {
-	time.Sleep(time.Duration(rand.Intn(500)) * time.Microsecond)
+	time.Sleep(time.Duration(rand.Intn(500)) * time.Microsecond) //nolint:gosec // ignore G404: Use of weak random number generator
 	return nil
 }

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/opencontainers/selinux/go-selinux/rchcon.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/opencontainers/selinux/go-selinux/rchcon_go115.go

No differences were encountered in the control files

More details

Full run details