2194 lines
88 KiB
Diff
2194 lines
88 KiB
Diff
From bf768e9a1fd103bb84503bcec4187b9e7cff10bd Mon Sep 17 00:00:00 2001
|
|
From: Danish Prakash <contact@danishpraka.sh>
|
|
Date: Mon, 17 Feb 2025 14:21:30 +0530
|
|
Subject: [PATCH] vendor: bump buildah to 1.37.6; CVE-2024-11218
|
|
|
|
Fix a time-of-check/time-of-use error when mounting type=bind and
|
|
type=cache directories that use a "src" flag. A hostile writer could
|
|
use a concurrently-running stage or build to replace that "src" location
|
|
between the point when we had resolved possible symbolic links and when
|
|
runc/crun/whatever actually went to create the bind mount
|
|
(CVE-2024-11218).
|
|
|
|
Stop ignoring the "src" option for cache mounts when there's no "from"
|
|
option.
|
|
|
|
Fixes: CVE-2024-11218 (bsc#1236270)
|
|
|
|
Signed-off-by: Danish Prakash <contact@danishpraka.sh>
|
|
---
|
|
go.mod | 2 +-
|
|
go.sum | 4 +-
|
|
.../github.com/containers/buildah/.cirrus.yml | 2 +-
|
|
.../containers/buildah/CHANGELOG.md | 20 +-
|
|
.../containers/buildah/changelog.txt | 19 +-
|
|
.../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 | 428 +++++++++++++-----
|
|
.../containers/buildah/pkg/overlay/overlay.go | 13 +
|
|
.../buildah/pkg/overlay/overlay_linux.go | 16 +
|
|
vendor/github.com/containers/buildah/run.go | 12 +-
|
|
.../containers/buildah/run_common.go | 174 ++++---
|
|
.../containers/buildah/run_freebsd.go | 11 +-
|
|
.../containers/buildah/run_linux.go | 40 +-
|
|
vendor/modules.txt | 3 +-
|
|
23 files changed, 1031 insertions(+), 235 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
|
|
|
|
diff --git a/go.mod b/go.mod
|
|
index 3081783c8c25..9624f7f694e1 100644
|
|
--- a/go.mod
|
|
+++ b/go.mod
|
|
@@ -12,7 +12,7 @@ require (
|
|
github.com/checkpoint-restore/checkpointctl v1.2.1
|
|
github.com/checkpoint-restore/go-criu/v7 v7.1.0
|
|
github.com/containernetworking/plugins v1.5.1
|
|
- github.com/containers/buildah v1.37.5
|
|
+ github.com/containers/buildah v1.37.6
|
|
github.com/containers/common v0.60.4
|
|
github.com/containers/conmon v2.0.20+incompatible
|
|
github.com/containers/gvisor-tap-vsock v0.7.4
|
|
diff --git a/go.sum b/go.sum
|
|
index 9effccfb855f..c422ae9f52e2 100644
|
|
--- a/go.sum
|
|
+++ b/go.sum
|
|
@@ -77,8 +77,8 @@ github.com/containernetworking/cni v1.2.3 h1:hhOcjNVUQTnzdRJ6alC5XF+wd9mfGIUaj8F
|
|
github.com/containernetworking/cni v1.2.3/go.mod h1:DuLgF+aPd3DzcTQTtp/Nvl1Kim23oFKdm2okJzBQA5M=
|
|
github.com/containernetworking/plugins v1.5.1 h1:T5ji+LPYjjgW0QM+KyrigZbLsZ8jaX+E5J/EcKOE4gQ=
|
|
github.com/containernetworking/plugins v1.5.1/go.mod h1:MIQfgMayGuHYs0XdNudf31cLLAC+i242hNm6KuDGqCM=
|
|
-github.com/containers/buildah v1.37.5 h1:oJ+cVbtgxB3ZHux4No9rKbWfcd7uoDpk8r7wcbm+Vbo=
|
|
-github.com/containers/buildah v1.37.5/go.mod h1:kiNTdC/78ek5XfqX6xUAq5aR8HNVy+CQ4ODjUNbiPJM=
|
|
+github.com/containers/buildah v1.37.6 h1:kr6KXZYdfc9SjKVgFsAoXJYRcbKm0jwA/zu3hX5PPoA=
|
|
+github.com/containers/buildah v1.37.6/go.mod h1:kiNTdC/78ek5XfqX6xUAq5aR8HNVy+CQ4ODjUNbiPJM=
|
|
github.com/containers/common v0.60.4 h1:H5+LAMHPZEqX6vVNOQ+IguVsaFl8kbO/SZ/VPXjxhy0=
|
|
github.com/containers/common v0.60.4/go.mod h1:I0upBi1qJX3QmzGbUOBN1LVP6RvkKhd3qQpZbQT+Q54=
|
|
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
|
|
diff --git a/vendor/github.com/containers/buildah/.cirrus.yml b/vendor/github.com/containers/buildah/.cirrus.yml
|
|
index 29b62eada576..f7ba01b903b9 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.37"
|
|
GOPATH: "/var/tmp/go"
|
|
GOSRC: "${GOPATH}/src/github.com/containers/buildah"
|
|
# Overrides default location (/tmp/cirrus) for repo clone
|
|
diff --git a/vendor/github.com/containers/buildah/CHANGELOG.md b/vendor/github.com/containers/buildah/CHANGELOG.md
|
|
index b2c1808959ae..278b4dc6f6b2 100644
|
|
--- a/vendor/github.com/containers/buildah/CHANGELOG.md
|
|
+++ b/vendor/github.com/containers/buildah/CHANGELOG.md
|
|
@@ -2,10 +2,24 @@
|
|
|
|
# Changelog
|
|
|
|
+## v1.37.6 (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
|
|
+ [release-1.37][CI:DOCS] Touch up changelogs
|
|
+ [release-1.37][CI:DOCS] touchup changelog
|
|
+ Update CHANGELOG.md
|
|
+
|
|
## v1.37.5 (2024-10-17)
|
|
|
|
- Bump the containers/storage library to v1.55.1
|
|
- Properly validate cache IDs and sources
|
|
+ Bump the containers/storage library to v1.55.1 - Resolves CVE-2024-9676
|
|
+ Properly validate cache IDs and sources - Resolves CVE-2024-9675
|
|
Packit: constrain koji job to fedora package to avoid dupes
|
|
|
|
## v1.37.4 (2024-10-07)
|
|
@@ -29,7 +43,7 @@
|
|
|
|
[release-1.37] Bump c/common v0.60.1, c/image v5.32.1
|
|
|
|
-## vv1.37.0 (2024-07-26)
|
|
+## v1.37.0 (2024-07-26)
|
|
|
|
Bump c/storage, c/image, c/common for v1.37.0
|
|
"build with basename resolving user arg" tests: correct ARG use
|
|
diff --git a/vendor/github.com/containers/buildah/changelog.txt b/vendor/github.com/containers/buildah/changelog.txt
|
|
index 4fe7dbf1f8ef..eda2b860950e 100644
|
|
--- a/vendor/github.com/containers/buildah/changelog.txt
|
|
+++ b/vendor/github.com/containers/buildah/changelog.txt
|
|
@@ -1,6 +1,19 @@
|
|
+- Changelog for v1.37.6 (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
|
|
+ * [release-1.37][CI:DOCS] Touch up changelogs
|
|
+ * [release-1.37][CI:DOCS] touchup changelog
|
|
+ * Update CHANGELOG.md
|
|
+
|
|
- Changelog for v1.37.5 (2024-10-17)
|
|
- * Bump the containers/storage library to v1.55.1
|
|
- * Properly validate cache IDs and sources
|
|
+ * Bump the containers/storage library to v1.55.1 - Resolves CVE-2024-9676
|
|
+ * Properly validate cache IDs and sources - Resolves CVE-2024-9675
|
|
* Packit: constrain koji job to fedora package to avoid dupes
|
|
|
|
- Changelog for v1.37.4 (2024-10-07)
|
|
@@ -20,7 +33,7 @@
|
|
- Changelog for v1.37.1 (2024-08-12)
|
|
* [release-1.37] Bump c/common v0.60.1, c/image v5.32.1
|
|
|
|
-- Changelog for vv1.37.0 (2024-07-26)
|
|
+- Changelog for v1.37.0 (2024-07-26)
|
|
* Bump c/storage, c/image, c/common for v1.37.0
|
|
* "build with basename resolving user arg" tests: correct ARG use
|
|
* bud-multiple-platform-no-run test: correct ARG use
|
|
diff --git a/vendor/github.com/containers/buildah/define/types.go b/vendor/github.com/containers/buildah/define/types.go
|
|
index 5267d08729e8..cd3fa4d50a64 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.37.5"
|
|
+ Version = "1.37.6"
|
|
|
|
// DefaultRuntime if containers.conf fails.
|
|
DefaultRuntime = "runc"
|
|
@@ -169,13 +169,13 @@ type SBOMScanOptions struct {
|
|
MergeStrategy SBOMMergeStrategy // how to merge the outputs of multiple scans
|
|
}
|
|
|
|
-// 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://") &&
|
|
@@ -188,19 +188,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
|
|
@@ -209,28 +214,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 34e836ad2fe9..a73f078c7fe4 100644
|
|
--- a/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
|
|
+++ b/vendor/github.com/containers/buildah/imagebuildah/stage_executor.go
|
|
@@ -635,7 +635,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
|
|
@@ -667,7 +672,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
|
|
}
|
|
}
|
|
@@ -678,14 +687,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 610e9fcf11b2..1014a470ef65 100644
|
|
--- a/vendor/github.com/containers/buildah/internal/volumes/volumes.go
|
|
+++ b/vendor/github.com/containers/buildah/internal/volumes/volumes.go
|
|
@@ -17,15 +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 (
|
|
@@ -56,18 +60,84 @@ 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
|
|
+}
|
|
+
|
|
// FIXME: this code needs to be merged with pkg/parse/parse.go ValidateVolumeOpts
|
|
+//
|
|
// 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 := ""
|
|
|
|
@@ -80,86 +150,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, argName)
|
|
- 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", "no-dereference":
|
|
if hasArgValue {
|
|
- return newMount, "", fmt.Errorf("%v: %w", val, errBadOptionArg)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", val, errBadOptionArg)
|
|
}
|
|
newMount.Options = append(newMount.Options, argName)
|
|
case "from":
|
|
if !hasArgValue {
|
|
- return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
fromImage = argValue
|
|
case "bind-propagation":
|
|
if !hasArgValue {
|
|
- return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
switch argValue {
|
|
default:
|
|
- return newMount, "", fmt.Errorf("%v: %q: %w", argName, argValue, errBadMntOption)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %q: %w", argName, argValue, 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, argValue)
|
|
case "src", "source":
|
|
if !hasArgValue {
|
|
- return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
newMount.Source = argValue
|
|
case "target", "dst", "destination":
|
|
if !hasArgValue {
|
|
- return newMount, "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
targetPath := argValue
|
|
+ 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
|
|
+ setRelabel = argValue
|
|
switch argValue {
|
|
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", argName, errBadMntOption)
|
|
+ return newMount, "", "", "", fmt.Errorf("%s mount option must be 'private' or 'shared': %w", argName, 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", argName, errBadMntOption)
|
|
+ return newMount, "", "", "", fmt.Errorf("%v: %w", argName, 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 {
|
|
@@ -170,16 +239,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
|
|
- }
|
|
- isImageMounted = true
|
|
+ return newMount, "", "", "", err
|
|
+ }
|
|
+ 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
|
|
}
|
|
@@ -190,48 +266,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
|
|
@@ -282,69 +383,69 @@ func GetCacheMount(args []string, store storage.Store, imageMountLabel string, a
|
|
sharing = argValue
|
|
case "bind-propagation":
|
|
if !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
switch argValue {
|
|
default:
|
|
- return newMount, nil, fmt.Errorf("%v: %q: %w", argName, argValue, errBadMntOption)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %q: %w", argName, argValue, 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, argValue)
|
|
case "id":
|
|
if !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
id = argValue
|
|
case "from":
|
|
if !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
fromStage = argValue
|
|
case "target", "dst", "destination":
|
|
if !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
targetPath := argValue
|
|
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 !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
newMount.Source = argValue
|
|
case "mode":
|
|
if !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
mode, err = strconv.ParseUint(argValue, 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 !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
uid, err = strconv.Atoi(argValue)
|
|
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 !hasArgValue {
|
|
- return newMount, nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadOptionArg)
|
|
}
|
|
gid, err = strconv.Atoi(argValue)
|
|
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", argName, errBadMntOption)
|
|
+ return newMount, "", nil, fmt.Errorf("%v: %w", argName, errBadMntOption)
|
|
}
|
|
}
|
|
|
|
@@ -355,16 +456,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
|
|
}
|
|
}
|
|
@@ -372,14 +475,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
|
|
- evaluated, err := copier.Eval(mountPoint, string(filepath.Separator)+newMount.Source, copier.EvalOptions{})
|
|
- if err != nil {
|
|
- return newMount, nil, err
|
|
- }
|
|
- newMount.Source = evaluated
|
|
+ thisCacheRoot = mountPoint
|
|
} else {
|
|
// we need to create cache on host if no image is being used
|
|
|
|
@@ -389,64 +487,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 != "" {
|
|
// Don't let the user control where we place the directory.
|
|
dirID := digest.FromString(id).Encoded()[:16]
|
|
- newMount.Source = filepath.Join(cacheParent, dirID)
|
|
+ thisCacheRoot = filepath.Join(cacheParent, dirID)
|
|
buildahLockFilesDir = filepath.Join(BuildahCacheLockfileDir, dirID)
|
|
} else {
|
|
// Don't let the user control where we place the directory.
|
|
dirID := digest.FromString(newMount.Destination).Encoded()[:16]
|
|
- newMount.Source = filepath.Join(cacheParent, dirID)
|
|
+ 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
|
|
@@ -464,12 +571,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) {
|
|
@@ -495,27 +619,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
|
|
}
|
|
@@ -525,24 +675,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)
|
|
}
|
|
}()
|
|
@@ -555,56 +734,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 666a0a01ae24..1320c6d020f0 100644
|
|
--- a/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
|
|
+++ b/vendor/github.com/containers/buildah/pkg/overlay/overlay.go
|
|
@@ -11,6 +11,7 @@ import (
|
|
"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"
|
|
@@ -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, ":", "\\:")
|
|
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 46d0c44aa1fc..aa6ce9ba6642 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
|
|
@@ -55,6 +56,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 != "" {
|
|
@@ -79,5 +83,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/run.go b/vendor/github.com/containers/buildah/run.go
|
|
index fa5a1c2c9d21..e1eb32859d92 100644
|
|
--- a/vendor/github.com/containers/buildah/run.go
|
|
+++ b/vendor/github.com/containers/buildah/run.go
|
|
@@ -180,18 +180,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 0076b71eb9d7..6a60a2873550 100644
|
|
--- a/vendor/github.com/containers/buildah/run_common.go
|
|
+++ b/vendor/github.com/containers/buildah/run_common.go
|
|
@@ -27,7 +27,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"
|
|
@@ -40,15 +39,14 @@ import (
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/common/pkg/subscriptions"
|
|
"github.com/containers/image/v5/types"
|
|
- imageTypes "github.com/containers/image/v5/types"
|
|
"github.com/containers/storage"
|
|
"github.com/containers/storage/pkg/fileutils"
|
|
"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"
|
|
@@ -1304,7 +1302,9 @@ func init() {
|
|
reexec.Register(runUsingRuntimeCommand, runUsingRuntimeMain)
|
|
}
|
|
|
|
-// If this succeeds, the caller must call cleanupMounts().
|
|
+// 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, compatBuiltinVolumes types.OptionalBool, volumeMounts []string, runFileMounts []string, runMountInfo runMountInfo) (*runMountArtifacts, error) {
|
|
// Start building a new list of mounts.
|
|
var mounts []specs.Mount
|
|
@@ -1363,14 +1363,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
|
|
@@ -1525,28 +1527,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
|
|
@@ -1574,29 +1609,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 {
|
|
@@ -1604,14 +1647,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)
|
|
}
|
|
@@ -1631,31 +1682,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) {
|
|
@@ -1932,52 +1985,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 341c80c4fc63..cfc2dba0c9bc 100644
|
|
--- a/vendor/github.com/containers/buildah/run_freebsd.go
|
|
+++ b/vendor/github.com/containers/buildah/run_freebsd.go
|
|
@@ -276,7 +276,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)
|
|
}
|
|
}()
|
|
@@ -351,9 +351,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 38a77ecf81a1..f32e1149012f 100644
|
|
--- a/vendor/github.com/containers/buildah/run_linux.go
|
|
+++ b/vendor/github.com/containers/buildah/run_linux.go
|
|
@@ -38,6 +38,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"
|
|
@@ -492,7 +493,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)
|
|
}
|
|
}()
|
|
@@ -1112,7 +1113,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)
|
|
@@ -1121,7 +1122,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
|
|
}
|
|
@@ -1379,24 +1380,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/modules.txt b/vendor/modules.txt
|
|
index 2e1419cd1c02..06d19dcc7b3a 100644
|
|
--- a/vendor/modules.txt
|
|
+++ b/vendor/modules.txt
|
|
@@ -140,7 +140,7 @@ github.com/containernetworking/cni/pkg/version
|
|
# github.com/containernetworking/plugins v1.5.1
|
|
## explicit; go 1.20
|
|
github.com/containernetworking/plugins/pkg/ns
|
|
-# github.com/containers/buildah v1.37.5
|
|
+# github.com/containers/buildah v1.37.6
|
|
## explicit; go 1.21.0
|
|
github.com/containers/buildah
|
|
github.com/containers/buildah/bind
|
|
@@ -153,6 +153,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/sbom
|
|
github.com/containers/buildah/internal/tmpdir
|
|
--
|
|
2.46.0
|
|
|