diff --git a/docs/source/markdown/options/mount.md b/docs/source/markdown/options/mount.md
index ac8e1b058d..cc53331587 100644
--- a/docs/source/markdown/options/mount.md
+++ b/docs/source/markdown/options/mount.md
@@ -6,7 +6,7 @@
Attach a filesystem mount to the container
-Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and **devpts**. [[1]](#Footnote1)
+Current supported mount TYPEs are **bind**, **devpts**, **glob**, **image**, **tmpfs** and **volume**. [[1]](#Footnote1)
e.g.
@@ -16,6 +16,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true
+ type=glob,src=/usr/lib/libfoo*,destination=/usr/lib,ro=true
+
type=volume,source=vol1,destination=/path/in/container,ro=true
type=tmpfs,tmpfs-size=512M,destination=/path/in/container
@@ -26,10 +28,12 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
Common Options:
- · src, source: mount source spec for bind and volume. Mandatory for bind.
+ · src, source: mount source spec for bind, glob, and volume. Mandatory for bind and glob.
· dst, destination, target: mount destination spec.
+ Paths matching globs, are mounted on the destination directory with the identical name inside the container.
+
Options specific to volume:
· ro, readonly: true or false (default).
@@ -47,7 +51,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
· rw, readwrite: true or false (default).
- Options specific to bind:
+ Options specific to bind and glob:
· ro, readonly: true or false (default).
diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in
index b247ce1e5c..0576675c5a 100644
--- a/docs/source/markdown/podman-create.1.md.in
+++ b/docs/source/markdown/podman-create.1.md.in
@@ -443,6 +443,12 @@ $ podman create --name container3 --requires container1,container2 -t -i fedora
$ podman start --attach container3
```
+### Exposing shared libraries inside of container as read-only using a glob
+
+```
+$ podman create --mount type=glob,src=/usr/lib64/libnvidia\*,ro -i -t fedora /bin/bash
+```
+
### Configure keep supplemental groups for access to volume
```
diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in
index 95f3632996..a42ddd67f7 100644
--- a/docs/source/markdown/podman-run.1.md.in
+++ b/docs/source/markdown/podman-run.1.md.in
@@ -468,6 +468,12 @@ $ podman run --read-only -i -t fedora /bin/bash
$ podman run --read-only --read-only-tmpfs=false --tmpfs /run -i -t fedora /bin/bash
```
+### Exposing shared libraries inside of container as read-only using a glob
+
+```
+$ podman run --mount type=glob,src=/usr/lib64/libnvidia\*,ro=true -i -t fedora /bin/bash
+```
+
### Exposing log messages from the container to the host's log
Bind mount the _/dev/log_ directory to have messages that are logged in the container show up in the host's
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 72859cc727..d3751cb29d 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"path"
+ "path/filepath"
"strings"
"github.com/containers/common/pkg/config"
@@ -18,7 +19,7 @@ import (
var (
errOptionArg = errors.New("must provide an argument for option")
errNoDest = errors.New("must set volume destination")
- errInvalidSyntax = errors.New("incorrect mount format: should be --mount type=,[src=,]target=[,options]")
+ errInvalidSyntax = errors.New("incorrect mount format: should be --mount type=,[src=,]target=[,options]")
)
// Parse all volume-related options in the create config into a set of mounts
@@ -196,6 +197,20 @@ func Mounts(mountFlag []string, configMounts []string) (map[string]spec.Mount, m
return fmt.Errorf("%v: %w", mount.Destination, specgen.ErrDuplicateDest)
}
finalMounts[mount.Destination] = mount
+ case "glob":
+ mounts, err := getGlobMounts(tokens)
+ if err != nil {
+ return err
+ }
+ for _, mount := range mounts {
+ if _, ok := finalMounts[mount.Destination]; ok {
+ if ignoreDup {
+ continue
+ }
+ return fmt.Errorf("%v: %w", mount.Destination, specgen.ErrDuplicateDest)
+ }
+ finalMounts[mount.Destination] = mount
+ }
case define.TypeTmpfs:
mount, err := getTmpfsMount(tokens)
if err != nil {
@@ -438,12 +453,49 @@ func parseMountOptions(mountType string, args []string) (*spec.Mount, error) {
return nil, fmt.Errorf("%s: %w", kv[0], util.ErrBadMntOption)
}
}
- if len(mnt.Destination) == 0 {
+ if mountType != "glob" && len(mnt.Destination) == 0 {
return nil, errNoDest
}
return &mnt, nil
}
+// Parse glob mounts entry from the --mount flag.
+func getGlobMounts(args []string) ([]spec.Mount, error) {
+ mounts := []spec.Mount{}
+
+ mnt, err := parseMountOptions("glob", args)
+ if err != nil {
+ return nil, err
+ }
+
+ globs, err := filepath.Glob(mnt.Source)
+ if err != nil {
+ return nil, err
+ }
+ if len(globs) == 0 {
+ return nil, fmt.Errorf("no file paths matching glob %q", mnt.Source)
+ }
+
+ options, err := parse.ValidateVolumeOpts(mnt.Options)
+ if err != nil {
+ return nil, err
+ }
+ for _, src := range globs {
+ var newMount spec.Mount
+ newMount.Type = define.TypeBind
+ newMount.Options = options
+ newMount.Source = src
+ if len(mnt.Destination) == 0 {
+ newMount.Destination = src
+ } else {
+ newMount.Destination = filepath.Join(mnt.Destination, filepath.Base(src))
+ }
+ mounts = append(mounts, newMount)
+ }
+
+ return mounts, nil
+}
+
// Parse a single bind mount entry from the --mount flag.
func getBindMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go
index dd823f79c6..d593bf8e24 100644
--- a/pkg/systemd/quadlet/quadlet.go
+++ b/pkg/systemd/quadlet/quadlet.go
@@ -664,7 +664,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse
paramsMap[kv[0]] = kv[1]
}
if paramType, ok := paramsMap["type"]; ok {
- if paramType == "volume" || paramType == "bind" {
+ if paramType == "volume" || paramType == "bind" || paramType == "glob" {
var err error
if paramSource, ok := paramsMap["source"]; ok {
paramsMap["source"], err = handleStorageSource(container, service, paramSource, names)
diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats
index c59b3e1d05..918b5e1189 100644
--- a/test/system/060-mount.bats
+++ b/test/system/060-mount.bats
@@ -247,4 +247,43 @@ EOF
buildah rm $external_cid
}
+@test "podman volume globs" {
+ v1a=v1_$(random_string)
+ v1b=v1_$(random_string)
+ v2=v2_$(random_string)
+ vol1a=${PODMAN_TMPDIR}/$v1a
+ vol1b=${PODMAN_TMPDIR}/$v1b
+ vol2=${PODMAN_TMPDIR}/$v2
+ touch $vol1a $vol1b $vol2
+
+ # if volumes source and dest match then pass
+ run_podman run --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,ro $IMAGE ls $vol1a $vol1b
+ run_podman 1 run --rm --mount source=${PODMAN_TMPDIR}/v1\*,type=glob,ro $IMAGE ls $vol2
+ is "$output" ".*No such file or directory" "$vol2 should not be mounted in the container"
+
+ run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v3\*,type=glob,ro $IMAGE ls $vol2
+ is "$output" "Error: no file paths matching glob \"${PODMAN_TMPDIR}/v3\*\"" "Glob does not match so should throw error"
+
+ run_podman 1 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=glob,ro,Z $IMAGE touch $vol2
+ is "$output" "touch: $vol2: Read-only file system" "Mount should be read-only"
+
+ run_podman run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=glob,ro=false,Z $IMAGE touch $vol2
+
+ run_podman run --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,destination=/non/existing/directory,ro $IMAGE ls /non/existing/directory
+ is "$output" ".*$v1a" "podman images --inspect should include $v1a"
+ is "$output" ".*$v1b" "podman images --inspect should include $v1b"
+
+ run_podman create --rm --mount type=glob,src=${PODMAN_TMPDIR}/v1\*,ro $IMAGE ls $vol1a $vol1b
+ cid=$output
+ run_podman container inspect $output
+ is "$output" ".*$vol1a" "podman images --inspect should include $vol1a"
+ is "$output" ".*$vol1b" "podman images --inspect should include $vol1b"
+
+ run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,type=bind,ro=false $IMAGE touch $vol2
+ is "$output" "Error: must set volume destination" "Bind mounts require destination"
+
+ run_podman 125 run --rm --mount source=${PODMAN_TMPDIR}/v2\*,destination=/tmp/foobar, ro=false $IMAGE touch $vol2
+ is "$output" "Error: invalid reference format" "Default mounts don not support globs"
+}
+
# vim: filetype=sh