diff --git a/go.mod b/go.mod index 07748330f9..960d2e6a7b 100644 --- a/go.mod +++ b/go.mod @@ -63,10 +63,10 @@ require ( github.com/stretchr/testify v1.11.1 github.com/vbauerster/mpb/v8 v8.12.0 github.com/vishvananda/netlink v1.3.1 - go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85 - go.podman.io/common v0.67.2-0.20260430194920-3ceb1b29d72d - go.podman.io/image/v5 v5.39.3-0.20260430194920-3ceb1b29d72d - go.podman.io/storage v1.62.1-0.20260430194920-3ceb1b29d72d + go.podman.io/buildah v1.42.1-0.20260501153811-377cf64e213b + go.podman.io/common v0.67.2-0.20260504145149-b5d50461d3b9 + go.podman.io/image/v5 v5.39.3-0.20260504145149-b5d50461d3b9 + go.podman.io/storage v1.62.1-0.20260504145149-b5d50461d3b9 golang.org/x/crypto v0.50.0 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 @@ -112,7 +112,7 @@ require ( github.com/docker/docker-credential-helpers v0.9.6 // indirect github.com/ebitengine/purego v0.10.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.10.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fsouza/go-dockerclient v1.13.1 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.3 // indirect @@ -168,7 +168,7 @@ require ( github.com/tklauser/numcpus v0.11.0 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/ulikunitz/xz v0.5.15 // indirect - github.com/vbatts/tar-split v0.12.2 // indirect + github.com/vbatts/tar-split v0.12.3 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.etcd.io/bbolt v1.4.3 // indirect diff --git a/go.sum b/go.sum index 8d0892dacb..4b4c952fd1 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,8 @@ github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWh github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v29.4.1+incompatible h1:02RT8QqqwtGRn+6SYypv8IUEbD/ltY6sfKCJIoUcGzk= -github.com/docker/cli v29.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.4.2+incompatible h1:nhxMY4v7wB0QMMc5ppeqV6FBMwzqv0n4t2gogu/R2DQ= +github.com/docker/cli v29.4.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0= @@ -119,8 +119,8 @@ github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/fsnotify/fsnotify v1.10.0 h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M= -github.com/fsnotify/fsnotify v1.10.0/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/fsouza/go-dockerclient v1.13.1 h1:HbkJO8UPUhuQ1wHIgX+ho7AUucBmjtOfzSWYUgmWL/8= github.com/fsouza/go-dockerclient v1.13.1/go.mod h1:0gx0SIFGV1F+79sM9p5K+UCc8enYKA8DBp7BlwlixpM= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= @@ -383,8 +383,8 @@ github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8 github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= -github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vbatts/tar-split v0.12.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw= +github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0= github.com/vbauerster/mpb/v8 v8.12.0 h1:+gneY3ifzc88tKDzOtfG8k8gfngCx615S2ZmFM4liWg= github.com/vbauerster/mpb/v8 v8.12.0/go.mod h1:V02YIuMVo301Y1VE9VtZlD8s84OMsk+EKN6mwvf/588= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= @@ -429,14 +429,14 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85 h1:rVKRagobPO6kGHOg2NhGNs6xWVgZctiOn4tmxa3cytA= -go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85/go.mod h1:JjFirF1zlILz55ZkVYYhLRnted7mPlmoS2w2ihYw8iw= -go.podman.io/common v0.67.2-0.20260430194920-3ceb1b29d72d h1:cfi7hq9/Y8BaCppWFnRfSYQq6VARfEpdbrd7Tysosy4= -go.podman.io/common v0.67.2-0.20260430194920-3ceb1b29d72d/go.mod h1:iRT7GS3Ju8VxV8cyPQ6Ts5z4Zu1l8NHG7k+3sU17Xww= -go.podman.io/image/v5 v5.39.3-0.20260430194920-3ceb1b29d72d h1:CW23G56hYIAb+nHiRSa+J8UE+G6DS3MSvAnz05fk2o4= -go.podman.io/image/v5 v5.39.3-0.20260430194920-3ceb1b29d72d/go.mod h1:VBEYYXmGMYh+oAZZc1tPCVYWiCnbW16SAZfLGJMfxwE= -go.podman.io/storage v1.62.1-0.20260430194920-3ceb1b29d72d h1:JHvCWd1irnPVwjP2srHfLEs+tpHLqNq81g9XTLXmQ4g= -go.podman.io/storage v1.62.1-0.20260430194920-3ceb1b29d72d/go.mod h1:c2/RBYEPTpqJ4BQfI3nDZB5zUKXuhX1nSGgrunZV2TM= +go.podman.io/buildah v1.42.1-0.20260501153811-377cf64e213b h1:i8ntFzITajbJA3ojnA0ZdpbC+I+ccweZvZaGIhQb4i8= +go.podman.io/buildah v1.42.1-0.20260501153811-377cf64e213b/go.mod h1:hPvgsjBU09C+15fKoIZJvKvNaxR+c0QvMg/n4NgBS7A= +go.podman.io/common v0.67.2-0.20260504145149-b5d50461d3b9 h1:rhSZjo2liJOlaa3SzPLvDTD93MDi4bl6E3RKTME9Hrc= +go.podman.io/common v0.67.2-0.20260504145149-b5d50461d3b9/go.mod h1:TYI+ocF4gfL8QCBo5GqOSUAOA3QnVgkjjg/nQZRG3o0= +go.podman.io/image/v5 v5.39.3-0.20260504145149-b5d50461d3b9 h1:xAeC/aqHQ01RWHq60B1FETR65XW2kfIaOBEmYNJFunc= +go.podman.io/image/v5 v5.39.3-0.20260504145149-b5d50461d3b9/go.mod h1:D+09OPzsrFuzeKqsJEaaxtItkSd12+eZyOdFyuJF8TY= +go.podman.io/storage v1.62.1-0.20260504145149-b5d50461d3b9 h1:1rviLyzh9boijwxX4UK6U6XUmE1Qyl21XPUSHNKTh0s= +go.podman.io/storage v1.62.1-0.20260504145149-b5d50461d3b9/go.mod h1:eZIqDigffFi9NlPezLvUVw/nsUIruaui436E5E4GmXs= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md index 4ed4864f47..3027f3c67a 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +1.10.1 2026-05-04 +----------------- + +### Changes and fixes + +- inotify: don't remove sibling watches sharing a path prefix ([#754]) + +- inotify, windows: don't rename sibling watches sharing a path prefix + ([#755]) + + +[#754]: https://github.com/fsnotify/fsnotify/pull/754 +[#755]: https://github.com/fsnotify/fsnotify/pull/755 + + 1.10.0 2026-04-30 ----------------- This version of fsnotify needs Go 1.23. diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index d8441aa061..2e56ef4c9a 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -171,6 +171,38 @@ distro's documentation): fs.inotify.max_user_watches=200000 fs.inotify.max_user_instances=256 +### Windows +Recursive watching is not currently enabled through fsnotify's public API +(see the FAQ "Are subdirectories watched?" above). The notes below +describe Windows backend behavior observed when recursive watching is +enabled internally (for example, in fsnotify's own tests). They are kept +here as a reference for maintainers and contributors who encounter the +behavior, since the recursive code path still exists in the backend. + +When recursive watching is enabled and you watch a directory, you may +receive a `Write` event for an intermediate directory whenever a child +entry inside it is created, renamed, or removed. For example, with a +recursive watch on `/a` and a new file `/a/b/c`, you will receive +`Create /a/b/c` and may also receive `Write /a/b`. + +This happens because, on NTFS-backed volumes, modifying the entries of a +directory updates that directory's last-write time, and the Windows +backend requests `FILE_NOTIFY_CHANGE_LAST_WRITE` to support `Write` events +on files. The same `Write` filter therefore picks up the directory's +metadata update. + +kqueue has the same "directory `Write` = directory contents changed" +semantics, so portable code that treats `Write` on a directory as +"something inside it changed" works on Windows and BSD/macOS, but not on +Linux (inotify uses `Write` only for file-content changes). If you only +care about file content, filter out `Write` events whose path refers to a +directory. + +Whether the directory `Write` is actually delivered alongside the child +events is not guaranteed: it depends on `ReadDirectoryChangesW` buffering, +NTFS metadata update timing, and event coalescing, none of which fsnotify +controls. + ### kqueue (macOS, all BSD systems) kqueue requires opening a file descriptor for every file that's being watched; diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go index cdb7812ac1..4c3f6f7c28 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go @@ -82,6 +82,13 @@ func (w *watches) len() int { return len(w.wd) } func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd } func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) } +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func (w *watches) removePath(path string) ([]uint32, error) { path, recurse := recursivePath(path) wd, ok := w.path[path] @@ -103,7 +110,7 @@ func (w *watches) removePath(path string) ([]uint32, error) { wds := make([]uint32, 0, 8) wds = append(wds, wd) for p, rwd := range w.path { - if strings.HasPrefix(p, path) { + if isSameOrDescendantPath(p, path) { delete(w.path, p) delete(w.wd, rwd) wds = append(wds, rwd) @@ -501,7 +508,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs if k == watch.wd || ww.path == ev.Name { continue } - if strings.HasPrefix(ww.path, ev.renamedFrom) { + if isSameOrDescendantPath(ww.path, ev.renamedFrom) { ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1) w.watches.wd[k] = ww } diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go index 8ef0eb0f62..fb9210f24e 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go @@ -36,6 +36,13 @@ type readDirChangesW struct { var defaultBufferSize = 50 +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func newBackend(ev chan Event, errs chan error) (backend, error) { port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) if err != nil { @@ -605,7 +612,7 @@ func (w *readDirChangesW) readEvents() { w.mu.Lock() for _, watchMap := range w.watches { for _, ww := range watchMap { - if strings.HasPrefix(ww.path, old) { + if isSameOrDescendantPath(ww.path, old) { ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old)) } } diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index c7a4cb309c..38cb4dd481 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -92,6 +92,28 @@ import ( // Sometimes it will send events for all files, sometimes it will send no // events, and often only for some files. // +// Recursive watching is not currently enabled through fsnotify's public +// API; the recursive code path is gated and only exercised by fsnotify's +// own tests. The note below describes backend behavior observed when +// recursive watching is enabled internally, and is kept here as a +// reference for maintainers and contributors who encounter it. +// +// When recursive watching is enabled and you watch a directory, you may +// receive a Write event for an intermediate directory whenever a child +// entry inside it is created, renamed, or removed. For example, with a +// recursive watch on /a and a new file /a/b/c, you will receive +// Create /a/b/c and may also receive Write /a/b. +// +// This happens because, on NTFS-backed volumes, modifying the entries of a +// directory updates that directory's last-write time, and the Windows +// backend requests FILE_NOTIFY_CHANGE_LAST_WRITE to support Write events +// on files. The same Write filter therefore picks up the directory's +// metadata update. +// +// Whether the directory Write is actually delivered alongside the child +// events is not guaranteed; it depends on ReadDirectoryChangesW buffering, +// NTFS metadata update timing, and event coalescing. +// // The default ReadDirectoryChangesW() buffer size is 64K, which is the largest // value that is guaranteed to work with SMB filesystems. If you have many // events in quick succession this may not be enough, and you will have to use @@ -128,8 +150,12 @@ type Watcher struct { // want to wait until you've stopped receiving them // (see the dedup example in cmd/fsnotify). // - // Some systems may send Write event for directories - // when the directory content changes. + // Some systems also send Write events for directories + // when the directory contents change. This is the + // case for kqueue, and on Windows for the directory + // that contains a created, renamed, or removed child + // entry. It does not happen on inotify. See the + // per-platform notes on [Watcher]. // // fsnotify.Chmod Attributes were changed. On Linux this is also sent // when a file is removed (or more accurately, when a @@ -178,7 +204,9 @@ const ( Create Op = 1 << iota // The pathname was written to; this does *not* mean the write has finished, - // and a write can be followed by more writes. + // and a write can be followed by more writes. On Windows and kqueue, a + // Write on a directory can also indicate that its contents changed; see + // the per-platform notes on [Watcher]. Write // The path was removed; any watches on it will be removed. Some "remove" diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_deprecated.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/device_deprecated.go deleted file mode 100644 index 9483f054dc..0000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_deprecated.go +++ /dev/null @@ -1,20 +0,0 @@ -package devices - -import "github.com/opencontainers/cgroups/devices/config" - -// Deprecated: use [github.com/opencontainers/cgroups/devices/config]. -const ( - Wildcard = config.Wildcard - WildcardDevice = config.WildcardDevice - BlockDevice = config.BlockDevice - CharDevice = config.CharDevice - FifoDevice = config.FifoDevice -) - -// Deprecated: use [github.com/opencontainers/cgroups/devices/config]. -type ( - Device = config.Device - Permissions = config.Permissions - Type = config.Type - Rule = config.Rule -) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go deleted file mode 100644 index 409e58e963..0000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go +++ /dev/null @@ -1,112 +0,0 @@ -//go:build !windows - -package devices - -import ( - "errors" - "os" - "path/filepath" - - "golang.org/x/sys/unix" -) - -// ErrNotADevice denotes that a file is not a valid linux device. -var ErrNotADevice = errors.New("not a device node") - -// Testing dependencies -var ( - unixLstat = unix.Lstat - osReadDir = os.ReadDir -) - -// DeviceFromPath takes the path to a device and its cgroup_permissions (which -// cannot be easily queried) to look up the information about a linux device -// and returns that information as a Device struct. -func DeviceFromPath(path, permissions string) (*Device, error) { - var stat unix.Stat_t - err := unixLstat(path, &stat) - if err != nil { - return nil, err - } - - var ( - devType Type - mode = stat.Mode - devNumber = uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS. - major = unix.Major(devNumber) - minor = unix.Minor(devNumber) - ) - switch mode & unix.S_IFMT { - case unix.S_IFBLK: - devType = BlockDevice - case unix.S_IFCHR: - devType = CharDevice - case unix.S_IFIFO: - devType = FifoDevice - default: - return nil, ErrNotADevice - } - return &Device{ - Rule: Rule{ - Type: devType, - Major: int64(major), - Minor: int64(minor), - Permissions: Permissions(permissions), - }, - Path: path, - FileMode: os.FileMode(mode &^ unix.S_IFMT), - Uid: stat.Uid, - Gid: stat.Gid, - }, nil -} - -// HostDevices returns all devices that can be found under /dev directory. -func HostDevices() ([]*Device, error) { - return GetDevices("/dev") -} - -// GetDevices recursively traverses a directory specified by path -// and returns all devices found there. -func GetDevices(path string) ([]*Device, error) { - files, err := osReadDir(path) - if err != nil { - return nil, err - } - var out []*Device - for _, f := range files { - switch { - case f.IsDir(): - switch f.Name() { - // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 - // ".udev" added to address https://github.com/opencontainers/runc/issues/2093 - case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts", ".udev": - continue - default: - sub, err := GetDevices(filepath.Join(path, f.Name())) - if err != nil { - return nil, err - } - - out = append(out, sub...) - continue - } - case f.Name() == "console": - continue - } - device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm") - if err != nil { - if errors.Is(err, ErrNotADevice) { - continue - } - if errors.Is(err, os.ErrNotExist) { - continue - } - return nil, err - } - if device.Type == FifoDevice { - continue - } - out = append(out, device) - } - return out, nil -} diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/format.go b/vendor/github.com/vbatts/tar-split/archive/tar/format.go index 60977980c5..6f31845e43 100644 --- a/vendor/github.com/vbatts/tar-split/archive/tar/format.go +++ b/vendor/github.com/vbatts/tar-split/archive/tar/format.go @@ -147,6 +147,12 @@ const ( // Max length of a special file (PAX header, GNU long name or link). // This matches the limit used by libarchive. maxSpecialFileSize = 1 << 20 + + // Maximum number of sparse file entries. + // We should never actually hit this limit + // (every sparse encoding will first be limited by maxSpecialFileSize), + // but this adds an additional layer of defense. + maxSparseFileEntries = 1 << 20 ) // blockPadding computes the number of bytes needed to pad offset up to the diff --git a/vendor/github.com/vbatts/tar-split/archive/tar/reader.go b/vendor/github.com/vbatts/tar-split/archive/tar/reader.go index a645c41605..ebe8579772 100644 --- a/vendor/github.com/vbatts/tar-split/archive/tar/reader.go +++ b/vendor/github.com/vbatts/tar-split/archive/tar/reader.go @@ -537,7 +537,8 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err } s := blk.GNU().Sparse() spd := make(sparseDatas, 0, s.MaxEntries()) - for { + totalSize := len(s) + for totalSize < maxSpecialFileSize { for i := 0; i < s.MaxEntries(); i++ { // This termination condition is identical to GNU and BSD tar. if s.Entry(i).Offset()[0] == 0x00 { @@ -548,7 +549,11 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err if p.err != nil { return nil, p.err } - spd = append(spd, sparseEntry{Offset: offset, Length: length}) + var err error + spd, err = appendSparseEntry(spd, sparseEntry{Offset: offset, Length: length}) + if err != nil { + return nil, err + } } if s.IsExtended()[0] > 0 { @@ -560,10 +565,12 @@ func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, err tr.rawBytes.Write(blk[:]) } s = blk.Sparse() + totalSize += len(s) continue } return spd, nil // Done } + return nil, errSparseTooLong } // readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format @@ -636,7 +643,10 @@ func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) { if err1 != nil || err2 != nil { return nil, ErrHeader } - spd = append(spd, sparseEntry{Offset: offset, Length: length}) + spd, err = appendSparseEntry(spd, sparseEntry{Offset: offset, Length: length}) + if err != nil { + return nil, err + } } return spd, nil } @@ -670,12 +680,22 @@ func readGNUSparseMap0x1(paxHdrs map[string]string) (sparseDatas, error) { if err1 != nil || err2 != nil { return nil, ErrHeader } - spd = append(spd, sparseEntry{Offset: offset, Length: length}) + spd, err = appendSparseEntry(spd, sparseEntry{Offset: offset, Length: length}) + if err != nil { + return nil, err + } sparseMap = sparseMap[2:] } return spd, nil } +func appendSparseEntry(spd sparseDatas, ent sparseEntry) (sparseDatas, error) { + if len(spd) >= maxSparseFileEntries { + return nil, errSparseTooLong + } + return append(spd, ent), nil +} + // Read reads from the current file in the tar archive. // It returns (0, io.EOF) when it reaches the end of that file, // until Next is called to advance to the next file. diff --git a/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go b/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go index 80c2522afe..a17b6eac1e 100644 --- a/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go +++ b/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go @@ -1,23 +1,168 @@ package asm import ( + "errors" "io" "github.com/vbatts/tar-split/archive/tar" "github.com/vbatts/tar-split/tar/storage" ) -// NewInputTarStream wraps the Reader stream of a tar archive and provides a -// Reader stream of the same. +// runInputTarStreamGoroutine is the goroutine entrypoint. // -// In the middle it will pack the segments and file metadata to storage.Packer -// `p`. +// It centralizes the goroutine protocol so the core parsing logic can be +// written as ordinary Go code that just "returns an error". // -// The the storage.FilePutter is where payload of files in the stream are -// stashed. If this stashing is not needed, you can provide a nil -// storage.FilePutter. Since the checksumming is still needed, then a default -// of NewDiscardFilePutter will be used internally -func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io.Reader, error) { +// Protocol guarantees: +// - pW is always closed exactly once (CloseWithError(nil) == Close()). +// - if done != nil, exactly one value is sent (nil on success, non-nil on failure). +// - panics are converted into a non-nil error (and the panic is rethrown). +func runInputTarStreamGoroutine(outputRdr io.Reader, pW *io.PipeWriter, p storage.Packer, fp storage.FilePutter, done chan<- error) { + // Default to a non-nil error so a panic can't accidentally look like success. + err := errors.New("panic in runInputTarStream") + defer func() { + // CloseWithError(nil) is equivalent to Close(). + pW.CloseWithError(err) + + if done != nil { + done <- err + } + + // Preserve panic semantics while still ensuring the protocol above runs. + if r := recover(); r != nil { + panic(r) + } + }() + + err = runInputTarStream(outputRdr, p, fp) +} + +// runInputTarStream drives tar-split parsing. +// +// It reads a tar stream from outputRdr and records tar-split metadata into the +// provided storage.Packer. +// +// Abort behavior: if the consumer closes the read end early, the tee reader will +// stop producing bytes (due to pipe write failure) and tar parsing will return +// an error. We propagate that error so the goroutine terminates promptly rather +// than draining the input stream for no benefit. +func runInputTarStream(outputRdr io.Reader, p storage.Packer, fp storage.FilePutter) error { + tr := tar.NewReader(outputRdr) + tr.RawAccounting = true + + for { + hdr, err := tr.Next() + if err != nil { + if err != io.EOF { + return err + } + // Even when EOF is reached, there is often 1024 null bytes at the end + // of an archive. Collect them too. + if b := tr.RawBytes(); len(b) > 0 { + if _, err := p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: b, + }); err != nil { + return err + } + } + break // Not return: we still need to drain any additional padding. + } + if hdr == nil { + break // Not return: we still need to drain any additional padding. + } + + if b := tr.RawBytes(); len(b) > 0 { + if _, err := p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: b, + }); err != nil { + return err + } + } + + var csum []byte + if hdr.Size > 0 { + _, csum, err = fp.Put(hdr.Name, tr) + if err != nil { + return err + } + } + + entry := storage.Entry{ + Type: storage.FileType, + Size: hdr.Size, + Payload: csum, + } + // For proper marshalling of non-utf8 characters + entry.SetName(hdr.Name) + + // File entries added, regardless of size + if _, err := p.AddEntry(entry); err != nil { + return err + } + + if b := tr.RawBytes(); len(b) > 0 { + if _, err := p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: b, + }); err != nil { + return err + } + } + } + + // It is allowable, and not uncommon that there is further padding on + // the end of an archive, apart from the expected 1024 null bytes. We + // do this in chunks rather than in one go to avoid cases where a + // maliciously crafted tar file tries to trick us into reading many GBs + // into memory. + const paddingChunkSize = 1024 * 1024 + var paddingChunk [paddingChunkSize]byte + for { + n, err := outputRdr.Read(paddingChunk[:]) + if n != 0 { + if _, aerr := p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: paddingChunk[:n], + }); aerr != nil { + return aerr + } + } + if err != nil { + if err == io.EOF { + break + } + return err + } + } + + return nil +} + +// newInputTarStreamCommon sets up the shared plumbing for NewInputTarStream and +// NewInputTarStreamWithDone. +// +// It constructs an io.Pipe and an io.TeeReader such that: +// +// - The caller reads tar bytes from the returned *io.PipeReader. +// - The background goroutine simultaneously reads the same stream from the +// TeeReader to perform tar-split parsing and metadata packing. +// +// Abort and synchronization semantics: +// +// - Closing the returned PipeReader causes the TeeReader to fail its write to +// the pipe, which in turn causes the background goroutine to exit promptly. +// - If withDone is true, a done channel is returned that receives exactly one +// error value (nil on success) once the background goroutine has fully +// terminated. This allows callers to safely wait until the input reader `r` +// is no longer in use. +func newInputTarStreamCommon( + r io.Reader, + p storage.Packer, + fp storage.FilePutter, + done chan<- error, +) (pr *io.PipeReader) { // What to do here... folks will want their own access to the Reader that is // their tar archive stream, but we'll need that same stream to use our // forked 'archive/tar'. @@ -34,123 +179,59 @@ func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io // only read what the outputRdr Read's. Since Tar archives have padding on // the end, we want to be the one reading the padding, even if the user's // `archive/tar` doesn't care. - pR, pW := io.Pipe() - outputRdr := io.TeeReader(r, pW) + pr, pw := io.Pipe() - // we need a putter that will generate the crc64 sums of file payloads if fp == nil { fp = storage.NewDiscardFilePutter() } - go func() { - tr := tar.NewReader(outputRdr) - tr.RawAccounting = true - for { - hdr, err := tr.Next() - if err != nil { - if err != io.EOF { - pW.CloseWithError(err) - return - } - // even when an EOF is reached, there is often 1024 null bytes on - // the end of an archive. Collect them too. - if b := tr.RawBytes(); len(b) > 0 { - _, err := p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: b, - }) - if err != nil { - pW.CloseWithError(err) - return - } - } - break // not return. We need the end of the reader. - } - if hdr == nil { - break // not return. We need the end of the reader. - } + outputRdr := io.TeeReader(r, pw) + go runInputTarStreamGoroutine(outputRdr, pw, p, fp, done) - if b := tr.RawBytes(); len(b) > 0 { - _, err := p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: b, - }) - if err != nil { - pW.CloseWithError(err) - return - } - } - - var csum []byte - if hdr.Size > 0 { - var err error - _, csum, err = fp.Put(hdr.Name, tr) - if err != nil { - pW.CloseWithError(err) - return - } - } - - entry := storage.Entry{ - Type: storage.FileType, - Size: hdr.Size, - Payload: csum, - } - // For proper marshalling of non-utf8 characters - entry.SetName(hdr.Name) - - // File entries added, regardless of size - _, err = p.AddEntry(entry) - if err != nil { - pW.CloseWithError(err) - return - } - - if b := tr.RawBytes(); len(b) > 0 { - _, err = p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: b, - }) - if err != nil { - pW.CloseWithError(err) - return - } - } - } - - // It is allowable, and not uncommon that there is further padding on - // the end of an archive, apart from the expected 1024 null bytes. We - // do this in chunks rather than in one go to avoid cases where a - // maliciously crafted tar file tries to trick us into reading many GBs - // into memory. - const paddingChunkSize = 1024 * 1024 - var paddingChunk [paddingChunkSize]byte - for { - var isEOF bool - n, err := outputRdr.Read(paddingChunk[:]) - if err != nil { - if err != io.EOF { - pW.CloseWithError(err) - return - } - isEOF = true - } - if n != 0 { - _, err = p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: paddingChunk[:n], - }) - if err != nil { - pW.CloseWithError(err) - return - } - } - if isEOF { - break - } - } - pW.Close() - }() - - return pR, nil + return pr +} + +// NewInputTarStream wraps the Reader stream of a tar archive and provides a +// Reader stream of the same. +// +// In the middle it will pack the segments and file metadata to storage.Packer +// `p`. +// +// The storage.FilePutter is where payload of files in the stream are +// stashed. If this stashing is not needed, you can provide a nil +// storage.FilePutter. Since the checksumming is still needed, then a default +// of NewDiscardFilePutter will be used internally +// +// If callers need to be able to abort early and/or wait for goroutine termination, +// prefer NewInputTarStreamWithDone. +// +// Deprecated: This leaves a goroutine around if the consumer aborts without consuming +// the whole stream, and does not allow the caller to know when r is safe to deallocate +// or when p has written everything. Use NewInputTarStreamWithDone instead. +func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io.Reader, error) { + pr := newInputTarStreamCommon(r, p, fp, nil) + return pr, nil +} + +// NewInputTarStreamWithDone wraps the Reader stream of a tar archive and provides a +// Reader stream of the same. +// +// In the middle it will pack the segments and file metadata to storage.Packer `p`. +// +// It also returns a done channel that will receive exactly one error value +// (nil on success) when the internal goroutine has fully completed parsing +// the tar stream (including the final paddingChunk draining loop) and has +// finished writing all entries to `p`. +// +// The returned reader is an io.ReadCloser so callers can stop early; closing it +// aborts the pipe so the internal goroutine can terminate promptly (rather than +// hanging on a blocked pipe write). +// +// The caller is expected to consume the returned reader fully until EOF +// (not just the tar EOF marker); closing the returned reader earlier will +// cause the done channel to return a failure. +func NewInputTarStreamWithDone(r io.Reader, p storage.Packer, fp storage.FilePutter) (io.ReadCloser, <-chan error, error) { + done := make(chan error, 1) + pr := newInputTarStreamCommon(r, p, fp, done) + return pr, done, nil } diff --git a/vendor/github.com/vbatts/tar-split/tar/asm/iterate.go b/vendor/github.com/vbatts/tar-split/tar/asm/iterate.go index 8a65887cf2..9db3ab509d 100644 --- a/vendor/github.com/vbatts/tar-split/tar/asm/iterate.go +++ b/vendor/github.com/vbatts/tar-split/tar/asm/iterate.go @@ -11,7 +11,7 @@ import ( // IterateHeaders calls handler for each tar header provided by Unpacker func IterateHeaders(unpacker storage.Unpacker, handler func(hdr *tar.Header) error) error { - // We assume about NewInputTarStream: + // We assume about NewInputTarStreamWithDone: // - There is a separate SegmentType entry for every tar header, but only one SegmentType entry for the full header incl. any extensions // - (There is a FileType entry for every tar header, we ignore it) // - Trailing padding of a file, if any, is included in the next SegmentType entry diff --git a/vendor/go.podman.io/buildah/.packit.yaml b/vendor/go.podman.io/buildah/.packit.yaml index 032e9e13f1..f80870aeff 100644 --- a/vendor/go.podman.io/buildah/.packit.yaml +++ b/vendor/go.podman.io/buildah/.packit.yaml @@ -121,12 +121,16 @@ jobs: id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/centos-stream-$releasever/rhcontainerbot-podman-next-centos-stream-$releasever.repo # Sync to Fedora + # NOTE: Breaking changes related to Podman6 will only be shipped to Fedora + # Rawhide (to be Fedora 45) and newer. + # TODO: Update dist_git_branches as and when new Fedora releases are + # available. - job: propose_downstream trigger: release packages: [buildah-fedora] update_release: false dist_git_branches: &fedora_targets - - fedora-all + - fedora-rawhide # Sync to CentOS Stream - job: propose_downstream diff --git a/vendor/go.podman.io/buildah/GOVERNANCE.md b/vendor/go.podman.io/buildah/GOVERNANCE.md index 94b838e394..38c2ebc9b5 100644 --- a/vendor/go.podman.io/buildah/GOVERNANCE.md +++ b/vendor/go.podman.io/buildah/GOVERNANCE.md @@ -7,6 +7,5 @@ except sections found in this document, which override those found in Podman's G # Maintainers File -The definitive source of truth for maintainers of this repository is the local [MAINTAINERS.md](./MAINTAINERS.md) file. The [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository is used for project-spanning roles, including Core Maintainer and Community Manager. Some repositories in the project will also have a local [OWNERS](./OWNERS) file, which the CI system uses to map users to roles. Any changes to the [OWNERS](./OWNERS) file must make a corresponding change to the [MAINTAINERS.md](./MAINTAINERS.md) file to ensure that the file remains up to date. Most changes to [MAINTAINERS.md](./MAINTAINERS.md) will require a change to the repository’s [OWNERS](.OWNERS) file (e.g., adding a Reviewer), but some will not (e.g., promoting a Maintainer to a Core Maintainer, which comes with no additional CI-related privileges). +The definitive source of truth for maintainers of this repository is the local [MAINTAINERS.md](./MAINTAINERS.md) file. The [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository is used for project-spanning roles, including Core Maintainer and Community Manager. -Any Core Maintainers listed in Podman’s [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file should also be added to the list of “approvers” in the local [OWNERS](./OWNERS) file and as a Core Maintainer in the list of “Maintainers” in the local [MAINTAINERS.md](./MAINTAINERS.md) file. diff --git a/vendor/go.podman.io/buildah/Makefile b/vendor/go.podman.io/buildah/Makefile index 2027962559..885c0febff 100644 --- a/vendor/go.podman.io/buildah/Makefile +++ b/vendor/go.podman.io/buildah/Makefile @@ -132,7 +132,7 @@ bin/grpcnoop: tests/rpc/noop/noop.go .PHONY: clean clean: - $(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true internal/mkcw/embed/entrypoint_amd64 internal/mkcw/embed/entrypoint_arm64 internal/mkcw/embed/entrypoint_ppc64le internal/mkcw/embed/entrypoint_s390x internal/mkcw/embed/*.gz internal/mkcw/embed/asm/*.o + $(RM) -r bin tests/testreport/testreport tests/conformance/testdata/mount-targets/true internal/mkcw/embed/entrypoint_arm64 internal/mkcw/embed/entrypoint_ppc64le internal/mkcw/embed/entrypoint_s390x internal/mkcw/embed/entrypoint_arm64.gz internal/mkcw/embed/entrypoint_ppc64le.gz internal/mkcw/embed/entrypoint_s390x.gz internal/mkcw/embed/asm/*.o $(MAKE) -C docs clean .PHONY: docs diff --git a/vendor/go.podman.io/buildah/OWNERS b/vendor/go.podman.io/buildah/OWNERS deleted file mode 100644 index 4deab29604..0000000000 --- a/vendor/go.podman.io/buildah/OWNERS +++ /dev/null @@ -1,25 +0,0 @@ -approvers: - - baude - - flouthoc - - giuseppe - - lsm5 - - Luap99 - - mheon - - mtrmac - - nalind - - rhatdan - - TomSweeneyRedHat -reviewers: - - ashley-cui - - baude - - flouthoc - - giuseppe - - Honny1 - - lsm5 - - Luap99 - - mheon - - mtrmac - - nalind - - rhatdan - - TomSweeneyRedHat - - vrothberg diff --git a/vendor/go.podman.io/buildah/copier/copier.go b/vendor/go.podman.io/buildah/copier/copier.go index e1f7b07921..048585d9b2 100644 --- a/vendor/go.podman.io/buildah/copier/copier.go +++ b/vendor/go.podman.io/buildah/copier/copier.go @@ -566,7 +566,7 @@ func convertToRelSubdirectory(root, directory string) (relative string, err erro return "", fmt.Errorf("expected root directory to be an absolute path, got %q", root) } if directory == "" || !filepath.IsAbs(directory) { - return "", fmt.Errorf("expected directory to be an absolute path, got %q", root) + return "", fmt.Errorf("expected directory to be an absolute path, got %q", directory) } if filepath.VolumeName(root) != filepath.VolumeName(directory) { return "", fmt.Errorf("%q and %q are on different volumes", root, directory) @@ -866,7 +866,6 @@ func copierMain() { fmt.Fprintf(os.Stderr, "error: can't change location of chroot from %q to %q", previousRequestRoot, req.Root) os.Exit(1) } - previousRequestRoot = req.Root } else { // Figure out where to chroot to, if we weren't told. if req.Root == "" { @@ -884,6 +883,8 @@ func copierMain() { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } + + previousRequestRoot = req.Root } req.preservedRoot = req.Root @@ -1786,7 +1787,7 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str return fmt.Errorf("copying %s: %w", contentPath, err) } if n != hdr.Size { - return fmt.Errorf("copying %s: incorrect size (expected %d bytes, read %d bytes)", contentPath, n, hdr.Size) + return fmt.Errorf("copying %s: incorrect size (expected %d bytes, read %d bytes)", contentPath, hdr.Size, n) } tw.Flush() } @@ -1828,7 +1829,7 @@ func copierHandlerPut(bulkReader io.Reader, req request, idMappings *idtools.IDM containerFilePair := idtools.IDPair{UID: *fileUID, GID: *fileGID} hostFilePair, err := idMappings.ToHost(containerFilePair) if err != nil { - return errorResponse("copier: put: error mapping container filesystem owner %d:%d to host filesystem owners: %v", fileUID, fileGID, err) + return errorResponse("copier: put: error mapping container filesystem owner %d:%d to host filesystem owners: %v", *fileUID, *fileGID, err) } fileUID, fileGID = &hostFilePair.UID, &hostFilePair.GID } diff --git a/vendor/go.podman.io/buildah/define/types_unix.go b/vendor/go.podman.io/buildah/define/types_unix.go index 20b1a7b2f2..a5744be46b 100644 --- a/vendor/go.podman.io/buildah/define/types_unix.go +++ b/vendor/go.podman.io/buildah/define/types_unix.go @@ -3,14 +3,14 @@ package define import ( - "github.com/opencontainers/runc/libcontainer/devices" + "github.com/opencontainers/cgroups/devices/config" ) // BuildahDevice is a wrapper around devices.Device // with additional support for renaming a device // using bind-mount in rootless environments. type BuildahDevice struct { - devices.Device + config.Device Source string Destination string } diff --git a/vendor/go.podman.io/buildah/install.md b/vendor/go.podman.io/buildah/install.md index e8713baf27..54620a270d 100644 --- a/vendor/go.podman.io/buildah/install.md +++ b/vendor/go.podman.io/buildah/install.md @@ -231,11 +231,11 @@ This project is using [go modules](https://github.com/golang/go/wiki/Modules) fo The following configuration files are required in order for Buildah to run appropriately. The majority of these files are commonly contained in the `containers-common` package. -### [registries.conf](https://github.com/containers/buildah/blob/main/docs/samples/registries.conf) +### [registries.conf](https://github.com/containers/container-libs/blob/main/image/registries.conf) -#### Man Page: [registries.conf.5](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md) +#### Man Page: [containers-registries.conf.5](https://github.com/containers/container-libs/blob/main/image/docs/containers-registries.conf.5.md) -`/etc/containers/registries.conf` +`/usr/share/containers/registries.conf`, `/etc/containers/registries.conf`, `$HOME/.config/containers/registries.conf` registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. @@ -243,31 +243,79 @@ registries.conf is the configuration file which specifies which container regist ``` cat /etc/containers/registries.conf -# This is a system-wide configuration file used to -# keep track of registries for various container backends. -# It adheres to TOML format and does not support recursive -# lists of registries. - -# The default location for this configuration file is /etc/containers/registries.conf. - -# The only valid categories are: 'registries.search', 'registries.insecure', -# and 'registries.block'. - -[registries.search] -registries = ['docker.io', 'registry.fedoraproject.org', 'quay.io', 'registry.access.redhat.com'] - -# If you need to access insecure registries, add the registry's fully-qualified name. -# An insecure registry is one that does not have a valid SSL certificate or only does HTTP. -[registries.insecure] -registries = [] - - -# If you need to block pull access from a registry, uncomment the section below -# and add the registries fully-qualified name. +# For more information on this configuration file, see containers-registries.conf(5). # -# Docker only -[registries.block] -registries = [] +# NOTE: RISK OF USING UNQUALIFIED IMAGE NAMES +# We recommend always using fully qualified image names including the registry +# server (full dns name), namespace, image name, and tag +# (e.g., registry.redhat.io/ubi8/ubi:latest). Pulling by digest (i.e., +# quay.io/repository/name@digest) further eliminates the ambiguity of tags. +# When using short names, there is always an inherent risk that the image being +# pulled could be spoofed. For example, a user wants to pull an image named +# `foobar` from a registry and expects it to come from myregistry.com. If +# myregistry.com is not first in the search list, an attacker could place a +# different `foobar` image at a registry earlier in the search list. The user +# would accidentally pull and run the attacker's image and code rather than the +# intended content. We recommend only adding registries which are completely +# trusted (i.e., registries which don't allow unknown or anonymous users to +# create accounts with arbitrary names). This will prevent an image from being +# spoofed, squatted or otherwise made insecure. If it is necessary to use one +# of these registries, it should be added at the end of the list. +# +# # An array of host[:port] registries to try when pulling an unqualified image, in order. +unqualified-search-registries = ["registry.fedoraproject.org", "registry.access.redhat.com", "docker.io", "quay.io"] +# +# [[registry]] +# # The "prefix" field is used to choose the relevant [[registry]] TOML table; +# # (only) the TOML table with the longest match for the input image name +# # (taking into account namespace/repo/tag/digest separators) is used. +# # +# # If the prefix field is missing, it defaults to be the same as the "location" field. +# prefix = "example.com/foo" +# +# # If true, unencrypted HTTP as well as TLS connections with untrusted +# # certificates are allowed. +# insecure = false +# +# # If true, pulling images with matching names is forbidden. +# blocked = false +# +# # The physical location of the "prefix"-rooted namespace. +# # +# # By default, this equal to "prefix" (in which case "prefix" can be omitted +# # and the [[registry]] TOML table can only specify "location"). +# # +# # Example: Given +# # prefix = "example.com/foo" +# # location = "internal-registry-for-example.net/bar" +# # requests for the image example.com/foo/myimage:latest will actually work with the +# # internal-registry-for-example.net/bar/myimage:latest image. +# location = "internal-registry-for-example.com/bar" +# +# # (Possibly-partial) mirrors for the "prefix"-rooted namespace. +# # +# # The mirrors are attempted in the specified order; the first one that can be +# # contacted and contains the image will be used (and if none of the mirrors contains the image, +# # the primary location specified by the "registry.location" field, or using the unmodified +# # user-specified reference, is tried last). +# # +# # Each TOML table in the "mirror" array can contain the following fields, with the same semantics +# # as if specified in the [[registry]] TOML table directly: +# # - location +# # - insecure +# [[registry.mirror]] +# location = "example-mirror-0.local/mirror-for-foo" +# [[registry.mirror]] +# location = "example-mirror-1.local/mirrors/foo" +# insecure = true +# # Given the above, a pull of example.com/foo/image:latest will try: +# # 1. example-mirror-0.local/mirror-for-foo/image:latest +# # 2. example-mirror-1.local/mirrors/foo/image:latest +# # 3. internal-registry-for-example.net/bar/image:latest +# # in order, and use the first one that exists. + +# Enforcing mode for short names is default for Fedora 34 and newer +short-name-mode="enforcing" ``` ### [mounts.conf](https://src.fedoraproject.org/rpms/skopeo/blob/main/f/mounts.conf) diff --git a/vendor/go.podman.io/buildah/pkg/parse/parse_unix.go b/vendor/go.podman.io/buildah/pkg/parse/parse_unix.go index b1aa21ddff..99fb36262b 100644 --- a/vendor/go.podman.io/buildah/pkg/parse/parse_unix.go +++ b/vendor/go.podman.io/buildah/pkg/parse/parse_unix.go @@ -7,8 +7,8 @@ import ( "os" "path/filepath" + "github.com/moby/sys/devices" "github.com/opencontainers/cgroups/devices/config" - "github.com/opencontainers/runc/libcontainer/devices" "go.podman.io/buildah/define" ) diff --git a/vendor/go.podman.io/buildah/run.go b/vendor/go.podman.io/buildah/run.go index e6d7a0c1cb..6d448ade97 100644 --- a/vendor/go.podman.io/buildah/run.go +++ b/vendor/go.podman.io/buildah/run.go @@ -189,6 +189,9 @@ type RunOptions struct { // made to contents of those changes when the container is subsequently // committed. CompatBuiltinVolumes types.OptionalBool + // ValidExitCodes is a list of exit codes which should be considered + // successful. If empty, only exit code 0 is considered success. + ValidExitCodes []int32 } // RunMountArtifacts are the artifacts created when using a run mount. diff --git a/vendor/go.podman.io/buildah/run_common.go b/vendor/go.podman.io/buildah/run_common.go index 6f760b07e2..599e8a2aa9 100644 --- a/vendor/go.podman.io/buildah/run_common.go +++ b/vendor/go.podman.io/buildah/run_common.go @@ -387,13 +387,6 @@ func DefaultNamespaceOptions() (define.NamespaceOptions, error) { func checkAndOverrideIsolationOptions(isolation define.Isolation, options *RunOptions) error { switch isolation { case IsolationOCIRootless: - // only change the netns if the caller did not set it - if ns := options.NamespaceOptions.Find(string(specs.NetworkNamespace)); ns == nil { - if _, err := exec.LookPath("slirp4netns"); err != nil { - // if slirp4netns is not installed we have to use the hosts net namespace - options.NamespaceOptions.AddOrReplace(define.NamespaceOption{Name: string(specs.NetworkNamespace), Host: true}) - } - } fallthrough case IsolationOCI: pidns := options.NamespaceOptions.Find(string(specs.PIDNamespace)) @@ -2234,3 +2227,31 @@ func (b *Builder) createMountTargets(spec *specs.Spec) ([]copier.ConditionalRemo // to clear them out itself now instead of waiting until commit-time return remove, nil } + +func checkExitCodeError(err error, validExitCodes []int32) error { + if len(validExitCodes) == 0 { + return err + } + exitCode, ok := exitCodeFromError(err) + if !ok { + return err + } + if slices.Contains(validExitCodes, exitCode) { + return nil + } + if err != nil { + return err + } + return fmt.Errorf("exit status %d is not in the valid exit codes list", exitCode) +} + +func exitCodeFromError(err error) (int32, bool) { + if err == nil { + return 0, true + } + var ee *exec.ExitError + if errors.As(err, &ee) { + return int32(ee.ExitCode()), true + } + return 0, false +} diff --git a/vendor/go.podman.io/buildah/run_freebsd.go b/vendor/go.podman.io/buildah/run_freebsd.go index 2461945b9e..9740c1e2dd 100644 --- a/vendor/go.podman.io/buildah/run_freebsd.go +++ b/vendor/go.podman.io/buildah/run_freebsd.go @@ -357,7 +357,7 @@ func (b *Builder) Run(command []string, options RunOptions) error { default: err = errors.New("don't know how to run this command") } - return err + return checkExitCodeError(err, options.ValidExitCodes) } func addCommonOptsToSpec(commonOpts *define.CommonBuildOptions, g *generate.Generator) error { diff --git a/vendor/go.podman.io/buildah/run_linux.go b/vendor/go.podman.io/buildah/run_linux.go index ba04bca4f5..3e3a77739c 100644 --- a/vendor/go.podman.io/buildah/run_linux.go +++ b/vendor/go.podman.io/buildah/run_linux.go @@ -12,7 +12,6 @@ import ( "slices" "strings" "sync" - "syscall" "github.com/docker/go-units" "github.com/opencontainers/runtime-spec/specs-go" @@ -33,7 +32,6 @@ import ( "go.podman.io/common/libnetwork/etchosts" "go.podman.io/common/libnetwork/pasta" "go.podman.io/common/libnetwork/resolvconf" - "go.podman.io/common/libnetwork/slirp4netns" nettypes "go.podman.io/common/libnetwork/types" netUtil "go.podman.io/common/libnetwork/util" "go.podman.io/common/pkg/capabilities" @@ -596,7 +594,7 @@ rootless=%d default: err = errors.New("don't know how to run this command") } - return err + return checkExitCodeError(err, options.ValidExitCodes) } func (b *Builder) setupOCIHooks(config *specs.Spec, hasVolumes bool) (map[string][]specs.Hook, error) { @@ -689,46 +687,6 @@ func addCommonOptsToSpec(commonOpts *define.CommonBuildOptions, g *generate.Gene return nil } -func setupSlirp4netnsNetwork(config *config.Config, netns, cid string, options, hostnames []string) (func(), *netResult, error) { - // we need the TmpDir for the slirp4netns code - if err := os.MkdirAll(config.Engine.TmpDir, 0o751); err != nil { - return nil, nil, fmt.Errorf("failed to create tempdir: %w", err) - } - res, err := slirp4netns.Setup(&slirp4netns.SetupOptions{ - Config: config, - ContainerID: cid, - Netns: netns, - ExtraOptions: options, - Pdeathsig: syscall.SIGKILL, - }) - if err != nil { - return nil, nil, err - } - - ip, err := slirp4netns.GetIP(res.Subnet) - if err != nil { - return nil, nil, fmt.Errorf("get slirp4netns ip: %w", err) - } - - dns, err := slirp4netns.GetDNS(res.Subnet) - if err != nil { - return nil, nil, fmt.Errorf("get slirp4netns dns ip: %w", err) - } - - result := &netResult{ - entries: etchosts.HostEntries{{IP: ip.String(), Names: hostnames}}, - dnsServers: []string{dns.String()}, - ipv6: res.IPv6, - keepHostResolvers: true, - } - - return func() { - syscall.Kill(res.Pid, syscall.SIGKILL) //nolint:errcheck - var status syscall.WaitStatus - syscall.Wait4(res.Pid, &status, 0, nil) //nolint:errcheck - }, result, nil -} - func setupPasta(config *config.Config, netns string, options, hostnames []string) (func(), *netResult, error) { res, err := pasta.Setup(&pasta.SetupOptions{ Config: config, @@ -776,8 +734,6 @@ func (b *Builder) runConfigureNetwork(pid int, isolation define.Isolation, optio } if isolation == IsolationOCIRootless && name == "" { switch defConfig.Network.DefaultRootlessNetworkCmd { - case slirp4netns.BinaryName, "": - name = slirp4netns.BinaryName case pasta.BinaryName: name = pasta.BinaryName default: @@ -787,8 +743,6 @@ func (b *Builder) runConfigureNetwork(pid int, isolation define.Isolation, optio } switch { - case name == slirp4netns.BinaryName: - return setupSlirp4netnsNetwork(defConfig, netns, containerName, netOpts, hostnames) case name == pasta.BinaryName: return setupPasta(defConfig, netns, netOpts, hostnames) diff --git a/vendor/go.podman.io/buildah/troubleshooting.md b/vendor/go.podman.io/buildah/troubleshooting.md index 1299b354f5..a7f15fc576 100644 --- a/vendor/go.podman.io/buildah/troubleshooting.md +++ b/vendor/go.podman.io/buildah/troubleshooting.md @@ -26,8 +26,8 @@ error building: error creating build container: no such image "alpine" in regist #### Solution - * Verify that the `/etc/containers/registries.conf` file exists. If not, verify that the containers-common package is installed. - * Verify that the entries in the `[registries.search]` section of the /etc/containers/registries file are valid and reachable. + * Verify that a `registries.conf` file exists, see [containers-registries.conf(5)](https://github.com/containers/container-libs/blob/main/image/docs/containers-registries.conf.5.md). If not, verify that the containers-common package is installed. + * Verify that the `unqualified-search-registries` option contains valid registries and that they are reachable. * Verify that the image you requested is either fully qualified, or that it exists on one of your search registries. * Verify that the image is public or that you have logged in to at least one search registry which contains the private image. * Verify that the other required [Configuration Files](https://github.com/containers/buildah/blob/main/install.md#configuration-files) are installed. diff --git a/vendor/go.podman.io/common/pkg/libartifact/events.go b/vendor/go.podman.io/common/pkg/libartifact/events.go new file mode 100644 index 0000000000..719dd56236 --- /dev/null +++ b/vendor/go.podman.io/common/pkg/libartifact/events.go @@ -0,0 +1,54 @@ +//go:build !remote + +package libartifact + +import ( + "time" + + "github.com/sirupsen/logrus" +) + +// EventType indicates the type of an event. +type EventType int + +const ( + // EventTypeUnknown is an uninitialized EventType. + EventTypeUnknown EventType = iota + // EventTypeArtifactPull represents an artifact pull. + EventTypeArtifactPull + // EventTypeArtifactPush represents an artifact push. + EventTypeArtifactPush + // EventTypeArtifactRemove represents an artifact removal. + EventTypeArtifactRemove + // EventTypeArtifactAdd represents an artifact being added. + EventTypeArtifactAdd +) + +// Event represents an event such as an artifact pull or push. +type Event struct { + // ID of the object (e.g., artifact digest). + ID string + // Name of the object (e.g., artifact name "quay.io/foobar/artifact:special") + Name string + // Time of the event. + Time time.Time + // Type of the event. + Type EventType + // Error in case of failure. + Error error +} + +// writeEvent writes the specified event to the store's event channel. The +// event is discarded if no event channel has been registered (yet). +func (as *ArtifactStore) writeEvent(event *Event) { + select { + case as.eventChannel <- event: + // Done + case <-time.After(2 * time.Second): + // The store's event channel has a buffer of size 100 which + // should be enough even under high load. However, we + // shouldn't block too long in case the buffer runs full (could + // be an honest user error or bug). + logrus.Warnf("Discarding libartifact event which was not read within 2 seconds: %v", event) + } +} diff --git a/vendor/go.podman.io/common/pkg/libartifact/store.go b/vendor/go.podman.io/common/pkg/libartifact/store.go index 70520873da..025b57fe62 100644 --- a/vendor/go.podman.io/common/pkg/libartifact/store.go +++ b/vendor/go.podman.io/common/pkg/libartifact/store.go @@ -41,6 +41,7 @@ type ArtifactStore struct { SystemContext *types.SystemContext storePath string lock *lockfile.LockFile + eventChannel chan *Event } // NewArtifactStore is a constructor for artifact stores. Most artifact dealings depend on this. Store path is @@ -81,10 +82,22 @@ func NewArtifactStore(storePath string, sc *types.SystemContext) (*ArtifactStore return artifactStore, nil } +// EventChannel creates a buffered channel for events that the ArtifactStore will use +// to write events to. Callers are expected to read from the channel in a +// timely manner. +// Can be called once for a given ArtifactStore. +func (as *ArtifactStore) EventChannel() chan *Event { + if as.eventChannel != nil { + return as.eventChannel + } + as.eventChannel = make(chan *Event, 100) + return as.eventChannel +} + // lookupArtifactLocked looks up an artifact by fully qualified name, // or name@digest, full ID, or partial ID. // note: lookupArtifactLocked must be called while under a store lock -func (as ArtifactStore) lookupArtifactLocked(ctx context.Context, asr ArtifactStoreReference) (*Artifact, error) { +func (as *ArtifactStore) lookupArtifactLocked(ctx context.Context, asr ArtifactStoreReference) (*Artifact, error) { artifacts, err := as.getArtifacts(ctx, nil) if err != nil { return nil, err @@ -146,7 +159,7 @@ func (as ArtifactStore) lookupArtifactLocked(ctx context.Context, asr ArtifactSt } // Remove an artifact from the local artifact store. -func (as ArtifactStore) Remove(ctx context.Context, asr ArtifactStoreReference) (*digest.Digest, error) { +func (as *ArtifactStore) Remove(ctx context.Context, asr ArtifactStoreReference) (_ *digest.Digest, removeErr error) { as.lock.Lock() defer as.lock.Unlock() @@ -154,15 +167,23 @@ func (as ArtifactStore) Remove(ctx context.Context, asr ArtifactStoreReference) if err != nil { return nil, err } + ir, err := layout.NewReference(as.storePath, arty.Name) if err != nil { return nil, err } - return &arty.Digest, ir.DeleteImage(ctx, as.SystemContext) + if err := ir.DeleteImage(ctx, as.SystemContext); err != nil { + return nil, err + } + + if as.eventChannel != nil { + as.writeEvent(&Event{ID: arty.Digest.String(), Name: arty.Name, Time: time.Now(), Type: EventTypeArtifactRemove}) + } + return &arty.Digest, nil } // Inspect an artifact in a local store. -func (as ArtifactStore) Inspect(ctx context.Context, asr ArtifactStoreReference) (*Artifact, error) { +func (as *ArtifactStore) Inspect(ctx context.Context, asr ArtifactStoreReference) (*Artifact, error) { as.lock.RLock() defer as.lock.Unlock() @@ -170,69 +191,78 @@ func (as ArtifactStore) Inspect(ctx context.Context, asr ArtifactStoreReference) } // List artifacts in the local store. -func (as ArtifactStore) List(ctx context.Context) (ArtifactList, error) { +func (as *ArtifactStore) List(ctx context.Context) (ArtifactList, error) { as.lock.RLock() defer as.lock.Unlock() return as.getArtifacts(ctx, nil) } -// Pull an artifact from an image registry to a local store. -func (as ArtifactStore) Pull(ctx context.Context, ref ArtifactReference, opts libimage.CopyOptions) (digest.Digest, error) { - srcRef, err := docker.NewReference(ref.ref) - if err != nil { - return "", err - } +// Execute fn while holding the store lock and providing a reference to the local layout. +func (as *ArtifactStore) withLockedLayout(localName string, fn func(localRef types.ImageReference) (digest.Digest, error)) (digest.Digest, error) { as.lock.Lock() defer as.lock.Unlock() - destRef, err := layout.NewReference(as.storePath, ref.String()) + ref, err := layout.NewReference(as.storePath, localName) if err != nil { return "", err } + + return fn(ref) +} + +// Copy artifact from srcRef to destRef and return the digest of the copied artifact. +func (as *ArtifactStore) copyArtifact(ctx context.Context, srcRef types.ImageReference, destRef types.ImageReference, opts libimage.CopyOptions) (_ digest.Digest, err error) { copyer, err := libimage.NewCopier(&opts, as.SystemContext) if err != nil { return "", err } + defer func() { + closeErr := copyer.Close() + if err == nil { + err = closeErr + } + }() artifactBytes, err := copyer.Copy(ctx, srcRef, destRef) if err != nil { return "", err } - err = copyer.Close() - if err != nil { - return "", err - } return digest.FromBytes(artifactBytes), nil } +// Pull an artifact from an image registry to a local store. +func (as *ArtifactStore) Pull(ctx context.Context, ref ArtifactReference, opts libimage.CopyOptions) (_ digest.Digest, pullErr error) { + srcRef, err := docker.NewReference(ref.ref) + if err != nil { + return "", err + } + artifactDigest, err := as.withLockedLayout(ref.String(), func(localRef types.ImageReference) (digest.Digest, error) { + return as.copyArtifact(ctx, srcRef, localRef, opts) + }) + if err != nil { + return "", err + } + if as.eventChannel != nil { + as.writeEvent(&Event{ID: artifactDigest.String(), Name: ref.String(), Time: time.Now(), Type: EventTypeArtifactPull}) + } + return artifactDigest, nil +} + // Push an artifact to an image registry. -func (as ArtifactStore) Push(ctx context.Context, src, dest ArtifactReference, opts libimage.CopyOptions) (digest.Digest, error) { +func (as *ArtifactStore) Push(ctx context.Context, src, dest ArtifactReference, opts libimage.CopyOptions) (_ digest.Digest, pushErr error) { destRef, err := docker.NewReference(dest.ref) if err != nil { return "", err } - - as.lock.Lock() - defer as.lock.Unlock() - - srcRef, err := layout.NewReference(as.storePath, src.String()) + artifactDigest, err := as.withLockedLayout(src.String(), func(localRef types.ImageReference) (digest.Digest, error) { + return as.copyArtifact(ctx, localRef, destRef, opts) + }) if err != nil { return "", err } - copyer, err := libimage.NewCopier(&opts, as.SystemContext) - if err != nil { - return "", err + if as.eventChannel != nil { + as.writeEvent(&Event{ID: artifactDigest.String(), Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactPush}) } - artifactBytes, err := copyer.Copy(ctx, srcRef, destRef) - if err != nil { - return "", err - } - - err = copyer.Close() - if err != nil { - return "", err - } - artifactDigest := digest.FromBytes(artifactBytes) return artifactDigest, nil } @@ -256,7 +286,7 @@ func createNewArtifactManifest(options *libartTypes.AddOptions) specV1.Manifest } // cleanupAfterAppend removes previous image when doing an append. -func cleanupAfterAppend(ctx context.Context, oldDigest digest.Digest, as ArtifactStore) error { +func cleanupAfterAppend(ctx context.Context, oldDigest digest.Digest, as *ArtifactStore) error { lrs, err := layout.List(as.storePath) if err != nil { return err @@ -279,7 +309,7 @@ func cleanupAfterAppend(ctx context.Context, oldDigest digest.Digest, as Artifac // Add takes one or more artifact blobs and add them to the local artifact store. The empty // string input is for possible custom artifact types. -func (as ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifactBlobs []libartTypes.ArtifactBlob, options *libartTypes.AddOptions) (*digest.Digest, error) { +func (as *ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifactBlobs []libartTypes.ArtifactBlob, options *libartTypes.AddOptions) (_ *digest.Digest, addErr error) { if options.Append && len(options.ArtifactMIMEType) > 0 { return nil, errors.New("append option is not compatible with type option") } @@ -447,10 +477,14 @@ func (as ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifac return nil, err } } + + if as.eventChannel != nil { + as.writeEvent(&Event{ID: artifactManifestDigest.String(), Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactAdd}) + } return &artifactManifestDigest, nil } -func getArtifactAndImageSource(ctx context.Context, as ArtifactStore, asr ArtifactStoreReference, options *libartTypes.FilterBlobOptions) (*Artifact, types.ImageSource, error) { +func getArtifactAndImageSource(ctx context.Context, as *ArtifactStore, asr ArtifactStoreReference, options *libartTypes.FilterBlobOptions) (*Artifact, types.ImageSource, error) { if len(options.Digest) > 0 && len(options.Title) > 0 { return nil, nil, errors.New("cannot specify both digest and title") } @@ -471,7 +505,7 @@ func getArtifactAndImageSource(ctx context.Context, as ArtifactStore, asr Artifa } // BlobMountPaths allows the caller to access the file names from the store and how they should be mounted. -func (as ArtifactStore) BlobMountPaths(ctx context.Context, asr ArtifactStoreReference, options *libartTypes.BlobMountPathOptions) ([]libartTypes.BlobMountPath, error) { +func (as *ArtifactStore) BlobMountPaths(ctx context.Context, asr ArtifactStoreReference, options *libartTypes.BlobMountPathOptions) ([]libartTypes.BlobMountPath, error) { // FIX ME // LOCKING BUG: getArtifactAndImageSource assumes a locked ArtifactStore arty, imgSrc, err := getArtifactAndImageSource(ctx, as, asr, &options.FilterBlobOptions) @@ -530,7 +564,7 @@ func (as ArtifactStore) BlobMountPaths(ctx context.Context, asr ArtifactStoreRef } // Extract an artifact to local file or directory. -func (as ArtifactStore) Extract(ctx context.Context, nameOrDigest ArtifactStoreReference, target string, options *libartTypes.ExtractOptions) error { +func (as *ArtifactStore) Extract(ctx context.Context, nameOrDigest ArtifactStoreReference, target string, options *libartTypes.ExtractOptions) error { // FIX ME // LOCKING BUG: getArtifactAndImageSource assumes a locked ArtifactStore arty, imgSrc, err := getArtifactAndImageSource(ctx, as, nameOrDigest, &options.FilterBlobOptions) @@ -599,7 +633,7 @@ func (as ArtifactStore) Extract(ctx context.Context, nameOrDigest ArtifactStoreR } // Extract an artifact to tar stream. -func (as ArtifactStore) ExtractTarStream(ctx context.Context, w io.Writer, asr ArtifactStoreReference, options *libartTypes.ExtractOptions) error { +func (as *ArtifactStore) ExtractTarStream(ctx context.Context, w io.Writer, asr ArtifactStoreReference, options *libartTypes.ExtractOptions) error { if options == nil { options = &libartTypes.ExtractOptions{} } @@ -798,7 +832,7 @@ func copyTrustedImageBlobToTarStream(ctx context.Context, imgSrc types.ImageSour return nil } -func (as ArtifactStore) createEmptyManifest() error { +func (as *ArtifactStore) createEmptyManifest() error { as.lock.Lock() defer as.lock.Unlock() index := specV1.Index{ @@ -813,13 +847,13 @@ func (as ArtifactStore) createEmptyManifest() error { return os.WriteFile(as.indexPath(), rawData, 0o644) } -func (as ArtifactStore) indexPath() string { +func (as *ArtifactStore) indexPath() string { return filepath.Join(as.storePath, specV1.ImageIndexFile) } // getArtifacts returns an ArtifactList based on the artifact's store. The return error and // unused opts is meant for future growth like filters, etc so the API does not change. -func (as ArtifactStore) getArtifacts(ctx context.Context, _ *libartTypes.GetArtifactOptions) (ArtifactList, error) { +func (as *ArtifactStore) getArtifacts(ctx context.Context, _ *libartTypes.GetArtifactOptions) (ArtifactList, error) { var al ArtifactList lrs, err := layout.List(as.storePath) diff --git a/vendor/go.podman.io/storage/layers.go b/vendor/go.podman.io/storage/layers.go index 1f556818e6..d176e456c1 100644 --- a/vendor/go.podman.io/storage/layers.go +++ b/vendor/go.podman.io/storage/layers.go @@ -2671,7 +2671,7 @@ func applyDiff(layerOptions *LayerOptions, diff io.Reader, tarSplitFile *os.File gidLog := make(map[uint32]struct{}) var uncompressedCounter *ioutils.WriteCounter - size, err := func() (int64, error) { // A scope for defer + size, err := func() (retSize int64, retErr error) { // A scope for defer compressor, err := pgzip.NewWriterLevel(tarSplitWriter, pgzip.BestSpeed) if err != nil { return -1, err @@ -2701,21 +2701,26 @@ func applyDiff(layerOptions *LayerOptions, diff io.Reader, tarSplitFile *os.File if uncompressedDigester != nil { uncompressedWriter = io.MultiWriter(uncompressedWriter, uncompressedDigester.Hash()) } - payload, err := asm.NewInputTarStream(io.TeeReader(uncompressed, uncompressedWriter), metadata, storage.NewDiscardFilePutter()) + payload, done, err := asm.NewInputTarStreamWithDone(io.TeeReader(uncompressed, uncompressedWriter), metadata, storage.NewDiscardFilePutter()) if err != nil { return -1, err } + defer func() { + payload.Close() + if doneErr := <-done; doneErr != nil && retErr == nil { + retErr = doneErr + } + }() size, err := applyDriverFunc(payload) if err != nil { return -1, err } // Fully consume the payload; it may contain trailing zero padding, and we need all of that - // recorded in tar-split (which happens when the data passes through NewInputTarStream). + // recorded in tar-split (which happens when the data passes through NewInputTarStreamWithDone). if _, err := io.Copy(io.Discard, payload); err != nil { return -1, err } - return size, nil }() if err != nil { diff --git a/vendor/go.podman.io/storage/pkg/chunked/compressor/compressor.go b/vendor/go.podman.io/storage/pkg/chunked/compressor/compressor.go index 85aced4578..5f6eacf578 100644 --- a/vendor/go.podman.io/storage/pkg/chunked/compressor/compressor.go +++ b/vendor/go.podman.io/storage/pkg/chunked/compressor/compressor.go @@ -240,174 +240,186 @@ func writeZstdChunkedStream(destFile io.Writer, outMetadata map[string]string, r } }() - its, err := asm.NewInputTarStream(reader, tarSplitData.packer, nil) - if err != nil { - return err - } - - tr := tar.NewReader(its) - tr.RawAccounting = true - - buf := make([]byte, 4096) - - zstdWriter, err := createZstdWriter(dest) - if err != nil { - return err - } - defer func() { - if zstdWriter != nil { - zstdWriter.Close() - } - }() - - restartCompression := func() (int64, error) { - var offset int64 - if zstdWriter != nil { - if err := zstdWriter.Close(); err != nil { - return 0, err - } - offset = dest.Count - zstdWriter.Reset(dest) - } - return offset, nil - } - - var metadata []minimal.FileMetadata - for { - hdr, err := tr.Next() + // Scope the NewInputTarStreamWithDone defer so we wait for done before + // returning to the outer function, which then closes tarSplitData.zstd. + metadata, err := func() (retMetadata []minimal.FileMetadata, retErr error) { + its, done, err := asm.NewInputTarStreamWithDone(reader, tarSplitData.packer, nil) if err != nil { - if err == io.EOF { - break + return nil, err + } + defer func() { + its.Close() + if doneErr := <-done; doneErr != nil && retErr == nil { + retErr = doneErr } - return err + }() + + tr := tar.NewReader(its) + tr.RawAccounting = true + + buf := make([]byte, 4096) + + zstdWriter, err := createZstdWriter(dest) + if err != nil { + return nil, err + } + defer func() { + if zstdWriter != nil { + zstdWriter.Close() + } + }() + + restartCompression := func() (int64, error) { + var offset int64 + if zstdWriter != nil { + if err := zstdWriter.Close(); err != nil { + return 0, err + } + offset = dest.Count + zstdWriter.Reset(dest) + } + return offset, nil + } + + var metadata []minimal.FileMetadata + for { + hdr, err := tr.Next() + if err != nil { + if err == io.EOF { + break + } + return nil, err + } + + rawBytes := tr.RawBytes() + if _, err := zstdWriter.Write(rawBytes); err != nil { + return nil, err + } + + payloadDigester := digest.Canonical.Digester() + chunkDigester := digest.Canonical.Digester() + + // Now handle the payload, if any + startOffset := int64(0) + lastOffset := int64(0) + lastChunkOffset := int64(0) + + checksum := "" + + chunks := []chunk{} + + hf := &holesFinder{ + threshold: holesThreshold, + reader: bufio.NewReader(tr), + } + + rcReader := &rollingChecksumReader{ + reader: hf, + rollsum: NewRollSum(), + } + + payloadDest := io.MultiWriter(payloadDigester.Hash(), chunkDigester.Hash(), zstdWriter) + for { + mustSplit, read, errRead := rcReader.Read(buf) + if errRead != nil && errRead != io.EOF { + return nil, errRead + } + // restart the compression only if there is a payload. + if read > 0 { + if startOffset == 0 { + startOffset, err = restartCompression() + if err != nil { + return nil, err + } + lastOffset = startOffset + } + + if _, err := payloadDest.Write(buf[:read]); err != nil { + return nil, err + } + } + if (mustSplit || errRead == io.EOF) && startOffset > 0 { + off, err := restartCompression() + if err != nil { + return nil, err + } + + chunkSize := rcReader.WrittenOut - lastChunkOffset + if chunkSize > 0 { + chunkType := minimal.ChunkTypeData + if rcReader.IsLastChunkZeros { + chunkType = minimal.ChunkTypeZeros + } + + chunks = append(chunks, chunk{ + ChunkOffset: lastChunkOffset, + Offset: lastOffset, + Checksum: chunkDigester.Digest().String(), + ChunkSize: chunkSize, + ChunkType: chunkType, + }) + } + + lastOffset = off + lastChunkOffset = rcReader.WrittenOut + chunkDigester = digest.Canonical.Digester() + payloadDest = io.MultiWriter(payloadDigester.Hash(), chunkDigester.Hash(), zstdWriter) + } + if errRead == io.EOF { + if startOffset > 0 { + checksum = payloadDigester.Digest().String() + } + break + } + } + + mainEntry, err := minimal.NewFileMetadata(hdr) + if err != nil { + return nil, err + } + mainEntry.Digest = checksum + mainEntry.Offset = startOffset + mainEntry.EndOffset = lastOffset + entries := []minimal.FileMetadata{mainEntry} + for i := 1; i < len(chunks); i++ { + entries = append(entries, minimal.FileMetadata{ + Type: minimal.TypeChunk, + Name: hdr.Name, + ChunkOffset: chunks[i].ChunkOffset, + }) + } + if len(chunks) > 1 { + for i := range chunks { + entries[i].ChunkSize = chunks[i].ChunkSize + entries[i].Offset = chunks[i].Offset + entries[i].ChunkDigest = chunks[i].Checksum + entries[i].ChunkType = chunks[i].ChunkType + } + } + metadata = append(metadata, entries...) } rawBytes := tr.RawBytes() if _, err := zstdWriter.Write(rawBytes); err != nil { - return err + return nil, err } - payloadDigester := digest.Canonical.Digester() - chunkDigester := digest.Canonical.Digester() - - // Now handle the payload, if any - startOffset := int64(0) - lastOffset := int64(0) - lastChunkOffset := int64(0) - - checksum := "" - - chunks := []chunk{} - - hf := &holesFinder{ - threshold: holesThreshold, - reader: bufio.NewReader(tr), + // make sure the entire tarball is flushed to the output as it might contain + // some trailing zeros that affect the checksum. + if _, err := io.Copy(zstdWriter, its); err != nil { + return nil, err } - rcReader := &rollingChecksumReader{ - reader: hf, - rollsum: NewRollSum(), + if err := zstdWriter.Close(); err != nil { + return nil, err } - - payloadDest := io.MultiWriter(payloadDigester.Hash(), chunkDigester.Hash(), zstdWriter) - for { - mustSplit, read, errRead := rcReader.Read(buf) - if errRead != nil && errRead != io.EOF { - return err - } - // restart the compression only if there is a payload. - if read > 0 { - if startOffset == 0 { - startOffset, err = restartCompression() - if err != nil { - return err - } - lastOffset = startOffset - } - - if _, err := payloadDest.Write(buf[:read]); err != nil { - return err - } - } - if (mustSplit || errRead == io.EOF) && startOffset > 0 { - off, err := restartCompression() - if err != nil { - return err - } - - chunkSize := rcReader.WrittenOut - lastChunkOffset - if chunkSize > 0 { - chunkType := minimal.ChunkTypeData - if rcReader.IsLastChunkZeros { - chunkType = minimal.ChunkTypeZeros - } - - chunks = append(chunks, chunk{ - ChunkOffset: lastChunkOffset, - Offset: lastOffset, - Checksum: chunkDigester.Digest().String(), - ChunkSize: chunkSize, - ChunkType: chunkType, - }) - } - - lastOffset = off - lastChunkOffset = rcReader.WrittenOut - chunkDigester = digest.Canonical.Digester() - payloadDest = io.MultiWriter(payloadDigester.Hash(), chunkDigester.Hash(), zstdWriter) - } - if errRead == io.EOF { - if startOffset > 0 { - checksum = payloadDigester.Digest().String() - } - break - } - } - - mainEntry, err := minimal.NewFileMetadata(hdr) - if err != nil { - return err - } - mainEntry.Digest = checksum - mainEntry.Offset = startOffset - mainEntry.EndOffset = lastOffset - entries := []minimal.FileMetadata{mainEntry} - for i := 1; i < len(chunks); i++ { - entries = append(entries, minimal.FileMetadata{ - Type: minimal.TypeChunk, - Name: hdr.Name, - ChunkOffset: chunks[i].ChunkOffset, - }) - } - if len(chunks) > 1 { - for i := range chunks { - entries[i].ChunkSize = chunks[i].ChunkSize - entries[i].Offset = chunks[i].Offset - entries[i].ChunkDigest = chunks[i].Checksum - entries[i].ChunkType = chunks[i].ChunkType - } - } - metadata = append(metadata, entries...) - } - - rawBytes := tr.RawBytes() - if _, err := zstdWriter.Write(rawBytes); err != nil { - zstdWriter.Close() + zstdWriter = nil + return metadata, nil + }() + if err != nil { return err } - // make sure the entire tarball is flushed to the output as it might contain - // some trailing zeros that affect the checksum. - if _, err := io.Copy(zstdWriter, its); err != nil { - zstdWriter.Close() - return err - } - - if err := zstdWriter.Close(); err != nil { - return err - } - zstdWriter = nil - if err := tarSplitData.zstd.Close(); err != nil { return err } diff --git a/vendor/modules.txt b/vendor/modules.txt index a07da14de2..5db15a2e23 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -212,7 +212,7 @@ github.com/ebitengine/purego/internal/xreflect # github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop -# github.com/fsnotify/fsnotify v1.10.0 +# github.com/fsnotify/fsnotify v1.10.1 ## explicit; go 1.23 github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify/internal @@ -503,7 +503,6 @@ github.com/opencontainers/image-spec/specs-go/v1 github.com/opencontainers/runc/internal/linux github.com/opencontainers/runc/internal/pathrs github.com/opencontainers/runc/libcontainer/apparmor -github.com/opencontainers/runc/libcontainer/devices # github.com/opencontainers/runtime-spec v1.3.0 ## explicit github.com/opencontainers/runtime-spec/specs-go @@ -637,8 +636,8 @@ github.com/ulikunitz/xz github.com/ulikunitz/xz/internal/hash github.com/ulikunitz/xz/internal/xlog github.com/ulikunitz/xz/lzma -# github.com/vbatts/tar-split v0.12.2 -## explicit; go 1.17 +# github.com/vbatts/tar-split v0.12.3 +## explicit; go 1.22.0 github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/storage @@ -699,7 +698,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.42.1-0.20260421143840-0acb6b8cca85 +# go.podman.io/buildah v1.42.1-0.20260501153811-377cf64e213b ## explicit; go 1.25.6 go.podman.io/buildah go.podman.io/buildah/bind @@ -736,7 +735,7 @@ go.podman.io/buildah/pkg/sshagent go.podman.io/buildah/pkg/util go.podman.io/buildah/pkg/volumes go.podman.io/buildah/util -# go.podman.io/common v0.67.2-0.20260430194920-3ceb1b29d72d +# go.podman.io/common v0.67.2-0.20260504145149-b5d50461d3b9 ## explicit; go 1.25.6 go.podman.io/common/internal go.podman.io/common/libimage @@ -802,7 +801,7 @@ go.podman.io/common/pkg/umask go.podman.io/common/pkg/util go.podman.io/common/pkg/version go.podman.io/common/version -# go.podman.io/image/v5 v5.39.3-0.20260430194920-3ceb1b29d72d +# go.podman.io/image/v5 v5.39.3-0.20260504145149-b5d50461d3b9 ## explicit; go 1.25.6 go.podman.io/image/v5/copy go.podman.io/image/v5/directory @@ -879,7 +878,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.62.1-0.20260430194920-3ceb1b29d72d +# go.podman.io/storage v1.62.1-0.20260504145149-b5d50461d3b9 ## explicit; go 1.25.0 go.podman.io/storage go.podman.io/storage/drivers