Merge pull request #28642 from mheon/check_buildah_vendor

Vendor container-libs and Buildah at main
This commit is contained in:
Lokesh Mandvekar 2026-05-04 15:00:49 -04:00 • committed by GitHub
commit dac8c35dd6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 788 additions and 615 deletions

12
go.mod
View file

@ -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

28
go.sum
View file

@ -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=

View file

@ -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.

View file

@ -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;

View file

@ -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
}

View file

@ -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))
}
}

View file

@ -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"

View file

@ -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
)

View file

@ -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
}

View file

@ -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

View file

@ -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.

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

25
vendor/go.podman.io/buildah/OWNERS generated vendored
View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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)

View file

@ -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"
)

3
vendor/go.podman.io/buildah/run.go generated vendored
View file

@ -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.

View file

@ -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
}

View file

@ -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 {

View file

@ -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)

View file

@ -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.

54
vendor/go.podman.io/common/pkg/libartifact/events.go generated vendored Normal file
View file

@ -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)
}
}

View file

@ -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)

View file

@ -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 {

View file

@ -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
}

15
vendor/modules.txt vendored
View file

@ -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