File 0001-vendor-update-c-buildah-to-1.33.12.patch of Package podman.37955
From 4d28145f1c5a3c8a0e4412269359e773eafeb992 Mon Sep 17 00:00:00 2001
From: Danish Prakash <contact@danishpraka.sh>
Date: Thu, 23 Jan 2025 12:54:08 +0530
Subject: [PATCH 1/6] vendor: update c/buildah to 1.33.12
This patch merges upstream the backports for
the following CVEs:
- CVE-2024-9676 (bsc#1231698)
- CVE-2024-9675 (bsc#1231499)
- CVE-2024-9407 (bsc#1231208)
- CVE-2024-9341 (bsc#1231230)
Fixes: CVE-2024-11218 (bsc#1236270)
Signed-off-by: Danish Prakash <contact@danishpraka.sh>
---
go.mod | 8 +-
go.sum | 16 +-
.../github.com/containers/buildah/.cirrus.yml | 12 +-
.../containers/buildah/.packit.yaml | 6 -
.../containers/buildah/CHANGELOG.md | 38 ++
.../containers/buildah/changelog.txt | 34 ++
.../containers/buildah/define/types.go | 42 +-
.../buildah/imagebuildah/stage_executor.go | 25 +-
.../containers/buildah/internal/open/open.go | 39 ++
.../buildah/internal/open/open_linux.go | 88 ++++
.../buildah/internal/open/open_types.go | 28 ++
.../buildah/internal/open/open_unix.go | 168 +++++++
.../buildah/internal/open/open_unsupported.go | 7 +
.../containers/buildah/internal/types.go | 8 +-
.../buildah/internal/volumes/bind_linux.go | 102 ++++
.../buildah/internal/volumes/bind_notlinux.go | 15 +
.../buildah/internal/volumes/volumes.go | 438 +++++++++++++-----
.../containers/buildah/pkg/overlay/overlay.go | 17 +-
.../buildah/pkg/overlay/overlay_linux.go | 16 +
.../pkg/overlay/overlay_unsupported.go | 20 +
vendor/github.com/containers/buildah/run.go | 12 +-
.../containers/buildah/run_common.go | 177 ++++---
.../containers/buildah/run_freebsd.go | 11 +-
.../containers/buildah/run_linux.go | 41 +-
.../common/pkg/subscriptions/subscriptions.go | 6 +-
.../containers/common/version/version.go | 2 +-
.../image/v5/docker/docker_image.go | 9 +
.../containers/image/v5/version/version.go | 2 +-
.../github.com/containers/storage/.cirrus.yml | 2 +-
vendor/github.com/containers/storage/VERSION | 2 +-
.../storage/drivers/overlay/overlay.go | 42 +-
.../github.com/containers/storage/userns.go | 92 ++--
.../containers/storage/userns_unsupported.go | 14 +
vendor/modules.txt | 9 +-
34 files changed, 1254 insertions(+), 294 deletions(-)
create mode 100644 vendor/github.com/containers/buildah/internal/open/open.go
create mode 100644 vendor/github.com/containers/buildah/internal/open/open_linux.go
create mode 100644 vendor/github.com/containers/buildah/internal/open/open_types.go
create mode 100644 vendor/github.com/containers/buildah/internal/open/open_unix.go
create mode 100644 vendor/github.com/containers/buildah/internal/open/open_unsupported.go
create mode 100644 vendor/github.com/containers/buildah/internal/volumes/bind_linux.go
create mode 100644 vendor/github.com/containers/buildah/internal/volumes/bind_notlinux.go
create mode 100644 vendor/github.com/containers/buildah/pkg/overlay/overlay_unsupported.go
create mode 100644 vendor/github.com/containers/storage/userns_unsupported.go
diff --git a/go.mod b/go.mod
index a84fbbb1b4bb..eef462a586d6 100644
--- a/go.mod
+++ b/go.mod
@@ -11,15 +11,15 @@ require (
github.com/checkpoint-restore/go-criu/v7 v7.0.0
github.com/containernetworking/cni v1.1.2
github.com/containernetworking/plugins v1.3.0
- github.com/containers/buildah v1.33.8
- github.com/containers/common v0.57.5
+ github.com/containers/buildah v1.33.12
+ github.com/containers/common v0.57.7
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/gvisor-tap-vsock v0.7.2
- github.com/containers/image/v5 v5.29.3
+ github.com/containers/image/v5 v5.29.4
github.com/containers/libhvee v0.5.0
github.com/containers/ocicrypt v1.1.10
github.com/containers/psgo v1.8.0
- github.com/containers/storage v1.51.0
+ github.com/containers/storage v1.51.2
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09
github.com/coreos/stream-metadata-go v0.4.4
github.com/crc-org/vfkit v0.1.2-0.20231030102423-f3c783d34420
diff --git a/go.sum b/go.sum
index 495035a32b6e..74b9a954c7c6 100644
--- a/go.sum
+++ b/go.sum
@@ -258,16 +258,16 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV
github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
github.com/containernetworking/plugins v1.3.0 h1:QVNXMT6XloyMUoO2wUOqWTC1hWFV62Q6mVDp5H1HnjM=
github.com/containernetworking/plugins v1.3.0/go.mod h1:Pc2wcedTQQCVuROOOaLBPPxrEXqqXBFt3cZ+/yVg6l0=
-github.com/containers/buildah v1.33.8 h1:/IfJm5gTHwWshFdRHgLTHkoHNZY85B/xePkpOypBKUw=
-github.com/containers/buildah v1.33.8/go.mod h1:aS1MZukKW39pe/yeJ7sRq9Jf2Sl04uePugPIto6ItNo=
-github.com/containers/common v0.57.5 h1:EgIahxAeYpcE0JKl4A4Z2oEUseve1jt+lMuXIqYnalE=
-github.com/containers/common v0.57.5/go.mod h1:dRw+mJGANzTOJZSs+KfJzrSVNQ4zK0u46/MhLCUfzPY=
+github.com/containers/buildah v1.33.12 h1:6/X3LPnl+xXel4TNNMSjp5I5ztkpxeW3xSkLvF1IiSI=
+github.com/containers/buildah v1.33.12/go.mod h1:TamPaH3SP4NP1ChtYYXWq0zE9vmaO7hX21nRAfCrcW0=
+github.com/containers/common v0.57.7 h1:xA6/dXNbScnaytcFNQKTFGn6VDxwvDlCngJtfdGAf7g=
+github.com/containers/common v0.57.7/go.mod h1:GRtgIWNPc8zmo/vcA7VoZfLWpgQRH01/kzQbeNZH8WQ=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/gvisor-tap-vsock v0.7.2 h1:6CyU5D85C0/DciRRd7W0bPljK4FAS+DPrrHEQMHfZKY=
github.com/containers/gvisor-tap-vsock v0.7.2/go.mod h1:6NiTxh2GCVxZQLPzfuEB78/Osp2Usd9uf6nLdd6PiUY=
-github.com/containers/image/v5 v5.29.3 h1:RJHdxP+ZiC+loIFG2DTmjlVNWTS7o5jrdrRScUrY1VE=
-github.com/containers/image/v5 v5.29.3/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E=
+github.com/containers/image/v5 v5.29.4 h1:EbYrwOscTvzeCXt4149OtU74T/ZuohEottcs/hz47O4=
+github.com/containers/image/v5 v5.29.4/go.mod h1:kQ7qcDsps424ZAz24thD+x7+dJw1vgur3A9tTDsj97E=
github.com/containers/libhvee v0.5.0 h1:rDhfG2NI8Q+VgeXht2dXezanxEdpj9pHqYX3vWfOGUw=
github.com/containers/libhvee v0.5.0/go.mod h1:yvU3Em2u1ZLl2VLd2glMIBWriBwfhWsDaRJsvixUIB0=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
@@ -282,8 +282,8 @@ github.com/containers/ocicrypt v1.1.10/go.mod h1:YfzSSr06PTHQwSTUKqDSjish9BeW1E4
github.com/containers/psgo v1.8.0 h1:2loGekmGAxM9ir5OsXWEfGwFxorMPYnc6gEDsGFQvhY=
github.com/containers/psgo v1.8.0/go.mod h1:T8ZxnX3Ur4RvnhxFJ7t8xJ1F48RhiZB4rSrOaR/qGHc=
github.com/containers/storage v1.43.0/go.mod h1:uZ147thiIFGdVTjMmIw19knttQnUCl3y9zjreHrg11s=
-github.com/containers/storage v1.51.0 h1:AowbcpiWXzAjHosKz7MKvPEqpyX+ryZA/ZurytRrFNA=
-github.com/containers/storage v1.51.0/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc=
+github.com/containers/storage v1.51.2 h1:Xw8p1AG1A+Nh6dCsb1UOB3YKF5uzlCkI3uAP4fsFup4=
+github.com/containers/storage v1.51.2/go.mod h1:ybl8a3j1PPtpyaEi/5A6TOFs+5TrEyObeKJzVtkUlfc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml
index ac12d66b20e2..a046ea9cfa28 100644
--- a/vendor/github.com/containers/buildah/.cirrus.yml
+++ b/vendor/github.com/containers/buildah/.cirrus.yml
@@ -6,7 +6,7 @@ env:
#### Global variables used for all tasks
####
# Name of the ultimate destination branch for this CI run, PR or post-merge.
- DEST_BRANCH: "main"
+ DEST_BRANCH: "release-1.33"
GOPATH: "/var/tmp/go"
GOSRC: "${GOPATH}/src/github.com/containers/buildah"
# Overrides default location (/tmp/cirrus) for repo clone
@@ -137,15 +137,9 @@ cross_build_task:
alias: cross_build
only_if: >-
$CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*'
-
- osx_instance:
- image: ghcr.io/cirruslabs/macos-ventura-base:latest
-
+ env:
+ HOME: /root
script:
- - brew update
- - brew install go
- - brew install go-md2man
- - brew install gpgme
- go version
- make cross CGO_ENABLED=0
diff --git a/vendor/github.com/containers/buildah/.packit.yaml b/vendor/github.com/containers/buildah/.packit.yaml
index 76b297c14aa9..07cb599ee457 100644
--- a/vendor/github.com/containers/buildah/.packit.yaml
+++ b/vendor/github.com/containers/buildah/.packit.yaml
@@ -18,12 +18,6 @@ jobs:
targets:
- fedora-all-x86_64
- fedora-all-aarch64
- - fedora-eln-x86_64
- - fedora-eln-aarch64
- - centos-stream+epel-next-8-x86_64
- - centos-stream+epel-next-8-aarch64
- - centos-stream+epel-next-9-x86_64
- - centos-stream+epel-next-9-aarch64
additional_repos:
- "copr://rhcontainerbot/podman-next"
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
index 6f8821722deb..d8764a67acef 100644
--- a/vendor/github.com/containers/buildah/CHANGELOG.md
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
@@ -2,6 +2,44 @@
# Changelog
+## v1.33.12 (2025-01-20)
+
+ Fix TOCTOU error when bind and cache mounts use "src" values
+ define.TempDirForURL(): always use an intermediate subdirectory
+ internal/volume.GetBindMount(): discard writes in bind mounts
+ pkg/overlay: add a MountLabel flag to Options
+ pkg/overlay: add a ForceMount flag to Options
+ Add internal/volumes.bindFromChroot()
+ Add an internal/open package
+ Allow cache mounts to be stages or additional build contexts
+ Disable most packit copr targets
+ Integration tests: switch some base images
+ [release-1.33] Bump to Buildah v1.33.11
+ [release-1.33] Bump c/storage to v1.51.2, fixes CVE-2024-9676
+ [release-1.33] Bump Buildah to v1.33.10
+ [release-1.33] CVE-2024-9407: validate "bind-propagation" flag settings
+ [release-1.33] Properly validate cache IDs and sources
+ vendor: update c/common to v0.57.7
+ [release-1.33] Bump to v1.33.9
+ [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+ Cross-build on Fedora
+
+## v1.33.11 (2024-10-25)
+
+ [release-1.33] Bump c/storage to v1.51.2, fixes CVE-2024-9676
+
+## v1.33.10 (2024-10-17)
+
+ [release-1.33] Properly validate cache IDs and sources
+ vendor: update c/common to v0.57.7
+ [release-1.33] Bump to v1.33.9
+ [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+ Cross-build on Fedora
+
+## v1.33.9 (2024-07-16)
+
+ [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+
## v1.33.8 (2024-05-17)
[release-1.33] Bump c/image v5.29.3, c/common v0.57.5, CVE-2024-3727
diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt
index 3d903e04abc6..c33668f0d295 100644
--- a/vendor/github.com/containers/buildah/changelog.txt
+++ b/vendor/github.com/containers/buildah/changelog.txt
@@ -1,3 +1,37 @@
+- Changelog for v1.33.12 (2025-01-20)
+ * Fix TOCTOU error when bind and cache mounts use "src" values
+ * define.TempDirForURL(): always use an intermediate subdirectory
+ * internal/volume.GetBindMount(): discard writes in bind mounts
+ * pkg/overlay: add a MountLabel flag to Options
+ * pkg/overlay: add a ForceMount flag to Options
+ * Add internal/volumes.bindFromChroot()
+ * Add an internal/open package
+ * Allow cache mounts to be stages or additional build contexts
+ * Disable most packit copr targets
+ * Integration tests: switch some base images
+ * [release-1.33] Bump to Buildah v1.33.11
+ * [release-1.33] Bump c/storage to v1.51.2, fixes CVE-2024-9676
+ * [release-1.33] Bump Buildah to v1.33.10
+ * [release-1.33] CVE-2024-9407: validate "bind-propagation" flag settings
+ * [release-1.33] Properly validate cache IDs and sources
+ * vendor: update c/common to v0.57.7
+ * [release-1.33] Bump to v1.33.9
+ * [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+ * Cross-build on Fedora
+
+- Changelog for v1.33.11 (2024-10-25)
+ * [release-1.33] Bump c/storage to v1.51.2, fixes CVE-2024-9676
+
+- Changelog for v1.33.10 (2024-10-17)
+ * [release-1.33] Properly validate cache IDs and sources
+ * vendor: update c/common to v0.57.7
+ * [release-1.33] Bump to v1.33.9
+ * [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+ * Cross-build on Fedora
+
+- Changelog for v1.33.9 (2024-07-16)
+ * [release-1.33] Fixes Listing tags in JFrog Artifactory may fail
+
- Changelog for v1.33.8 (2024-05-17)
* [release-1.33] Bump c/image v5.29.3, c/common v0.57.5, CVE-2024-3727
* integration test: handle new labels in "bud and test --unsetlabel"
diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go
index 583e0b81f3d6..fdac857b1b43 100644
--- a/vendor/github.com/containers/buildah/define/types.go
+++ b/vendor/github.com/containers/buildah/define/types.go
@@ -29,7 +29,7 @@ const (
// identify working containers.
Package = "buildah"
// Version for the Package. Also used by .packit.sh for Packit builds.
- Version = "1.33.8"
+ Version = "1.33.12"
// DefaultRuntime if containers.conf fails.
DefaultRuntime = "runc"
@@ -130,13 +130,13 @@ type ConfidentialWorkloadOptions struct {
FirmwareLibrary string
}
-// TempDirForURL checks if the passed-in string looks like a URL or -. If it is,
-// TempDirForURL creates a temporary directory, arranges for its contents to be
-// the contents of that URL, and returns the temporary directory's path, along
-// with the name of a subdirectory which should be used as the build context
-// (which may be empty or "."). Removal of the temporary directory is the
-// responsibility of the caller. If the string doesn't look like a URL,
-// TempDirForURL returns empty strings and a nil error code.
+// TempDirForURL checks if the passed-in string looks like a URL or "-". If it
+// is, TempDirForURL creates a temporary directory, arranges for its contents
+// to be the contents of that URL, and returns the temporary directory's path,
+// along with the relative name of a subdirectory which should be used as the
+// build context (which may be empty or "."). Removal of the temporary
+// directory is the responsibility of the caller. If the string doesn't look
+// like a URL or "-", TempDirForURL returns empty strings and a nil error code.
func TempDirForURL(dir, prefix, url string) (name string, subdir string, err error) {
if !strings.HasPrefix(url, "http://") &&
!strings.HasPrefix(url, "https://") &&
@@ -149,19 +149,24 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
if err != nil {
return "", "", fmt.Errorf("creating temporary directory for %q: %w", url, err)
}
+ downloadDir := filepath.Join(name, "download")
+ if err = os.MkdirAll(downloadDir, 0o700); err != nil {
+ return "", "", fmt.Errorf("creating directory %q for %q: %w", downloadDir, url, err)
+ }
urlParsed, err := urlpkg.Parse(url)
if err != nil {
return "", "", fmt.Errorf("parsing url %q: %w", url, err)
}
if strings.HasPrefix(url, "git://") || strings.HasSuffix(urlParsed.Path, ".git") {
- combinedOutput, gitSubDir, err := cloneToDirectory(url, name)
+ combinedOutput, gitSubDir, err := cloneToDirectory(url, downloadDir)
if err != nil {
if err2 := os.RemoveAll(name); err2 != nil {
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
}
return "", "", fmt.Errorf("cloning %q to %q:\n%s: %w", url, name, string(combinedOutput), err)
}
- return name, gitSubDir, nil
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, gitSubDir))
+ return name, filepath.Join(filepath.Base(downloadDir), gitSubDir), nil
}
if strings.HasPrefix(url, "github.com/") {
ghurl := url
@@ -170,28 +175,29 @@ func TempDirForURL(dir, prefix, url string) (name string, subdir string, err err
subdir = path.Base(ghurl) + "-master"
}
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
- err = downloadToDirectory(url, name)
+ err = downloadToDirectory(url, downloadDir)
if err != nil {
if err2 := os.RemoveAll(name); err2 != nil {
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
}
- return "", subdir, err
+ return "", "", err
}
- return name, subdir, nil
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
+ return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
}
if url == "-" {
- err = stdinToDirectory(name)
+ err = stdinToDirectory(downloadDir)
if err != nil {
if err2 := os.RemoveAll(name); err2 != nil {
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
}
- return "", subdir, err
+ return "", "", err
}
- logrus.Debugf("Build context is at %q", name)
- return name, subdir, nil
+ logrus.Debugf("Build context is at %q", filepath.Join(downloadDir, subdir))
+ return name, filepath.Join(filepath.Base(downloadDir), subdir), nil
}
logrus.Debugf("don't know how to retrieve %q", url)
- if err2 := os.Remove(name); err2 != nil {
+ if err2 := os.RemoveAll(name); err2 != nil {
logrus.Debugf("error removing temporary directory %q: %v", name, err2)
}
return "", "", errors.New("unreachable code reached")
diff --git a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
index 9398dcef8da2..e985690dfea9 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
@@ -597,7 +597,12 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
// to `mountPoint` replaced from additional
// build-context. Reason: Parser will use this
// `from` to refer from stageMountPoints map later.
- stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint}
+ stageMountPoints[from] = internal.StageMountDetails{
+ IsAdditionalBuildContext: true,
+ IsImage: true,
+ DidExecute: true,
+ MountPoint: mountPoint,
+ }
break
} else {
// Most likely this points to path on filesystem
@@ -629,7 +634,11 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
mountPoint = additionalBuildContext.DownloadedCache
}
}
- stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: true, MountPoint: mountPoint}
+ stageMountPoints[from] = internal.StageMountDetails{
+ IsAdditionalBuildContext: true,
+ DidExecute: true,
+ MountPoint: mountPoint,
+ }
break
}
}
@@ -640,14 +649,22 @@ func (s *StageExecutor) runStageMountPoints(mountList []string) (map[string]inte
return nil, err
}
if otherStage, ok := s.executor.stages[from]; ok && otherStage.index < s.index {
- stageMountPoints[from] = internal.StageMountDetails{IsStage: true, DidExecute: otherStage.didExecute, MountPoint: otherStage.mountPoint}
+ stageMountPoints[from] = internal.StageMountDetails{
+ IsStage: true,
+ DidExecute: otherStage.didExecute,
+ MountPoint: otherStage.mountPoint,
+ }
break
} else {
mountPoint, err := s.getImageRootfs(s.ctx, from)
if err != nil {
return nil, fmt.Errorf("%s from=%s: no stage or image found with that name", flag, from)
}
- stageMountPoints[from] = internal.StageMountDetails{IsStage: false, DidExecute: true, MountPoint: mountPoint}
+ stageMountPoints[from] = internal.StageMountDetails{
+ IsImage: true,
+ DidExecute: true,
+ MountPoint: mountPoint,
+ }
break
}
default:
diff --git a/vendor/github.com/containers/buildah/internal/open/open.go b/vendor/github.com/containers/buildah/internal/open/open.go
new file mode 100644
index 000000000000..863dc79f2130
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/open/open.go
@@ -0,0 +1,39 @@
+package open
+
+import (
+ "errors"
+ "fmt"
+ "syscall"
+)
+
+// InChroot opens the file at `path` after chrooting to `root` and then
+// changing its working directory to `wd`. Both `wd` and `path` are evaluated
+// in the chroot.
+// Returns a file handle, an Errno value if there was an error and the
+// underlying error was a standard library error code, and a non-empty error if
+// one was detected.
+func InChroot(root, wd, path string, mode int, perm uint32) (fd int, errno syscall.Errno, err error) {
+ requests := requests{
+ Root: root,
+ Wd: wd,
+ Open: []request{
+ {
+ Path: path,
+ Mode: mode,
+ Perms: perm,
+ },
+ },
+ }
+ results := inChroot(requests)
+ if len(results.Open) != 1 {
+ return -1, 0, fmt.Errorf("got %d results back instead of 1", len(results.Open))
+ }
+ if results.Open[0].Err != "" {
+ if results.Open[0].Errno != 0 {
+ err = fmt.Errorf("%s: %w", results.Open[0].Err, results.Open[0].Errno)
+ } else {
+ err = errors.New(results.Open[0].Err)
+ }
+ }
+ return int(results.Open[0].Fd), results.Open[0].Errno, err
+}
diff --git a/vendor/github.com/containers/buildah/internal/open/open_linux.go b/vendor/github.com/containers/buildah/internal/open/open_linux.go
new file mode 100644
index 000000000000..3d9d608b5f66
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/open/open_linux.go
@@ -0,0 +1,88 @@
+package open
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/containers/storage/pkg/reexec"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ bindFdToPathCommand = "buildah-bind-fd-to-path"
+)
+
+func init() {
+ reexec.Register(bindFdToPathCommand, bindFdToPathMain)
+}
+
+// BindFdToPath creates a bind mount from the open file (which is actually a
+// directory) to the specified location. If it succeeds, the caller will need
+// to unmount the targetPath when it's finished using it. Regardless, it
+// closes the passed-in descriptor.
+func BindFdToPath(fd uintptr, targetPath string) error {
+ f := os.NewFile(fd, "passed-in directory descriptor")
+ defer func() {
+ if err := f.Close(); err != nil {
+ logrus.Debugf("closing descriptor %d after attempting to bind to %q: %v", fd, targetPath, err)
+ }
+ }()
+ pipeReader, pipeWriter, err := os.Pipe()
+ if err != nil {
+ return err
+ }
+ cmd := reexec.Command(bindFdToPathCommand)
+ cmd.Stdin = pipeReader
+ var stdout bytes.Buffer
+ var stderr bytes.Buffer
+ cmd.Stdout, cmd.Stderr = &stdout, &stderr
+ cmd.ExtraFiles = append(cmd.ExtraFiles, f)
+
+ err = cmd.Start()
+ pipeReader.Close()
+ if err != nil {
+ pipeWriter.Close()
+ return fmt.Errorf("starting child: %w", err)
+ }
+
+ encoder := json.NewEncoder(pipeWriter)
+ if err := encoder.Encode(&targetPath); err != nil {
+ return fmt.Errorf("sending target path to child: %w", err)
+ }
+ pipeWriter.Close()
+ err = cmd.Wait()
+ trimmedOutput := strings.TrimSpace(stdout.String()) + strings.TrimSpace(stderr.String())
+ if err != nil {
+ if len(trimmedOutput) > 0 {
+ err = fmt.Errorf("%s: %w", trimmedOutput, err)
+ }
+ } else {
+ if len(trimmedOutput) > 0 {
+ err = errors.New(trimmedOutput)
+ }
+ }
+ return err
+}
+
+func bindFdToPathMain() {
+ var targetPath string
+ decoder := json.NewDecoder(os.Stdin)
+ if err := decoder.Decode(&targetPath); err != nil {
+ fmt.Fprintf(os.Stderr, "error decoding target path")
+ os.Exit(1)
+ }
+ if err := unix.Fchdir(3); err != nil {
+ fmt.Fprintf(os.Stderr, "fchdir(): %v", err)
+ os.Exit(1)
+ }
+ if err := unix.Mount(".", targetPath, "bind", unix.MS_BIND, ""); err != nil {
+ fmt.Fprintf(os.Stderr, "bind-mounting passed-in directory to %q: %v", targetPath, err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
diff --git a/vendor/github.com/containers/buildah/internal/open/open_types.go b/vendor/github.com/containers/buildah/internal/open/open_types.go
new file mode 100644
index 000000000000..11dbe38db97f
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/open/open_types.go
@@ -0,0 +1,28 @@
+package open
+
+import (
+ "syscall"
+)
+
+type request struct {
+ Path string
+ Mode int
+ Perms uint32
+}
+
+type requests struct {
+ Root string
+ Wd string
+ Open []request
+}
+
+type result struct {
+ Fd uintptr // as returned by open()
+ Err string // if err was not `nil`, err.Error()
+ Errno syscall.Errno // if err was not `nil` and included a syscall.Errno, its value
+}
+
+type results struct {
+ Err string
+ Open []result
+}
diff --git a/vendor/github.com/containers/buildah/internal/open/open_unix.go b/vendor/github.com/containers/buildah/internal/open/open_unix.go
new file mode 100644
index 000000000000..fd254e8745c8
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/open/open_unix.go
@@ -0,0 +1,168 @@
+//go:build linux || freebsd || darwin
+
+package open
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "os"
+ "syscall"
+
+ "github.com/containers/storage/pkg/reexec"
+ "golang.org/x/sys/unix"
+)
+
+const (
+ inChrootCommand = "buildah-open-in-chroot"
+)
+
+func init() {
+ reexec.Register(inChrootCommand, inChrootMain)
+}
+
+func inChroot(requests requests) results {
+ sock, err := unix.Socketpair(unix.AF_UNIX, unix.SOCK_STREAM, 0)
+ if err != nil {
+ return results{Err: fmt.Errorf("creating socket pair: %w", err).Error()}
+ }
+ parentSock := sock[0]
+ childSock := sock[1]
+ parentEnd := os.NewFile(uintptr(parentSock), "parent end of socket pair")
+ childEnd := os.NewFile(uintptr(childSock), "child end of socket pair")
+ cmd := reexec.Command(inChrootCommand)
+ cmd.ExtraFiles = append(cmd.ExtraFiles, childEnd)
+ err = cmd.Start()
+ childEnd.Close()
+ defer parentEnd.Close()
+ if err != nil {
+ return results{Err: err.Error()}
+ }
+ encoder := json.NewEncoder(parentEnd)
+ if err := encoder.Encode(&requests); err != nil {
+ return results{Err: fmt.Errorf("sending request down socket: %w", err).Error()}
+ }
+ if err := unix.Shutdown(parentSock, unix.SHUT_WR); err != nil {
+ return results{Err: fmt.Errorf("finishing sending request down socket: %w", err).Error()}
+ }
+ b := make([]byte, 65536)
+ oob := make([]byte, 65536)
+ n, oobn, _, _, err := unix.Recvmsg(parentSock, b, oob, 0)
+ if err != nil {
+ return results{Err: fmt.Errorf("receiving message: %w", err).Error()}
+ }
+ if err := unix.Shutdown(parentSock, unix.SHUT_RD); err != nil {
+ return results{Err: fmt.Errorf("finishing socket: %w", err).Error()}
+ }
+ if n > len(b) {
+ return results{Err: fmt.Errorf("too much regular data: %d > %d", n, len(b)).Error()}
+ }
+ if oobn > len(oob) {
+ return results{Err: fmt.Errorf("too much OOB data: %d > %d", oobn, len(oob)).Error()}
+ }
+ scms, err := unix.ParseSocketControlMessage(oob[:oobn])
+ if err != nil {
+ return results{Err: fmt.Errorf("parsing control message: %w", err).Error()}
+ }
+ var receivedFds []int
+ for i := range scms {
+ fds, err := unix.ParseUnixRights(&scms[i])
+ if err != nil {
+ return results{Err: fmt.Errorf("parsing rights message %d: %w", i, err).Error()}
+ }
+ receivedFds = append(receivedFds, fds...)
+ }
+ decoder := json.NewDecoder(bytes.NewReader(b[:n]))
+ var result results
+ if err := decoder.Decode(&result); err != nil {
+ return results{Err: fmt.Errorf("decoding results: %w", err).Error()}
+ }
+ j := 0
+ for i := range result.Open {
+ if result.Open[i].Err == "" {
+ if j >= len(receivedFds) {
+ for _, fd := range receivedFds {
+ unix.Close(fd)
+ }
+ return results{Err: fmt.Errorf("didn't receive enough FDs").Error()}
+ }
+ result.Open[i].Fd = uintptr(receivedFds[j])
+ j++
+ }
+ }
+ return result
+}
+
+func inChrootMain() {
+ var theseRequests requests
+ var theseResults results
+ sockFd := 3
+ sock := os.NewFile(uintptr(sockFd), "socket connection to parent process")
+ defer sock.Close()
+ encoder := json.NewEncoder(sock)
+ decoder := json.NewDecoder(sock)
+ if err := decoder.Decode(&theseRequests); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("decoding request: %w", err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ }
+ if theseRequests.Root != "" {
+ if err := os.Chdir(theseRequests.Root); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("changing to %q: %w", theseRequests.Root, err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ if err := unix.Chroot(theseRequests.Root); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("chrooting to %q: %w", theseRequests.Root, err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ if err := os.Chdir("/"); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("changing to new root: %w", err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ }
+ if theseRequests.Wd != "" {
+ if err := os.Chdir(theseRequests.Wd); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("changing to %q in chroot: %w", theseRequests.Wd, err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ }
+ var fds []int
+ for _, request := range theseRequests.Open {
+ fd, err := unix.Open(request.Path, request.Mode, request.Perms)
+ thisResult := result{Fd: uintptr(fd)}
+ if err == nil {
+ fds = append(fds, fd)
+ } else {
+ var errno syscall.Errno
+ thisResult.Err = err.Error()
+ if errors.As(err, &errno) {
+ thisResult.Errno = errno
+ }
+ }
+ theseResults.Open = append(theseResults.Open, thisResult)
+ }
+ rights := unix.UnixRights(fds...)
+ inband, err := json.Marshal(&theseResults)
+ if err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("sending response: %w", err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ if err := unix.Sendmsg(sockFd, inband, rights, nil, 0); err != nil {
+ if err := encoder.Encode(results{Err: fmt.Errorf("sending response: %w", err).Error()}); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
diff --git a/vendor/github.com/containers/buildah/internal/open/open_unsupported.go b/vendor/github.com/containers/buildah/internal/open/open_unsupported.go
new file mode 100644
index 000000000000..111056a18bdf
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/open/open_unsupported.go
@@ -0,0 +1,7 @@
+//go:build !linux && !freebsd && !darwin
+
+package open
+
+func inChroot(requests requests) results {
+ return results{Err: "open-in-chroot not available on this platform"}
+}
diff --git a/vendor/github.com/containers/buildah/internal/types.go b/vendor/github.com/containers/buildah/internal/types.go
index ee87eca2255c..45aac0b016cf 100644
--- a/vendor/github.com/containers/buildah/internal/types.go
+++ b/vendor/github.com/containers/buildah/internal/types.go
@@ -12,7 +12,9 @@ const (
// StageExecutor has ability to mount stages/images in current context and
// automatically clean them up.
type StageMountDetails struct {
- DidExecute bool // tells if the stage which is being mounted was freshly executed or was part of older cache
- IsStage bool // tells if mountpoint returned from stage executor is stage or image
- MountPoint string // mountpoint of stage/image
+ DidExecute bool // true if this is a freshly-executed stage, or an image, possibly from a non-local cache
+ IsStage bool // true if the mountpoint is a stage's rootfs
+ IsImage bool // true if the mountpoint is an image's rootfs
+ IsAdditionalBuildContext bool // true if the mountpoint is an additional build context
+ MountPoint string // mountpoint of the stage or image's root directory or path of the additional build context
}
diff --git a/vendor/github.com/containers/buildah/internal/volumes/bind_linux.go b/vendor/github.com/containers/buildah/internal/volumes/bind_linux.go
new file mode 100644
index 000000000000..f8723eb080cd
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/volumes/bind_linux.go
@@ -0,0 +1,102 @@
+package volumes
+
+import (
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/containers/buildah/internal/open"
+ "github.com/containers/storage/pkg/mount"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+// bindFromChroot opens "path" inside of "root" using a chrooted subprocess
+// that returns a descriptor, then creates a uniquely-named temporary directory
+// or file under "tmp" and bind-mounts the opened descriptor to it, returning
+// the path of the temporary file or directory. The caller is responsible for
+// unmounting and removing the temporary.
+func bindFromChroot(root, path, tmp string) (string, error) {
+ fd, _, err := open.InChroot(root, "", path, unix.O_DIRECTORY|unix.O_RDONLY, 0)
+ if err != nil {
+ if !errors.Is(err, unix.ENOTDIR) {
+ return "", fmt.Errorf("opening directory %q under %q: %w", path, root, err)
+ }
+ fd, _, err = open.InChroot(root, "", path, unix.O_RDWR, 0)
+ if err != nil {
+ return "", fmt.Errorf("opening non-directory %q under %q: %w", path, root, err)
+ }
+ }
+ defer func() {
+ if err := unix.Close(fd); err != nil {
+ logrus.Debugf("closing %q under %q: %v", path, root, err)
+ }
+ }()
+
+ succeeded := false
+ var dest string
+ var destF *os.File
+ defer func() {
+ if !succeeded {
+ if destF != nil {
+ if err := destF.Close(); err != nil {
+ logrus.Debugf("closing bind target %q: %v", dest, err)
+ }
+ }
+ if dest != "" {
+ if err := os.Remove(dest); err != nil {
+ logrus.Debugf("removing bind target %q: %v", dest, err)
+ }
+ }
+ }
+ }()
+
+ var st unix.Stat_t
+ if err = unix.Fstat(fd, &st); err != nil {
+ return "", fmt.Errorf("checking if %q under %q was a directory: %w", path, root, err)
+ }
+
+ if st.Mode&unix.S_IFDIR == unix.S_IFDIR {
+ if dest, err = os.MkdirTemp(tmp, "bind"); err != nil {
+ return "", fmt.Errorf("creating a bind target directory: %w", err)
+ }
+ } else {
+ if destF, err = os.CreateTemp(tmp, "bind"); err != nil {
+ return "", fmt.Errorf("creating a bind target non-directory: %w", err)
+ }
+ if err := destF.Close(); err != nil {
+ logrus.Debugf("closing bind target %q: %v", dest, err)
+ }
+ dest = destF.Name()
+ }
+ defer func() {
+ if !succeeded {
+ if err := os.Remove(dest); err != nil {
+ logrus.Debugf("removing bind target %q: %v", dest, err)
+ }
+ }
+ }()
+
+ if err := unix.Mount(fmt.Sprintf("/proc/self/fd/%d", fd), dest, "bind", unix.MS_BIND, ""); err != nil {
+ return "", fmt.Errorf("bind-mounting passed-in descriptor to %q: %w", dest, err)
+ }
+ defer func() {
+ if !succeeded {
+ if err := mount.Unmount(dest); err != nil {
+ logrus.Debugf("unmounting bound target %q: %v", dest, err)
+ }
+ }
+ }()
+
+ var st2 unix.Stat_t
+ if err = unix.Stat(dest, &st2); err != nil {
+ return "", fmt.Errorf("looking up device/inode of newly-bind-mounted %q: %w", dest, err)
+ }
+
+ if st2.Dev != st.Dev || st2.Ino != st.Ino {
+ return "", fmt.Errorf("device/inode weren't what we expected after bind mounting: %w", err)
+ }
+
+ succeeded = true
+ return dest, nil
+}
diff --git a/vendor/github.com/containers/buildah/internal/volumes/bind_notlinux.go b/vendor/github.com/containers/buildah/internal/volumes/bind_notlinux.go
new file mode 100644
index 000000000000..d9340c188f13
--- /dev/null
+++ b/vendor/github.com/containers/buildah/internal/volumes/bind_notlinux.go
@@ -0,0 +1,15 @@
+//go:build !linux
+
+package volumes
+
+import "errors"
+
+// bindFromChroot would open "path" inside of "root" using a chrooted
+// subprocess that returns a descriptor, then would create a uniquely-named
+// temporary directory or file under "tmp" and bind-mount the opened descriptor
+// to it, returning the path of the temporary file or directory. The caller
+// would be responsible for unmounting and removing the temporary. For now,
+// this just returns an error because it is not implemented for this platform.
+func bindFromChroot(root, path, tmp string) (string, error) {
+ return "", errors.New("not available on this system")
+}
diff --git a/vendor/github.com/containers/buildah/internal/volumes/volumes.go b/vendor/github.com/containers/buildah/internal/volumes/volumes.go
index fd1ff7f98592..28a9f259ace7 100644
--- a/vendor/github.com/containers/buildah/internal/volumes/volumes.go
+++ b/vendor/github.com/containers/buildah/internal/volumes/volumes.go
@@ -17,14 +17,19 @@ import (
internalParse "github.com/containers/buildah/internal/parse"
"github.com/containers/buildah/internal/tmpdir"
internalUtil "github.com/containers/buildah/internal/util"
+ "github.com/containers/buildah/pkg/overlay"
"github.com/containers/common/pkg/parse"
"github.com/containers/image/v5/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/lockfile"
+ "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/unshare"
+ digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/runtime-spec/specs-go"
selinux "github.com/opencontainers/selinux/go-selinux"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/exp/slices"
)
const (
@@ -55,17 +60,82 @@ func CacheParent() string {
return filepath.Join(tmpdir.GetTempDir(), buildahCacheDir+"-"+strconv.Itoa(unshare.GetRootlessUID()))
}
+func mountIsReadWrite(m specs.Mount) bool {
+ // in case of conflicts, the last one wins, so it's not enough
+ // to check for the presence of either "rw" or "ro" anywhere
+ // with e.g. slices.Contains()
+ rw := true
+ for _, option := range m.Options {
+ switch option {
+ case "rw":
+ rw = true
+ case "ro":
+ rw = false
+ }
+ }
+ return rw
+}
+
+func convertToOverlay(m specs.Mount, store storage.Store, mountLabel, tmpDir string, uid, gid int) (specs.Mount, string, error) {
+ overlayDir, err := overlay.TempDir(tmpDir, uid, gid)
+ if err != nil {
+ return specs.Mount{}, "", fmt.Errorf("setting up overlay for %q: %w", m.Destination, err)
+ }
+ options := overlay.Options{GraphOpts: slices.Clone(store.GraphOptions()), ForceMount: true, MountLabel: mountLabel}
+ fileInfo, err := os.Stat(m.Source)
+ if err != nil {
+ return specs.Mount{}, "", fmt.Errorf("setting up overlay of %q: %w", m.Source, err)
+ }
+ // we might be trying to "overlay" for a non-directory, and the kernel doesn't like that very much
+ var mountThisInstead specs.Mount
+ if fileInfo.IsDir() {
+ // do the normal thing of mounting this directory as a lower with a temporary upper
+ mountThisInstead, err = overlay.MountWithOptions(overlayDir, m.Source, m.Destination, &options)
+ if err != nil {
+ return specs.Mount{}, "", fmt.Errorf("setting up overlay of %q: %w", m.Source, err)
+ }
+ } else {
+ // mount the parent directory as the lower with a temporary upper, and return a
+ // bind mount from the non-directory in the merged directory to the destination
+ sourceDir := filepath.Dir(m.Source)
+ sourceBase := filepath.Base(m.Source)
+ destination := m.Destination
+ mountedOverlay, err := overlay.MountWithOptions(overlayDir, sourceDir, destination, &options)
+ if err != nil {
+ return specs.Mount{}, "", fmt.Errorf("setting up overlay of %q: %w", sourceDir, err)
+ }
+ if mountedOverlay.Type != define.TypeBind {
+ if err2 := overlay.RemoveTemp(overlayDir); err2 != nil {
+ return specs.Mount{}, "", fmt.Errorf("cleaning up after failing to set up overlay: %v, while setting up overlay for %q: %w", err2, destination, err)
+ }
+ return specs.Mount{}, "", fmt.Errorf("setting up overlay for %q at %q: %w", mountedOverlay.Source, destination, err)
+ }
+ mountThisInstead = mountedOverlay
+ mountThisInstead.Source = filepath.Join(mountedOverlay.Source, sourceBase)
+ mountThisInstead.Destination = destination
+ }
+ return mountThisInstead, overlayDir, nil
+}
+
// GetBindMount parses a single bind mount entry from the --mount flag.
-// Returns specifiedMount and a string which contains name of image that we mounted otherwise its empty.
-// Caller is expected to perform unmount of any mounted images
-func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, string, error) {
+//
+// Returns a Mount to add to the runtime spec's list of mounts, the ID of the
+// image we mounted if we mounted one, the path of a mounted location if one
+// needs to be unmounted and removed, and the path of an overlay mount if one
+// needs to be cleaned up, or an error.
+//
+// The caller is expected to, after the command which uses the mount exits,
+// clean up the overlay filesystem (if we provided a path to it), unmount and
+// remove the mountpoint for the mounted filesystem (if we provided the path to
+// its mountpoint), and then unmount the image (if we mounted one).
+func GetBindMount(sys *types.SystemContext, args []string, contextDir string, store storage.Store, mountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir, tmpDir string) (specs.Mount, string, string, string, error) {
newMount := specs.Mount{
Type: define.TypeBind,
}
- setRelabel := false
- mountReadability := false
- setDest := false
+ setRelabel := ""
+ mountReadability := ""
+ setDest := ""
bindNonRecursive := false
fromImage := ""
@@ -78,80 +148,85 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st
case "bind-nonrecursive":
newMount.Options = append(newMount.Options, "bind")
bindNonRecursive = true
- case "ro", "nosuid", "nodev", "noexec":
+ case "nosuid", "nodev", "noexec":
// TODO: detect duplication of these options.
// (Is this necessary?)
newMount.Options = append(newMount.Options, kv[0])
- mountReadability = true
case "rw", "readwrite":
newMount.Options = append(newMount.Options, "rw")
- mountReadability = true
- case "readonly":
- // Alias for "ro"
+ mountReadability = "rw"
+ case "ro", "readonly":
newMount.Options = append(newMount.Options, "ro")
- mountReadability = true
+ mountReadability = "ro"
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z", "U":
newMount.Options = append(newMount.Options, kv[0])
case "from":
if len(kv) == 1 {
- return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", "", "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
fromImage = kv[1]
case "bind-propagation":
if len(kv) == 1 {
- return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", "", "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ }
+ switch kv[1] {
+ default:
+ return newMount, "", "", "", fmt.Errorf("%v: %q: %w", kv[0], kv[1], errBadMntOption)
+ case "shared", "rshared", "private", "rprivate", "slave", "rslave":
+ // this should be the relevant parts of the same list of options we accepted above
}
newMount.Options = append(newMount.Options, kv[1])
case "src", "source":
if len(kv) == 1 {
- return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", "", "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
newMount.Source = kv[1]
case "target", "dst", "destination":
if len(kv) == 1 {
- return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", "", "", fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
targetPath := kv[1]
+ setDest = targetPath
if !path.IsAbs(targetPath) {
targetPath = filepath.Join(workDir, targetPath)
}
if err := parse.ValidateVolumeCtrDir(targetPath); err != nil {
- return newMount, "", err
+ return newMount, "", "", "", err
}
newMount.Destination = targetPath
- setDest = true
case "relabel":
- if setRelabel {
- return newMount, "", fmt.Errorf("cannot pass 'relabel' option more than once: %w", errBadOptionArg)
+ if setRelabel != "" {
+ return newMount, "", "", "", fmt.Errorf("cannot pass 'relabel' option more than once: %w", errBadOptionArg)
}
- setRelabel = true
if len(kv) != 2 {
- return newMount, "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], errBadMntOption)
+ return newMount, "", "", "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], errBadMntOption)
}
+ setRelabel = kv[1]
switch kv[1] {
case "private":
newMount.Options = append(newMount.Options, "Z")
case "shared":
newMount.Options = append(newMount.Options, "z")
default:
- return newMount, "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], errBadMntOption)
+ return newMount, "", "", "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", kv[0], errBadMntOption)
}
case "consistency":
// Option for OS X only, has no meaning on other platforms
// and can thus be safely ignored.
// See also the handling of the equivalent "delegated" and "cached" in ValidateVolumeOpts
default:
- return newMount, "", fmt.Errorf("%v: %w", kv[0], errBadMntOption)
+ return newMount, "", "", "", fmt.Errorf("%v: %w", kv[0], errBadMntOption)
}
}
// default mount readability is always readonly
- if !mountReadability {
+ if mountReadability == "" {
newMount.Options = append(newMount.Options, "ro")
}
// Following variable ensures that we return imagename only if we did additional mount
- isImageMounted := false
+ succeeded := false
+ mountedImage := ""
if fromImage != "" {
mountPoint := ""
if additionalMountPoints != nil {
@@ -162,16 +237,23 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st
// if mountPoint of image was not found in additionalMap
// or additionalMap was nil, try mounting image
if mountPoint == "" {
- image, err := internalUtil.LookupImage(ctx, store, fromImage)
+ image, err := internalUtil.LookupImage(sys, store, fromImage)
if err != nil {
- return newMount, "", err
+ return newMount, "", "", "", err
}
- mountPoint, err = image.Mount(context.Background(), nil, imageMountLabel)
+ mountPoint, err = image.Mount(context.Background(), nil, mountLabel)
if err != nil {
- return newMount, "", err
+ return newMount, "", "", "", err
}
- isImageMounted = true
+ mountedImage = image.ID()
+ defer func() {
+ if !succeeded {
+ if _, err := store.UnmountImage(mountedImage, false); err != nil {
+ logrus.Debugf("unmounting bind-mounted image %q: %v", fromImage, err)
+ }
+ }
+ }()
}
contextDir = mountPoint
}
@@ -182,48 +264,73 @@ func GetBindMount(ctx *types.SystemContext, args []string, contextDir string, st
newMount.Options = append(newMount.Options, "rbind")
}
- if !setDest {
- return newMount, fromImage, errBadVolDest
+ if setDest == "" {
+ return newMount, "", "", "", errBadVolDest
}
// buildkit parity: support absolute path for sources from current build context
if contextDir != "" {
// path should be /contextDir/specified path
- evaluated, err := copier.Eval(contextDir, newMount.Source, copier.EvalOptions{})
+ evaluated, err := copier.Eval(contextDir, contextDir+string(filepath.Separator)+newMount.Source, copier.EvalOptions{})
if err != nil {
- return newMount, "", err
+ return newMount, "", "", "", err
}
newMount.Source = evaluated
} else {
// looks like its coming from `build run --mount=type=bind` allow using absolute path
// error out if no source is set
if newMount.Source == "" {
- return newMount, "", errBadVolSrc
+ return newMount, "", "", "", errBadVolSrc
}
if err := parse.ValidateVolumeHostDir(newMount.Source); err != nil {
- return newMount, "", err
+ return newMount, "", "", "", err
}
}
opts, err := parse.ValidateVolumeOpts(newMount.Options)
if err != nil {
- return newMount, fromImage, err
+ return newMount, "", "", "", err
}
newMount.Options = opts
- if !isImageMounted {
- // we don't want any cleanups if image was not mounted explicitly
- // so dont return anything
- fromImage = ""
+ var intermediateMount string
+ if contextDir != "" && newMount.Source != contextDir {
+ rel, err := filepath.Rel(contextDir, newMount.Source)
+ if err != nil {
+ return newMount, "", "", "", fmt.Errorf("computing pathname of bind subdirectory: %w", err)
+ }
+ if rel != "." && rel != "/" {
+ mnt, err := bindFromChroot(contextDir, rel, tmpDir)
+ if err != nil {
+ return newMount, "", "", "", fmt.Errorf("sanitizing bind subdirectory %q: %w", newMount.Source, err)
+ }
+ logrus.Debugf("bind-mounted %q under %q to %q", rel, contextDir, mnt)
+ intermediateMount = mnt
+ newMount.Source = intermediateMount
+ }
}
- return newMount, fromImage, nil
+ overlayDir := ""
+ if mountedImage != "" || mountIsReadWrite(newMount) {
+ if newMount, overlayDir, err = convertToOverlay(newMount, store, mountLabel, tmpDir, 0, 0); err != nil {
+ return newMount, "", "", "", err
+ }
+ }
+
+ succeeded = true
+ return newMount, mountedImage, intermediateMount, overlayDir, nil
}
// GetCacheMount parses a single cache mount entry from the --mount flag.
//
-// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??).
-func GetCacheMount(args []string, store storage.Store, imageMountLabel string, additionalMountPoints map[string]internal.StageMountDetails, workDir string) (specs.Mount, *lockfile.LockFile, error) {
+// Returns a Mount to add to the runtime spec's list of mounts, the path of a
+// mounted filesystem if one needs to be unmounted, and an optional lock that
+// needs to be released, or an error.
+//
+// The caller is expected to, after the command which uses the mount exits,
+// unmount and remove the mountpoint of the mounted filesystem (if we provided
+// the path to its mountpoint) and release the lock (if we took one).
+func GetCacheMount(args []string, additionalMountPoints map[string]internal.StageMountDetails, workDir, tmpDir string) (specs.Mount, string, *lockfile.LockFile, error) {
var err error
var mode uint64
var buildahLockFilesDir string
@@ -274,63 +381,69 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
sharing = kv[1]
case "bind-propagation":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ }
+ switch kv[1] {
+ default:
+ return newMount, "", nil, fmt.Errorf("%v: %q: %w", kv[0], kv[1], errBadMntOption)
+ case "shared", "rshared", "private", "rprivate", "slave", "rslave":
+ // this should be the relevant parts of the same list of options we accepted above
}
newMount.Options = append(newMount.Options, kv[1])
case "id":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
id = kv[1]
case "from":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
fromStage = kv[1]
case "target", "dst", "destination":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
targetPath := kv[1]
if !path.IsAbs(targetPath) {
targetPath = filepath.Join(workDir, targetPath)
}
if err := parse.ValidateVolumeCtrDir(targetPath); err != nil {
- return newMount, nil, err
+ return newMount, "", nil, err
}
newMount.Destination = targetPath
setDest = true
case "src", "source":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
newMount.Source = kv[1]
case "mode":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
mode, err = strconv.ParseUint(kv[1], 8, 32)
if err != nil {
- return newMount, nil, fmt.Errorf("unable to parse cache mode: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to parse cache mode: %w", err)
}
case "uid":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
uid, err = strconv.Atoi(kv[1])
if err != nil {
- return newMount, nil, fmt.Errorf("unable to parse cache uid: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to parse cache uid: %w", err)
}
case "gid":
if len(kv) == 1 {
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadOptionArg)
}
gid, err = strconv.Atoi(kv[1])
if err != nil {
- return newMount, nil, fmt.Errorf("unable to parse cache gid: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to parse cache gid: %w", err)
}
default:
- return newMount, nil, fmt.Errorf("%v: %w", kv[0], errBadMntOption)
+ return newMount, "", nil, fmt.Errorf("%v: %w", kv[0], errBadMntOption)
}
}
@@ -341,16 +454,18 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
}
if !setDest {
- return newMount, nil, errBadVolDest
+ return newMount, "", nil, errBadVolDest
}
+ thisCacheRoot := ""
if fromStage != "" {
- // do not create cache on host
- // instead use read-only mounted stage as cache
+ // do not create and use a cache directory on the host,
+ // instead use the location in the mounted stage or
+ // temporary directory as the cache
mountPoint := ""
if additionalMountPoints != nil {
if val, ok := additionalMountPoints[fromStage]; ok {
- if val.IsStage {
+ if !val.IsImage {
mountPoint = val.MountPoint
}
}
@@ -358,10 +473,9 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
// Cache does not supports using image so if not stage found
// return with error
if mountPoint == "" {
- return newMount, nil, fmt.Errorf("no stage found with name %s", fromStage)
+ return newMount, "", nil, fmt.Errorf("no stage or additional build context found with name %s", fromStage)
}
- // path should be /contextDir/specified path
- newMount.Source = filepath.Join(mountPoint, filepath.Clean(string(filepath.Separator)+newMount.Source))
+ thisCacheRoot = mountPoint
} else {
// we need to create cache on host if no image is being used
@@ -371,60 +485,73 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
// cache parent directory: creates separate cache parent for each user.
cacheParent := CacheParent()
+
// create cache on host if not present
err = os.MkdirAll(cacheParent, os.FileMode(0755))
if err != nil {
- return newMount, nil, fmt.Errorf("unable to create build cache directory: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to create build cache directory: %w", err)
}
if id != "" {
- newMount.Source = filepath.Join(cacheParent, filepath.Clean(id))
- buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, filepath.Clean(id))
+ // Don't let the user control where we place the directory.
+ dirID := digest.FromString(id).Encoded()[:16]
+ thisCacheRoot = filepath.Join(cacheParent, dirID)
+ buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, dirID)
} else {
- newMount.Source = filepath.Join(cacheParent, filepath.Clean(newMount.Destination))
- buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, filepath.Clean(newMount.Destination))
+ // Don't let the user control where we place the directory.
+ dirID := digest.FromString(newMount.Destination).Encoded()[:16]
+ thisCacheRoot = filepath.Join(cacheParent, dirID)
+ buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, dirID)
}
+
idPair := idtools.IDPair{
UID: uid,
GID: gid,
}
- //buildkit parity: change uid and gid if specified otheriwise keep `0`
- err = idtools.MkdirAllAndChownNew(newMount.Source, os.FileMode(mode), idPair)
+ // buildkit parity: change uid and gid if specified, otherwise keep `0`
+ err = idtools.MkdirAllAndChownNew(thisCacheRoot, os.FileMode(mode), idPair)
if err != nil {
- return newMount, nil, fmt.Errorf("unable to change uid,gid of cache directory: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to change uid,gid of cache directory: %w", err)
}
// create a subdirectory inside `cacheParent` just to store lockfiles
buildahLockFilesDir = filepath.Join(cacheParent, buildahLockFilesDir)
err = os.MkdirAll(buildahLockFilesDir, os.FileMode(0700))
if err != nil {
- return newMount, nil, fmt.Errorf("unable to create build cache lockfiles directory: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to create build cache lockfiles directory: %w", err)
}
}
- var targetLock *lockfile.LockFile // = nil
+ // path should be /mountPoint/specified path
+ evaluated, err := copier.Eval(thisCacheRoot, thisCacheRoot+string(filepath.Separator)+newMount.Source, copier.EvalOptions{})
+ if err != nil {
+ return newMount, "", nil, err
+ }
+ newMount.Source = evaluated
+
succeeded := false
- defer func() {
- if !succeeded && targetLock != nil {
- targetLock.Unlock()
- }
- }()
+ var targetLock *lockfile.LockFile
switch sharing {
case "locked":
// lock parent cache
lockfile, err := lockfile.GetLockFile(filepath.Join(buildahLockFilesDir, BuildahCacheLockfile))
if err != nil {
- return newMount, nil, fmt.Errorf("unable to acquire lock when sharing mode is locked: %w", err)
+ return newMount, "", nil, fmt.Errorf("unable to acquire lock when sharing mode is locked: %w", err)
}
// Will be unlocked after the RUN step is executed.
lockfile.Lock()
targetLock = lockfile
+ defer func() {
+ if !succeeded {
+ targetLock.Unlock()
+ }
+ }()
case "shared":
// do nothing since default is `shared`
break
default:
// error out for unknown values
- return newMount, nil, fmt.Errorf("unrecognized value %q for field `sharing`: %w", sharing, err)
+ return newMount, "", nil, fmt.Errorf("unrecognized value %q for field `sharing`: %w", sharing, err)
}
// buildkit parity: default sharing should be shared
@@ -442,12 +569,29 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
opts, err := parse.ValidateVolumeOpts(newMount.Options)
if err != nil {
- return newMount, nil, err
+ return newMount, "", nil, err
}
newMount.Options = opts
+ var intermediateMount string
+ if newMount.Source != thisCacheRoot {
+ rel, err := filepath.Rel(thisCacheRoot, newMount.Source)
+ if err != nil {
+ return newMount, "", nil, fmt.Errorf("computing pathname of cache subdirectory: %w", err)
+ }
+ if rel != "." && rel != "/" {
+ mnt, err := bindFromChroot(thisCacheRoot, rel, tmpDir)
+ if err != nil {
+ return newMount, "", nil, fmt.Errorf("sanitizing cache subdirectory %q: %w", newMount.Source, err)
+ }
+ logrus.Debugf("bind-mounted %q under %q to %q", rel, thisCacheRoot, mnt)
+ intermediateMount = mnt
+ newMount.Source = intermediateMount
+ }
+ }
+
succeeded = true
- return newMount, targetLock, nil
+ return newMount, intermediateMount, targetLock, nil
}
func getVolumeMounts(volumes []string) (map[string]specs.Mount, error) {
@@ -473,27 +617,53 @@ func UnlockLockArray(locks []*lockfile.LockFile) {
}
}
-// GetVolumes gets the volumes from --volume and --mount
+// GetVolumes gets the volumes from --volume and --mount flags.
//
-// If this function succeeds, the caller must unlock the returned *lockfile.LockFile s if any (when??).
-func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string, mounts []string, contextDir string, workDir string) ([]specs.Mount, []string, []*lockfile.LockFile, error) {
- unifiedMounts, mountedImages, targetLocks, err := getMounts(ctx, store, mounts, contextDir, workDir)
+// Returns a slice of Mounts to add to the runtime spec's list of mounts, the
+// IDs of any images we mounted, a slice of bind-mounted paths, a slice of
+// overlay directories and a slice of locks that we acquired, or an error.
+//
+// The caller is expected to, after the command which uses the mounts and
+// volumes exits, clean up the overlay directories, unmount and remove the
+// mountpoints for the bind-mounted paths, unmount any images we mounted, and
+// release the locks we returned (either using UnlockLockArray() or by
+// iterating over them and unlocking them).
+func GetVolumes(ctx *types.SystemContext, store storage.Store, mountLabel string, volumes []string, mounts []string, contextDir, workDir, tmpDir string) ([]specs.Mount, []string, []string, []string, []*lockfile.LockFile, error) {
+ unifiedMounts, mountedImages, intermediateMounts, overlayMounts, targetLocks, err := getMounts(ctx, store, mountLabel, mounts, contextDir, workDir, tmpDir)
if err != nil {
- return nil, mountedImages, nil, err
+ return nil, nil, nil, nil, nil, err
}
succeeded := false
defer func() {
if !succeeded {
+ for _, overlayMount := range overlayMounts {
+ if err := overlay.RemoveTemp(overlayMount); err != nil {
+ logrus.Debugf("unmounting overlay at %q: %v", overlayMount, err)
+ }
+ }
+ for _, intermediateMount := range intermediateMounts {
+ if err := mount.Unmount(intermediateMount); err != nil {
+ logrus.Debugf("unmounting intermediate mount point %q: %v", intermediateMount, err)
+ }
+ if err := os.Remove(intermediateMount); err != nil {
+ logrus.Debugf("removing should-be-empty directory %q: %v", intermediateMount, err)
+ }
+ }
+ for _, image := range mountedImages {
+ if _, err := store.UnmountImage(image, false); err != nil {
+ logrus.Debugf("unmounting image %q: %v", image, err)
+ }
+ }
UnlockLockArray(targetLocks)
}
}()
volumeMounts, err := getVolumeMounts(volumes)
if err != nil {
- return nil, mountedImages, nil, err
+ return nil, nil, nil, nil, nil, err
}
for dest, mount := range volumeMounts {
if _, ok := unifiedMounts[dest]; ok {
- return nil, mountedImages, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%v: %w", dest, errDuplicateDest)
}
unifiedMounts[dest] = mount
}
@@ -503,24 +673,53 @@ func GetVolumes(ctx *types.SystemContext, store storage.Store, volumes []string,
finalMounts = append(finalMounts, mount)
}
succeeded = true
- return finalMounts, mountedImages, targetLocks, nil
+ return finalMounts, mountedImages, intermediateMounts, overlayMounts, targetLocks, nil
}
-// getMounts takes user-provided input from the --mount flag and creates OCI
-// spec mounts.
-// buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
-// buildah run --mount type=tmpfs,target=/dev/shm ...
+// getMounts takes user-provided inputs from the --mount flag and returns a
+// slice of OCI spec mounts, a slice of mounted image IDs, a slice of other
+// mount locations, a slice of overlay mounts, and a slice of locks, or an
+// error.
//
-// If this function succeeds, the caller must unlock the returned *lockfile.LockFile s if any (when??).
-func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, contextDir string, workDir string) (map[string]specs.Mount, []string, []*lockfile.LockFile, error) {
+// buildah run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
+// buildah run --mount type=cache,target=/var/cache ...
+// buildah run --mount type=tmpfs,target=/dev/shm ...
+//
+// The caller is expected to, after the command which uses the mounts exits,
+// unmount the overlay filesystems (if we mounted any), unmount the other
+// mounted filesystems and remove their mountpoints (if we provided any paths
+// to mountpoints), unmount any mounted images (if we provided the IDs of any),
+// and then unlock the locks we returned (either using UnlockLockArray() or by
+// iterating over them and unlocking them).
+func getMounts(ctx *types.SystemContext, store storage.Store, mountLabel string, mounts []string, contextDir, workDir, tmpDir string) (map[string]specs.Mount, []string, []string, []string, []*lockfile.LockFile, error) {
// If `type` is not set default to "bind"
mountType := define.TypeBind
- finalMounts := make(map[string]specs.Mount)
- mountedImages := make([]string, 0)
- targetLocks := make([]*lockfile.LockFile, 0)
+ finalMounts := make(map[string]specs.Mount, len(mounts))
+ mountedImages := make([]string, 0, len(mounts))
+ intermediateMounts := make([]string, 0, len(mounts))
+ overlayMounts := make([]string, 0, len(mounts))
+ targetLocks := make([]*lockfile.LockFile, 0, len(mounts))
succeeded := false
defer func() {
if !succeeded {
+ for _, overlayDir := range overlayMounts {
+ if err := overlay.RemoveTemp(overlayDir); err != nil {
+ logrus.Debugf("unmounting overlay mount at %q: %v", overlayDir, err)
+ }
+ }
+ for _, intermediateMount := range intermediateMounts {
+ if err := mount.Unmount(intermediateMount); err != nil {
+ logrus.Debugf("unmounting intermediate mount point %q: %v", intermediateMount, err)
+ }
+ if err := os.Remove(intermediateMount); err != nil {
+ logrus.Debugf("removing should-be-empty directory %q: %v", intermediateMount, err)
+ }
+ }
+ for _, image := range mountedImages {
+ if _, err := store.UnmountImage(image, false); err != nil {
+ logrus.Debugf("unmounting image %q: %v", image, err)
+ }
+ }
UnlockLockArray(targetLocks)
}
}()
@@ -533,56 +732,67 @@ func getMounts(ctx *types.SystemContext, store storage.Store, mounts []string, c
for _, mount := range mounts {
tokens := strings.Split(mount, ",")
if len(tokens) < 2 {
- return nil, mountedImages, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax)
}
for _, field := range tokens {
if strings.HasPrefix(field, "type=") {
kv := strings.Split(field, "=")
if len(kv) != 2 {
- return nil, mountedImages, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%q: %w", mount, errInvalidSyntax)
}
mountType = kv[1]
}
}
switch mountType {
case define.TypeBind:
- mount, image, err := GetBindMount(ctx, tokens, contextDir, store, "", nil, workDir)
+ mount, image, intermediateMount, overlayMount, err := GetBindMount(ctx, tokens, contextDir, store, mountLabel, nil, workDir, tmpDir)
if err != nil {
- return nil, mountedImages, nil, err
+ return nil, nil, nil, nil, nil, err
+ }
+ if image != "" {
+ mountedImages = append(mountedImages, image)
+ }
+ if intermediateMount != "" {
+ intermediateMounts = append(intermediateMounts, intermediateMount)
+ }
+ if overlayMount != "" {
+ overlayMounts = append(overlayMounts, overlayMount)
}
if _, ok := finalMounts[mount.Destination]; ok {
- return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
}
finalMounts[mount.Destination] = mount
- mountedImages = append(mountedImages, image)
case TypeCache:
- mount, tl, err := GetCacheMount(tokens, store, "", nil, workDir)
+ mount, intermediateMount, tl, err := GetCacheMount(tokens, nil, workDir, tmpDir)
if err != nil {
- return nil, mountedImages, nil, err
+ return nil, nil, nil, nil, nil, err
+ }
+ if intermediateMount != "" {
+ intermediateMounts = append(intermediateMounts, intermediateMount)
}
if tl != nil {
targetLocks = append(targetLocks, tl)
}
if _, ok := finalMounts[mount.Destination]; ok {
- return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
}
finalMounts[mount.Destination] = mount
case TypeTmpfs:
mount, err := GetTmpfsMount(tokens)
if err != nil {
- return nil, mountedImages, nil, err
+ return nil, nil, nil, nil, nil, err
}
if _, ok := finalMounts[mount.Destination]; ok {
- return nil, mountedImages, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
+ return nil, nil, nil, nil, nil, fmt.Errorf("%v: %w", mount.Destination, errDuplicateDest)
}
finalMounts[mount.Destination] = mount
default:
- return nil, mountedImages, nil, fmt.Errorf("invalid filesystem type %q", mountType)
+ return nil, nil, nil, nil, nil, fmt.Errorf("invalid filesystem type %q", mountType)
}
}
succeeded = true
- return finalMounts, mountedImages, targetLocks, nil
+ return finalMounts, mountedImages, intermediateMounts, overlayMounts, targetLocks, nil
}
// GetTmpfsMount parses a single tmpfs mount entry from the --mount flag
diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
index e416ecd780ef..83c702c28dbb 100644
--- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
@@ -6,15 +6,16 @@ import (
"os/exec"
"path/filepath"
"strings"
+ "syscall"
"errors"
"github.com/containers/storage/pkg/idtools"
+ "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/system"
"github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
- "golang.org/x/sys/unix"
)
// Options type holds various configuration options for overlay
@@ -49,6 +50,12 @@ type Options struct {
RootUID int
// RootGID is not used yet but keeping it here for legacy reasons.
RootGID int
+ // Force overlay mounting and return a bind mount, rather than
+ // attempting to optimize by having the runtime actually mount and
+ // manage the overlay filesystem.
+ ForceMount bool
+ // MountLabel is a label to force for the overlay filesystem.
+ MountLabel string
}
// TempDir generates an overlay Temp directory in the container content
@@ -145,6 +152,12 @@ func mountWithMountProgram(mountProgram, overlayOptions, mergeDir string) error
return nil
}
+// mountNatively mounts an overlay at mergeDir using the kernel's mount()
+// system call.
+func mountNatively(overlayOptions, mergeDir string) error {
+ return mount.Mount("overlay", mergeDir, "overlay", overlayOptions)
+}
+
// Convert ":" to "\:", the path which will be overlay mounted need to be escaped
func escapeColon(source string) string {
return strings.ReplaceAll(source, ":", "\\:")
@@ -180,7 +193,7 @@ func Unmount(contentDir string) error {
}
// Ignore EINVAL as the specified merge dir is not a mount point
- if err := unix.Unmount(mergeDir, 0); err != nil && !errors.Is(err, os.ErrNotExist) && err != unix.EINVAL {
+ if err := mount.Unmount(mergeDir); err != nil && !errors.Is(err, os.ErrNotExist) && err != syscall.EINVAL {
return fmt.Errorf("unmount overlay %s: %w", mergeDir, err)
}
return nil
diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go
index 9bd72bc24064..57b22dd5db24 100644
--- a/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay_linux.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/storage/pkg/unshare"
"github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/selinux/go-selinux/label"
)
// MountWithOptions creates a subdir of the contentDir based on the source directory
@@ -52,6 +53,9 @@ func MountWithOptions(contentDir, source, dest string, opts *Options) (mount spe
}
overlayOptions = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s,private", escapeColon(source), upperDir, workDir)
}
+ if opts.MountLabel != "" {
+ overlayOptions = overlayOptions + "," + label.FormatMountLabel("", opts.MountLabel)
+ }
mountProgram := findMountProgram(opts.GraphOpts)
if mountProgram != "" {
@@ -76,5 +80,17 @@ func MountWithOptions(contentDir, source, dest string, opts *Options) (mount spe
mount.Type = "overlay"
mount.Options = strings.Split(overlayOptions, ",")
+ if opts.ForceMount {
+ if err := mountNatively(overlayOptions, mergeDir); err != nil {
+ return mount, err
+ }
+
+ mount.Source = mergeDir
+ mount.Destination = dest
+ mount.Type = "bind"
+ mount.Options = []string{"bind", "slave"}
+ return mount, nil
+ }
+
return mount, nil
}
diff --git a/vendor/github.com/containers/buildah/pkg/overlay/overlay_unsupported.go b/vendor/github.com/containers/buildah/pkg/overlay/overlay_unsupported.go
new file mode 100644
index 000000000000..538db65e0f79
--- /dev/null
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay_unsupported.go
@@ -0,0 +1,20 @@
+//go:build !freebsd && !linux
+// +build !freebsd,!linux
+
+package overlay
+
+import (
+ "fmt"
+ "runtime"
+
+ "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// MountWithOptions creates a subdir of the contentDir based on the source directory
+// from the source system. It then mounts up the source directory on to the
+// generated mount point and returns the mount point to the caller.
+// But allows api to set custom workdir, upperdir and other overlay options
+// Following API is being used by podman at the moment
+func MountWithOptions(contentDir, source, dest string, opts *Options) (mount specs.Mount, err error) {
+ return mount, fmt.Errorf("read/write overlay mounts not supported on %q", runtime.GOOS)
+}
diff --git a/vendor/github.com/containers/buildah/run.go b/vendor/github.com/containers/buildah/run.go
index 77887dfe8fc2..9d5798816e05 100644
--- a/vendor/github.com/containers/buildah/run.go
+++ b/vendor/github.com/containers/buildah/run.go
@@ -168,18 +168,22 @@ type RunOptions struct {
// RunMountArtifacts are the artifacts created when using a run mount.
type runMountArtifacts struct {
- // RunMountTargets are the run mount targets inside the container
+ // RunMountTargets are the run mount targets inside the container which should be removed
RunMountTargets []string
+ // RunOverlayDirs are overlay directories which will need to be cleaned up using overlay.RemoveTemp()
+ RunOverlayDirs []string
// TmpFiles are artifacts that need to be removed outside the container
TmpFiles []string
- // Any external images which were mounted inside container
+ // Any images which were mounted, which should be unmounted
MountedImages []string
- // Agents are the ssh agents started
+ // Agents are the ssh agents started, which should have their Shutdown() methods called
Agents []*sshagent.AgentServer
// SSHAuthSock is the path to the ssh auth sock inside the container
SSHAuthSock string
- // TargetLocks to be unlocked if there are any.
+ // Lock files, which should have their Unlock() methods called
TargetLocks []*lockfile.LockFile
+ // Intermediate mount points, which should be Unmount()ed and Removed()d
+ IntermediateMounts []string
}
// RunMountInfo are the available run mounts for this run
diff --git a/vendor/github.com/containers/buildah/run_common.go b/vendor/github.com/containers/buildah/run_common.go
index 93550f62bb5b..50e3bfa96735 100644
--- a/vendor/github.com/containers/buildah/run_common.go
+++ b/vendor/github.com/containers/buildah/run_common.go
@@ -26,7 +26,6 @@ import (
"github.com/containers/buildah/copier"
"github.com/containers/buildah/define"
"github.com/containers/buildah/internal"
- internalUtil "github.com/containers/buildah/internal/util"
"github.com/containers/buildah/internal/volumes"
"github.com/containers/buildah/pkg/overlay"
"github.com/containers/buildah/pkg/sshagent"
@@ -38,14 +37,14 @@ import (
netUtil "github.com/containers/common/libnetwork/util"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/subscriptions"
- imageTypes "github.com/containers/image/v5/types"
+ "github.com/containers/image/v5/types"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/lockfile"
+ "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/reexec"
"github.com/containers/storage/pkg/unshare"
- storageTypes "github.com/containers/storage/types"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
@@ -1283,8 +1282,10 @@ func init() {
reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain)
}
-// If this succeeds, the caller must call cleanupMounts().
-func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes, volumeMounts []string, runFileMounts []string, runMountInfo runMountInfo) (*runMountArtifacts, error) {
+// If this succeeds, after the command which uses the spec finishes running,
+// the caller must call b.cleanupRunMounts() on the returned runMountArtifacts
+// structure.
+func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath string, optionMounts []specs.Mount, bindFiles map[string]string, builtinVolumes []string, volumeMounts []string, runFileMounts []string, runMountInfo runMountInfo) (*runMountArtifacts, error) {
// Start building a new list of mounts.
var mounts []specs.Mount
haveMount := func(destination string) bool {
@@ -1342,14 +1343,16 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, bundlePath st
processGID: int(processGID),
}
// Get the list of mounts that are just for this Run() call.
- runMounts, mountArtifacts, err := b.runSetupRunMounts(mountPoint, runFileMounts, runMountInfo, idMaps)
+ runMounts, mountArtifacts, err := b.runSetupRunMounts(mountPoint, bundlePath, runFileMounts, runMountInfo, idMaps)
if err != nil {
return nil, err
}
succeeded := false
defer func() {
if !succeeded {
- volumes.UnlockLockArray(mountArtifacts.TargetLocks)
+ if err := b.cleanupRunMounts(mountPoint, mountArtifacts); err != nil {
+ b.Logger.Debugf("cleaning up run mounts: %v", err)
+ }
}
}()
// Add temporary copies of the contents of volume locations at the
@@ -1498,28 +1501,61 @@ func checkIfMountDestinationPreExists(root string, dest string) (bool, error) {
// runSetupRunMounts sets up mounts that exist only in this RUN, not in subsequent runs
//
-// If this function succeeds, the caller must unlock runMountArtifacts.TargetLocks (when??)
-func (b *Builder) runSetupRunMounts(mountPoint string, mounts []string, sources runMountInfo, idMaps IDMaps) ([]specs.Mount, *runMountArtifacts, error) {
- mountTargets := make([]string, 0, 10)
+// If this function succeeds, the caller must free the returned
+// runMountArtifacts by calling b.cleanupRunMounts() after the command being
+// executed with those mounts has finished.
+func (b *Builder) runSetupRunMounts(mountPoint, bundlePath string, mounts []string, sources runMountInfo, idMaps IDMaps) ([]specs.Mount, *runMountArtifacts, error) {
+ mountTargets := make([]string, 0, len(mounts))
tmpFiles := make([]string, 0, len(mounts))
- mountImages := make([]string, 0, 10)
+ mountImages := make([]string, 0, len(mounts))
+ intermediateMounts := make([]string, 0, len(mounts))
finalMounts := make([]specs.Mount, 0, len(mounts))
agents := make([]*sshagent.AgentServer, 0, len(mounts))
- sshCount := 0
defaultSSHSock := ""
targetLocks := []*lockfile.LockFile{}
+ var overlayDirs []string
succeeded := false
defer func() {
if !succeeded {
+ for _, agent := range agents {
+ servePath := agent.ServePath()
+ if err := agent.Shutdown(); err != nil {
+ b.Logger.Errorf("shutting down SSH agent at %q: %v", servePath, err)
+ }
+ }
+ for _, overlayDir := range overlayDirs {
+ if err := overlay.RemoveTemp(overlayDir); err != nil {
+ b.Logger.Error(err.Error())
+ }
+ }
+ for _, intermediateMount := range intermediateMounts {
+ if err := mount.Unmount(intermediateMount); err != nil {
+ b.Logger.Errorf("unmounting %q: %v", intermediateMount, err)
+ }
+ if err := os.Remove(intermediateMount); err != nil {
+ b.Logger.Errorf("removing should-be-empty directory %q: %v", intermediateMount, err)
+ }
+ }
+ for _, mountImage := range mountImages {
+ if _, err := b.store.UnmountImage(mountImage, false); err != nil {
+ b.Logger.Error(err.Error())
+ }
+ }
+ for _, tmpFile := range tmpFiles {
+ if err := os.Remove(tmpFile); err != nil && !errors.Is(err, os.ErrNotExist) {
+ b.Logger.Error(err.Error())
+ }
+ }
volumes.UnlockLockArray(targetLocks)
}
}()
for _, mount := range mounts {
var mountSpec *specs.Mount
var err error
- var envFile, image string
+ var envFile, image, bundleMountsDir, overlayDir, intermediateMount string
var agent *sshagent.AgentServer
var tl *lockfile.LockFile
+
tokens := strings.Split(mount, ",")
// If `type` is not set default to TypeBind
@@ -1547,29 +1583,37 @@ func (b *Builder) runSetupRunMounts(mountPoint string, mounts []string, sources
}
}
case "ssh":
- mountSpec, agent, err = b.getSSHMount(tokens, sshCount, sources.SSHSources, idMaps)
+ mountSpec, agent, err = b.getSSHMount(tokens, len(agents), sources.SSHSources, idMaps)
if err != nil {
return nil, nil, err
}
if mountSpec != nil {
finalMounts = append(finalMounts, *mountSpec)
- agents = append(agents, agent)
- if sshCount == 0 {
+ if len(agents) == 0 {
defaultSSHSock = mountSpec.Destination
}
- // Count is needed as the default destination of the ssh sock inside the container is /run/buildkit/ssh_agent.{i}
- sshCount++
+ agents = append(agents, agent)
}
case define.TypeBind:
- mountSpec, image, err = b.getBindMount(tokens, sources.SystemContext, sources.ContextDir, sources.StageMountPoints, idMaps, sources.WorkDir)
+ if bundleMountsDir == "" {
+ if bundleMountsDir, err = os.MkdirTemp(bundlePath, "mounts"); err != nil {
+ return nil, nil, err
+ }
+ }
+ mountSpec, image, intermediateMount, overlayDir, err = b.getBindMount(tokens, sources.SystemContext, sources.ContextDir, sources.StageMountPoints, idMaps, sources.WorkDir, bundleMountsDir)
if err != nil {
return nil, nil, err
}
- finalMounts = append(finalMounts, *mountSpec)
- // only perform cleanup if image was mounted ignore everything else
if image != "" {
mountImages = append(mountImages, image)
}
+ if overlayDir != "" {
+ overlayDirs = append(overlayDirs, overlayDir)
+ }
+ if intermediateMount != "" {
+ intermediateMounts = append(intermediateMounts, intermediateMount)
+ }
+ finalMounts = append(finalMounts, *mountSpec)
case "tmpfs":
mountSpec, err = b.getTmpfsMount(tokens, idMaps)
if err != nil {
@@ -1577,14 +1621,22 @@ func (b *Builder) runSetupRunMounts(mountPoint string, mounts []string, sources
}
finalMounts = append(finalMounts, *mountSpec)
case "cache":
- mountSpec, tl, err = b.getCacheMount(tokens, sources.StageMountPoints, idMaps, sources.WorkDir)
+ if bundleMountsDir == "" {
+ if bundleMountsDir, err = os.MkdirTemp(bundlePath, "mounts"); err != nil {
+ return nil, nil, err
+ }
+ }
+ mountSpec, intermediateMount, tl, err = b.getCacheMount(tokens, sources.StageMountPoints, idMaps, sources.WorkDir, bundleMountsDir)
if err != nil {
return nil, nil, err
}
- finalMounts = append(finalMounts, *mountSpec)
+ if intermediateMount != "" {
+ intermediateMounts = append(intermediateMounts, intermediateMount)
+ }
if tl != nil {
targetLocks = append(targetLocks, tl)
}
+ finalMounts = append(finalMounts, *mountSpec)
default:
return nil, nil, fmt.Errorf("invalid mount type %q", mountType)
}
@@ -1604,31 +1656,33 @@ func (b *Builder) runSetupRunMounts(mountPoint string, mounts []string, sources
}
succeeded = true
artifacts := &runMountArtifacts{
- RunMountTargets: mountTargets,
- TmpFiles: tmpFiles,
- Agents: agents,
- MountedImages: mountImages,
- SSHAuthSock: defaultSSHSock,
- TargetLocks: targetLocks,
+ RunMountTargets: mountTargets,
+ RunOverlayDirs: overlayDirs,
+ TmpFiles: tmpFiles,
+ Agents: agents,
+ MountedImages: mountImages,
+ SSHAuthSock: defaultSSHSock,
+ TargetLocks: targetLocks,
+ IntermediateMounts: intermediateMounts,
}
return finalMounts, artifacts, nil
}
-func (b *Builder) getBindMount(tokens []string, context *imageTypes.SystemContext, contextDir string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*specs.Mount, string, error) {
+func (b *Builder) getBindMount(tokens []string, sys *types.SystemContext, contextDir string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir, tmpDir string) (*specs.Mount, string, string, string, error) {
if contextDir == "" {
- return nil, "", errors.New("Context Directory for current run invocation is not configured")
+ return nil, "", "", "", errors.New("context directory for current run invocation is not configured")
}
var optionMounts []specs.Mount
- mount, image, err := volumes.GetBindMount(context, tokens, contextDir, b.store, b.MountLabel, stageMountPoints, workDir)
+ mount, image, intermediateMount, overlayMount, err := volumes.GetBindMount(sys, tokens, contextDir, b.store, b.MountLabel, stageMountPoints, workDir, tmpDir)
if err != nil {
- return nil, image, err
+ return nil, "", "", "", err
}
optionMounts = append(optionMounts, mount)
volumes, err := b.runSetupVolumeMounts(b.MountLabel, nil, optionMounts, idMaps)
if err != nil {
- return nil, image, err
+ return nil, "", "", "", err
}
- return &volumes[0], image, nil
+ return &volumes[0], image, intermediateMount, overlayMount, nil
}
func (b *Builder) getTmpfsMount(tokens []string, idMaps IDMaps) (*specs.Mount, error) {
@@ -1900,52 +1954,53 @@ func (b *Builder) cleanupTempVolumes() {
}
// cleanupRunMounts cleans up run mounts so they only appear in this run.
-func (b *Builder) cleanupRunMounts(context *imageTypes.SystemContext, mountpoint string, artifacts *runMountArtifacts) error {
+func (b *Builder) cleanupRunMounts(mountpoint string, artifacts *runMountArtifacts) error {
for _, agent := range artifacts.Agents {
- err := agent.Shutdown()
- if err != nil {
+ servePath := agent.ServePath()
+ if err := agent.Shutdown(); err != nil {
+ return fmt.Errorf("shutting down SSH agent at %q: %v", servePath, err)
+ }
+ }
+ // clean up any overlays we mounted
+ for _, overlayDirectory := range artifacts.RunOverlayDirs {
+ if err := overlay.RemoveTemp(overlayDirectory); err != nil {
return err
}
}
-
- //cleanup any mounted images for this run
+ // unmount anything that needs unmounting
+ for _, intermediateMount := range artifacts.IntermediateMounts {
+ if err := mount.Unmount(intermediateMount); err != nil && !errors.Is(err, os.ErrNotExist) {
+ return fmt.Errorf("unmounting %q: %w", intermediateMount, err)
+ }
+ if err := os.Remove(intermediateMount); err != nil && !errors.Is(err, os.ErrNotExist) {
+ return fmt.Errorf("removing should-be-empty directory %q: %w", intermediateMount, err)
+ }
+ }
+ // unmount any images we mounted for this run
for _, image := range artifacts.MountedImages {
- if image != "" {
- // if flow hits here some image was mounted for this run
- i, err := internalUtil.LookupImage(context, b.store, image)
- if err == nil {
- // silently try to unmount and do nothing
- // if image is being used by something else
- _ = i.Unmount(false)
- }
- if errors.Is(err, storageTypes.ErrImageUnknown) {
- // Ignore only if ErrImageUnknown
- // Reason: Image is already unmounted do nothing
- continue
- }
- return err
+ if _, err := b.store.UnmountImage(image, false); err != nil {
+ logrus.Debugf("umounting image %q: %v", image, err)
}
}
+ // remove mount targets that were created for this run
opts := copier.RemoveOptions{
All: true,
}
for _, path := range artifacts.RunMountTargets {
- err := copier.Remove(mountpoint, path, opts)
- if err != nil {
- return err
+ if err := copier.Remove(mountpoint, path, opts); err != nil {
+ return fmt.Errorf("removing mount target %q %q: %w", mountpoint, path, err)
}
}
var prevErr error
for _, path := range artifacts.TmpFiles {
- err := os.Remove(path)
- if !errors.Is(err, os.ErrNotExist) {
+ if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) {
if prevErr != nil {
logrus.Error(prevErr)
}
- prevErr = err
+ prevErr = fmt.Errorf("removing temporary file: %w", err)
}
}
- // unlock if any locked files from this RUN statement
+ // unlock locks we took, most likely for cache mounts
volumes.UnlockLockArray(artifacts.TargetLocks)
return prevErr
}
diff --git a/vendor/github.com/containers/buildah/run_freebsd.go b/vendor/github.com/containers/buildah/run_freebsd.go
index 9344876df9c6..369cdd330bbf 100644
--- a/vendor/github.com/containers/buildah/run_freebsd.go
+++ b/vendor/github.com/containers/buildah/run_freebsd.go
@@ -240,7 +240,7 @@ func (b *Builder) Run(command []string, options RunOptions) error {
}
defer func() {
- if err := b.cleanupRunMounts(options.SystemContext, mountPoint, runArtifacts); err != nil {
+ if err := b.cleanupRunMounts(mountPoint, runArtifacts); err != nil {
options.Logger.Errorf("unable to cleanup run mounts %v", err)
}
}()
@@ -313,9 +313,12 @@ func setupSpecialMountSpecChanges(spec *spec.Spec, shmSize string) ([]specs.Moun
return spec.Mounts, nil
}
-// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??).
-func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*spec.Mount, *lockfile.LockFile, error) {
- return nil, nil, errors.New("cache mounts not supported on freebsd")
+// If this succeeded, the caller would be expected to, after the command which
+// uses the mount exits, unmount the mounted filesystem and remove its
+// mountpoint (if we provided the path to its mountpoint), and release the lock
+// (if we took one).
+func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir, tmpDir string) (*specs.Mount, string, *lockfile.LockFile, error) {
+ return nil, "", nil, errors.New("cache mounts not supported on freebsd")
}
func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts []specs.Mount, idMaps IDMaps) (mounts []specs.Mount, Err error) {
diff --git a/vendor/github.com/containers/buildah/run_linux.go b/vendor/github.com/containers/buildah/run_linux.go
index 5263abeccce9..69c51c1ed96d 100644
--- a/vendor/github.com/containers/buildah/run_linux.go
+++ b/vendor/github.com/containers/buildah/run_linux.go
@@ -39,6 +39,7 @@ import (
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/lockfile"
+ "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/unshare"
"github.com/docker/go-units"
@@ -46,6 +47,7 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/sirupsen/logrus"
+ "golang.org/x/exp/slices"
"golang.org/x/sys/unix"
)
@@ -349,7 +351,7 @@ rootless=%d
}
defer func() {
- if err := b.cleanupRunMounts(options.SystemContext, mountPoint, runArtifacts); err != nil {
+ if err := b.cleanupRunMounts(mountPoint, runArtifacts); err != nil {
options.Logger.Errorf("unable to cleanup run mounts %v", err)
}
}()
@@ -950,7 +952,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
RootGID: idMaps.rootGID,
UpperDirOptionFragment: upperDir,
WorkDirOptionFragment: workDir,
- GraphOpts: b.store.GraphOptions(),
+ GraphOpts: slices.Clone(b.store.GraphOptions()),
}
overlayMount, err := overlay.MountWithOptions(contentDir, host, container, &overlayOpts)
@@ -959,7 +961,7 @@ func (b *Builder) runSetupVolumeMounts(mountLabel string, volumeMounts []string,
}
// If chown true, add correct ownership to the overlay temp directories.
- if foundU {
+ if err == nil && foundU {
if err := chown.ChangeHostPathOwnership(contentDir, true, idMaps.processUID, idMaps.processGID); err != nil {
return specs.Mount{}, err
}
@@ -1217,24 +1219,39 @@ func checkIdsGreaterThan5(ids []specs.LinuxIDMapping) bool {
return false
}
-// If this function succeeds and returns a non-nil *lockfile.LockFile, the caller must unlock it (when??).
-func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir string) (*specs.Mount, *lockfile.LockFile, error) {
+// Returns a Mount to add to the runtime spec's list of mounts, an optional
+// path of a mounted filesystem, unmounted, and an optional lock, or an error.
+//
+// The caller is expected to, after the command which uses the mount exits,
+// unmount the mounted filesystem (if we provided the path to its mountpoint)
+// and remove its mountpoint, , and release the lock (if we took one).
+func (b *Builder) getCacheMount(tokens []string, stageMountPoints map[string]internal.StageMountDetails, idMaps IDMaps, workDir, tmpDir string) (*specs.Mount, string, *lockfile.LockFile, error) {
var optionMounts []specs.Mount
- mount, targetLock, err := volumes.GetCacheMount(tokens, b.store, b.MountLabel, stageMountPoints, workDir)
+ optionMount, intermediateMount, targetLock, err := volumes.GetCacheMount(tokens, stageMountPoints, workDir, tmpDir)
if err != nil {
- return nil, nil, err
+ return nil, "", nil, err
}
succeeded := false
defer func() {
- if !succeeded && targetLock != nil {
- targetLock.Unlock()
+ if !succeeded {
+ if intermediateMount != "" {
+ if err := mount.Unmount(intermediateMount); err != nil {
+ b.Logger.Debugf("unmounting %q: %v", intermediateMount, err)
+ }
+ if err := os.Remove(intermediateMount); err != nil {
+ b.Logger.Debugf("removing should-be-empty directory %q: %v", intermediateMount, err)
+ }
+ }
+ if targetLock != nil {
+ targetLock.Unlock()
+ }
}
}()
- optionMounts = append(optionMounts, mount)
+ optionMounts = append(optionMounts, optionMount)
volumes, err := b.runSetupVolumeMounts(b.MountLabel, nil, optionMounts, idMaps)
if err != nil {
- return nil, nil, err
+ return nil, "", nil, err
}
succeeded = true
- return &volumes[0], targetLock, nil
+ return &volumes[0], intermediateMount, targetLock, nil
}
diff --git a/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go
index 6ba2154a7790..d976329f7527 100644
--- a/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go
+++ b/vendor/github.com/containers/common/pkg/subscriptions/subscriptions.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/common/pkg/umask"
"github.com/containers/storage/pkg/idtools"
+ securejoin "github.com/cyphar/filepath-securejoin"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/sirupsen/logrus"
@@ -345,7 +346,10 @@ func addFIPSModeSubscription(mounts *[]rspec.Mount, containerRunDir, mountPoint,
srcBackendDir := "/usr/share/crypto-policies/back-ends/FIPS"
destDir := "/etc/crypto-policies/back-ends"
- srcOnHost := filepath.Join(mountPoint, srcBackendDir)
+ srcOnHost, err := securejoin.SecureJoin(mountPoint, srcBackendDir)
+ if err != nil {
+ return fmt.Errorf("resolve %s in the container: %w", srcBackendDir, err)
+ }
if _, err := os.Stat(srcOnHost); err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go
index 9182b589f104..131d5bb4b4a4 100644
--- a/vendor/github.com/containers/common/version/version.go
+++ b/vendor/github.com/containers/common/version/version.go
@@ -1,4 +1,4 @@
package version
// Version is the version of the build.
-const Version = "0.57.5"
+const Version = "0.57.7"
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go
index 4c80bb2b5251..9741afc3f099 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image.go
@@ -14,6 +14,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
+ "github.com/sirupsen/logrus"
)
// Image is a Docker-specific implementation of types.ImageCloser with a few extra methods
@@ -90,6 +91,14 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
}
for _, tag := range tagsHolder.Tags {
if _, err := reference.WithTag(dr.ref, tag); err != nil { // Ensure the tag does not contain unexpected values
+ // Per https://github.com/containers/skopeo/issues/2346 , unknown versions of JFrog Artifactory,
+ // contrary to the tag format specified in
+ // https://github.com/opencontainers/distribution-spec/blob/8a871c8234977df058f1a14e299fe0a673853da2/spec.md?plain=1#L160 ,
+ // include digests in the list.
+ if _, err := digest.Parse(tag); err == nil {
+ logrus.Debugf("Ignoring invalid tag %q matching a digest format", tag)
+ continue
+ }
return nil, fmt.Errorf("registry returned invalid tag %q: %w", tag, err)
}
tags = append(tags, tag)
diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go
index 62d824b3eb20..441e46706818 100644
--- a/vendor/github.com/containers/image/v5/version/version.go
+++ b/vendor/github.com/containers/image/v5/version/version.go
@@ -8,7 +8,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 29
// VersionPatch is for backwards-compatible bug fixes
- VersionPatch = 3
+ VersionPatch = 4
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = ""
diff --git a/vendor/github.com/containers/storage/.cirrus.yml b/vendor/github.com/containers/storage/.cirrus.yml
index c41dd5da2c52..9e61509c27ee 100644
--- a/vendor/github.com/containers/storage/.cirrus.yml
+++ b/vendor/github.com/containers/storage/.cirrus.yml
@@ -119,7 +119,7 @@ lint_task:
env:
CIRRUS_WORKING_DIR: "/go/src/github.com/containers/storage"
container:
- image: golang
+ image: golang:1.19
modules_cache:
fingerprint_script: cat go.sum
folder: $GOPATH/pkg/mod
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index ba0a719118ce..aa618f0d56c0 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.51.0
+1.51.2
diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
index 04ecf871fd05..08532fbda638 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
@@ -1670,13 +1670,21 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
}
if err := idtools.MkdirAllAs(diffDir, perms, rootUID, rootGID); err != nil {
- return "", err
+ if !inAdditionalStore {
+ return "", err
+ }
+ // if it is in an additional store, do not fail if the directory already exists
+ if _, err2 := os.Stat(diffDir); err2 != nil {
+ return "", err
+ }
}
mergedDir := path.Join(workDirBase, "merged")
- // Create the driver merged dir
- if err := idtools.MkdirAs(mergedDir, 0o700, rootUID, rootGID); err != nil && !os.IsExist(err) {
- return "", err
+ // Attempt to create the merged dir only if it doesn't exist.
+ if _, err := os.Stat(mergedDir); err != nil && os.IsNotExist(err) {
+ if err := idtools.MkdirAs(mergedDir, 0o700, rootUID, rootGID); err != nil && !os.IsExist(err) {
+ return "", err
+ }
}
if count := d.ctr.Increment(mergedDir); count > 1 {
return mergedDir, nil
@@ -1841,7 +1849,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
// Put unmounts the mount path created for the give id.
func (d *Driver) Put(id string) error {
- dir := d.dir(id)
+ dir, _, inAdditionalStore := d.dir2(id)
if _, err := os.Stat(dir); err != nil {
return err
}
@@ -1902,11 +1910,27 @@ func (d *Driver) Put(id string) error {
}
}
- if err := unix.Rmdir(mountpoint); err != nil && !os.IsNotExist(err) {
- logrus.Debugf("Failed to remove mountpoint %s overlay: %s - %v", id, mountpoint, err)
- return fmt.Errorf("removing mount point %q: %w", mountpoint, err)
- }
+ if !inAdditionalStore {
+ uid, gid := int(0), int(0)
+ fi, err := os.Stat(mountpoint)
+ if err != nil {
+ return err
+ }
+ if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
+ uid, gid = int(stat.Uid), int(stat.Gid)
+ }
+ tmpMountpoint := path.Join(dir, "merged.1")
+ if err := idtools.MkdirAs(tmpMountpoint, 0o700, uid, gid); err != nil && !errors.Is(err, os.ErrExist) {
+ return err
+ }
+ // rename(2) can be used on an empty directory, as it is the mountpoint after umount, and it retains
+ // its atomic semantic. In this way the "merged" directory is never removed.
+ if err := unix.Rename(tmpMountpoint, mountpoint); err != nil {
+ logrus.Debugf("Failed to replace mountpoint %s overlay: %s - %v", id, mountpoint, err)
+ return fmt.Errorf("replacing mount point %q: %w", mountpoint, err)
+ }
+ }
return nil
}
diff --git a/vendor/github.com/containers/storage/userns.go b/vendor/github.com/containers/storage/userns.go
index 32ae830beaec..2c855da7c615 100644
--- a/vendor/github.com/containers/storage/userns.go
+++ b/vendor/github.com/containers/storage/userns.go
@@ -1,18 +1,21 @@
+//go:build linux
+
package storage
import (
"fmt"
"os"
"os/user"
- "path/filepath"
"strconv"
drivers "github.com/containers/storage/drivers"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/unshare"
"github.com/containers/storage/types"
+ securejoin "github.com/cyphar/filepath-securejoin"
libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
"github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
)
// getAdditionalSubIDs looks up the additional IDs configured for
@@ -85,40 +88,59 @@ const nobodyUser = 65534
// parseMountedFiles returns the maximum UID and GID found in the /etc/passwd and
// /etc/group files.
func parseMountedFiles(containerMount, passwdFile, groupFile string) uint32 {
+ var (
+ passwd *os.File
+ group *os.File
+ size int
+ err error
+ )
if passwdFile == "" {
- passwdFile = filepath.Join(containerMount, "etc/passwd")
+ passwd, err = secureOpen(containerMount, "/etc/passwd")
+ } else {
+ // User-specified override from a volume. Will not be in
+ // container root.
+ passwd, err = os.Open(passwdFile)
}
- if groupFile == "" {
- groupFile = filepath.Join(groupFile, "etc/group")
- }
-
- size := 0
-
- users, err := libcontainerUser.ParsePasswdFile(passwdFile)
if err == nil {
- for _, u := range users {
- // Skip the "nobody" user otherwise we end up with 65536
- // ids with most images
- if u.Name == "nobody" {
- continue
- }
- if u.Uid > size && u.Uid != nobodyUser {
- size = u.Uid
- }
- if u.Gid > size && u.Gid != nobodyUser {
- size = u.Gid
+ defer passwd.Close()
+
+ users, err := libcontainerUser.ParsePasswd(passwd)
+ if err == nil {
+ for _, u := range users {
+ // Skip the "nobody" user otherwise we end up with 65536
+ // ids with most images
+ if u.Name == "nobody" || u.Name == "nogroup" {
+ continue
+ }
+ if u.Uid > size && u.Uid != nobodyUser {
+ size = u.Uid + 1
+ }
+ if u.Gid > size && u.Gid != nobodyUser {
+ size = u.Gid + 1
+ }
}
}
}
- groups, err := libcontainerUser.ParseGroupFile(groupFile)
+ if groupFile == "" {
+ group, err = secureOpen(containerMount, "/etc/group")
+ } else {
+ // User-specified override from a volume. Will not be in
+ // container root.
+ group, err = os.Open(groupFile)
+ }
if err == nil {
- for _, g := range groups {
- if g.Name == "nobody" {
- continue
- }
- if g.Gid > size && g.Gid != nobodyUser {
- size = g.Gid
+ defer group.Close()
+
+ groups, err := libcontainerUser.ParseGroup(group)
+ if err == nil {
+ for _, g := range groups {
+ if g.Name == "nobody" || g.Name == "nogroup" {
+ continue
+ }
+ if g.Gid > size && g.Gid != nobodyUser {
+ size = g.Gid + 1
+ }
}
}
}
@@ -309,3 +331,19 @@ func getAutoUserNSIDMappings(
gidMap := append(availableGIDs.zip(requestedContainerGIDs), additionalGIDMappings...)
return uidMap, gidMap, nil
}
+
+// Securely open (read-only) a file in a container mount.
+func secureOpen(containerMount, file string) (*os.File, error) {
+ filePath, err := securejoin.SecureJoin(containerMount, file)
+ if err != nil {
+ return nil, err
+ }
+
+ flags := unix.O_PATH | unix.O_CLOEXEC | unix.O_RDONLY
+ fileHandle, err := os.OpenFile(filePath, flags, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ return fileHandle, nil
+}
diff --git a/vendor/github.com/containers/storage/userns_unsupported.go b/vendor/github.com/containers/storage/userns_unsupported.go
new file mode 100644
index 000000000000..e37c18fe4381
--- /dev/null
+++ b/vendor/github.com/containers/storage/userns_unsupported.go
@@ -0,0 +1,14 @@
+//go:build !linux
+
+package storage
+
+import (
+ "errors"
+
+ "github.com/containers/storage/pkg/idtools"
+ "github.com/containers/storage/types"
+)
+
+func (s *store) getAutoUserNS(_ *types.AutoUserNsOptions, _ *Image, _ rwLayerStore, _ []roLayerStore) ([]idtools.IDMap, []idtools.IDMap, error) {
+ return nil, nil, errors.New("user namespaces are not supported on this platform")
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index d99eb4f52102..52048c4e0033 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -147,7 +147,7 @@ github.com/containernetworking/cni/pkg/version
# github.com/containernetworking/plugins v1.3.0
## explicit; go 1.20
github.com/containernetworking/plugins/pkg/ns
-# github.com/containers/buildah v1.33.8
+# github.com/containers/buildah v1.33.12
## explicit; go 1.20
github.com/containers/buildah
github.com/containers/buildah/bind
@@ -160,6 +160,7 @@ github.com/containers/buildah/internal
github.com/containers/buildah/internal/config
github.com/containers/buildah/internal/mkcw
github.com/containers/buildah/internal/mkcw/types
+github.com/containers/buildah/internal/open
github.com/containers/buildah/internal/parse
github.com/containers/buildah/internal/tmpdir
github.com/containers/buildah/internal/util
@@ -176,7 +177,7 @@ github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/pkg/volumes
github.com/containers/buildah/util
-# github.com/containers/common v0.57.5
+# github.com/containers/common v0.57.7
## explicit; go 1.18
github.com/containers/common/internal/attributedstring
github.com/containers/common/libimage
@@ -243,7 +244,7 @@ github.com/containers/conmon/runner/config
# github.com/containers/gvisor-tap-vsock v0.7.2
## explicit; go 1.20
github.com/containers/gvisor-tap-vsock/pkg/types
-# github.com/containers/image/v5 v5.29.3
+# github.com/containers/image/v5 v5.29.4
## explicit; go 1.19
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
@@ -353,7 +354,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.51.0
+# github.com/containers/storage v1.51.2
## explicit; go 1.19
github.com/containers/storage
github.com/containers/storage/drivers
--
2.46.0