diff options
-rw-r--r-- | docs/source/markdown/podman-create.1.md | 7 | ||||
-rw-r--r-- | docs/source/markdown/podman-run.1.md | 8 | ||||
-rw-r--r-- | pkg/specgenutil/volumes.go | 63 | ||||
-rw-r--r-- | test/e2e/run_volume_test.go | 61 |
4 files changed, 135 insertions, 4 deletions
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 0c48f105e..4a20e4e45 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -595,6 +595,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared + type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true + type=volume,source=vol1,destination=/path/in/container,ro=true type=tmpfs,tmpfs-size=512M,destination=/path/in/container @@ -613,6 +615,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · ro, readonly: true or false (default). + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. + Options specific to image: · rw, readwrite: true or false (default). @@ -627,6 +631,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and . relabel: shared, private. + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. + Options specific to tmpfs: · ro, readonly: true or false (default). @@ -639,6 +645,7 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · notmpcopyup: Disable copying files from the image to the tmpfs. + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. #### **--name**=*name* diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 7b6a56fc6..7a2e09dab 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -615,6 +615,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared + type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true + type=volume,source=vol1,destination=/path/in/container,ro=true type=tmpfs,tmpfs-size=512M,destination=/path/in/container @@ -633,6 +635,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · ro, readonly: true or false (default). + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. + Options specific to image: · rw, readwrite: true or false (default). @@ -647,6 +651,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and . relabel: shared, private. + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. + Options specific to tmpfs: · ro, readonly: true or false (default). @@ -659,6 +665,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and · notmpcopyup: Disable copying files from the image to the tmpfs. + . U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container. + #### **--name**=*name* Assign a name to the container. diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go index 0ed08198f..3ce96164f 100644 --- a/pkg/specgenutil/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -243,7 +243,7 @@ func getBindMount(args []string) (spec.Mount, error) { Type: define.TypeBind, } - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool + var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -343,6 +343,18 @@ func getBindMount(args []string) (spec.Mount, error) { default: return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) } + case "U", "chown": + if setOwnership { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newMount, err + } + if ok { + newMount.Options = append(newMount.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -375,7 +387,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) { Source: define.TypeTmpfs, } - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool + var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -431,6 +443,18 @@ func getTmpfsMount(args []string) (spec.Mount, error) { } newMount.Destination = filepath.Clean(kv[1]) setDest = true + case "U", "chown": + if setOwnership { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newMount, err + } + if ok { + newMount.Options = append(newMount.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -486,7 +510,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { func getNamedVolume(args []string) (*specgen.NamedVolume, error) { newVolume := new(specgen.NamedVolume) - var setSource, setDest, setRORW, setSuid, setDev, setExec bool + var setSource, setDest, setRORW, setSuid, setDev, setExec, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -532,6 +556,18 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { } newVolume.Dest = filepath.Clean(kv[1]) setDest = true + case "U", "chown": + if setOwnership { + return newVolume, errors.Wrapf(optionArgError, "cannot pass 'U' or 'chown' option more than once") + } + ok, err := validChownFlag(val) + if err != nil { + return newVolume, err + } + if ok { + newVolume.Options = append(newVolume.Options, "U") + } + setOwnership = true case "consistency": // Often used on MACs and mistakenly on Linux platforms. // Since Docker ignores this option so shall we. @@ -628,3 +664,24 @@ func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { } return m, nil } + +// validChownFlag ensures that the U or chown flag is correctly used +func validChownFlag(flag string) (bool, error) { + kv := strings.SplitN(flag, "=", 2) + switch len(kv) { + case 1: + case 2: + // U=[true|false] + switch strings.ToLower(kv[1]) { + case "true": + case "false": + return false, nil + default: + return false, errors.Wrapf(optionArgError, "'U' or 'chown' must be set to true or false, instead received %q", kv[1]) + } + default: + return false, errors.Wrapf(optionArgError, "badly formatted option %q", flag) + } + + return true, nil +} diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 59937b6c0..690c53de6 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -614,7 +614,7 @@ VOLUME /test/`, ALPINE) Expect(len(session.OutputToStringArray())).To(Equal(2)) }) - It("podman run with U volume flag", func() { + It("podman run with --volume and U flag", func() { SkipIfRemote("Overlay volumes only work locally") u, err := user.Current() @@ -665,6 +665,65 @@ VOLUME /test/`, ALPINE) Expect(found).Should(BeTrue()) }) + It("podman run with --mount and U flag", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + name := u.Username + if name == "root" { + name = "containers" + } + + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + + mountPath := filepath.Join(podmanTest.TempDir, "foo") + os.Mkdir(mountPath, 0755) + + // false bind mount + vol := "type=bind,src=" + mountPath + ",dst=" + dest + ",U=false" + session := podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).ShouldNot(Equal("888:888")) + + // invalid bind mount + vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=invalid" + session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + // true bind mount + vol = "type=bind,src=" + mountPath + ",dst=" + dest + ",U=true" + session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("888:888")) + + // tmpfs mount + vol = "type=tmpfs," + "dst=" + dest + ",chown" + session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("888:888")) + + // named volume mount + namedVolume := podmanTest.Podman([]string{"volume", "create", "foo"}) + namedVolume.WaitWithDefaultTimeout() + Expect(namedVolume).Should(Exit(0)) + + vol = "type=volume,src=foo,dst=" + dest + ",chown=true" + session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--mount", vol, ALPINE, "stat", "-c", "%u:%g", dest}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("888:888")) + }) + It("volume permissions after run", func() { imgName := "testimg" dockerfile := fmt.Sprintf(`FROM %s |