Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libpod: support relative positions for idmaps #17522

Merged
merged 1 commit into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion docs/source/markdown/options/mount.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
26 changes: 23 additions & 3 deletions libpod/container_internal_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,38 @@ 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
}
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
Expand All @@ -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
}
Expand Down
31 changes: 28 additions & 3 deletions libpod/container_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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) {
Expand Down