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 fdcc2257df.

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 <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2023-02-16 10:33:39 +01:00
parent 7f2b4234c7
commit 96af9e4e0f
No known key found for this signature in database
GPG Key ID: 67E38F7A8BA21772
3 changed files with 53 additions and 7 deletions

View File

@ -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:

View File

@ -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
}

View File

@ -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) {