From 96af9e4e0fd545c3e7ab0e3b8db983131f709975 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Thu, 16 Feb 2023 10:33:39 +0100 Subject: [PATCH] libpod: support relative positions for idmaps we were previously using an experimental feature in crun, but we lost this capability once we moved to using the OCI runtime spec to specify the volume mappings in fdcc2257df0fb0cb72d3fbe1b5aa8625955e1219. Add the same feature to libpod, so that we can support relative positions for the idmaps. Closes: https://github.com/containers/podman/issues/17517 Signed-off-by: Giuseppe Scrivano --- docs/source/markdown/options/mount.md | 3 ++- libpod/container_internal_common.go | 26 +++++++++++++++++++--- libpod/container_internal_test.go | 31 ++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/docs/source/markdown/options/mount.md b/docs/source/markdown/options/mount.md index badba4cb94..ac8e1b058d 100644 --- a/docs/source/markdown/options/mount.md +++ b/docs/source/markdown/options/mount.md @@ -40,7 +40,8 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and The idmap option supports a custom mapping that can be different than the user namespace used by the container. The mapping can be specified after the idmap option like: `idmap=uids=0-1-10#10-11-10;gids=0-100-10`. For each triplet, the first value is the start of the backing file system IDs that are mapped to the second value on the host. The length of this mapping is given in the third value. - Multiple ranges are separated with #. + Multiple ranges are separated with #. If the specified mapping is prepended with a '@' then the mapping is considered relative to the container + user namespace. The host ID for the mapping is changed to account for the relative position of the container user in the container user namespace. Options specific to image: diff --git a/libpod/container_internal_common.go b/libpod/container_internal_common.go index a5d1fc047a..26b5a729ac 100644 --- a/libpod/container_internal_common.go +++ b/libpod/container_internal_common.go @@ -57,11 +57,17 @@ import ( "github.com/sirupsen/logrus" ) -func parseOptionIDs(option string) ([]idtools.IDMap, error) { +func parseOptionIDs(ctrMappings []idtools.IDMap, option string) ([]idtools.IDMap, error) { ranges := strings.Split(option, "#") ret := make([]idtools.IDMap, len(ranges)) for i, m := range ranges { var v idtools.IDMap + + relative := false + if m[0] == '@' { + relative = true + m = m[1:] + } _, err := fmt.Sscanf(m, "%d-%d-%d", &v.ContainerID, &v.HostID, &v.Size) if err != nil { return nil, err @@ -69,6 +75,20 @@ func parseOptionIDs(option string) ([]idtools.IDMap, error) { if v.ContainerID < 0 || v.HostID < 0 || v.Size < 1 { return nil, fmt.Errorf("invalid value for %q", option) } + + if relative { + found := false + for _, m := range ctrMappings { + if v.ContainerID >= m.ContainerID && v.ContainerID < m.ContainerID+m.Size { + v.HostID += m.HostID - m.ContainerID + found = true + break + } + } + if !found { + return nil, fmt.Errorf("could not find a user namespace mapping for the relative mapping %q", option) + } + } ret[i] = v } return ret, nil @@ -83,12 +103,12 @@ func parseIDMapMountOption(idMappings stypes.IDMappingOptions, option string, in for _, i := range options { switch { case strings.HasPrefix(i, "uids="): - uidMap, err = parseOptionIDs(strings.Replace(i, "uids=", "", 1)) + uidMap, err = parseOptionIDs(idMappings.UIDMap, strings.Replace(i, "uids=", "", 1)) if err != nil { return nil, nil, err } case strings.HasPrefix(i, "gids="): - gidMap, err = parseOptionIDs(strings.Replace(i, "gids=", "", 1)) + gidMap, err = parseOptionIDs(idMappings.GIDMap, strings.Replace(i, "gids=", "", 1)) if err != nil { return nil, nil, err } diff --git a/libpod/container_internal_test.go b/libpod/container_internal_test.go index 50103d944c..22f074fbf1 100644 --- a/libpod/container_internal_test.go +++ b/libpod/container_internal_test.go @@ -18,10 +18,18 @@ import ( var hookPath string func TestParseOptionIDs(t *testing.T) { - _, err := parseOptionIDs("uids=100-200-2") + idMap := []idtools.IDMap{ + { + ContainerID: 0, + HostID: 1, + Size: 10000, + }, + } + + _, err := parseOptionIDs(idMap, "uids=100-200-2") assert.NotNil(t, err) - mappings, err := parseOptionIDs("100-200-2") + mappings, err := parseOptionIDs(idMap, "100-200-2") assert.Nil(t, err) assert.NotNil(t, mappings) @@ -31,7 +39,7 @@ func TestParseOptionIDs(t *testing.T) { assert.Equal(t, mappings[0].HostID, 200) assert.Equal(t, mappings[0].Size, 2) - mappings, err = parseOptionIDs("100-200-2#300-400-5") + mappings, err = parseOptionIDs(idMap, "100-200-2#300-400-5") assert.Nil(t, err) assert.NotNil(t, mappings) @@ -44,6 +52,23 @@ func TestParseOptionIDs(t *testing.T) { assert.Equal(t, mappings[1].ContainerID, 300) assert.Equal(t, mappings[1].HostID, 400) assert.Equal(t, mappings[1].Size, 5) + + mappings, err = parseOptionIDs(idMap, "@100-200-2#@300-400-5") + assert.Nil(t, err) + assert.NotNil(t, mappings) + + assert.Equal(t, len(mappings), 2) + + assert.Equal(t, mappings[0].ContainerID, 100) + assert.Equal(t, mappings[0].HostID, 201) + assert.Equal(t, mappings[0].Size, 2) + + assert.Equal(t, mappings[1].ContainerID, 300) + assert.Equal(t, mappings[1].HostID, 401) + assert.Equal(t, mappings[1].Size, 5) + + _, err = parseOptionIDs(idMap, "@10000-20000-2") + assert.NotNil(t, err) } func TestParseIDMapMountOption(t *testing.T) {