mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-10-30 09:47:39 +00:00
Bump to Buildah v1.45.2 and storage v1.64.2
Signed-off-by: Matt Heon <matthew.heon@pm.me>
This commit is contained in:
parent
2215c3b8a7
commit
3c4bc1617c
48 changed files with 1626 additions and 886 deletions
4
go.mod
4
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
|
||||
|
|
|
|||
8
go.sum
8
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=
|
||||
|
|
|
|||
1
vendor/go.podman.io/buildah/.packit.yaml
generated
vendored
1
vendor/go.podman.io/buildah/.packit.yaml
generated
vendored
|
|
@ -131,6 +131,7 @@ jobs:
|
|||
update_release: false
|
||||
dist_git_branches: &fedora_targets
|
||||
- fedora-rawhide
|
||||
- fedora-45
|
||||
|
||||
# Sync to CentOS Stream
|
||||
- job: propose_downstream
|
||||
|
|
|
|||
7
vendor/go.podman.io/buildah/CHANGELOG.md
generated
vendored
7
vendor/go.podman.io/buildah/CHANGELOG.md
generated
vendored
|
|
@ -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
|
||||
|
|
|
|||
6
vendor/go.podman.io/buildah/changelog.txt
generated
vendored
6
vendor/go.podman.io/buildah/changelog.txt
generated
vendored
|
|
@ -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
|
||||
|
|
|
|||
143
vendor/go.podman.io/buildah/copier/copier.go
generated
vendored
143
vendor/go.podman.io/buildah/copier/copier.go
generated
vendored
|
|
@ -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
|
||||
|
|
|
|||
2
vendor/go.podman.io/buildah/define/types.go
generated
vendored
2
vendor/go.podman.io/buildah/define/types.go
generated
vendored
|
|
@ -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"
|
||||
|
|
|
|||
2
vendor/go.podman.io/storage/VERSION
generated
vendored
2
vendor/go.podman.io/storage/VERSION
generated
vendored
|
|
@ -1 +1 @@
|
|||
1.64.1
|
||||
1.64.2
|
||||
|
|
|
|||
148
vendor/go.podman.io/storage/internal/rootlookupcache/cache.go
generated
vendored
Normal file
148
vendor/go.podman.io/storage/internal/rootlookupcache/cache.go
generated
vendored
Normal file
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//go:build !freebsd
|
||||
|
||||
package system
|
||||
package stat
|
||||
|
||||
type platformStatT struct{}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
16
vendor/go.podman.io/storage/internal/stat/stat_linux.go
generated
vendored
Normal file
16
vendor/go.podman.io/storage/internal/stat/stat_linux.go
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
73
vendor/go.podman.io/storage/internal/stat/stat_unix.go
generated
vendored
Normal file
73
vendor/go.podman.io/storage/internal/stat/stat_unix.go
generated
vendored
Normal file
|
|
@ -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))
|
||||
}
|
||||
68
vendor/go.podman.io/storage/internal/stat/stat_windows.go
generated
vendored
Normal file
68
vendor/go.podman.io/storage/internal/stat/stat_windows.go
generated
vendored
Normal file
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
8
vendor/go.podman.io/storage/internal/xattrs/errors.go
generated
vendored
Normal file
8
vendor/go.podman.io/storage/internal/xattrs/errors.go
generated
vendored
Normal file
|
|
@ -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")
|
||||
115
vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go
generated
vendored
Normal file
115
vendor/go.podman.io/storage/internal/xattrs/extattr_freebsd.go
generated
vendored
Normal file
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
181
vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go
generated
vendored
Normal file
181
vendor/go.podman.io/storage/internal/xattrs/xattrs_darwin.go
generated
vendored
Normal file
|
|
@ -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()
|
||||
}
|
||||
177
vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go
generated
vendored
Normal file
177
vendor/go.podman.io/storage/internal/xattrs/xattrs_freebsd.go
generated
vendored
Normal file
|
|
@ -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()
|
||||
}
|
||||
184
vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go
generated
vendored
Normal file
184
vendor/go.podman.io/storage/internal/xattrs/xattrs_linux.go
generated
vendored
Normal file
|
|
@ -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()
|
||||
}
|
||||
48
vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go
generated
vendored
Normal file
48
vendor/go.podman.io/storage/internal/xattrs/xattrs_unix.go
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
78
vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go
generated
vendored
Normal file
78
vendor/go.podman.io/storage/internal/xattrs/xattrs_unsupported.go
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
105
vendor/go.podman.io/storage/pkg/archive/archive.go
generated
vendored
105
vendor/go.podman.io/storage/pkg/archive/archive.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
vendor/go.podman.io/storage/pkg/archive/archive_linux.go
generated
vendored
21
vendor/go.podman.io/storage/pkg/archive/archive_linux.go
generated
vendored
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
vendor/go.podman.io/storage/pkg/archive/archive_other.go
generated
vendored
2
vendor/go.podman.io/storage/pkg/archive/archive_other.go
generated
vendored
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
package archive
|
||||
|
||||
func getWhiteoutConverter(_ WhiteoutFormat, _ any, _ *TarOptions) tarWhiteoutConverter {
|
||||
func getWhiteoutConverter(_ WhiteoutFormat, _ any) tarWhiteoutConverter {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
64
vendor/go.podman.io/storage/pkg/archive/changes.go
generated
vendored
64
vendor/go.podman.io/storage/pkg/archive/changes.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
99
vendor/go.podman.io/storage/pkg/archive/changes_linux.go
generated
vendored
99
vendor/go.podman.io/storage/pkg/archive/changes_linux.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
23
vendor/go.podman.io/storage/pkg/archive/changes_other.go
generated
vendored
23
vendor/go.podman.io/storage/pkg/archive/changes_other.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
21
vendor/go.podman.io/storage/pkg/idtools/idtools.go
generated
vendored
21
vendor/go.podman.io/storage/pkg/idtools/idtools.go
generated
vendored
|
|
@ -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{
|
||||
|
|
|
|||
47
vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go
generated
vendored
47
vendor/go.podman.io/storage/pkg/idtools/idtools_supported.go
generated
vendored
|
|
@ -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),
|
||||
|
|
|
|||
6
vendor/go.podman.io/storage/pkg/system/errors.go
generated
vendored
6
vendor/go.podman.io/storage/pkg/system/errors.go
generated
vendored
|
|
@ -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
|
||||
|
|
|
|||
94
vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go
generated
vendored
94
vendor/go.podman.io/storage/pkg/system/extattr_freebsd.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
17
vendor/go.podman.io/storage/pkg/system/lstat.go
generated
vendored
Normal file
17
vendor/go.podman.io/storage/pkg/system/lstat.go
generated
vendored
Normal file
|
|
@ -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
|
||||
}
|
||||
14
vendor/go.podman.io/storage/pkg/system/lstat_unix.go
generated
vendored
14
vendor/go.podman.io/storage/pkg/system/lstat_unix.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
18
vendor/go.podman.io/storage/pkg/system/lstat_windows.go
generated
vendored
18
vendor/go.podman.io/storage/pkg/system/lstat_windows.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
19
vendor/go.podman.io/storage/pkg/system/stat_linux.go
generated
vendored
19
vendor/go.podman.io/storage/pkg/system/stat_linux.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
60
vendor/go.podman.io/storage/pkg/system/stat_unix.go
generated
vendored
60
vendor/go.podman.io/storage/pkg/system/stat_unix.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
63
vendor/go.podman.io/storage/pkg/system/stat_windows.go
generated
vendored
63
vendor/go.podman.io/storage/pkg/system/stat_windows.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
55
vendor/go.podman.io/storage/pkg/system/xattrs.go
generated
vendored
55
vendor/go.podman.io/storage/pkg/system/xattrs.go
generated
vendored
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
134
vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go
generated
vendored
134
vendor/go.podman.io/storage/pkg/system/xattrs_darwin.go
generated
vendored
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
143
vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go
generated
vendored
143
vendor/go.podman.io/storage/pkg/system/xattrs_freebsd.go
generated
vendored
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
146
vendor/go.podman.io/storage/pkg/system/xattrs_linux.go
generated
vendored
146
vendor/go.podman.io/storage/pkg/system/xattrs_linux.go
generated
vendored
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
29
vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go
generated
vendored
29
vendor/go.podman.io/storage/pkg/system/xattrs_unsupported.go
generated
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
14
vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go
generated
vendored
14
vendor/go.podman.io/storage/pkg/unshare/unshare_linux.go
generated
vendored
|
|
@ -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() {
|
||||
|
|
|
|||
7
vendor/modules.txt
vendored
7
vendor/modules.txt
vendored
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue