diff --git a/pkg/idtools/idtools.go b/pkg/idtools/idtools.go index 7c8f4d10c2..7a8fec0ce5 100644 --- a/pkg/idtools/idtools.go +++ b/pkg/idtools/idtools.go @@ -3,15 +3,18 @@ package idtools import ( "bufio" "fmt" + "io/ioutil" "os" "os/user" "sort" "strconv" "strings" + "sync" "syscall" "github.com/containers/storage/pkg/system" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // IDMap contains a single entry for user namespace range remapping. An array @@ -203,6 +206,67 @@ func (i *IDMappings) ToHost(pair IDPair) (IDPair, error) { return target, err } +var ( + overflowUIDOnce sync.Once + overflowGIDOnce sync.Once + overflowUID int + overflowGID int +) + +// getOverflowUID returns the UID mapped to the overflow user +func getOverflowUID() int { + overflowUIDOnce.Do(func() { + // 65534 is the value on older kernels where /proc/sys/kernel/overflowuid is not present + overflowUID = 65534 + if content, err := ioutil.ReadFile("/proc/sys/kernel/overflowuid"); err == nil { + if tmp, err := strconv.Atoi(string(content)); err == nil { + overflowUID = tmp + } + } + }) + return overflowUID +} + +// getOverflowUID returns the GID mapped to the overflow user +func getOverflowGID() int { + overflowGIDOnce.Do(func() { + // 65534 is the value on older kernels where /proc/sys/kernel/overflowgid is not present + overflowGID = 65534 + if content, err := ioutil.ReadFile("/proc/sys/kernel/overflowgid"); err == nil { + if tmp, err := strconv.Atoi(string(content)); err == nil { + overflowGID = tmp + } + } + }) + return overflowGID +} + +// ToHost returns the host UID and GID for the container uid, gid. +// Remapping is only performed if the ids aren't already the remapped root ids +// If the mapping is not possible because the target ID is not mapped into +// the namespace, then the overflow ID is used. +func (i *IDMappings) ToHostOverflow(pair IDPair) (IDPair, error) { + var err error + target := i.RootPair() + + if pair.UID != target.UID { + target.UID, err = RawToHost(pair.UID, i.uids) + if err != nil { + target.UID = getOverflowUID() + logrus.Debugf("Failed to map UID %v to the target mapping, using the overflow ID %v", pair.UID, target.UID) + } + } + + if pair.GID != target.GID { + target.GID, err = RawToHost(pair.GID, i.gids) + if err != nil { + target.GID = getOverflowGID() + logrus.Debugf("Failed to map GID %v to the target mapping, using the overflow ID %v", pair.GID, target.GID) + } + } + return target, nil +} + // ToContainer returns the container UID and GID for the host uid and gid func (i *IDMappings) ToContainer(pair IDPair) (int, int, error) { uid, err := RawToContainer(pair.UID, i.uids) diff --git a/pkg/idtools/idtools_test.go b/pkg/idtools/idtools_test.go index c22b87d99b..1315e5d89e 100644 --- a/pkg/idtools/idtools_test.go +++ b/pkg/idtools/idtools_test.go @@ -46,6 +46,48 @@ func TestToHost(t *testing.T) { } } +func TestToHostOverflow(t *testing.T) { + idMappings := []IDMap{ + { + ContainerID: 0, + HostID: 1000, + Size: 1, + }, + { + ContainerID: 1, + HostID: 100000, + Size: 65536, + }, + } + + mappings := IDMappings{ + uids: idMappings, + gids: idMappings, + } + + pair, err := mappings.ToHostOverflow(IDPair{UID: 65538, GID: 0}) + if err != nil { + t.Fatal(err) + } + if pair.UID != getOverflowUID() { + t.Fatalf("Converted to the wrong UID") + } + if pair.GID != 1000 { + t.Fatalf("Converted to the wrong GID") + } + + pair, err = mappings.ToHostOverflow(IDPair{UID: 10, GID: 65539}) + if err != nil { + t.Fatal(err) + } + if pair.UID != 100009 { + t.Fatalf("Converted to the wrong UID") + } + if pair.GID != getOverflowGID() { + t.Fatalf("Converted to the wrong GID") + } +} + func TestGetRootUIDGID(t *testing.T) { mappingsUIDs := []IDMap{ {