From 4e2a04dedcd96db332927b6c447175d87dffa1c7 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 1 Sep 2025 11:44:11 +0200 Subject: [PATCH] do not pass volume-opt as bind mounts options to runtime Starting with runc 1.3.0 it errors when we pass unknown mount options to the runtime, the volume-opt options are specifc to the volume we create and should not be passed to the mount in the oci spec. Fixes: #26938 Signed-off-by: Paul Holzinger --- libpod/runtime_ctr.go | 13 +++++++++++++ test/e2e/run_volume_test.go | 18 +++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 941d3e048c..5651c4669d 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -504,6 +504,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai _, err := r.state.Volume(vol.Name) if err == nil { // The volume exists, we're good + // Make sure to drop all volume-opt options as they only apply to + // the volume create which we don't do again. + var volOpts []string + for _, opts := range vol.Options { + if !strings.HasPrefix(opts, "volume-opt") { + volOpts = append(volOpts, opts) + } + } + vol.Options = volOpts continue } else if !errors.Is(err, define.ErrNoSuchVolume) { return nil, fmt.Errorf("retrieving named volume %s for new container: %w", vol.Name, err) @@ -530,6 +539,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai if len(vol.Options) > 0 { isDriverOpts := false driverOpts := make(map[string]string) + var volOpts []string for _, opts := range vol.Options { if strings.HasPrefix(opts, "volume-opt") { isDriverOpts = true @@ -538,8 +548,11 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai return nil, err } driverOpts[driverOptKey] = driverOptValue + } else { + volOpts = append(volOpts, opts) } } + vol.Options = volOpts if isDriverOpts { parsedOptions := []VolumeCreateOption{WithVolumeOptions(driverOpts)} volOptions = append(volOptions, parsedOptions...) diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index b0020e1c67..fcd0e57b70 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -8,6 +8,7 @@ import ( "os/exec" "os/user" "path/filepath" + "strconv" "strings" . "github.com/containers/podman/v5/test/utils" @@ -859,14 +860,17 @@ VOLUME /test/`, ALPINE) It("podman run with --mount and named volume with driver-opts", func() { // anonymous volume mount with driver opts vol := "type=volume,source=test_vol,dst=/test,volume-opt=type=tmpfs,volume-opt=device=tmpfs,volume-opt=o=nodev" - session := podmanTest.Podman([]string{"run", "--rm", "--mount", vol, ALPINE, "echo", "hello"}) - session.WaitWithDefaultTimeout() - Expect(session).Should(ExitCleanly()) + // Loop twice to cover both the initial code path that creates the volume and the ones which reuses it. + for i := range 2 { + name := "testctr" + strconv.Itoa(i) + podmanTest.PodmanExitCleanly("run", "--name", name, "--mount", vol, ALPINE, "echo", "hello") - inspectVol := podmanTest.Podman([]string{"volume", "inspect", "test_vol"}) - inspectVol.WaitWithDefaultTimeout() - Expect(inspectVol).Should(ExitCleanly()) - Expect(inspectVol.OutputToString()).To(ContainSubstring("nodev")) + inspectVol := podmanTest.PodmanExitCleanly("volume", "inspect", "test_vol") + Expect(inspectVol.OutputToString()).To(ContainSubstring("nodev")) + + inspect := podmanTest.PodmanExitCleanly("container", "inspect", name, "--format", "{{range .Mounts}}{{.Options}}{{end}}") + Expect(inspect.OutputToString()).To(ContainSubstring("[nosuid nodev rbind]")) + } }) It("volume permissions after run", func() {