Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion libpod/container_internal_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if err := c.relabel(m.Source, c.MountLabel(), label.IsShared(o)); err != nil {
return nil, err
}

case "no-dereference":
// crun calls the option `copy-symlink`.
// Podman decided for --no-dereference as many
// bin-utils tools (e..g, touch, chown, cp) do.
options = append(options, "copy-symlink")
case "copy", "nocopy":
// no real OCI runtime bind mount options, these should already be handled by the named volume mount above
default:
options = append(options, o)
}
Expand Down
13 changes: 13 additions & 0 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if err == nil {
ctrNamedVolumes = append(ctrNamedVolumes, dbVol)
// 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("error retrieving named volume %s for new container: %w", vol.Name, err)
Expand All @@ -487,6 +496,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
Expand All @@ -495,8 +505,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...)
Expand Down
44 changes: 34 additions & 10 deletions test/e2e/run_volume_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package integration

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"strings"

"github.com/containers/podman/v4/pkg/rootless"
. "github.com/containers/podman/v4/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
"github.com/opencontainers/runtime-spec/specs-go"
)

// in-container mount point: using a path that is definitely not present
Expand Down Expand Up @@ -453,9 +456,27 @@ var _ = Describe("Podman run with volumes", func() {
Expect(separateVolumeSession).Should(Exit(0))
Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput))

copySession := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol3:/etc/apk:copy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch"})
copySession.WaitWithDefaultTimeout()
Expect(copySession).Should(Exit(0))
podmanTest.PodmanExitCleanly("run", "--name", "testctr", "-v", "testvol3:/etc/apk:copy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch")

inspect := podmanTest.PodmanExitCleanly("container", "inspect", "testctr", "--format", "{{.OCIConfigPath}}")

// Make extra check that the OCI config does not contain the copy opt, runc 1.3.0 fails on that while crun does not.
// We only test crun upstream so make sure the spec is sane: https://github.com/containers/podman/issues/26938
f, err := os.Open(inspect.OutputToString())
Expect(err).ToNot(HaveOccurred())
defer f.Close()
var spec specs.Spec
err = json.NewDecoder(f).Decode(&spec)
Expect(err).ToNot(HaveOccurred())

found := false
for _, m := range spec.Mounts {
if m.Destination == "/etc/apk" {
found = true
Expect(m.Options).To(Equal([]string{"rprivate", "nosuid", "nodev", "rbind"}))
}
}
Expect(found).To(BeTrue(), "OCI spec contains /etc/apk mount")

noCopySession := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol4:/etc/apk:nocopy", ALPINE, "stat", "-c", "%h", "/etc/apk/arch"})
noCopySession.WaitWithDefaultTimeout()
Expand Down Expand Up @@ -877,14 +898,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(Exit(0))
// 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(Exit(0))
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() {
Expand Down