From 3c4bc1617caa7d2cd58e2348171506ec74af1d37 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Mon, 12 Oct 2026 13:01:31 -0400 Subject: [PATCH] Bump to Buildah v1.45.2 and storage v1.64.2 Signed-off-by: Matt Heon --- go.mod | 4 +- go.sum | 8 +- vendor/go.podman.io/buildah/.packit.yaml | 1 + vendor/go.podman.io/buildah/CHANGELOG.md | 7 + vendor/go.podman.io/buildah/changelog.txt | 6 + vendor/go.podman.io/buildah/copier/copier.go | 143 ++++++++++++-- vendor/go.podman.io/buildah/define/types.go | 2 +- vendor/go.podman.io/storage/VERSION | 2 +- .../storage/internal/rootlookupcache/cache.go | 148 ++++++++++++++ .../system => internal/stat}/stat_common.go | 2 +- .../system => internal/stat}/stat_darwin.go | 8 +- .../system => internal/stat}/stat_freebsd.go | 8 +- .../storage/internal/stat/stat_linux.go | 16 ++ .../system => internal/stat}/stat_netbsd.go | 8 +- .../system => internal/stat}/stat_openbsd.go | 8 +- .../system => internal/stat}/stat_solaris.go | 8 +- .../storage/internal/stat/stat_unix.go | 73 +++++++ .../storage/internal/stat/stat_windows.go | 68 +++++++ .../storage/internal/xattrs/errors.go | 8 + .../internal/xattrs/extattr_freebsd.go | 115 +++++++++++ .../storage/internal/xattrs/xattrs_darwin.go | 181 +++++++++++++++++ .../storage/internal/xattrs/xattrs_freebsd.go | 177 +++++++++++++++++ .../storage/internal/xattrs/xattrs_linux.go | 184 ++++++++++++++++++ .../storage/internal/xattrs/xattrs_unix.go | 48 +++++ .../internal/xattrs/xattrs_unsupported.go | 78 ++++++++ .../storage/pkg/archive/archive.go | 105 ++++++---- .../storage/pkg/archive/archive_linux.go | 21 +- .../storage/pkg/archive/archive_other.go | 2 +- .../storage/pkg/archive/changes.go | 64 ++++-- .../storage/pkg/archive/changes_linux.go | 99 +++++++--- .../storage/pkg/archive/changes_other.go | 23 ++- .../storage/pkg/idtools/idtools.go | 21 +- .../storage/pkg/idtools/idtools_supported.go | 47 +++-- .../go.podman.io/storage/pkg/system/errors.go | 6 +- .../storage/pkg/system/extattr_freebsd.go | 94 +-------- .../go.podman.io/storage/pkg/system/lstat.go | 17 ++ .../storage/pkg/system/lstat_unix.go | 14 +- .../storage/pkg/system/lstat_windows.go | 18 +- .../storage/pkg/system/stat_linux.go | 19 +- .../storage/pkg/system/stat_unix.go | 60 +----- .../storage/pkg/system/stat_windows.go | 63 +----- .../go.podman.io/storage/pkg/system/xattrs.go | 55 ++++-- .../storage/pkg/system/xattrs_darwin.go | 134 ------------- .../storage/pkg/system/xattrs_freebsd.go | 143 -------------- .../storage/pkg/system/xattrs_linux.go | 146 -------------- .../storage/pkg/system/xattrs_unsupported.go | 29 --- .../storage/pkg/unshare/unshare_linux.go | 14 +- vendor/modules.txt | 7 +- 48 files changed, 1626 insertions(+), 886 deletions(-) create mode 100644 vendor/go.podman.io/storage/internal/rootlookupcache/cache.go rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_common.go (94%) rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_darwin.go (51%) rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_freebsd.go (70%) create mode 100644 vendor/go.podman.io/storage/internal/stat/stat_linux.go rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_netbsd.go (51%) rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_openbsd.go (50%) rename vendor/go.podman.io/storage/{pkg/system => internal/stat}/stat_solaris.go (50%) create mode 100644 vendor/go.podman.io/storage/internal/stat/stat_unix.go create mode 100644 vendor/go.podman.io/storage/internal/stat/stat_windows.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/errors.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go create mode 100644 vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go create mode 100644 vendor/go.podman.io/storage/pkg/system/lstat.go diff --git a/go.mod b/go.mod index 1fd7c8821a..420277e7d5 100644 --- a/go.mod +++ b/go.mod @@ -64,10 +64,10 @@ require ( github.com/vbauerster/mpb/v8 v8.14.0 github.com/vishvananda/netlink v1.3.1 go.etcd.io/bbolt v1.5.0 - go.podman.io/buildah v1.45.1 + go.podman.io/buildah v1.45.2 go.podman.io/common v0.69.2 go.podman.io/image/v5 v5.41.2 - go.podman.io/storage v1.64.1 + go.podman.io/storage v1.64.2 golang.org/x/crypto v0.57.0 golang.org/x/net v0.59.0 golang.org/x/sync v0.23.0 diff --git a/go.sum b/go.sum index c5cb6e8ab2..1633301ac2 100644 --- a/go.sum +++ b/go.sum @@ -433,14 +433,14 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= -go.podman.io/buildah v1.45.1 h1:QYJrpxBaTM9b5IraRwG53kcWxfrBAlSqBJAQIgrIuZY= -go.podman.io/buildah v1.45.1/go.mod h1:RwoZrDdgSYBQHHqBVPgnOIa1Ee7cLeTEgL8uhMGb/pA= +go.podman.io/buildah v1.45.2 h1:nMyRiNCoIK4Ign2z1ho0HPoq7vMzP+xNYp4i4RFC37E= +go.podman.io/buildah v1.45.2/go.mod h1:cXUOsSy78r05MpwDfvdTwZPG/Ksobx87wvrFFVpekNg= go.podman.io/common v0.69.2 h1:5ZV7xkfFTOwgf21xaqNZSqjy9G9JVG7p2bF5Tj6IVjE= go.podman.io/common v0.69.2/go.mod h1:cQ1+nD9QcwnUGiuJHMX61SoYK5MU90NDbl0lLWmDF2s= go.podman.io/image/v5 v5.41.2 h1:YnVaFrGLPWSBmipKfNvlOsGcBtk5bUxlPxQ+A6CS/uo= go.podman.io/image/v5 v5.41.2/go.mod h1:T/bzxEFpdpAzyHabPmcq5K+my3wBsht22sFkFjJSKT4= -go.podman.io/storage v1.64.1 h1:DqbG8scW/bD3xHiSRzV7O5fzn46qVC+SwmUN6C6Fr5w= -go.podman.io/storage v1.64.1/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc= +go.podman.io/storage v1.64.2 h1:sIBU2vsdGo2gghTF17oxw4G4gojEZkPmt0LKiZWIwqc= +go.podman.io/storage v1.64.2/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= diff --git a/vendor/go.podman.io/buildah/.packit.yaml b/vendor/go.podman.io/buildah/.packit.yaml index f80870aeff..57be1a6a3d 100644 --- a/vendor/go.podman.io/buildah/.packit.yaml +++ b/vendor/go.podman.io/buildah/.packit.yaml @@ -131,6 +131,7 @@ jobs: update_release: false dist_git_branches: &fedora_targets - fedora-rawhide + - fedora-45 # Sync to CentOS Stream - job: propose_downstream diff --git a/vendor/go.podman.io/buildah/CHANGELOG.md b/vendor/go.podman.io/buildah/CHANGELOG.md index 53f1a419fa..59349556f6 100644 --- a/vendor/go.podman.io/buildah/CHANGELOG.md +++ b/vendor/go.podman.io/buildah/CHANGELOG.md @@ -2,6 +2,13 @@ # Changelog +## v1.45.2 (2026-10-12) + + copier: reject relative symlink escapes, only resolve absolute ones + copier: put: resolve symlinks before os.Root path check + ci: pin go version for lint to 1.26 + vendor: update storage to v1.64.2 + ## v1.45.1 (2026-09-15) Bump go.podman.io/storage, go.podman.io/image/v5, and go.podman.io/common diff --git a/vendor/go.podman.io/buildah/changelog.txt b/vendor/go.podman.io/buildah/changelog.txt index e6f15f62f6..e25e7c1eef 100644 --- a/vendor/go.podman.io/buildah/changelog.txt +++ b/vendor/go.podman.io/buildah/changelog.txt @@ -1,3 +1,9 @@ +- Changelog for v1.45.2 (2026-10-12) + * copier: reject relative symlink escapes, only resolve absolute ones + * copier: put: resolve symlinks before os.Root path check + * ci: pin go version for lint to 1.26 + * vendor: update storage to v1.64.2 + - Changelog for v1.45.1 (2026-09-15) * Bump go.podman.io/storage, go.podman.io/image/v5, and go.podman.io/common * Add FIXMEs for the read path diff --git a/vendor/go.podman.io/buildah/copier/copier.go b/vendor/go.podman.io/buildah/copier/copier.go index 0efa898df5..99b8d9751f 100644 --- a/vendor/go.podman.io/buildah/copier/copier.go +++ b/vendor/go.podman.io/buildah/copier/copier.go @@ -681,6 +681,135 @@ func cleanerReldirectory(candidate string) string { return cleaned } +// pathHasAbsoluteSymlink resolves name component by component using readlink +// (typically osRoot.Readlink) and returns true if any symlink in the +// resolution has an absolute target. This distinguishes absolute symlinks +// (e.g. /var/run -> /run) that need chroot-style resolution from relative +// escapes (e.g. escape -> ..) that should be rejected. +// +// When a relative symlink is encountered, its target components are +// prepended to the remaining work list, mirroring the algorithm used by +// resolvePath and os.Root. Escape detection relies on readlink rejecting +// paths that leave the root (e.g. readlink("..") must fail); without that +// guarantee, this function does not confine. +func pathHasAbsoluteSymlink(name string, readlink func(string) (string, error)) bool { + workingRel := "" + components := strings.Split(filepath.Clean(name), string(os.PathSeparator)) + linksFollowed := 0 + + for len(components) > 0 { + comp := components[0] + components = components[1:] + if comp == "" || comp == "." { + continue + } + + nextRel := filepath.Join(workingRel, comp) + target, err := readlink(nextRel) + if err != nil { + // Not a symlink (or does not exist): advance. + // When readlink is osRoot.Readlink, this also covers + // the case where ".." escapes the root. Readlink + // rejects the path, we advance with a broken workingRel, + // and all subsequent readlink calls fail too. + workingRel = nextRel + continue + } + + linksFollowed++ + if linksFollowed > maxLoopsFollowed { + return false + } + + if filepath.IsAbs(target) || (len(target) > 0 && looksLikeAbs(target)) { + return true + } + + // Relative symlink: prepend target components to the work + // list and continue from the current position. Any ".." + // components are handled naturally by filepath.Join at the + // top of the loop, and readlink will reject paths that + // escape the root. + components = append(strings.Split(target, string(os.PathSeparator)), components...) + } + return false +} + +// lstatPathComponents walks name component by component using osRoot.Lstat, +// returning the first error encountered (or nil). This is used as a +// read-only compatibility check against recent versions of go-archive/tar +// which reject paths that escape from the root. +func lstatPathComponents(osRoot *os.Root, name string) error { + var components []string + for name != "." && name != "/" { + components = append(components, name) + name = filepath.Dir(name) + } + for _, partial := range slices.Backward(components) { + if _, err := osRoot.Lstat(partial); err != nil { + return err + } + } + return nil +} + +// checkArchivePathEscape is a read-only check verifying that a tar entry +// path does not escape from the target directory. It never modifies any +// paths used for actual file operations. +// +// osRoot must be opened on targetDirectory (the resolved form of directory +// within root). root is the container rootfs used by resolvePath as the +// chroot boundary for symlink resolution. +// +// osRoot rejects symlinks with absolute targets (e.g. /var/run -> /run), +// even when those symlinks are valid inside a container. When the raw path +// is rejected and the rejection is caused by an absolute symlink, this +// function resolves symlinks with chroot-like semantics and re-checks the +// resolved path through osRoot. Relative escapes (e.g. escape -> ..) are +// kept rejected, matching Docker behavior (moby/go-archive#93). +func checkArchivePathEscape(osRoot *os.Root, root, directory, targetDirectory, cleanerHdrName string) error { + // Fast path: os.Root walks the raw entry path component by component. + // Example: "var/run/foo" where var/run is a plain directory -- OK. + err := lstatPathComponents(osRoot, cleanerHdrName) + if err == nil || errors.Is(err, os.ErrNotExist) { + return nil + } + + // osRoot rejected the path. Only fall back to chroot-style resolution + // when the path traverses an absolute symlink (e.g. /var/run -> /run). + // Relative escapes (e.g. escape -> ..) are kept rejected, matching + // Docker behavior (moby/go-archive#93). + if !pathHasAbsoluteSymlink(cleanerHdrName, osRoot.Readlink) { + return err + } + + // An absolute symlink was found. Resolve symlinks treating root as a + // chroot boundary (absolute targets become root-relative). + // - /var/run -> /run resolves to root/run, relPath = "run/foo" + resolvedPath, resolveErr := resolvePath(root, filepath.Join(directory, cleanerHdrName), false) + if resolveErr != nil { + return errors.Join(err, resolveErr) + } + + // Verify the resolved path did not land outside targetDirectory. + relPath, relErr := filepath.Rel(targetDirectory, resolvedPath) + if relErr != nil { + return errors.Join(err, relErr) + } + if !filepath.IsLocal(relPath) { + return err + } + + // Re-check the resolved (symlink-free) path through os.Root. + // Example: "run/foo" -- no symlinks left, os.Root is happy. + // If os.Root still rejects it, it is a genuine escape. + resolvedErr := lstatPathComponents(osRoot, relPath) + if resolvedErr == nil || errors.Is(resolvedErr, os.ErrNotExist) { + return nil + } + return err +} + // convertToRelSubdirectory returns the path of directory, bound and relative to // root, as a relative path, or an error if that path can't be computed or if // the two directories are on different volumes @@ -2200,19 +2329,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM // of the root, to improve our compatibility with // recent versions of go-archive cleanerHdrName := cleanerReldirectory(filepath.FromSlash(hdr.Name)) - if err := func(hdrName string) error { - var hdrNameByComponent []string - for hdrName != "." && hdrName != "/" { - hdrNameByComponent = append(hdrNameByComponent, hdrName) - hdrName = filepath.Dir(hdrName) - } - for _, partial := range slices.Backward(hdrNameByComponent) { - if _, err := osRoot.Lstat(partial); err != nil { - return err - } - } - return nil - }(cleanerHdrName); err != nil && !errors.Is(err, os.ErrNotExist) { + if err := checkArchivePathEscape(osRoot, req.Root, req.Directory, targetDirectory, cleanerHdrName); err != nil { return err } // figure out who should own this new item diff --git a/vendor/go.podman.io/buildah/define/types.go b/vendor/go.podman.io/buildah/define/types.go index ce0c58ab8d..a4d3c2aaa5 100644 --- a/vendor/go.podman.io/buildah/define/types.go +++ b/vendor/go.podman.io/buildah/define/types.go @@ -30,7 +30,7 @@ const ( // identify working containers. Package = "buildah" // Version for the Package. Also used by .packit.sh for Packit builds. - Version = "1.45.1" + Version = "1.45.2" // DefaultRuntime if containers.conf fails. DefaultRuntime = "runc" diff --git a/vendor/go.podman.io/storage/VERSION b/vendor/go.podman.io/storage/VERSION index 0936d60d49..ea9eb1abc4 100644 --- a/vendor/go.podman.io/storage/VERSION +++ b/vendor/go.podman.io/storage/VERSION @@ -1 +1 @@ -1.64.1 +1.64.2 diff --git a/vendor/go.podman.io/storage/internal/rootlookupcache/cache.go b/vendor/go.podman.io/storage/internal/rootlookupcache/cache.go new file mode 100644 index 0000000000..76f68e717f --- /dev/null +++ b/vendor/go.podman.io/storage/internal/rootlookupcache/cache.go @@ -0,0 +1,148 @@ +package rootlookupcache + +import ( + "fmt" + "os" + "path" +) + +// Cache attempts to speed up repeated path lookups in an os.Root. +// +// As of Go 1.26, operations on os.Root always open a handle for the parent directory of the target +// path before performing the actual operation. That is currently fairly costly, opening a handle +// for each path component individually (e.g. it does not use openat2 on Linux). +// +// So, try to amortize the cost: if the target path is only a basename, os.Root can just do the +// actual operation as a single system call. +// Even with openat2 this would avoid repeated openat2() lookups of the parent. +// +// This is valuable for code needing to do further I/O on files within a fs.WalkDir handler +// (sadly fs.WalkDir does not give us a parent handle, it needs one internally); or for similar +// code that needs to repeatedly make operations within a single directory. +type Cache struct { + root *os.Root + rootFile *os.File // For root; or nil + + // This is a minimal implementation, ideal for fairly shallow directory hierarchies with several files. + // For a deeper hierarchy, operations on (root/dir/file1, root/dir/subdir1, root/dir/file2, root/dir/subdir2, ...) + // would, e.g. with fs.WalkDir, result in requests for (root/dir, root/dir/subdir1, root/dir, root/dir/subdir2, ...). + // That would be better handled by caching a few of the parent directories (but not caching siblings). + // + // Also, the cache key is a potentially long string. In situations where we receive arbitrary paths, that’s + // the best we can do; but callers that implement tree walks manually could also explicitly manage the lifetime + // / equality of cache values (e.g. while recursing to subdirectories, the caller knows when it returns back to a + // parent, and could perhaps maintain some kind of a direct reference to a cache entry). + cachedDirRoot *os.Root // Or nil + cachedPath string // Valid if cachedDirRoot != nil + cachedDirFile *os.File // For cachedDirRoot, which must be non-nil; or nil +} + +// NewCache creates a cache for operations within root. +// The root must be valid until Close() is called. +func NewCache(root *os.Root) *Cache { + return &Cache{ + root: root, + } +} + +// Close releases any possibly cached handles. +func (c *Cache) Close() error { + if c.rootFile != nil { + _ = c.rootFile.Close() + } + if c.cachedDirRoot != nil { + _ = c.cachedDirRoot.Close() + } + if c.cachedDirFile != nil { + _ = c.cachedDirFile.Close() + } + return nil +} + +// RootForDir returns an *os.Root for a specified directory (a fs.ValidPath) within the cache’s root. +// The returned value can be used only until the next call to RootForDir() or Close(); +// the caller must not close it manually. +func (c *Cache) RootForDir(dir string) (*os.Root, error) { + if dir == "." { + return c.root, nil + } + + if c.cachedDirRoot != nil && c.cachedPath == dir { + return c.cachedDirRoot, nil + } + + dirRoot, err := c.root.OpenRoot(dir) + if err != nil { + return nil, err + } + if c.cachedDirRoot != nil { + _ = c.cachedDirRoot.Close() + } + if c.cachedDirFile != nil { + _ = c.cachedDirFile.Close() + c.cachedDirFile = nil + } + c.cachedDirRoot = dirRoot + c.cachedPath = dir + + return dirRoot, nil +} + +// PreparePath returns a parent-directory *os.Root and a basename for efficiently accessing fsPath (per fs.ValidPath), +// while restricting it to the cache’s root. +// +// The returned root can be used only until the next call to RootForDir() or Close(); +// the caller must not close it manually. +func (c *Cache) PreparePath(fsPath string) (*os.Root, string, error) { + parentPath := path.Dir(fsPath) + parentRoot, err := c.RootForDir(parentPath) + if err != nil { + return nil, "", err + } + // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentPath and therefore from c.root. + basename := path.Base(fsPath) + if basename == ".." { + return nil, "", fmt.Errorf("internal error: trailing .. in a path that should conform to fs.ValidPath: %q", fsPath) + } + return parentRoot, basename, nil +} + +// FileForRoot returns, or newly opens, an *os.File descriptor for reading root (which must have been the last value obtained by RootForDir or PreparePath). +// The returned value can be used only until the next call to RootForDir(), FileForRoot() or Close(); +// the caller must not close it manually. +func (c *Cache) FileForRoot(root *os.Root) (*os.File, error) { + // Ideally, we should be able to use the file descriptor already existing in *os.Root, without the extra Open(".")/Close(). + + // We should not really _need_ the special case for c.root, we probably only need one cached *os.File + // for accessing all files in a directory, and c.root is not really special WRT expected use patterns. + // + // But we have the RootForDir(".") special case because it is cheap to implement there, and without a + // corresponding File cache for "." we’d need a more complex invalidation logic in RootForDir. + if root == c.root { + if c.rootFile == nil { + fd, err := c.root.Open(".") + if err != nil { + return nil, err + } + c.rootFile = fd + } + return c.rootFile, nil + } + + if c.cachedDirRoot != nil && root == c.cachedDirRoot { + if c.cachedDirFile == nil { + fd, err := c.cachedDirRoot.Open(".") + if err != nil { + return nil, err + } + c.cachedDirFile = fd + } + return c.cachedDirFile, nil + } + + // Otherwise we would need to open a new file descriptor, and then we would need to close it at some point, + // i.e. we would need to be tracking it in the cache. + // That could work under the assumption that the caller is following the cache rules, but under that assumption, + // we should never get here. + return nil, fmt.Errorf("internal error: FileForRoot called with an unexpected root") +} diff --git a/vendor/go.podman.io/storage/pkg/system/stat_common.go b/vendor/go.podman.io/storage/internal/stat/stat_common.go similarity index 94% rename from vendor/go.podman.io/storage/pkg/system/stat_common.go rename to vendor/go.podman.io/storage/internal/stat/stat_common.go index 1d57b7f401..4502a22385 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_common.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_common.go @@ -1,6 +1,6 @@ //go:build !freebsd -package system +package stat type platformStatT struct{} diff --git a/vendor/go.podman.io/storage/pkg/system/stat_darwin.go b/vendor/go.podman.io/storage/internal/stat/stat_darwin.go similarity index 51% rename from vendor/go.podman.io/storage/pkg/system/stat_darwin.go rename to vendor/go.podman.io/storage/internal/stat/stat_darwin.go index 57850a883f..3412e1aea5 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_darwin.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_darwin.go @@ -1,9 +1,9 @@ -package system +package stat import "syscall" -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { return &StatT{ size: s.Size, mode: uint32(s.Mode), @@ -11,5 +11,5 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtimespec, - }, nil + } } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_freebsd.go b/vendor/go.podman.io/storage/internal/stat/stat_freebsd.go similarity index 70% rename from vendor/go.podman.io/storage/pkg/system/stat_freebsd.go rename to vendor/go.podman.io/storage/internal/stat/stat_freebsd.go index 4b95073a3f..251c33924e 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_freebsd.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_freebsd.go @@ -1,4 +1,4 @@ -package system +package stat import "syscall" @@ -11,8 +11,8 @@ func (s StatT) Flags() uint32 { return s.flags } -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { st := &StatT{ size: s.Size, mode: uint32(s.Mode), @@ -24,5 +24,5 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { } st.flags = s.Flags st.dev = s.Dev - return st, nil + return st } diff --git a/vendor/go.podman.io/storage/internal/stat/stat_linux.go b/vendor/go.podman.io/storage/internal/stat/stat_linux.go new file mode 100644 index 0000000000..fafd662bd1 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/stat/stat_linux.go @@ -0,0 +1,16 @@ +package stat + +import "syscall" + +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { + return &StatT{ + size: s.Size, + mode: s.Mode, + uid: s.Uid, + gid: s.Gid, + rdev: uint64(s.Rdev), //nolint:unconvert + mtim: s.Mtim, + dev: uint64(s.Dev), //nolint:unconvert + } +} diff --git a/vendor/go.podman.io/storage/pkg/system/stat_netbsd.go b/vendor/go.podman.io/storage/internal/stat/stat_netbsd.go similarity index 51% rename from vendor/go.podman.io/storage/pkg/system/stat_netbsd.go rename to vendor/go.podman.io/storage/internal/stat/stat_netbsd.go index 57850a883f..3412e1aea5 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_netbsd.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_netbsd.go @@ -1,9 +1,9 @@ -package system +package stat import "syscall" -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { return &StatT{ size: s.Size, mode: uint32(s.Mode), @@ -11,5 +11,5 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtimespec, - }, nil + } } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_openbsd.go b/vendor/go.podman.io/storage/internal/stat/stat_openbsd.go similarity index 50% rename from vendor/go.podman.io/storage/pkg/system/stat_openbsd.go rename to vendor/go.podman.io/storage/internal/stat/stat_openbsd.go index a413e17148..766b583622 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_openbsd.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_openbsd.go @@ -1,9 +1,9 @@ -package system +package stat import "syscall" -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { return &StatT{ size: s.Size, mode: uint32(s.Mode), @@ -11,5 +11,5 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtim, - }, nil + } } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_solaris.go b/vendor/go.podman.io/storage/internal/stat/stat_solaris.go similarity index 50% rename from vendor/go.podman.io/storage/pkg/system/stat_solaris.go rename to vendor/go.podman.io/storage/internal/stat/stat_solaris.go index a413e17148..766b583622 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_solaris.go +++ b/vendor/go.podman.io/storage/internal/stat/stat_solaris.go @@ -1,9 +1,9 @@ -package system +package stat import "syscall" -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { +// FromStatT converts a syscall.Stat_t type to a StatT type +func FromStatT(s *syscall.Stat_t) *StatT { return &StatT{ size: s.Size, mode: uint32(s.Mode), @@ -11,5 +11,5 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) { gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtim, - }, nil + } } diff --git a/vendor/go.podman.io/storage/internal/stat/stat_unix.go b/vendor/go.podman.io/storage/internal/stat/stat_unix.go new file mode 100644 index 0000000000..2e6dabf423 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/stat/stat_unix.go @@ -0,0 +1,73 @@ +//go:build !windows + +package stat + +import ( + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +// StatT type contains status of a file. It contains metadata +// like permission, owner, group, size, etc about a file. +// +// Note that this is public as pkg/system.StatT. +type StatT struct { + mode uint32 + uid uint32 + gid uint32 + rdev uint64 + size int64 + mtim syscall.Timespec + dev uint64 + platformStatT +} + +// Mode returns file's permission mode. +func (s StatT) Mode() uint32 { + return s.mode +} + +// UID returns file's user id of owner. +func (s StatT) UID() uint32 { + return s.uid +} + +// GID returns file's group id of owner. +func (s StatT) GID() uint32 { + return s.gid +} + +// Rdev returns file's device ID (if it's special file). +func (s StatT) Rdev() uint64 { + return s.rdev +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() syscall.Timespec { + return s.mtim +} + +// Dev returns a unique identifier for owning filesystem +func (s StatT) Dev() uint64 { + return s.dev +} + +func (s StatT) IsDir() bool { + return (s.mode & unix.S_IFDIR) != 0 +} + +func (s StatT) IsSymlink() bool { + return (s.mode & unix.S_IFMT) == unix.S_IFLNK +} + +// FromFileInfo converts a os.FileInfo type to a StatT type +func FromFileInfo(fi os.FileInfo) *StatT { + return FromStatT(fi.Sys().(*syscall.Stat_t)) +} diff --git a/vendor/go.podman.io/storage/internal/stat/stat_windows.go b/vendor/go.podman.io/storage/internal/stat/stat_windows.go new file mode 100644 index 0000000000..d97a7b5b20 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/stat/stat_windows.go @@ -0,0 +1,68 @@ +package stat + +import ( + "os" + "time" +) + +// StatT type contains status of a file. It contains metadata +// like permission, size, etc about a file. +// +// Note that this is public as pkg/system.StatT. +type StatT struct { + mode os.FileMode + size int64 + mtim time.Time + platformStatT +} + +// Size returns file's size. +func (s StatT) Size() int64 { + return s.size +} + +// Mode returns file's permission mode. +func (s StatT) Mode() os.FileMode { + return os.FileMode(s.mode) +} + +// Mtim returns file's last modification time. +func (s StatT) Mtim() time.Time { + return time.Time(s.mtim) +} + +// UID returns file's user id of owner. +// +// on windows this is always 0 because there is no concept of UID +func (s StatT) UID() uint32 { + return 0 +} + +// GID returns file's group id of owner. +// +// on windows this is always 0 because there is no concept of GID +func (s StatT) GID() uint32 { + return 0 +} + +// Dev returns a unique identifier for owning filesystem +func (s StatT) Dev() uint64 { + return 0 +} + +func (s StatT) IsDir() bool { + return s.Mode().IsDir() +} + +func (s StatT) IsSymlink() bool { + return s.Mode()&os.ModeSymlink != 0 +} + +// FromFileInfo converts a os.FileInfo type to a StatT type +func FromFileInfo(fi os.FileInfo) *StatT { + return &StatT{ + size: fi.Size(), + mode: fi.Mode(), + mtim: fi.ModTime(), + } +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/errors.go b/vendor/go.podman.io/storage/internal/xattrs/errors.go new file mode 100644 index 0000000000..3351ef6503 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/errors.go @@ -0,0 +1,8 @@ +package xattrs + +import ( + "errors" +) + +// ErrNotSupportedPlatform means the platform is not supported. +var ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") diff --git a/vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go b/vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go new file mode 100644 index 0000000000..c215fea325 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go @@ -0,0 +1,115 @@ +//go:build freebsd + +package xattrs + +import ( + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +// extattrGet is the logic underlying ExtattrGetLink and extattrGetFd. +// Returns a []byte slice if the extattr is set and nil otherwise. +func extattrGet(syscallName string, pathInError func() string, getSyscall func(dest uintptr, nbytes int) (int, error)) ([]byte, error) { + size, errno := getSyscall(uintptr(unsafe.Pointer(nil)), 0) + if errno != nil { + if errno == unix.ENOATTR { + return nil, nil + } + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + if size == 0 { + return []byte{}, nil + } + + dest := make([]byte, size) + size, errno = getSyscall(uintptr(unsafe.Pointer(&dest[0])), size) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + return dest[:size], nil +} + +// ExtattrGetLink retrieves the value of the extended attribute identified by attrname +// in the given namespace and associated with the given path in the file system. +// If the path is a symbolic link, the extended attribute is retrieved from the link itself. +// Returns a []byte slice if the extattr is set and nil otherwise. +func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) { + return extattrGet("extattr_get_link", func() string { return path }, func(dest uintptr, nbytes int) (int, error) { + return unix.ExtattrGetLink(path, attrnamespace, attrname, dest, nbytes) + }) +} + +// extattrGetFd retrieves the value of the extended attribute identified by attrname +// in the given namespace and associated with the given file descriptor. +// Returns a []byte slice if the extattr is set and nil otherwise. +func extattrGetFd(fd int, pathInError func() string, attrnamespace int, attrname string) ([]byte, error) { + return extattrGet("extattr_get_fd", pathInError, func(dest uintptr, nbytes int) (int, error) { + return unix.ExtattrGetFd(fd, attrnamespace, attrname, dest, nbytes) + }) +} + +// ExtattrSetLink sets the value of extended attribute identified by attrname +// in the given namespace and associated with the given path in the file system. +// If the path is a symbolic link, the extended attribute is set on the link itself. +func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error { + if len(data) == 0 { + data = []byte{} // ensure non-nil for empty data + } + if _, errno := unix.ExtattrSetLink(path, attrnamespace, attrname, + uintptr(unsafe.Pointer(&data[0])), len(data)); errno != nil { + return &os.PathError{Op: "extattr_set_link", Path: path, Err: errno} + } + + return nil +} + +// extattrList is the logic underlying ExtattrListLink and extattrListFd. +func extattrList(syscallName string, pathInError func() string, listSyscall func(dest uintptr, nbytes int) (int, error)) ([]string, error) { + size, errno := listSyscall(uintptr(unsafe.Pointer(nil)), 0) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + if size == 0 { + return []string{}, nil + } + + dest := make([]byte, size) + size, errno = listSyscall(uintptr(unsafe.Pointer(&dest[0])), size) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + var attrs []string + for i := 0; i < size; { + // Each attribute is preceded by a single byte length + length := int(dest[i]) + i++ + if i+length > size { + break + } + attrs = append(attrs, string(dest[i:i+length])) + i += length + } + + return attrs, nil +} + +// ExtattrListLink lists extended attributes associated with the given path +// in the specified namespace. If the path is a symbolic link, the attributes +// are listed from the link itself. +func ExtattrListLink(path string, attrnamespace int) ([]string, error) { + return extattrList("extattr_list_link", func() string { return path }, func(dest uintptr, nbytes int) (int, error) { + return unix.ExtattrListLink(path, attrnamespace, dest, nbytes) + }) +} + +// extattrListFd lists extended attributes associated with fd +// in the specified namespace. +func extattrListFd(fd int, pathInError func() string, attrnamespace int) ([]string, error) { + return extattrList("extattr_list_fd", pathInError, func(dest uintptr, nbytes int) (int, error) { + return unix.ExtattrListFd(fd, attrnamespace, dest, nbytes) + }) +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go b/vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go new file mode 100644 index 0000000000..3934d699e3 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go @@ -0,0 +1,181 @@ +package xattrs + +import ( + "bytes" + "os" + "path/filepath" + + "go.podman.io/storage/internal/rootlookupcache" + "golang.org/x/sys/unix" +) + +const ( + // Not in x/sys/unix as of v0.40.0. + o_RESOLVE_BENEATH = 0x00001000 +) + +// handleForFSPathBeneath creates a Handle for rootFile/fsPath (using errorRoot/fsPath for error reporting). +func handleForFSPathBeneath(rootFile *os.File, fsPath string, errorRoot *os.Root) (*Handle, error) { + fd, err := syscallConnControl(rootFile, func(rootFD uintptr) (int, error) { + // macOS does not have O_PATH, so hope the user has enough permissions. + return unix.Openat(int(rootFD), filepath.FromSlash(fsPath), unix.O_RDONLY|unix.O_CLOEXEC|unix.O_SYMLINK|o_RESOLVE_BENEATH, 0) + }) + if err != nil { + return nil, err + } + + return &Handle{ + fd: fd, + errorRoot: errorRoot, + errorPath: fsPath, + }, nil +} + +// NewLHandle creates a Handle for fsBasename in parentRoot, which was the one last obtained from rootCache. +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +// +// The handle must be closed using .Close(). +func NewLHandle(parentRoot *os.Root, fsBasename string, rootCache *rootlookupcache.Cache) (*Handle, error) { + parentFile, err := rootCache.FileForRoot(parentRoot) + if err != nil { + return nil, err + } + return handleForFSPathBeneath(parentFile, fsBasename, parentRoot) +} + +// getxattr is the logic underlying Lgetxattr and Fgetxattr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func getxattr(syscallName string, pathInError func() string, getSyscall func(dest []byte) (int, error)) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := getSyscall(dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = getSyscall([]byte{}) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + dest = make([]byte, sz) + sz, errno = getSyscall(dest) + } + + switch { + case errno == unix.ENOATTR: + return nil, nil + case errno != nil: + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + return dest[:sz], nil +} + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// Returns a []byte slice if the xattr is set and nil otherwise. +func Lgetxattr(path string, attr string) ([]byte, error) { + return getxattr("lgetxattr", func() string { return path }, func(dest []byte) (int, error) { + return unix.Lgetxattr(path, attr, dest) + }) +} + +// Getxattr retrieves the value of the extended attribute identified by attr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func (h *Handle) Getxattr(attr string) ([]byte, error) { + return getxattr("Handle.Getxattr", h.pathInError, func(dest []byte) (int, error) { + return unix.Fgetxattr(h.fd, attr, dest) + }) +} + +// RootLgetxattr retrieves the value of the extended attribute identified by attr +// in fsPath (per fs.ValidPath) under root. +// Returns a []byte slice if the xattr is set and nil otherwise. +func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { + // We can’t use root.Open(fsPath) because it follows trailing symlinks. + rootFD, err := root.Open(".") + if err != nil { + return nil, err + } + defer rootFD.Close() + + handle, err := handleForFSPathBeneath(rootFD, fsPath, root) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Getxattr(attr) +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + if err := unix.Lsetxattr(path, attr, data, flags); err != nil { + return &os.PathError{Op: "lsetxattr", Path: path, Err: err} + } + + return nil +} + +// listxattr is the logic underlying Llistxattr and RootLlistxattr. +func listxattr(syscallName string, pathInError func() string, listSyscall func(dest []byte) (int, error)) ([]string, error) { + dest := make([]byte, 128) + sz, errno := listSyscall(dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = listSyscall([]byte{}) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + dest = make([]byte, sz) + sz, errno = listSyscall(dest) + } + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + var attrs []string + for token := range bytes.SplitSeq(dest[:sz], []byte{0}) { + if len(token) > 0 { + attrs = append(attrs, string(token)) + } + } + + return attrs, nil +} + +// Llistxattr lists extended attributes associated with the given path +// in the file system. +func Llistxattr(path string) ([]string, error) { + return listxattr("llistxattr", func() string { return path }, func(dest []byte) (int, error) { + return unix.Llistxattr(path, dest) + }) +} + +// Listxattr lists extended attributes associated with the given handle. +func (h *Handle) Listxattr() ([]string, error) { + return listxattr("Handle.Listxattr", h.pathInError, func(dest []byte) (int, error) { + return unix.Flistxattr(h.fd, dest) + }) +} + +// RootLlistxattr lists extended attributes associated with +// fsPath (per fs.ValidPath) under root. +func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { + // We can’t use root.Open(fsPath) because it follows trailing symlinks. + rootFD, err := root.Open(".") + if err != nil { + return nil, err + } + defer rootFD.Close() + + handle, err := handleForFSPathBeneath(rootFD, fsPath, root) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Listxattr() +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go b/vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go new file mode 100644 index 0000000000..07f1584ae3 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go @@ -0,0 +1,177 @@ +package xattrs + +import ( + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "go.podman.io/storage/internal/rootlookupcache" + "golang.org/x/sys/unix" +) + +// O_PATH value on freebsd. We must define O_PATH ourselves +// until https://github.com/golang/go/issues/54355 is fixed. +const o_PATH = 0x00400000 //nolint:staticcheck // ST1003: should not use ALL_CAPS + +// newLHandle creates a Handle for parentFile/fsBasename (using errorRoot/errorPath for error reporting). +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +func newLHandle(parentFile *os.File, fsBasename string, errorRoot *os.Root, errorPath string) (*Handle, error) { + // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentFile. + if fsBasename == ".." { + return nil, fmt.Errorf("trailing .. in newLhandle in %q", errorPath) + } + fd, err := syscallConnControl(parentFile, func(parentDir uintptr) (int, error) { + return unix.Openat(int(parentDir), filepath.FromSlash(fsBasename), o_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) + }) + if err != nil { + return nil, err + } + + return &Handle{ + fd: fd, + errorRoot: errorRoot, + errorPath: errorPath, + }, nil +} + +// NewLHandle creates a Handle for fsBasename in parentRoot, which was the one last obtained from rootCache. +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +// +// The handle must be closed using .Close(). +func NewLHandle(parentRoot *os.Root, fsBasename string, rootCache *rootlookupcache.Cache) (*Handle, error) { + parentFile, err := rootCache.FileForRoot(parentRoot) + if err != nil { + return nil, err + } + return newLHandle(parentFile, fsBasename, parentRoot, fsBasename) +} + +var namespaceMap = map[string]int{ + "user": unix.EXTATTR_NAMESPACE_USER, + "system": unix.EXTATTR_NAMESPACE_SYSTEM, +} + +func xattrToExtattr(xattr string) (namespace int, extattr string, err error) { + namespaceName, extattr, found := strings.Cut(xattr, ".") + if !found { + return -1, "", unix.ENOTSUP + } + + namespace, ok := namespaceMap[namespaceName] + if !ok { + return -1, "", unix.ENOTSUP + } + return namespace, extattr, nil +} + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// Returns a []byte slice if the xattr is set and nil otherwise. +func Lgetxattr(path string, attr string) ([]byte, error) { + namespace, extattr, err := xattrToExtattr(attr) + if err != nil { + return nil, err + } + return ExtattrGetLink(path, namespace, extattr) +} + +// Getxattr retrieves the value of the extended attribute identified by attr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func (h *Handle) Getxattr(attr string) ([]byte, error) { + namespace, extattr, err := xattrToExtattr(attr) + if err != nil { + return nil, err + } + return extattrGetFd(h.fd, h.pathInError, namespace, extattr) +} + +// RootLgetxattr retrieves the value of the extended attribute identified by attr +// in fsPath (per fs.ValidPath) under root. +// Returns a []byte slice if the xattr is set and nil otherwise. +func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { + // We can’t use root.Open(fsPath) because it follows trailing symlinks. + parentDir, err := root.Open(path.Dir(fsPath)) + if err != nil { + return nil, err + } + defer parentDir.Close() + + handle, err := newLHandle(parentDir, path.Base(fsPath), root, fsPath) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Getxattr(attr) +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, value []byte, flags int) error { + if flags != 0 { + // FIXME: Flags are not supported on FreeBSD, but we can implement + // them mimicking the behavior of the Linux implementation. + // See lsetxattr(2) on Linux for more information. + return unix.ENOTSUP + } + + namespace, extattr, err := xattrToExtattr(attr) + if err != nil { + return err + } + return ExtattrSetLink(path, namespace, extattr, value) +} + +// listxattr is the logic underlying Llistxattr and RootLlistxattr. +func listxattr(listOperation func(namespace int) ([]string, error)) ([]string, error) { + attrs := []string{} + + for namespaceName, namespace := range namespaceMap { + namespaceAttrs, err := listOperation(namespace) + if err != nil { + return nil, err + } + + for _, attr := range namespaceAttrs { + attrs = append(attrs, namespaceName+"."+attr) + } + } + + return attrs, nil +} + +// Llistxattr lists extended attributes associated with the given path +// in the file system. +func Llistxattr(path string) ([]string, error) { + return listxattr(func(namespace int) ([]string, error) { + return ExtattrListLink(path, namespace) + }) +} + +// Listxattr lists extended attributes associated with the given handle. +func (h *Handle) Listxattr() ([]string, error) { + return listxattr(func(namespace int) ([]string, error) { + return extattrListFd(h.fd, h.pathInError, namespace) + }) +} + +// RootLlistxattr lists extended attributes associated with +// fsPath (per fs.ValidPath) under root. +func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { + // We can’t use root.Open(fsPath) because it follows trailing symlinks. + parentDir, err := root.Open(path.Dir(fsPath)) + if err != nil { + return nil, err + } + defer parentDir.Close() + + handle, err := newLHandle(parentDir, path.Base(fsPath), root, fsPath) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Listxattr() +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go b/vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go new file mode 100644 index 0000000000..05457759e8 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go @@ -0,0 +1,184 @@ +package xattrs + +import ( + "bytes" + "fmt" + "os" + "path" + "path/filepath" + + "go.podman.io/storage/internal/rootlookupcache" + "golang.org/x/sys/unix" +) + +// newLHandle creates a Handle for parentFile/fsBasename (using errorRoot/errorPath for error reporting). +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +func newLHandle(parentFile *os.File, fsBasename string, errorRoot *os.Root, errorPath string) (*Handle, error) { + // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentFile. + if fsBasename == ".." { + return nil, fmt.Errorf("trailing .. in newLhandle in %q", errorPath) + } + fd, err := syscallConnControl(parentFile, func(parentDir uintptr) (int, error) { + return unix.Openat(int(parentDir), filepath.FromSlash(fsBasename), unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) + }) + if err != nil { + return nil, err + } + + return &Handle{ + fd: fd, + errorRoot: errorRoot, + errorPath: errorPath, + }, nil +} + +// NewLHandle creates a Handle for fsBasename in parentRoot, which was the one last obtained from rootCache. +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +// +// The handle must be closed using .Close(). +func NewLHandle(parentRoot *os.Root, fsBasename string, rootCache *rootlookupcache.Cache) (*Handle, error) { + parentFile, err := rootCache.FileForRoot(parentRoot) + if err != nil { + return nil, err + } + return newLHandle(parentFile, fsBasename, parentRoot, fsBasename) +} + +// getxattr is the logic underlying Lgetxattr and RootLgetxattr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func getxattr(syscallName string, pathInError func() string, getSyscall func(dest []byte) (int, error)) ([]byte, error) { + // Start with a 128 length byte array + dest := make([]byte, 128) + sz, errno := getSyscall(dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = getSyscall([]byte{}) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + dest = make([]byte, sz) + sz, errno = getSyscall(dest) + } + + switch { + case errno == unix.ENODATA: + return nil, nil + case errno != nil: + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + return dest[:sz], nil +} + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// Returns a []byte slice if the xattr is set and nil otherwise. +func Lgetxattr(path string, attr string) ([]byte, error) { + return getxattr("lgetxattr", func() string { return path }, func(dest []byte) (int, error) { + return unix.Lgetxattr(path, attr, dest) + }) +} + +// Getxattr retrieves the value of the extended attribute identified by attr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func (h *Handle) Getxattr(attr string) ([]byte, error) { + // Ideally we would use getxattrat() here, but as of 2026-05 that might be too recent. + return getxattr("Handle.Getxattr", h.pathInError, func(dest []byte) (int, error) { + return unix.Getxattr(fmt.Sprintf("/proc/self/fd/%d", h.fd), attr, dest) + }) +} + +// RootLgetxattr retrieves the value of the extended attribute identified by attr +// in fsPath (per fs.ValidPath) under root. +// Returns a []byte slice if the xattr is set and nil otherwise. +func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { + // We can use neither root.Open nor pathrs.OpenatInRoot to get the target file descriptor because they follow trailing symlinks. + parentDir, err := root.Open(path.Dir(fsPath)) + if err != nil { + return nil, err + } + defer parentDir.Close() + + handle, err := newLHandle(parentDir, path.Base(fsPath), root, fsPath) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Getxattr(attr) +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + if err := unix.Lsetxattr(path, attr, data, flags); err != nil { + return &os.PathError{Op: "lsetxattr", Path: path, Err: err} + } + + return nil +} + +// listxattr is the logic underlying Llistxattr and RootLlistxattr. +func listxattr(syscallName string, pathInError func() string, listSyscall func(dest []byte) (int, error)) ([]string, error) { + dest := make([]byte, 128) + sz, errno := listSyscall(dest) + + for errno == unix.ERANGE { + // Buffer too small, use zero-sized buffer to get the actual size + sz, errno = listSyscall([]byte{}) + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + dest = make([]byte, sz) + sz, errno = listSyscall(dest) + } + if errno != nil { + return nil, &os.PathError{Op: syscallName, Path: pathInError(), Err: errno} + } + + var attrs []string + for token := range bytes.SplitSeq(dest[:sz], []byte{0}) { + if len(token) > 0 { + attrs = append(attrs, string(token)) + } + } + + return attrs, nil +} + +// Llistxattr lists extended attributes associated with the given path +// in the file system. +func Llistxattr(path string) ([]string, error) { + return listxattr("llistxattr", func() string { return path }, func(dest []byte) (int, error) { + return unix.Llistxattr(path, dest) + }) +} + +// Listxattr lists extended attributes associated with the given handle. +func (h *Handle) Listxattr() ([]string, error) { + // Ideally we would use listxattrat() here, but as of 2026-05 that might be too recent. + return listxattr("Handle.Listxattr", h.pathInError, func(dest []byte) (int, error) { + return unix.Listxattr(fmt.Sprintf("/proc/self/fd/%d", h.fd), dest) + }) +} + +// RootLlistxattr lists extended attributes associated with +// fsPath (per fs.ValidPath) under root. +func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { + // We can use neither root.Open nor pathrs.OpenatInRoot to get the target file descriptor because they follow trailing symlinks. + parentDir, err := root.Open(path.Dir(fsPath)) + if err != nil { + return nil, err + } + defer parentDir.Close() + + handle, err := newLHandle(parentDir, path.Base(fsPath), root, fsPath) + if err != nil { + return nil, err + } + defer handle.Close() + + return handle.Listxattr() +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go b/vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go new file mode 100644 index 0000000000..96d24a8e02 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go @@ -0,0 +1,48 @@ +//go:build linux || darwin || freebsd + +package xattrs + +import ( + "os" + "path/filepath" + + "golang.org/x/sys/unix" +) + +// XattrHandle allows efficient llistxattr / llistxattr operations on a single file. +type Handle struct { + fd int + + // Only for error reporting + errorRoot *os.Root + errorPath string // Within errorRoot +} + +func (h *Handle) Close() error { + return unix.Close(h.fd) +} + +func (h *Handle) pathInError() string { + return filepath.Join(h.errorRoot.Name(), h.errorPath) +} + +// syscallConnControl calls fn with the file descriptor of fd, +// simplifying the boilerplate of f.SyscallConn().Control(). +func syscallConnControl[T any](fd *os.File, fn func(uintptr) (T, error)) (T, error) { + var zeroRes T + conn, err := fd.SyscallConn() + if err != nil { + return zeroRes, err + } + var res T + var resErr error + if err := conn.Control(func(fd uintptr) { + res, resErr = fn(fd) + }); err != nil { + return zeroRes, err + } + if resErr != nil { + return zeroRes, resErr + } + return res, nil +} diff --git a/vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go b/vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go new file mode 100644 index 0000000000..0dc268f4f7 --- /dev/null +++ b/vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go @@ -0,0 +1,78 @@ +//go:build !linux && !darwin && !freebsd + +package xattrs + +import ( + "os" + "syscall" + + "go.podman.io/storage/internal/rootlookupcache" +) + +const ( + // Value is larger than the maximum size allowed + E2BIG syscall.Errno = syscall.Errno(0) + + // Operation not supported + ENOTSUP syscall.Errno = syscall.Errno(0) + + // Value is too small or too large for maximum size allowed + EOVERFLOW syscall.Errno = syscall.Errno(0) +) + +// XattrHandle allows efficient llistxattr / llistxattr operations on a single file. +type Handle struct { +} + +// NewLHandle creates a Handle for fsBasename in parentRoot, which was the one last obtained from rootCache. +// If fsBasename is a symbolic link, it refers to the symbolic link, not to the target. +// +// The handle must be closed using .Close(). +func NewLHandle(parentRoot *os.Root, fsBasename string, rootCache *rootlookupcache.Cache) (*Handle, error) { + // This is not implemented, but we don’t fail, so that callers don’t need to add an extra ErrNotSupportedPlatform check; + // Those checks need to exist in the individual operations anyway. + return &Handle{}, nil +} + +func (h *Handle) Close() error { + return nil +} + +// Lgetxattr is not supported on platforms other than linux. +func Lgetxattr(path string, attr string) ([]byte, error) { + return nil, ErrNotSupportedPlatform +} + +// Getxattr retrieves the value of the extended attribute identified by attr. +// Returns a []byte slice if the xattr is set and nil otherwise. +func (h *Handle) Getxattr(attr string) ([]byte, error) { + return nil, ErrNotSupportedPlatform +} + +// RootLgetxattr retrieves the value of the extended attribute identified by attr +// in fsPath (per fs.ValidPath) under root. +// Returns a []byte slice if the xattr is set and nil otherwise. +func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { + return nil, ErrNotSupportedPlatform +} + +// Lsetxattr is not supported on platforms other than linux. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return ErrNotSupportedPlatform +} + +// Llistxattr is not supported on platforms other than linux. +func Llistxattr(path string) ([]string, error) { + return nil, ErrNotSupportedPlatform +} + +// Listxattr lists extended attributes associated with the given handle. +func (h *Handle) Listxattr() ([]string, error) { + return nil, ErrNotSupportedPlatform +} + +// RootLlistxattr lists extended attributes associated with +// fsPath (per fs.ValidPath) under root. +func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { + return nil, ErrNotSupportedPlatform +} diff --git a/vendor/go.podman.io/storage/pkg/archive/archive.go b/vendor/go.podman.io/storage/pkg/archive/archive.go index 1210659d94..23fa6f0df6 100644 --- a/vendor/go.podman.io/storage/pkg/archive/archive.go +++ b/vendor/go.podman.io/storage/pkg/archive/archive.go @@ -24,6 +24,8 @@ import ( "github.com/sirupsen/logrus" "github.com/ulikunitz/xz" "go.podman.io/storage/internal/createpath" + "go.podman.io/storage/internal/rootlookupcache" + "go.podman.io/storage/internal/xattrs" "go.podman.io/storage/pkg/fileutils" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/pools" @@ -470,21 +472,21 @@ func normalizeCapabilityRootID(idMappings *idtools.IDMappings, capData []byte) ( } // readSecurityXattrToTarHeader reads security.capability, security,image -// xattrs from filesystem to a tar header -func (ta *tarWriter) readSecurityXattrToTarHeader(root *os.Root, fsPath string, hdr *tar.Header) error { +// xattrs from filesystem to a tar header, using xattrHandle (if not nil) or a non-os.Root access to parentRoot/fsBasename. +func (ta *tarWriter) readSecurityXattrToTarHeader(xattrHandle *xattrs.Handle, parentRoot *os.Root, fsBasename string, hdr *tar.Header) error { if hdr.PAXRecords == nil { hdr.PAXRecords = make(map[string]string) } for _, xattr := range []string{"security.capability", "security.ima"} { var capability []byte var err error - if !ta.runningInMinimalChroot { - capability, err = system.RootLgetxattr(root, fsPath, xattr) + if xattrHandle != nil { + capability, err = xattrHandle.Getxattr(xattr) } else { - capability, err = system.Lgetxattr(filepath.Join(root.Name(), filepath.FromSlash(fsPath)), xattr) + capability, err = xattrs.Lgetxattr(filepath.Join(parentRoot.Name(), filepath.FromSlash(fsBasename)), xattr) } if err != nil && !errors.Is(err, system.ENOTSUP) && err != system.ErrNotSupportedPlatform { - return fmt.Errorf("failed to read %q attribute from %q in %q: %w", xattr, fsPath, root.Name(), err) + return fmt.Errorf("failed to read %q attribute from %q in %q: %w", xattr, fsBasename, parentRoot.Name(), err) } if capability == nil { continue @@ -492,7 +494,7 @@ func (ta *tarWriter) readSecurityXattrToTarHeader(root *os.Root, fsPath string, if xattr == "security.capability" { capability, err = normalizeCapabilityRootID(ta.IDMappings, capability) if err != nil { - return fmt.Errorf("failed to normalize %q attribute from %q: %w", xattr, fsPath, err) + return fmt.Errorf("failed to normalize %q attribute from %q: %w", xattr, fsBasename, err) } } hdr.PAXRecords[PaxSchilyXattr+xattr] = string(capability) @@ -500,30 +502,31 @@ func (ta *tarWriter) readSecurityXattrToTarHeader(root *os.Root, fsPath string, return nil } -// readUserXattrToTarHeader reads user.* xattr from filesystem to a tar header -func (ta *tarWriter) readUserXattrToTarHeader(root *os.Root, fsPath string, hdr *tar.Header) error { - var xattrs []string +// readUserXattrToTarHeader reads user.* xattr from filesystem to a tar header, +// using xattrHandle (if not nil) or a non-os.Root access to parentRoot/fsBasename. +func (ta *tarWriter) readUserXattrToTarHeader(xattrHandle *xattrs.Handle, parentRoot *os.Root, fsBasename string, hdr *tar.Header) error { + var xattrNames []string var err error - if !ta.runningInMinimalChroot { - xattrs, err = system.RootLlistxattr(root, fsPath) + if xattrHandle != nil { + xattrNames, err = xattrHandle.Listxattr() } else { - xattrs, err = system.Llistxattr(filepath.Join(root.Name(), filepath.FromSlash(fsPath))) + xattrNames, err = xattrs.Llistxattr(filepath.Join(parentRoot.Name(), filepath.FromSlash(fsBasename))) } if err != nil && !errors.Is(err, system.ENOTSUP) && err != system.ErrNotSupportedPlatform { return err } - for _, key := range xattrs { + for _, key := range xattrNames { if strings.HasPrefix(key, "user.") && !strings.HasPrefix(key, "user.overlay.") { var value []byte var err error - if !ta.runningInMinimalChroot { - value, err = system.RootLgetxattr(root, fsPath, key) + if xattrHandle != nil { + value, err = xattrHandle.Getxattr(key) } else { - value, err = system.Lgetxattr(filepath.Join(root.Name(), filepath.FromSlash(fsPath)), key) + value, err = xattrs.Lgetxattr(filepath.Join(parentRoot.Name(), filepath.FromSlash(fsBasename)), key) } if err != nil { if errors.Is(err, system.E2BIG) { - logrus.Errorf("archive: Skipping xattr for file %q in %q since value is too big: %s", fsPath, root.Name(), key) + logrus.Errorf("archive: Skipping xattr for file %q in %q since value is too big: %s", fsBasename, parentRoot.Name(), key) continue } return err @@ -554,12 +557,15 @@ type TarWhiteoutConverter interface { type tarWhiteoutConverter interface { TarWhiteoutConverter - convertWrite(hdr *tar.Header, root *os.Root, fsPath string, fi os.FileInfo) (*tar.Header, error) + // convertWrite updates hdr from on-disk format to the desired tar format, based on fi, + // and reading xattrs from xattrHandle (if not nil) or a non-os.Root access to parentRoot/fsBasename. + // If representing the whiteout requires an extra tar entry, it returns one. + convertWrite(hdr *tar.Header, xattrHandle *xattrs.Handle, parentRoot *os.Root, fsBasename string, fi os.FileInfo) (*tar.Header, error) } // GetWhiteoutConverter has no documented way to be called externally. Do not add any users outside of c/storage. func GetWhiteoutConverter(format WhiteoutFormat, data any) TarWhiteoutConverter { - return getWhiteoutConverter(format, data, nil) + return getWhiteoutConverter(format, data) } type tarWriter struct { @@ -623,7 +629,7 @@ func canonicalTarName(name string, isDir bool) (string, error) { type addFileData struct { // The path within a separately-provided root from which to read contents. - fsPath string + fsBasename string // os.Stat for the above. fi os.FileInfo @@ -636,29 +642,24 @@ type addFileData struct { } // prepareAddFile generates the tar file header(s) for adding a file -// from fsPath within root as tarName to the tar archive, without writing to the +// from fsBasename in parentRoot as tarName to the tar archive, without writing to the // tar stream. Thus, any error may be ignored without corrupting the // tar file. A (nil, nil) return means that the file should be // ignored for non-error reasons. -func (ta *tarWriter) prepareAddFile(root *os.Root, fsPath, tarName string) (*addFileData, error) { +func (ta *tarWriter) prepareAddFile(parentRoot *os.Root, fsBasename string, fi os.FileInfo, tarName string, rootCache *rootlookupcache.Cache) (*addFileData, error) { // WARNING: This function is called in contexts where the contents of root may be maliciously // concurrently modified. - fi, err := root.Lstat(fsPath) // FIXME: can we eliminate this and Lstat only once, e.g. from fs.WalkDir()? - if err != nil { - return nil, err - } - var link string if fi.Mode()&os.ModeSymlink != 0 { var err error - link, err = root.Readlink(fsPath) + link, err = parentRoot.Readlink(fsBasename) if err != nil { return nil, err } } if fi.Mode()&os.ModeSocket != 0 { - logrus.Infof("archive: skipping %q in %q since it is a socket", fsPath, root.Name()) + logrus.Infof("archive: skipping %q in %q since it is a socket", fsBasename, parentRoot.Name()) return nil, nil } @@ -666,10 +667,19 @@ func (ta *tarWriter) prepareAddFile(root *os.Root, fsPath, tarName string) (*add if err != nil { return nil, err } - if err := ta.readSecurityXattrToTarHeader(root, fsPath, hdr); err != nil { + var xattrHandle *xattrs.Handle + if !ta.runningInMinimalChroot { + h, err := xattrs.NewLHandle(parentRoot, fsBasename, rootCache) + if err != nil { + return nil, err + } + defer h.Close() + xattrHandle = h + } + if err := ta.readSecurityXattrToTarHeader(xattrHandle, parentRoot, fsBasename, hdr); err != nil { return nil, err } - if err := ta.readUserXattrToTarHeader(root, fsPath, hdr); err != nil { + if err := ta.readUserXattrToTarHeader(xattrHandle, parentRoot, fsBasename, hdr); err != nil { return nil, err } if err := readFileFlagsToTarHeader(fi, hdr); err != nil { @@ -726,9 +736,9 @@ func (ta *tarWriter) prepareAddFile(root *os.Root, fsPath, tarName string) (*add maybeTruncateHeaderModTime(hdr) result := &addFileData{ - fsPath: fsPath, - hdr: hdr, - fi: fi, + fsBasename: fsBasename, + hdr: hdr, + fi: fi, } if ta.whiteoutConverter != nil { // The whiteoutConverter suggests a generic mechanism, @@ -742,7 +752,7 @@ func (ta *tarWriter) prepareAddFile(root *os.Root, fsPath, tarName string) (*add // should be represented as a directory containing a // magic whiteout empty regular file, hence the // extraWhiteout header returned here. - result.extraWhiteout, err = ta.whiteoutConverter.convertWrite(hdr, root, fsPath, fi) + result.extraWhiteout, err = ta.whiteoutConverter.convertWrite(hdr, xattrHandle, parentRoot, fsBasename, fi) if err != nil { return nil, err } @@ -752,7 +762,7 @@ func (ta *tarWriter) prepareAddFile(root *os.Root, fsPath, tarName string) (*add } // addFile performs the write. An error here corrupts the tar file. -func (ta *tarWriter) addFile(root *os.Root, headers *addFileData) error { +func (ta *tarWriter) addFile(parentRoot *os.Root, headers *addFileData) error { // WARNING: This function is called in contexts where the contents of root may be maliciously // concurrently modified. @@ -777,7 +787,7 @@ func (ta *tarWriter) addFile(root *os.Root, headers *addFileData) error { } if hdr.Typeflag == tar.TypeReg && hdr.Size > 0 { - file, err := root.Open(headers.fsPath) + file, err := parentRoot.Open(headers.fsBasename) if err != nil { return err } @@ -1046,7 +1056,7 @@ func tarWithOptionsTo(dest io.Writer, srcPath string, options *TarOptions) (resu options.Timestamp, options.InternalRunningInMinimalChroot, ) - ta.whiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData, options) + ta.whiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData) ta.CopyPass = options.CopyPass includeFiles := options.IncludeFiles @@ -1090,6 +1100,8 @@ func tarWithOptionsTo(dest io.Writer, srcPath string, options *TarOptions) (resu return err } defer root.Close() + rootCache := rootlookupcache.NewCache(root) + defer rootCache.Close() if len(includeFiles) == 0 { includeFiles = []string{"."} @@ -1185,11 +1197,22 @@ func tarWithOptionsTo(dest io.Writer, srcPath string, options *TarOptions) (resu relFilePath = strings.Replace(relFilePath, include, replacement, 1) } - headers, err := ta.prepareAddFile(root, fsFilePath, relFilePath) + fi, err := d.Info() // This is free and never fails, root.FS().ReadDir() always calls lstatat to get this data. + if err != nil { + logrus.Errorf("Can't add file %q in %q to tar: %s; skipping", fsFilePath, root.Name(), err) + return nil + } + + parentRoot, fsBasename, err := rootCache.PreparePath(fsFilePath) + if err != nil { + logrus.Errorf("Can't add file %q in %q to tar: %s; skipping", fsFilePath, root.Name(), err) + return nil + } + headers, err := ta.prepareAddFile(parentRoot, fsBasename, fi, relFilePath, rootCache) if err != nil { logrus.Errorf("Can't add file %q in %q to tar: %s; skipping", fsFilePath, root.Name(), err) } else if headers != nil { - if err := ta.addFile(root, headers); err != nil { + if err := ta.addFile(parentRoot, headers); err != nil { return err } } diff --git a/vendor/go.podman.io/storage/pkg/archive/archive_linux.go b/vendor/go.podman.io/storage/pkg/archive/archive_linux.go index 4d76aa04de..263f1c225e 100644 --- a/vendor/go.podman.io/storage/pkg/archive/archive_linux.go +++ b/vendor/go.podman.io/storage/pkg/archive/archive_linux.go @@ -10,6 +10,7 @@ import ( "strings" "syscall" + "go.podman.io/storage/internal/xattrs" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/system" "golang.org/x/sys/unix" @@ -19,15 +20,14 @@ func getOverlayOpaqueXattrName() string { return GetOverlayXattrName("opaque") } -func getWhiteoutConverter(format WhiteoutFormat, data any, options *TarOptions) tarWhiteoutConverter { +func getWhiteoutConverter(format WhiteoutFormat, data any) tarWhiteoutConverter { if format == OverlayWhiteoutFormat { var roLayers []string = nil if rolayers, ok := data.([]string); ok && len(rolayers) > 0 { roLayers = rolayers } return overlayWhiteoutConverter{ - rolayers: roLayers, - runningInMinimalChroot: options != nil && options.InternalRunningInMinimalChroot, + rolayers: roLayers, } } return nil @@ -35,13 +35,6 @@ func getWhiteoutConverter(format WhiteoutFormat, data any, options *TarOptions) type overlayWhiteoutConverter struct { rolayers []string - // runningInMinimalChroot indicates that we are confined to a fairly strict chroot, - // so we don’t need to worry about Lgetxattr / Llistxattr escaping the source directory. - // - // We need this because: - // - getxattrat() would be ideal, but requires Linux 6.13, and as of 2026-05 that might still be too new - // - Our fallback is to open "/proc/self/fd/$fd" of an O_PATH file handle, but /proc is not available in these chroots. - runningInMinimalChroot bool } func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (*tar.Header, error) { @@ -50,14 +43,14 @@ func (o overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi }) } -func (o overlayWhiteoutConverter) convertWrite(hdr *tar.Header, root *os.Root, fsPath string, fi os.FileInfo) (*tar.Header, error) { - if !o.runningInMinimalChroot { +func (o overlayWhiteoutConverter) convertWrite(hdr *tar.Header, xattrHandle *xattrs.Handle, parentRoot *os.Root, fsBasename string, fi os.FileInfo) (*tar.Header, error) { + if xattrHandle != nil { return o.convertWriteWithGetxattr(hdr, fi, func(attrName string) ([]byte, error) { - return system.RootLgetxattr(root, fsPath, attrName) + return xattrHandle.Getxattr(attrName) }) } else { return o.convertWriteWithGetxattr(hdr, fi, func(attrName string) ([]byte, error) { - return system.Lgetxattr(filepath.Join(root.Name(), filepath.FromSlash(fsPath)), attrName) + return system.Lgetxattr(filepath.Join(parentRoot.Name(), filepath.FromSlash(fsBasename)), attrName) }) } } diff --git a/vendor/go.podman.io/storage/pkg/archive/archive_other.go b/vendor/go.podman.io/storage/pkg/archive/archive_other.go index f03055aceb..29525c2792 100644 --- a/vendor/go.podman.io/storage/pkg/archive/archive_other.go +++ b/vendor/go.podman.io/storage/pkg/archive/archive_other.go @@ -2,7 +2,7 @@ package archive -func getWhiteoutConverter(_ WhiteoutFormat, _ any, _ *TarOptions) tarWhiteoutConverter { +func getWhiteoutConverter(_ WhiteoutFormat, _ any) tarWhiteoutConverter { return nil } diff --git a/vendor/go.podman.io/storage/pkg/archive/changes.go b/vendor/go.podman.io/storage/pkg/archive/changes.go index c4ed1b5e6f..df0b950700 100644 --- a/vendor/go.podman.io/storage/pkg/archive/changes.go +++ b/vendor/go.podman.io/storage/pkg/archive/changes.go @@ -18,6 +18,7 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/sirupsen/logrus" + "go.podman.io/storage/internal/rootlookupcache" "go.podman.io/storage/pkg/fileutils" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/pools" @@ -94,7 +95,7 @@ func aufsMetadataSkip(fsPath string) (bool, error) { return skip, err } -func aufsDeletedFile(root *os.Root, fsPath string, fi os.FileInfo) (string, error) { +func aufsDeletedFile(rootCache *rootlookupcache.Cache, fsPath string, fi os.FileInfo) (string, error) { f := path.Base(fsPath) // If there is a whiteout, then the file was removed @@ -142,14 +143,14 @@ func isENOTDIR(err error) bool { type ( skipChange func(string) (bool, error) - deleteChange func(*os.Root, string, os.FileInfo) (string, error) + deleteChange func(*rootlookupcache.Cache, string, os.FileInfo) (string, error) whiteoutChange func(string, string) (bool, error) ) // changes walks the path rw and determines changes for the files in the path, // with respect to the parent layers. // -// deleteConverter returns "" for most files; if path (relative to rw, per fs.ValidPath) indicates a deletion, +// deleteConverter returns "" for most files; if fsPath (relative to rw, per fs.ValidPath) indicates a deletion, // it returns the path (relative to rw, per fs.ValidPath) of the file that was deleted. // // skipCondition, if not nil, should return true if a file path (relative to rw, per fs.ValidPath) should be skipped. @@ -173,6 +174,8 @@ func changes(layers []string, rw string, deleteConverter deleteChange, skipCondi return nil, err } defer root.Close() + rootCache := rootlookupcache.NewCache(root) + defer rootCache.Close() err = fs.WalkDir(root.FS(), ".", func(fsPath string, dirEntry fs.DirEntry, err error) error { if err != nil { @@ -198,7 +201,9 @@ func changes(layers []string, rw string, deleteConverter deleteChange, skipCondi Path: filepath.FromSlash("/" + fsPath), // We have skipped ".", and no other fsPath values start with "." or "/", so blindly prepending "/" is safe. } - deletedFile, err := deleteConverter(root, fsPath, f) + // We _usually_ don’t do I/O on root/fsPath; even overlayDeletedFile does it only for directories. + // So we don’t unconditionally do rootCache.PreparePath(), but let the deleteConverter do it if necessary. + deletedFile, err := deleteConverter(rootCache, fsPath, f) if err != nil { return err } @@ -466,6 +471,8 @@ func ChangesSizeWithError(newDir string, changes []Change) (int64, error) { return -1, err } defer root.Close() + rootCache := rootlookupcache.NewCache(root) + defer rootCache.Close() var ( size int64 @@ -473,7 +480,16 @@ func ChangesSizeWithError(newDir string, changes []Change) (int64, error) { ) for _, change := range changes { if change.Kind == ChangeModify || change.Kind == ChangeAdd { - fileInfo, err := root.Lstat(strings.TrimPrefix(filepath.ToSlash(change.Path), "/")) + fsFilePath := strings.TrimPrefix(filepath.ToSlash(change.Path), "/") + + parentRoot, fsBasename, err := rootCache.PreparePath(fsFilePath) + if err != nil { + // We don’t _fail_ on these errors, this is intended to be at least minimally-useful + // with concurrent modifications happening. + logrus.Errorf("Can't prepare %q in %q: %s", fsFilePath, newDir, err) + continue + } + fileInfo, err := parentRoot.Lstat(fsBasename) if err != nil { // We don’t _fail_ on these errors, this is intended to be at least minimally-useful // with concurrent modifications happening. @@ -514,7 +530,7 @@ func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMa // concurrently modified. reader, writer := io.Pipe() - go func() { + producer := func() error { ta := newTarWriter(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil, nil, false) // this buffer is needed for the duration of this piped stream @@ -524,10 +540,11 @@ func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMa root, err := os.OpenRoot(dir) if err != nil { - writer.CloseWithError(err) - return + return err } defer root.Close() + rootCache := rootlookupcache.NewCache(root) + defer rootCache.Close() // In general we log errors here but ignore them because // during e.g. a diff operation the container can continue @@ -551,13 +568,27 @@ func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMa } } else { relPath := change.Path[1:] - headers, err := ta.prepareAddFile(root, filepath.ToSlash(relPath), relPath) + fsPath := filepath.ToSlash(relPath) + + parentRoot, fsBasename, err := rootCache.PreparePath(fsPath) + if err != nil { + logrus.Debugf("Can't open parent of %q in %q: %s", change.Path, root.Name(), err) + continue + } + + fi, err := parentRoot.Lstat(fsBasename) if err != nil { logrus.Debugf("Can't add file %q in %q to tar: %s", change.Path, root.Name(), err) - } else if headers != nil { - if err := ta.addFile(root, headers); err != nil { - writer.CloseWithError(err) - return + continue + } + headers, err := ta.prepareAddFile(parentRoot, fsBasename, fi, relPath, rootCache) + if err != nil { + logrus.Debugf("Can't add file %q in %q to tar: %s", change.Path, root.Name(), err) + continue + } + if headers != nil { + if err := ta.addFile(parentRoot, headers); err != nil { + return err } } } @@ -567,6 +598,13 @@ func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMa if err := ta.TarWriter.Close(); err != nil { logrus.Debugf("Can't close layer: %s", err) } + return nil + } + go func() { + if err := producer(); err != nil { + writer.CloseWithError(err) + return + } if err := writer.Close(); err != nil { logrus.Debugf("failed close Changes writer: %s", err) } diff --git a/vendor/go.podman.io/storage/pkg/archive/changes_linux.go b/vendor/go.podman.io/storage/pkg/archive/changes_linux.go index 15bbb6311e..17ac4dfd43 100644 --- a/vendor/go.podman.io/storage/pkg/archive/changes_linux.go +++ b/vendor/go.podman.io/storage/pkg/archive/changes_linux.go @@ -17,6 +17,9 @@ import ( securejoin "github.com/cyphar/filepath-securejoin" "github.com/cyphar/filepath-securejoin/pathrs-lite" "github.com/sirupsen/logrus" + "go.podman.io/storage/internal/rootlookupcache" + "go.podman.io/storage/internal/stat" + "go.podman.io/storage/internal/xattrs" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/system" "golang.org/x/sys/unix" @@ -31,10 +34,10 @@ import ( // directly. Eliminating stat calls in this way can save up to seconds on large // images. type walker struct { - root1 *os.Root - root2 *os.Root rootFileInfo1 *FileInfo rootFileInfo2 *FileInfo + rootCache1 *rootlookupcache.Cache + rootCache2 *rootlookupcache.Cache } // collectFileInfoForChanges returns a complete representation of the trees @@ -52,17 +55,21 @@ func collectFileInfoForChanges(dir1, dir2 string, idmap1, idmap2 *idtools.IDMapp return nil, nil, err } defer root1.Close() + rootCache1 := rootlookupcache.NewCache(root1) + defer rootCache1.Close() root2, err := os.OpenRoot(dir2) if err != nil { return nil, nil, err } defer root2.Close() + rootCache2 := rootlookupcache.NewCache(root2) + defer rootCache2.Close() w := &walker{ - root1: root1, - root2: root2, rootFileInfo1: newRootFileInfo(idmap1), rootFileInfo2: newRootFileInfo(idmap2), + rootCache1: rootCache1, + rootCache2: rootCache2, } i1, err := root1.Lstat(".") @@ -74,16 +81,18 @@ func collectFileInfoForChanges(dir1, dir2 string, idmap1, idmap2 *idtools.IDMapp return nil, nil, err } - if err := w.walk(".", i1, i2); err != nil { + if err := w.walk(".", ".", root1, root2, i1, i2); err != nil { return nil, nil, err } return w.rootFileInfo1, w.rootFileInfo2, nil } -// Given a FileInfo, its path info, and a reference to the root of the tree -// being constructed, register this file with the tree. -func walkchunk(root *os.Root, fsPath string, fi os.FileInfo, rootFI *FileInfo) error { +// Register file fsPath with rootFI, which is accessible as parentRoot/fsBasename (coming from rootCache) +// and for which fi is already available. +// +// parentRoot+fi can be simultaneously nil, in that case do nothing. +func walkchunk(rootFI *FileInfo, fsPath string, parentRoot *os.Root, fsBasename string, fi os.FileInfo, rootCache *rootlookupcache.Cache) error { // WARNING: This is called in contexts where the contents of root may be maliciously // concurrently modified. @@ -102,12 +111,14 @@ func walkchunk(root *os.Root, fsPath string, fi os.FileInfo, rootFI *FileInfo) e idMappings: rootFI.idMappings, target: "", } - stat, err := system.FromStatT(fi.Sys().(*syscall.Stat_t)) + info.stat = stat.FromFileInfo(fi) + var err error + xattrHandle, err := xattrs.NewLHandle(parentRoot, fsBasename, rootCache) if err != nil { return err } - info.stat = stat - info.capability, err = system.RootLgetxattr(root, fsPath, "security.capability") // lgetxattr(2): fs access + defer xattrHandle.Close() + info.capability, err = xattrHandle.Getxattr("security.capability") // lgetxattr(2): fs access if err != nil && !errors.Is(err, system.ENOTSUP) { return err } @@ -115,16 +126,16 @@ func walkchunk(root *os.Root, fsPath string, fi os.FileInfo, rootFI *FileInfo) e if err != nil { return err } - xattrs, err := system.RootLlistxattr(root, fsPath) + xattrs, err := xattrHandle.Listxattr() if err != nil && !errors.Is(err, system.ENOTSUP) { return err } for _, key := range xattrs { if strings.HasPrefix(key, "user.") { - value, err := system.RootLgetxattr(root, fsPath, key) + value, err := xattrHandle.Getxattr(key) if err != nil { if errors.Is(err, system.E2BIG) { - logrus.Errorf("archive: Skipping xattr for file %q in %q since value is too big: %s", fsPath, root.Name(), key) + logrus.Errorf("archive: Skipping xattr for file %q in %q since value is too big: %s", fsBasename, parentRoot.Name(), key) continue } return err @@ -136,7 +147,7 @@ func walkchunk(root *os.Root, fsPath string, fi os.FileInfo, rootFI *FileInfo) e } } if fi.Mode()&os.ModeSymlink != 0 { - info.target, err = root.Readlink(fsPath) + info.target, err = parentRoot.Readlink(fsBasename) if err != nil { return err } @@ -147,17 +158,22 @@ func walkchunk(root *os.Root, fsPath string, fi os.FileInfo, rootFI *FileInfo) e // Walk a subtree rooted at the same path in both trees being iterated. For // example, /docker/overlay/1234/a/b/c/d and /docker/overlay/8888/a/b/c/d -func (w *walker) walk(fsPath string, i1, i2 os.FileInfo) (err error) { +// parentRoot1+i1, and parentRoot2+i2, may be nil. +// fsPath is path within the subtree, per fs.ValidPath; +// the files are accessible as parentRootN/fsBasename. +// +// NOTE: parentRootN comes from w.rootcacheN, and may be invalidated by this function using the cache. +func (w *walker) walk(fsPath string, fsBasename string, parentRoot1, parentRoot2 *os.Root, i1, i2 os.FileInfo) (err error) { // WARNING: This is called in contexts where the contents of w.root1 or w.root2 may be maliciously // concurrently modified. // Register these nodes with the return trees, unless we're still at the // (already-created) roots: if fsPath != "." { - if err := walkchunk(w.root1, fsPath, i1, w.rootFileInfo1); err != nil { + if err := walkchunk(w.rootFileInfo1, fsPath, parentRoot1, fsBasename, i1, w.rootCache1); err != nil { return err } - if err := walkchunk(w.root2, fsPath, i2, w.rootFileInfo2); err != nil { + if err := walkchunk(w.rootFileInfo2, fsPath, parentRoot2, fsBasename, i2, w.rootCache2); err != nil { return err } } @@ -182,13 +198,13 @@ func (w *walker) walk(fsPath string, i1, i2 os.FileInfo) (err error) { // Fetch the names of all the files contained in both directories being walked: var names1, names2 []nameIno if is1Dir { - names1, err = readdirnames(w.root1, fsPath) // getdents(2): fs access + names1, err = readdirnames(parentRoot1, fsBasename) // getdents(2): fs access if err != nil { return err } } if is2Dir { - names2, err = readdirnames(w.root2, fsPath) // getdents(2): fs access + names2, err = readdirnames(parentRoot2, fsBasename) // getdents(2): fs access if err != nil { return err } @@ -231,24 +247,42 @@ func (w *walker) walk(fsPath string, i1, i2 os.FileInfo) (err error) { ix2++ } + // NOTE: parentRootN can be invalidated from now on, and can no longer be used. + // For each of the names present in either or both of the directories being // iterated, stat the name under each root, and recurse the pair of them: for _, name := range names { fsFname := path.Join(fsPath, name) + // We need to look up childRootN from the cache in each iteration because w.walk might have invalidated them. + // + // Locally, w.walk() could return a value indicating whether it used the cache, and we could avoid the cache lookup + // for leaf directories. + // + // Ideally, the cache could be directly controlled by our walk, e.g. we know when we are leaving a directory never to return, + // and we know when we are returning to a previously-used parent. + var childRoot1, childRoot2 *os.Root var cInfo1, cInfo2 os.FileInfo if is1Dir { - cInfo1, err = w.root1.Lstat(fsFname) // lstat(2): fs access + childRoot1, err = w.rootCache1.RootForDir(fsPath) + if err != nil { + return err + } + cInfo1, err = childRoot1.Lstat(name) // lstat(2): fs access if err != nil && !os.IsNotExist(err) { return err } } if is2Dir { - cInfo2, err = w.root2.Lstat(fsFname) // lstat(2): fs access + childRoot2, err = w.rootCache2.RootForDir(fsPath) + if err != nil { + return err + } + cInfo2, err = childRoot2.Lstat(name) // lstat(2): fs access if err != nil && !os.IsNotExist(err) { return err } } - if err = w.walk(fsFname, cInfo1, cInfo2); err != nil { + if err = w.walk(fsFname, name, childRoot1, childRoot2, cInfo1, cInfo2); err != nil { // May invalidate childRootN return err } } @@ -340,8 +374,8 @@ func OverlayChanges(layers []string, rw string) ([]Change, error) { // WARNING: This is called in contexts where the contents of rw (but not layers) may be maliciously // concurrently modified. - dc := func(root *os.Root, fsPath string, fi os.FileInfo) (string, error) { - r, err := overlayDeletedFile(layers, root, fsPath, fi) + dc := func(rootCache *rootlookupcache.Cache, fsPath string, fi os.FileInfo) (string, error) { + r, err := overlayDeletedFile(layers, rootCache, fsPath, fi) if err != nil { return "", fmt.Errorf("overlay deleted file query: %w", err) } @@ -370,7 +404,7 @@ func overlayLowerContainsWhiteout(root, fsPath string) (bool, error) { return false, nil } -func overlayDeletedFile(layers []string, root *os.Root, fsPath string, fi os.FileInfo) (string, error) { +func overlayDeletedFile(layers []string, rootCache *rootlookupcache.Cache, fsPath string, fi os.FileInfo) (string, error) { // If it's a whiteout item, then a file or directory with that name is removed by this layer. if fi.Mode()&os.ModeCharDevice != 0 { if isWhiteOut(fi) { @@ -382,7 +416,18 @@ func overlayDeletedFile(layers []string, root *os.Root, fsPath string, fi os.Fil return "", nil } // If the directory isn't marked as opaque, then it's just a normal directory. - opaque, err := system.RootLgetxattr(root, fsPath, getOverlayOpaqueXattrName()) + parentRoot, fsBasename, err := rootCache.PreparePath(fsPath) + if err != nil { + return "", err + } + opaque, err := func() ([]byte, error) { // A scope for defer + xattrHandle, err := xattrs.NewLHandle(parentRoot, fsBasename, rootCache) + if err != nil { + return nil, err + } + defer xattrHandle.Close() + return xattrHandle.Getxattr(getOverlayOpaqueXattrName()) + }() if err != nil { return "", fmt.Errorf("failed querying overlay opaque xattr: %w", err) } diff --git a/vendor/go.podman.io/storage/pkg/archive/changes_other.go b/vendor/go.podman.io/storage/pkg/archive/changes_other.go index 14eaf43669..05130d39cb 100644 --- a/vendor/go.podman.io/storage/pkg/archive/changes_other.go +++ b/vendor/go.podman.io/storage/pkg/archive/changes_other.go @@ -8,6 +8,9 @@ import ( "os" "path/filepath" + "go.podman.io/storage/internal/rootlookupcache" + "go.podman.io/storage/internal/stat" + "go.podman.io/storage/internal/xattrs" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/system" ) @@ -49,6 +52,8 @@ func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInf return nil, err } defer root.Close() + rootCache := rootlookupcache.NewCache(root) + defer rootCache.Close() rootFileInfo := newRootFileInfo(idMappings) @@ -81,10 +86,11 @@ func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInf idMappings: idMappings, } - s, err := system.RootLstat(root, fsPath) + fi, err := d.Info() // This is free and never fails, root.FS().ReadDir() always calls lstatat() to get this data. if err != nil { return err } + s := stat.FromFileInfo(fi) // Don't cross mount points. This ignores file mounts to avoid // generating a diff which deletes all files following the @@ -93,10 +99,21 @@ func collectFileInfo(sourceDir string, idMappings *idtools.IDMappings) (*FileInf return filepath.SkipDir } + parentRoot, fsBasename, err := rootCache.PreparePath(fsPath) + if err != nil { + return err + } info.stat = s - info.capability, _ = system.RootLgetxattr(root, fsPath, "security.capability") + info.capability, _ = func() ([]byte, error) { // A scope for defer + h, err := xattrs.NewLHandle(parentRoot, fsBasename, rootCache) + if err != nil { + return nil, err + } + defer h.Close() + return h.Getxattr("security.capability") + }() if s.IsSymlink() { - info.target, err = root.Readlink(fsPath) + info.target, err = parentRoot.Readlink(fsBasename) if err != nil { return err } diff --git a/vendor/go.podman.io/storage/pkg/idtools/idtools.go b/vendor/go.podman.io/storage/pkg/idtools/idtools.go index de9356517f..60160e3a19 100644 --- a/vendor/go.podman.io/storage/pkg/idtools/idtools.go +++ b/vendor/go.podman.io/storage/pkg/idtools/idtools.go @@ -156,23 +156,28 @@ type IDMappings struct { gids []IDMap } -// NewIDMappings takes a requested user and group name and -// using the data from /etc/sub{uid,gid} ranges, creates the -// proper uid and gid remapping ranges for that user/group pair -func NewIDMappings(username, groupname string) (*IDMappings, error) { - subuidRanges, err := readSubuid(username) +// NewIDMappings takes the username (or user ID) for each sub{uid,gid} +// mapping. Note that both files are indexed via username (or user ID), +// and NOT the group name even for the subgid files. +// It reads the data from the /etc/sub{uid,gid} files or +// when using the libsubid build tag it then uses the libsubid functions +// to query the user ranges which resolves them according to the system NSS +// configuration, potentially fetching the ranges from a central network source. +// It returns proper uid and gid remapping ranges for the given names. +func NewIDMappings(usernameForUIDs, usernameForGIDs string) (*IDMappings, error) { + subuidRanges, err := readSubuid(usernameForUIDs) if err != nil { return nil, err } - subgidRanges, err := readSubgid(groupname) + subgidRanges, err := readSubgid(usernameForGIDs) if err != nil { return nil, err } if len(subuidRanges) == 0 { - return nil, fmt.Errorf("no subuid ranges found for user %q in %s", username, subuidFileName) + return nil, fmt.Errorf("no subuid ranges found for user %q in %s", usernameForUIDs, subuidFileName) } if len(subgidRanges) == 0 { - return nil, fmt.Errorf("no subgid ranges found for group %q in %s", groupname, subgidFileName) + return nil, fmt.Errorf("no subgid ranges found for user %q in %s", usernameForGIDs, subgidFileName) } return &IDMappings{ diff --git a/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go b/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go index c1ef5ac331..695890ac29 100644 --- a/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go +++ b/vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go @@ -5,6 +5,7 @@ package idtools import ( "errors" "os/user" + "runtime" "sync" "unsafe" ) @@ -20,22 +21,30 @@ struct subid_range get_range(struct subid_range *ranges, int i) return ranges[i]; } -// helper for stderr to avoid referencing C.stderr from Go code, -// which breaks cgo on musl due to stderr being declared as FILE *const -static FILE *subid_stderr(void) { - return stderr; -} - #if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4) # define subid_init libsubid_init # define subid_get_uid_ranges get_subuid_ranges # define subid_get_gid_ranges get_subgid_ranges #endif +#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 5) +# define subid_free free +#endif + +// Define our own static init function here instead of calling subid_init +// from the go code so we can have the storage string inlined here. +// static variables cannot be referenced from the go code. +static void containers_storage_subid_init() { + subid_init("storage", stderr); +} + */ import "C" -var onceInit sync.Once +var ( + libsubidLock sync.Mutex + subidInitialized bool +) func readSubid(username string, isUser bool) ([]subIDRange, error) { var ret []subIDRange @@ -49,9 +58,21 @@ func readSubid(username string, isUser bool) ([]subIDRange, error) { uidstr = u.Uid } - onceInit.Do(func() { - C.subid_init(C.CString("storage"), C.subid_stderr()) - }) + // libsubid is not thread safe currently, concurrent calls often fail. + // https://github.com/shadow-maint/shadow/issues/1362 + libsubidLock.Lock() + defer libsubidLock.Unlock() + + // Avoid the goroutine being switched to different threads during our calls. + // While right now the library does not seem to depend on any thread local state + // lets be future proof in case it will be. + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if !subidInitialized { + C.containers_storage_subid_init() + subidInitialized = true + } cUsername := C.CString(username) defer C.free(unsafe.Pointer(cUsername)) @@ -75,10 +96,10 @@ func readSubid(username string, isUser bool) ([]subIDRange, error) { if nRanges < 0 { return nil, errors.New("cannot read subids") } - defer C.free(unsafe.Pointer(cRanges)) + defer C.subid_free(unsafe.Pointer(cRanges)) - for i := 0; i < int(nRanges); i++ { - r := C.get_range(cRanges, C.int(i)) + for i := C.int(0); i < nRanges; i++ { + r := C.get_range(cRanges, i) newRange := subIDRange{ Start: int(r.start), Length: int(r.count), diff --git a/vendor/go.podman.io/storage/pkg/system/errors.go b/vendor/go.podman.io/storage/pkg/system/errors.go index b87d419b57..0e960c4b0f 100644 --- a/vendor/go.podman.io/storage/pkg/system/errors.go +++ b/vendor/go.podman.io/storage/pkg/system/errors.go @@ -1,8 +1,6 @@ package system -import ( - "errors" -) +import "go.podman.io/storage/internal/xattrs" // ErrNotSupportedPlatform means the platform is not supported. -var ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") +var ErrNotSupportedPlatform = xattrs.ErrNotSupportedPlatform diff --git a/vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go b/vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go index 02dbdad384..a9fa9ac65a 100644 --- a/vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go +++ b/vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go @@ -3,10 +3,7 @@ package system import ( - "os" - "strconv" - "unsafe" - + "go.podman.io/storage/internal/xattrs" "golang.org/x/sys/unix" ) @@ -16,107 +13,24 @@ const ( EXTATTR_NAMESPACE_SYSTEM = unix.EXTATTR_NAMESPACE_SYSTEM ) -// extattrGet is the logic underlying ExtattrGetLink and extattrGetFd. -// Returns a []byte slice if the extattr is set and nil otherwise. -func extattrGet(syscallName string, pathInError string, getSyscall func(dest uintptr, nbytes int) (int, error)) ([]byte, error) { - size, errno := getSyscall(uintptr(unsafe.Pointer(nil)), 0) - if errno != nil { - if errno == unix.ENOATTR { - return nil, nil - } - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - if size == 0 { - return []byte{}, nil - } - - dest := make([]byte, size) - size, errno = getSyscall(uintptr(unsafe.Pointer(&dest[0])), size) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - return dest[:size], nil -} - // ExtattrGetLink retrieves the value of the extended attribute identified by attrname // in the given namespace and associated with the given path in the file system. // If the path is a symbolic link, the extended attribute is retrieved from the link itself. // Returns a []byte slice if the extattr is set and nil otherwise. func ExtattrGetLink(path string, attrnamespace int, attrname string) ([]byte, error) { - return extattrGet("extattr_get_link", path, func(dest uintptr, nbytes int) (int, error) { - return unix.ExtattrGetLink(path, attrnamespace, attrname, dest, nbytes) - }) -} - -// extattrGetFd retrieves the value of the extended attribute identified by attrname -// in the given namespace and associated with the given file descriptor. -// Returns a []byte slice if the extattr is set and nil otherwise. -func extattrGetFd(fd int, attrnamespace int, attrname string) ([]byte, error) { - return extattrGet("extattr_get_fd", strconv.Itoa(fd), func(dest uintptr, nbytes int) (int, error) { - return unix.ExtattrGetFd(fd, attrnamespace, attrname, dest, nbytes) - }) + return xattrs.ExtattrGetLink(path, attrnamespace, attrname) } // ExtattrSetLink sets the value of extended attribute identified by attrname // in the given namespace and associated with the given path in the file system. // If the path is a symbolic link, the extended attribute is set on the link itself. func ExtattrSetLink(path string, attrnamespace int, attrname string, data []byte) error { - if len(data) == 0 { - data = []byte{} // ensure non-nil for empty data - } - if _, errno := unix.ExtattrSetLink(path, attrnamespace, attrname, - uintptr(unsafe.Pointer(&data[0])), len(data)); errno != nil { - return &os.PathError{Op: "extattr_set_link", Path: path, Err: errno} - } - - return nil -} - -// extattrList is the logic underlying ExtattrListLink and extattrListFd. -func extattrList(syscallName string, pathInError string, listSyscall func(dest uintptr, nbytes int) (int, error)) ([]string, error) { - size, errno := listSyscall(uintptr(unsafe.Pointer(nil)), 0) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - if size == 0 { - return []string{}, nil - } - - dest := make([]byte, size) - size, errno = listSyscall(uintptr(unsafe.Pointer(&dest[0])), size) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - var attrs []string - for i := 0; i < size; { - // Each attribute is preceded by a single byte length - length := int(dest[i]) - i++ - if i+length > size { - break - } - attrs = append(attrs, string(dest[i:i+length])) - i += length - } - - return attrs, nil + return xattrs.ExtattrSetLink(path, attrnamespace, attrname, data) } // ExtattrListLink lists extended attributes associated with the given path // in the specified namespace. If the path is a symbolic link, the attributes // are listed from the link itself. func ExtattrListLink(path string, attrnamespace int) ([]string, error) { - return extattrList("extattr_list_link", path, func(dest uintptr, nbytes int) (int, error) { - return unix.ExtattrListLink(path, attrnamespace, dest, nbytes) - }) -} - -// extattrListFd lists extended attributes associated with fd -// in the specified namespace. -func extattrListFd(fd int, attrnamespace int) ([]string, error) { - return extattrList("extattr_list_fd", strconv.Itoa(fd), func(dest uintptr, nbytes int) (int, error) { - return unix.ExtattrListFd(fd, attrnamespace, dest, nbytes) - }) + return xattrs.ExtattrListLink(path, attrnamespace) } diff --git a/vendor/go.podman.io/storage/pkg/system/lstat.go b/vendor/go.podman.io/storage/pkg/system/lstat.go new file mode 100644 index 0000000000..ce1af4e32f --- /dev/null +++ b/vendor/go.podman.io/storage/pkg/system/lstat.go @@ -0,0 +1,17 @@ +package system + +import ( + "os" + + "go.podman.io/storage/internal/stat" +) + +// RootLstat takes fsPath within root and returns +// a system.StatT type pertaining to that file. +func RootLstat(root *os.Root, fsPath string) (*StatT, error) { + fi, err := root.Lstat(fsPath) + if err != nil { + return nil, err + } + return stat.FromFileInfo(fi), nil +} diff --git a/vendor/go.podman.io/storage/pkg/system/lstat_unix.go b/vendor/go.podman.io/storage/pkg/system/lstat_unix.go index 42523b101f..ef329ef188 100644 --- a/vendor/go.podman.io/storage/pkg/system/lstat_unix.go +++ b/vendor/go.podman.io/storage/pkg/system/lstat_unix.go @@ -5,6 +5,8 @@ package system import ( "os" "syscall" + + "go.podman.io/storage/internal/stat" ) // Lstat takes a path to a file and returns @@ -16,15 +18,5 @@ func Lstat(path string) (*StatT, error) { if err := syscall.Lstat(path, s); err != nil { return nil, &os.PathError{Op: "Lstat", Path: path, Err: err} } - return fromStatT(s) -} - -// RootLstat takes fsPath within root and returns -// a system.StatT type pertaining to that file. -func RootLstat(root *os.Root, fsPath string) (*StatT, error) { - fi, err := root.Lstat(fsPath) - if err != nil { - return nil, err - } - return fromStatT(fi.Sys().(*syscall.Stat_t)) + return stat.FromStatT(s), nil } diff --git a/vendor/go.podman.io/storage/pkg/system/lstat_windows.go b/vendor/go.podman.io/storage/pkg/system/lstat_windows.go index e0810d942b..600a325a71 100644 --- a/vendor/go.podman.io/storage/pkg/system/lstat_windows.go +++ b/vendor/go.podman.io/storage/pkg/system/lstat_windows.go @@ -1,6 +1,10 @@ package system -import "os" +import ( + "os" + + "go.podman.io/storage/internal/stat" +) // Lstat calls os.Lstat to get a fileinfo interface back. // This is then copied into our own locally defined structure. @@ -10,15 +14,5 @@ func Lstat(path string) (*StatT, error) { return nil, err } - return fromStatT(&fi) -} - -// RootLstat takes fsPath within root and returns -// a system.StatT type pertaining to that file. -func RootLstat(root *os.Root, fsPath string) (*StatT, error) { - fi, err := root.Lstat(fsPath) - if err != nil { - return nil, err - } - return fromStatT(&fi) + return stat.FromFileInfo(fi), nil } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_linux.go b/vendor/go.podman.io/storage/pkg/system/stat_linux.go index 0dee88d1b8..365b744abe 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_linux.go +++ b/vendor/go.podman.io/storage/pkg/system/stat_linux.go @@ -1,22 +1,13 @@ package system -import "syscall" +import ( + "syscall" -// fromStatT converts a syscall.Stat_t type to a system.Stat_t type -func fromStatT(s *syscall.Stat_t) (*StatT, error) { - return &StatT{ - size: s.Size, - mode: s.Mode, - uid: s.Uid, - gid: s.Gid, - rdev: uint64(s.Rdev), //nolint:unconvert - mtim: s.Mtim, - dev: uint64(s.Dev), //nolint:unconvert - }, nil -} + "go.podman.io/storage/internal/stat" +) // FromStatT converts a syscall.Stat_t type to a system.Stat_t type // This is exposed on Linux as pkg/archive/changes uses it. func FromStatT(s *syscall.Stat_t) (*StatT, error) { - return fromStatT(s) + return stat.FromStatT(s), nil } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_unix.go b/vendor/go.podman.io/storage/pkg/system/stat_unix.go index 67a98fc806..bb413f17bf 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_unix.go +++ b/vendor/go.podman.io/storage/pkg/system/stat_unix.go @@ -7,64 +7,12 @@ import ( "strconv" "syscall" - "golang.org/x/sys/unix" + "go.podman.io/storage/internal/stat" ) // StatT type contains status of a file. It contains metadata // like permission, owner, group, size, etc about a file. -type StatT struct { - mode uint32 - uid uint32 - gid uint32 - rdev uint64 - size int64 - mtim syscall.Timespec - dev uint64 - platformStatT -} - -// Mode returns file's permission mode. -func (s StatT) Mode() uint32 { - return s.mode -} - -// UID returns file's user id of owner. -func (s StatT) UID() uint32 { - return s.uid -} - -// GID returns file's group id of owner. -func (s StatT) GID() uint32 { - return s.gid -} - -// Rdev returns file's device ID (if it's special file). -func (s StatT) Rdev() uint64 { - return s.rdev -} - -// Size returns file's size. -func (s StatT) Size() int64 { - return s.size -} - -// Mtim returns file's last modification time. -func (s StatT) Mtim() syscall.Timespec { - return s.mtim -} - -// Dev returns a unique identifier for owning filesystem -func (s StatT) Dev() uint64 { - return s.dev -} - -func (s StatT) IsDir() bool { - return (s.mode & unix.S_IFDIR) != 0 -} - -func (s StatT) IsSymlink() bool { - return (s.mode & unix.S_IFMT) == unix.S_IFLNK -} +type StatT = stat.StatT // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. @@ -75,7 +23,7 @@ func Stat(path string) (*StatT, error) { if err := syscall.Stat(path, s); err != nil { return nil, &os.PathError{Op: "Stat", Path: path, Err: err} } - return fromStatT(s) + return stat.FromStatT(s), nil } // Fstat takes an open file descriptor and returns @@ -87,5 +35,5 @@ func Fstat(fd int) (*StatT, error) { if err := syscall.Fstat(fd, s); err != nil { return nil, &os.PathError{Op: "Fstat", Path: strconv.Itoa(fd), Err: err} } - return fromStatT(s) + return stat.FromStatT(s), nil } diff --git a/vendor/go.podman.io/storage/pkg/system/stat_windows.go b/vendor/go.podman.io/storage/pkg/system/stat_windows.go index a0669b5e9c..d3081d5fd6 100644 --- a/vendor/go.podman.io/storage/pkg/system/stat_windows.go +++ b/vendor/go.podman.io/storage/pkg/system/stat_windows.go @@ -2,59 +2,13 @@ package system import ( "os" - "time" + + "go.podman.io/storage/internal/stat" ) // StatT type contains status of a file. It contains metadata // like permission, size, etc about a file. -type StatT struct { - mode os.FileMode - size int64 - mtim time.Time - platformStatT -} - -// Size returns file's size. -func (s StatT) Size() int64 { - return s.size -} - -// Mode returns file's permission mode. -func (s StatT) Mode() os.FileMode { - return os.FileMode(s.mode) -} - -// Mtim returns file's last modification time. -func (s StatT) Mtim() time.Time { - return time.Time(s.mtim) -} - -// UID returns file's user id of owner. -// -// on windows this is always 0 because there is no concept of UID -func (s StatT) UID() uint32 { - return 0 -} - -// GID returns file's group id of owner. -// -// on windows this is always 0 because there is no concept of GID -func (s StatT) GID() uint32 { - return 0 -} - -// Dev returns a unique identifier for owning filesystem -func (s StatT) Dev() uint64 { - return 0 -} - -func (s StatT) IsDir() bool { - return s.Mode().IsDir() -} - -func (s StatT) IsSymlink() bool { - return s.Mode()&os.ModeSymlink != 0 -} +type StatT = stat.StatT // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. @@ -65,14 +19,5 @@ func Stat(path string) (*StatT, error) { if err != nil { return nil, err } - return fromStatT(&fi) -} - -// fromStatT converts a os.FileInfo type to a system.StatT type -func fromStatT(fi *os.FileInfo) (*StatT, error) { - return &StatT{ - size: (*fi).Size(), - mode: (*fi).Mode(), - mtim: (*fi).ModTime(), - }, nil + return stat.FromFileInfo(fi), nil } diff --git a/vendor/go.podman.io/storage/pkg/system/xattrs.go b/vendor/go.podman.io/storage/pkg/system/xattrs.go index 57d3d24cb5..011cc6d767 100644 --- a/vendor/go.podman.io/storage/pkg/system/xattrs.go +++ b/vendor/go.podman.io/storage/pkg/system/xattrs.go @@ -1,24 +1,39 @@ package system -import "os" +import ( + "os" -// syscallConnControl calls fn with the file descriptor of fd, -// simplifying the boilerplate of f.SyscallConn().Control(). -func syscallConnControl[T any](fd *os.File, fn func(uintptr) (T, error)) (T, error) { - var zeroRes T - conn, err := fd.SyscallConn() - if err != nil { - return zeroRes, err - } - var res T - var resErr error - if err := conn.Control(func(fd uintptr) { - res, resErr = fn(fd) - }); err != nil { - return zeroRes, err - } - if resErr != nil { - return zeroRes, resErr - } - return res, nil + "go.podman.io/storage/internal/xattrs" +) + +// Lgetxattr retrieves the value of the extended attribute identified by attr +// and associated with the given path in the file system. +// Returns a []byte slice if the xattr is set and nil otherwise. +func Lgetxattr(path string, attr string) ([]byte, error) { + return xattrs.Lgetxattr(path, attr) +} + +// RootLgetxattr retrieves the value of the extended attribute identified by attr +// in fsPath (per fs.ValidPath) under root. +// Returns a []byte slice if the xattr is set and nil otherwise. +func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { + return xattrs.RootLgetxattr(root, fsPath, attr) +} + +// Lsetxattr sets the value of the extended attribute identified by attr +// and associated with the given path in the file system. +func Lsetxattr(path string, attr string, data []byte, flags int) error { + return xattrs.Lsetxattr(path, attr, data, flags) +} + +// Llistxattr lists extended attributes associated with the given path +// in the file system. +func Llistxattr(path string) ([]string, error) { + return xattrs.Llistxattr(path) +} + +// RootLlistxattr lists extended attributes associated with +// fsPath (per fs.ValidPath) under root. +func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { + return xattrs.RootLlistxattr(root, fsPath) } diff --git a/vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go b/vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go index 215c96616a..c86291b3de 100644 --- a/vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go +++ b/vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go @@ -1,10 +1,6 @@ package system import ( - "bytes" - "os" - "path/filepath" - "golang.org/x/sys/unix" ) @@ -18,133 +14,3 @@ const ( // Not in x/sys/unix as of v0.40.0. O_RESOLVE_BENEATH = 0x00001000 ) - -// getxattr is the logic underlying Lgetxattr and Fgetxattr. -// Returns a []byte slice if the xattr is set and nil otherwise. -func getxattr(syscallName string, pathInError string, getSyscall func(dest []byte) (int, error)) ([]byte, error) { - // Start with a 128 length byte array - dest := make([]byte, 128) - sz, errno := getSyscall(dest) - - for errno == unix.ERANGE { - // Buffer too small, use zero-sized buffer to get the actual size - sz, errno = getSyscall([]byte{}) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - dest = make([]byte, sz) - sz, errno = getSyscall(dest) - } - - switch { - case errno == unix.ENOATTR: - return nil, nil - case errno != nil: - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - return dest[:sz], nil -} - -// Lgetxattr retrieves the value of the extended attribute identified by attr -// and associated with the given path in the file system. -// Returns a []byte slice if the xattr is set and nil otherwise. -func Lgetxattr(path string, attr string) ([]byte, error) { - return getxattr("lgetxattr", path, func(dest []byte) (int, error) { - return unix.Lgetxattr(path, attr, dest) - }) -} - -// RootLgetxattr retrieves the value of the extended attribute identified by attr -// in fsPath (per fs.ValidPath) under root. -// Returns a []byte slice if the xattr is set and nil otherwise. -func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { - // We can’t use root.Open(fsPath) because it follows trailing symlinks. - rootFD, err := root.Open(".") - if err != nil { - return nil, err - } - defer rootFD.Close() - fd, err := syscallConnControl(rootFD, func(rootFD uintptr) (int, error) { - // macOS does not have O_PATH, so hope the user has enough permissions. - return unix.Openat(int(rootFD), filepath.FromSlash(fsPath), unix.O_RDONLY|unix.O_CLOEXEC|unix.O_SYMLINK|O_RESOLVE_BENEATH, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - return getxattr("RootLgetxattr", fsPath, func(dest []byte) (int, error) { - return unix.Fgetxattr(fd, attr, dest) - }) -} - -// Lsetxattr sets the value of the extended attribute identified by attr -// and associated with the given path in the file system. -func Lsetxattr(path string, attr string, data []byte, flags int) error { - if err := unix.Lsetxattr(path, attr, data, flags); err != nil { - return &os.PathError{Op: "lsetxattr", Path: path, Err: err} - } - - return nil -} - -// listxattr is the logic underlying Llistxattr and RootLlistxattr. -func listxattr(syscallName string, pathInError string, listSyscall func(dest []byte) (int, error)) ([]string, error) { - dest := make([]byte, 128) - sz, errno := listSyscall(dest) - - for errno == unix.ERANGE { - // Buffer too small, use zero-sized buffer to get the actual size - sz, errno = listSyscall([]byte{}) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - dest = make([]byte, sz) - sz, errno = listSyscall(dest) - } - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - var attrs []string - for token := range bytes.SplitSeq(dest[:sz], []byte{0}) { - if len(token) > 0 { - attrs = append(attrs, string(token)) - } - } - - return attrs, nil -} - -// Llistxattr lists extended attributes associated with the given path -// in the file system. -func Llistxattr(path string) ([]string, error) { - return listxattr("llistxattr", path, func(dest []byte) (int, error) { - return unix.Llistxattr(path, dest) - }) -} - -// RootLlistxattr lists extended attributes associated with -// fsPath (per fs.ValidPath) under root. -func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { - // We can’t use root.Open(fsPath) because it follows trailing symlinks. - rootFD, err := root.Open(".") - if err != nil { - return nil, err - } - defer rootFD.Close() - // macOS does not have O_PATH, so hope the user has enough permissions. - fd, err := syscallConnControl(rootFD, func(rootFD uintptr) (int, error) { - return unix.Openat(int(rootFD), filepath.FromSlash(fsPath), unix.O_RDONLY|unix.O_CLOEXEC|unix.O_SYMLINK|O_RESOLVE_BENEATH, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - return listxattr("RootLlistxattr", fsPath, func(dest []byte) (int, error) { - return unix.Flistxattr(fd, dest) - }) -} diff --git a/vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go b/vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go index 83dc83f181..0b3f400b7b 100644 --- a/vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go +++ b/vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go @@ -1,12 +1,6 @@ package system import ( - "fmt" - "os" - "path" - "path/filepath" - "strings" - "golang.org/x/sys/unix" ) @@ -20,140 +14,3 @@ const ( // Value is too small or too large for maximum size allowed EOVERFLOW unix.Errno = unix.EOVERFLOW ) - -var namespaceMap = map[string]int{ - "user": EXTATTR_NAMESPACE_USER, - "system": EXTATTR_NAMESPACE_SYSTEM, -} - -func xattrToExtattr(xattr string) (namespace int, extattr string, err error) { - namespaceName, extattr, found := strings.Cut(xattr, ".") - if !found { - return -1, "", ENOTSUP - } - - namespace, ok := namespaceMap[namespaceName] - if !ok { - return -1, "", ENOTSUP - } - return namespace, extattr, nil -} - -// Lgetxattr retrieves the value of the extended attribute identified by attr -// and associated with the given path in the file system. -// Returns a []byte slice if the xattr is set and nil otherwise. -func Lgetxattr(path string, attr string) ([]byte, error) { - namespace, extattr, err := xattrToExtattr(attr) - if err != nil { - return nil, err - } - return ExtattrGetLink(path, namespace, extattr) -} - -// RootLgetxattr retrieves the value of the extended attribute identified by attr -// in fsPath (per fs.ValidPath) under root. -// Returns a []byte slice if the xattr is set and nil otherwise. -func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { - // O_PATH value on freebsd. We must define O_PATH ourselves - // until https://github.com/golang/go/issues/54355 is fixed. - const O_PATH = 0x00400000 //nolint:staticcheck // ST1003: should not use ALL_CAPS - - // We can’t use root.Open(fsPath) because it follows trailing symlinks. - parentDir, err := root.Open(path.Dir(fsPath)) - if err != nil { - return nil, err - } - defer parentDir.Close() - // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentDir. - fsBase := path.Base(fsPath) - if fsBase == ".." { - return nil, fmt.Errorf("trailing .. in RootLgetxattr(%q)", fsPath) - } - fd, err := syscallConnControl(parentDir, func(parentDir uintptr) (int, error) { - return unix.Openat(int(parentDir), filepath.FromSlash(fsBase), O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - namespace, extattr, err := xattrToExtattr(attr) - if err != nil { - return nil, err - } - return extattrGetFd(fd, namespace, extattr) -} - -// Lsetxattr sets the value of the extended attribute identified by attr -// and associated with the given path in the file system. -func Lsetxattr(path string, attr string, value []byte, flags int) error { - if flags != 0 { - // FIXME: Flags are not supported on FreeBSD, but we can implement - // them mimicking the behavior of the Linux implementation. - // See lsetxattr(2) on Linux for more information. - return ENOTSUP - } - - namespace, extattr, err := xattrToExtattr(attr) - if err != nil { - return err - } - return ExtattrSetLink(path, namespace, extattr, value) -} - -// listxattr is the logic underlying Llistxattr and RootLlistxattr. -func listxattr(listOperation func(namespace int) ([]string, error)) ([]string, error) { - attrs := []string{} - - for namespaceName, namespace := range namespaceMap { - namespaceAttrs, err := listOperation(namespace) - if err != nil { - return nil, err - } - - for _, attr := range namespaceAttrs { - attrs = append(attrs, namespaceName+"."+attr) - } - } - - return attrs, nil -} - -// Llistxattr lists extended attributes associated with the given path -// in the file system. -func Llistxattr(path string) ([]string, error) { - return listxattr(func(namespace int) ([]string, error) { - return ExtattrListLink(path, namespace) - }) -} - -// RootLlistxattr lists extended attributes associated with -// fsPath (per fs.ValidPath) under root. -func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { - // O_PATH value on freebsd. We must define O_PATH ourselves - // until https://github.com/golang/go/issues/54355 is fixed. - const O_PATH = 0x00400000 //nolint:staticcheck // ST1003: should not use ALL_CAPS - - // We can’t use root.Open(fsPath) because it follows trailing symlinks. - parentDir, err := root.Open(path.Dir(fsPath)) - if err != nil { - return nil, err - } - defer parentDir.Close() - // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentDir. - fsBase := path.Base(fsPath) - if fsBase == ".." { - return nil, fmt.Errorf("trailing .. in RootLlistxattr(%q)", fsPath) - } - fd, err := syscallConnControl(parentDir, func(parentDir uintptr) (int, error) { - return unix.Openat(int(parentDir), filepath.FromSlash(fsBase), O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - return listxattr(func(namespace int) ([]string, error) { - return extattrListFd(fd, namespace) - }) -} diff --git a/vendor/go.podman.io/storage/pkg/system/xattrs_linux.go b/vendor/go.podman.io/storage/pkg/system/xattrs_linux.go index b73351d773..0b3f400b7b 100644 --- a/vendor/go.podman.io/storage/pkg/system/xattrs_linux.go +++ b/vendor/go.podman.io/storage/pkg/system/xattrs_linux.go @@ -1,12 +1,6 @@ package system import ( - "bytes" - "fmt" - "os" - "path" - "path/filepath" - "golang.org/x/sys/unix" ) @@ -20,143 +14,3 @@ const ( // Value is too small or too large for maximum size allowed EOVERFLOW unix.Errno = unix.EOVERFLOW ) - -// getxattr is the logic underlying Lgetxattr and RootLgetxattr. -// Returns a []byte slice if the xattr is set and nil otherwise. -func getxattr(syscallName string, pathInError string, getSyscall func(dest []byte) (int, error)) ([]byte, error) { - // Start with a 128 length byte array - dest := make([]byte, 128) - sz, errno := getSyscall(dest) - - for errno == unix.ERANGE { - // Buffer too small, use zero-sized buffer to get the actual size - sz, errno = getSyscall([]byte{}) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - dest = make([]byte, sz) - sz, errno = getSyscall(dest) - } - - switch { - case errno == unix.ENODATA: - return nil, nil - case errno != nil: - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - return dest[:sz], nil -} - -// Lgetxattr retrieves the value of the extended attribute identified by attr -// and associated with the given path in the file system. -// Returns a []byte slice if the xattr is set and nil otherwise. -func Lgetxattr(path string, attr string) ([]byte, error) { - return getxattr("lgetxattr", path, func(dest []byte) (int, error) { - return unix.Lgetxattr(path, attr, dest) - }) -} - -// RootLgetxattr retrieves the value of the extended attribute identified by attr -// in fsPath (per fs.ValidPath) under root. -// Returns a []byte slice if the xattr is set and nil otherwise. -func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { - // We can use neither root.Open nor pathrs.OpenatInRoot to get the target file descriptor because they follow trailing symlinks. - parentDir, err := root.Open(path.Dir(fsPath)) - if err != nil { - return nil, err - } - defer parentDir.Close() - // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentDir. - fsBase := path.Base(fsPath) - if fsBase == ".." { - return nil, fmt.Errorf("trailing .. in RootLgetxattr(%q)", fsPath) - } - // Ideally we would use getxattrat() here, but as of 2026-05 that might be too recent. - fd, err := syscallConnControl(parentDir, func(parentDir uintptr) (int, error) { - return unix.Openat(int(parentDir), filepath.FromSlash(fsBase), unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - return getxattr("RootLgetxattr", fsPath, func(dest []byte) (int, error) { - return unix.Getxattr(fmt.Sprintf("/proc/self/fd/%d", fd), attr, dest) - }) -} - -// Lsetxattr sets the value of the extended attribute identified by attr -// and associated with the given path in the file system. -func Lsetxattr(path string, attr string, data []byte, flags int) error { - if err := unix.Lsetxattr(path, attr, data, flags); err != nil { - return &os.PathError{Op: "lsetxattr", Path: path, Err: err} - } - - return nil -} - -// listxattr is the logic underlying Llistxattr and RootLlistxattr. -func listxattr(syscallName string, pathInError string, listSyscall func(dest []byte) (int, error)) ([]string, error) { - dest := make([]byte, 128) - sz, errno := listSyscall(dest) - - for errno == unix.ERANGE { - // Buffer too small, use zero-sized buffer to get the actual size - sz, errno = listSyscall([]byte{}) - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - dest = make([]byte, sz) - sz, errno = listSyscall(dest) - } - if errno != nil { - return nil, &os.PathError{Op: syscallName, Path: pathInError, Err: errno} - } - - var attrs []string - for token := range bytes.SplitSeq(dest[:sz], []byte{0}) { - if len(token) > 0 { - attrs = append(attrs, string(token)) - } - } - - return attrs, nil -} - -// Llistxattr lists extended attributes associated with the given path -// in the file system. -func Llistxattr(path string) ([]string, error) { - return listxattr("llistxattr", path, func(dest []byte) (int, error) { - return unix.Llistxattr(path, dest) - }) -} - -// RootLlistxattr lists extended attributes associated with -// fsPath (per fs.ValidPath) under root. -func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { - // We can use neither root.Open nor pathrs.OpenatInRoot to get the target file descriptor because they follow trailing symlinks. - parentDir, err := root.Open(path.Dir(fsPath)) - if err != nil { - return nil, err - } - defer parentDir.Close() - // A path per fs.ValidPath should not contain a ".."; reject it so that we can ensure no escape from parentDir. - fsBase := path.Base(fsPath) - if fsBase == ".." { - return nil, fmt.Errorf("trailing .. in RootLlistxattr(%q)", fsPath) - } - // Ideally we would use listxattrat() here, but as of 2026-05 that might be too recent. - fd, err := syscallConnControl(parentDir, func(parentDir uintptr) (int, error) { - return unix.Openat(int(parentDir), filepath.FromSlash(fsBase), unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - }) - if err != nil { - return nil, err - } - defer unix.Close(fd) - - return listxattr("RootLlistxattr", fsPath, func(dest []byte) (int, error) { - return unix.Listxattr(fmt.Sprintf("/proc/self/fd/%d", fd), dest) - }) -} diff --git a/vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go b/vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go index 2c02a6d530..43f0245818 100644 --- a/vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go +++ b/vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go @@ -3,7 +3,6 @@ package system import ( - "os" "syscall" ) @@ -17,31 +16,3 @@ const ( // Value is too small or too large for maximum size allowed EOVERFLOW syscall.Errno = syscall.Errno(0) ) - -// Lgetxattr is not supported on platforms other than linux. -func Lgetxattr(path string, attr string) ([]byte, error) { - return nil, ErrNotSupportedPlatform -} - -// RootLgetxattr retrieves the value of the extended attribute identified by attr -// in fsPath (per fs.ValidPath) under root. -// Returns a []byte slice if the xattr is set and nil otherwise. -func RootLgetxattr(root *os.Root, fsPath string, attr string) ([]byte, error) { - return nil, ErrNotSupportedPlatform -} - -// Lsetxattr is not supported on platforms other than linux. -func Lsetxattr(path string, attr string, data []byte, flags int) error { - return ErrNotSupportedPlatform -} - -// Llistxattr is not supported on platforms other than linux. -func Llistxattr(path string) ([]string, error) { - return nil, ErrNotSupportedPlatform -} - -// RootLlistxattr lists extended attributes associated with -// fsPath (per fs.ValidPath) under root. -func RootLlistxattr(root *os.Root, fsPath string) ([]string, error) { - return nil, ErrNotSupportedPlatform -} diff --git a/vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go b/vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go index 0022b48bff..42bc28f1ad 100644 --- a/vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go +++ b/vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go @@ -703,11 +703,17 @@ func GetHostIDMappings(pid string) ([]specs.LinuxIDMapping, []specs.LinuxIDMappi return uidmap, gidmap, nil } -// GetSubIDMappings reads mappings from /etc/subuid and /etc/subgid. -func GetSubIDMappings(user, group string) ([]specs.LinuxIDMapping, []specs.LinuxIDMapping, error) { - mappings, err := idtools.NewIDMappings(user, group) +// GetSubIDMappings reads the uid and gid mappings for the given usernames. +// It reads the data from the /etc/sub{uid,gid} files or +// when using the libsubid build tag it then uses the libsubid functions +// to query the user ranges which resolves them according to the system NSS +// configuration, potentially fetching the ranges from a central network source. +// It returns the uid mapping as first return value and the gid mapping as second +// or an error otherwise. +func GetSubIDMappings(usernameForUIDs, usernameForGIDs string) ([]specs.LinuxIDMapping, []specs.LinuxIDMapping, error) { + mappings, err := idtools.NewIDMappings(usernameForUIDs, usernameForGIDs) if err != nil { - return nil, nil, fmt.Errorf("reading subuid mappings for user %q and subgid mappings for group %q: %w", user, group, err) + return nil, nil, err } var uidmap, gidmap []specs.LinuxIDMapping for _, m := range mappings.UIDs() { diff --git a/vendor/modules.txt b/vendor/modules.txt index 7005867b81..9bd427aa14 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -741,7 +741,7 @@ go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded go.opentelemetry.io/otel/trace/internal/telemetry go.opentelemetry.io/otel/trace/noop -# go.podman.io/buildah v1.45.1 +# go.podman.io/buildah v1.45.2 ## explicit; go 1.26.0 go.podman.io/buildah go.podman.io/buildah/bind @@ -922,7 +922,7 @@ go.podman.io/image/v5/transports go.podman.io/image/v5/transports/alltransports go.podman.io/image/v5/types go.podman.io/image/v5/version -# go.podman.io/storage v1.64.1 +# go.podman.io/storage v1.64.2 ## explicit; go 1.25.0 go.podman.io/storage go.podman.io/storage/drivers @@ -938,8 +938,11 @@ go.podman.io/storage/internal/createpath go.podman.io/storage/internal/dedup go.podman.io/storage/internal/driver go.podman.io/storage/internal/rawfilelock +go.podman.io/storage/internal/rootlookupcache go.podman.io/storage/internal/staging_lockfile +go.podman.io/storage/internal/stat go.podman.io/storage/internal/tempdir +go.podman.io/storage/internal/xattrs go.podman.io/storage/pkg/archive go.podman.io/storage/pkg/chrootarchive go.podman.io/storage/pkg/chunked