diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 07d3d2ad..c92b3a58 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,16 +4,16 @@ jobs: test: strategy: matrix: - go-version: [1.17.x, 1.18.x] - platform: [ubuntu-20.04, ubuntu-22.04, windows-latest, macos-11] + go-version: [1.17.x, 1.21.x, 1.22.x] + platform: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, windows-latest, macos-12, macos-14] runs-on: ${{ matrix.platform }} steps: - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: go mod tidy run: | make tidy diff --git a/Makefile b/Makefile index ba4ddc4c..14e79bd9 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ lint: $(BINDIR)/golangci-lint done $(BINDIR)/golangci-lint: $(BINDIR) - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(BINDIR) v1.45.2 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(BINDIR) v1.59.1 $(BINDIR): mkdir -p $(BINDIR) diff --git a/mount/go.mod b/mount/go.mod index f0219792..a2e0957e 100644 --- a/mount/go.mod +++ b/mount/go.mod @@ -1,8 +1,8 @@ module github.com/moby/sys/mount -go 1.16 +go 1.17 require ( github.com/moby/sys/mountinfo v0.6.2 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a + golang.org/x/sys v0.1.0 ) diff --git a/mount/go.sum b/mount/go.sum index a2d6c47a..b57b0acb 100644 --- a/mount/go.sum +++ b/mount/go.sum @@ -1,4 +1,5 @@ github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/mount/mount_unix.go b/mount/mount_unix.go index 4053fbbe..56ca7472 100644 --- a/mount/mount_unix.go +++ b/mount/mount_unix.go @@ -23,7 +23,7 @@ func Mount(device, target, mType, options string) error { // a normal unmount. If target is not a mount point, no error is returned. func Unmount(target string) error { err := unix.Unmount(target, mntDetach) - if err == nil || err == unix.EINVAL { //nolint:errorlint // unix errors are bare + if err == nil || err == unix.EINVAL { // Ignore "not mounted" error here. Note the same error // can be returned if flags are invalid, so this code // assumes that the flags value is always correct. @@ -71,7 +71,8 @@ func RecursiveUnmount(target string) error { if err != nil { if i == lastMount { if suberr != nil { - return fmt.Errorf("%w (possible cause: %s)", err, suberr) + // TODO: switch to %w for suberr once we stop supporting go < 1.20. + return fmt.Errorf("%w (possible cause: %s)", err, suberr.Error()) } return err } diff --git a/mountinfo/go.mod b/mountinfo/go.mod index e1bcdfe7..42a37060 100644 --- a/mountinfo/go.mod +++ b/mountinfo/go.mod @@ -1,5 +1,5 @@ module github.com/moby/sys/mountinfo -go 1.16 +go 1.17 -require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +require golang.org/x/sys v0.1.0 diff --git a/mountinfo/go.sum b/mountinfo/go.sum index af14a66e..b69ea857 100644 --- a/mountinfo/go.sum +++ b/mountinfo/go.sum @@ -1,2 +1,2 @@ -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/mountinfo/mounted_linux.go b/mountinfo/mounted_linux.go index e78e7261..58f13c26 100644 --- a/mountinfo/mounted_linux.go +++ b/mountinfo/mounted_linux.go @@ -51,7 +51,7 @@ func mountedByOpenat2(path string) (bool, error) { Resolve: unix.RESOLVE_NO_XDEV, }) _ = unix.Close(dirfd) - switch err { //nolint:errorlint // unix errors are bare + switch err { case nil: // definitely not a mount _ = unix.Close(fd) return false, nil diff --git a/sequential/go.mod b/sequential/go.mod index 59c2412a..4e620fe1 100644 --- a/sequential/go.mod +++ b/sequential/go.mod @@ -2,4 +2,4 @@ module github.com/moby/sys/sequential go 1.17 -require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +require golang.org/x/sys v0.1.0 diff --git a/sequential/go.sum b/sequential/go.sum index af14a66e..b69ea857 100644 --- a/sequential/go.sum +++ b/sequential/go.sum @@ -1,2 +1,2 @@ -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/sequential/sequential_windows.go b/sequential/sequential_windows.go index f489d30b..3500ecc6 100644 --- a/sequential/sequential_windows.go +++ b/sequential/sequential_windows.go @@ -111,8 +111,10 @@ func openSequential(path string, mode int) (fd windows.Handle, err error) { } // Helpers for CreateTemp -var rand uint32 -var randmu sync.Mutex +var ( + rand uint32 + randmu sync.Mutex +) func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) diff --git a/signal/go.mod b/signal/go.mod index b664ff21..f76c5ad3 100644 --- a/signal/go.mod +++ b/signal/go.mod @@ -1,5 +1,5 @@ module github.com/moby/sys/signal -go 1.16 +go 1.17 -require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +require golang.org/x/sys v0.1.0 diff --git a/signal/go.sum b/signal/go.sum index af14a66e..b69ea857 100644 --- a/signal/go.sum +++ b/signal/go.sum @@ -1,2 +1,2 @@ -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/symlink/README.md b/symlink/README.md deleted file mode 100644 index 8dba54fd..00000000 --- a/symlink/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Package symlink implements EvalSymlinksInScope which is an extension of filepath.EvalSymlinks, -as well as a Windows long-path aware version of filepath.EvalSymlinks -from the [Go standard library](https://golang.org/pkg/path/filepath). - -The code from filepath.EvalSymlinks has been adapted in fs.go. -Please read the LICENSE.BSD file that governs fs.go and LICENSE.APACHE for fs_test.go. diff --git a/symlink/doc.go b/symlink/doc.go index d2de7ecd..7c22ca27 100644 --- a/symlink/doc.go +++ b/symlink/doc.go @@ -1,4 +1,11 @@ -// Package symlink implements EvalSymlinksInScope which is an extension of -// filepath.EvalSymlinks, as well as a Windows long-path aware version of -// filepath.EvalSymlinks from the Go standard library (https://golang.org/pkg/path/filepath). +// Package symlink implements [FollowSymlinkInScope] which is an extension +// of [path/filepath.EvalSymlinks], as well as a Windows long-path aware +// version of [path/filepath.EvalSymlinks] from the Go standard library. +// +// The code from [path/filepath.EvalSymlinks] has been adapted in fs.go. +// Read the [LICENSE.BSD] file that governs fs.go and [LICENSE.APACHE] for +// fs_unix_test.go. +// +// [LICENSE.APACHE]: https://github.com/moby/sys/blob/symlink/v0.2.0/symlink/LICENSE.APACHE +// [LICENSE.BSD]: https://github.com/moby/sys/blob/symlink/v0.2.0/symlink/LICENSE.APACHE package symlink diff --git a/symlink/fs.go b/symlink/fs.go index ac26ca11..6b826613 100644 --- a/symlink/fs.go +++ b/symlink/fs.go @@ -2,7 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.BSD file. -// This code is a modified version of path/filepath/symlink.go from the Go standard library. +// This code is a modified version of path/filepath/symlink.go from the Go +// standard library in [docker@fa3ec89], which was based on [go1.3.3], +// with Windows implementatinos being added in [docker@9b648df]. +// +// [docker@fa3ec89]: https://github.com/moby/moby/commit/fa3ec89515431ce425f924c8a9a804d5cb18382f +// [go1.3.3]: https://github.com/golang/go/blob/go1.3.3/src/pkg/path/filepath/symlink.go +// [docker@9b648df]: https://github.com/moby/moby/commit/9b648dfac6453de5944ee4bb749115d85a253a05 package symlink @@ -14,8 +20,34 @@ import ( "strings" ) -// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an -// absolute path. This function handles paths in a platform-agnostic manner. +// FollowSymlinkInScope evaluates symbolic links in "path" within a scope "root" +// and returns a result guaranteed to be contained within the scope "root" at +// the time of the call. It returns an error of either "path" or "root" cannot +// be converted to an absolute path. +// +// Symbolic links in "root" are not evaluated and left as-is. Errors encountered +// while attempting to evaluate symlinks in path are returned, but non-existing +// paths are valid and do not constitute an error. "path" must contain "root" +// as a prefix, or else an error is returned. Trying to break out from "root" +// does not constitute an error, instead resolves the path within "root". +// +// Example: +// +// // If "/foo/bar" is a symbolic link to "/outside": +// FollowSymlinkInScope("/foo/bar", "/foo") // Returns "/foo/outside" instead of "/outside" +// +// IMPORTANT: It is the caller's responsibility to call FollowSymlinkInScope +// after relevant symbolic links are created to avoid Time-of-check Time-of-use +// (TOCTOU) race conditions ([CWE-367]). No additional symbolic links must be +// created after evaluating, as those could potentially make a previously-safe +// path unsafe. +// +// For example, if "/foo/bar" does not exist, FollowSymlinkInScope("/foo/bar", "/foo") +// evaluates the path to "/foo/bar". If one makes "/foo/bar" a symbolic link to +// "/baz" subsequently, then "/foo/bar" should no longer be considered safely +// contained in "/foo". +// +// [CWE-367]: https://cwe.mitre.org/data/definitions/367.html func FollowSymlinkInScope(path, root string) (string, error) { path, err := filepath.Abs(filepath.FromSlash(path)) if err != nil { @@ -28,24 +60,9 @@ func FollowSymlinkInScope(path, root string) (string, error) { return evalSymlinksInScope(path, root) } -// evalSymlinksInScope will evaluate symlinks in `path` within a scope `root` and return -// a result guaranteed to be contained within the scope `root`, at the time of the call. -// Symlinks in `root` are not evaluated and left as-is. -// Errors encountered while attempting to evaluate symlinks in path will be returned. -// Non-existing paths are valid and do not constitute an error. -// `path` has to contain `root` as a prefix, or else an error will be returned. -// Trying to break out from `root` does not constitute an error. -// -// Example: -// -// If /foo/bar -> /outside, -// FollowSymlinkInScope("/foo/bar", "/foo") == "/foo/outside" instead of "/outside" -// -// IMPORTANT: it is the caller's responsibility to call evalSymlinksInScope *after* relevant symlinks -// are created and not to create subsequently, additional symlinks that could potentially make a -// previously-safe path, unsafe. Example: if /foo/bar does not exist, evalSymlinksInScope("/foo/bar", "/foo") -// would return "/foo/bar". If one makes /foo/bar a symlink to /baz subsequently, then "/foo/bar" should -// no longer be considered safely contained in "/foo". +// evalSymlinksInScope evaluates symbolic links in "path" within a scope "root" +// and returns a result guaranteed to be contained within the scope "root" at +// the time of the call. Refer to [FollowSymlinkInScope] for details. func evalSymlinksInScope(path, root string) (string, error) { root = filepath.Clean(root) if path == root { @@ -133,11 +150,15 @@ func evalSymlinksInScope(path, root string) (string, error) { return filepath.Clean(root + filepath.Clean(string(filepath.Separator)+b.String())), nil } +// EvalSymlinks is a modified version of [path/filepath.EvalSymlinks] from +// the Go standard library with support for Windows long paths (paths prepended +// with "\\?\"). On non-Windows platforms, it's an alias for [path/filepath.EvalSymlinks]. +// // EvalSymlinks returns the path name after the evaluation of any symbolic -// links. -// If path is relative the result will be relative to the current directory, -// unless one of the components is an absolute symbolic link. -// This version has been updated to support long paths prepended with `\\?\`. +// links. If path is relative, the result will be relative to the current +// directory, unless one of the components is an absolute symbolic link. +// +// EvalSymlinks calls [path/filepath.Clean] on the result. func EvalSymlinks(path string) (string, error) { return evalSymlinks(path) } diff --git a/symlink/fs_windows.go b/symlink/fs_windows.go index 87fd1b88..819b7289 100644 --- a/symlink/fs_windows.go +++ b/symlink/fs_windows.go @@ -1,3 +1,15 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.BSD file. + +// This code is a modified version of [path/filepath/symlink_windows.go] +// and [path/filepath/symlink.go] from the Go 1.4.2 standard library, and +// added in [docker@9b648df]. +// +// [path/filepath/symlink_windows.go]: https://github.com/golang/go/blob/go1.4.2/src/path/filepath/symlink_windows.go +// [path/filepath/symlink.go]: https://github.com/golang/go/blob/go1.4.2/src/path/filepath/symlink.go +// [docker@9b648df]: https://github.com/moby/moby/commit/9b648dfac6453de5944ee4bb749115d85a253a05 + package symlink import ( diff --git a/symlink/go.mod b/symlink/go.mod index f7ab8a30..9b73f0c5 100644 --- a/symlink/go.mod +++ b/symlink/go.mod @@ -1,5 +1,5 @@ module github.com/moby/sys/symlink -go 1.16 +go 1.17 -require golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +require golang.org/x/sys v0.1.0 diff --git a/symlink/go.sum b/symlink/go.sum index af14a66e..b69ea857 100644 --- a/symlink/go.sum +++ b/symlink/go.sum @@ -1,2 +1,2 @@ -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/user/user.go b/user/user.go index 984466d1..198c4936 100644 --- a/user/user.go +++ b/user/user.go @@ -197,7 +197,6 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { for { var line []byte line, isPrefix, err = rd.ReadLine() - if err != nil { // We should return no error if EOF is reached // without a match.