Skip to content

Commit

Permalink
Merge pull request #1220 from giuseppe/chown-to-overflow-id
Browse files Browse the repository at this point in the history
chown: use overflow id as fallback when chowning
  • Loading branch information
rhatdan committed Apr 28, 2022
2 parents 572ad81 + 28c166f commit 754b868
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 28 deletions.
2 changes: 1 addition & 1 deletion drivers/chown_unix.go
Expand Up @@ -76,7 +76,7 @@ func (c *platformChowner) LChown(path string, info os.FileInfo, toHost, toContai
UID: uid,
GID: gid,
}
mappedPair, err := toHost.ToHost(pair)
mappedPair, err := toHost.ToHostOverflow(pair)
if err != nil {
return fmt.Errorf("error mapping container ID pair %#v for %q to host: %v", pair, path, err)
}
Expand Down
54 changes: 27 additions & 27 deletions drivers/graphtest/graphtest_unix.go
Expand Up @@ -125,38 +125,38 @@ func DriverTestCreateBase(t testing.TB, drivername string, driverOptions ...stri
driver := GetDriver(t, drivername, driverOptions...)
defer PutDriver(t)

createBase(t, driver, "Base")
createBase(t, driver, "Base1")
defer func() {
require.NoError(t, driver.Remove("Base"))
require.NoError(t, driver.Remove("Base1"))
}()
verifyBase(t, driver, "Base", defaultPerms)
verifyBase(t, driver, "Base1", defaultPerms)
}

// DriverTestCreateSnap Create a driver and snap and verify.
func DriverTestCreateSnap(t testing.TB, drivername string, driverOptions ...string) {
driver := GetDriver(t, drivername, driverOptions...)
defer PutDriver(t)

createBase(t, driver, "Base")
createBase(t, driver, "Base2")
defer func() {
require.NoError(t, driver.Remove("Base"))
require.NoError(t, driver.Remove("Base2"))
}()

err := driver.Create("Snap", "Base", nil)
err := driver.Create("Snap2", "Base2", nil)
require.NoError(t, err)
defer func() {
require.NoError(t, driver.Remove("Snap"))
require.NoError(t, driver.Remove("Snap2"))
}()

verifyBase(t, driver, "Snap", defaultPerms)
verifyBase(t, driver, "Snap2", defaultPerms)

root, err := driver.Get("Snap", graphdriver.MountOpts{})
root, err := driver.Get("Snap2", graphdriver.MountOpts{})
assert.NoError(t, err)
err = os.Chmod(root, modifiedPerms)
require.NoError(t, err)
driver.Put("Snap")
driver.Put("Snap2")

err = driver.Create("SecondSnap", "Snap", nil)
err = driver.Create("SecondSnap", "Snap2", nil)
require.NoError(t, err)
defer func() {
require.NoError(t, driver.Remove("SecondSnap"))
Expand All @@ -171,45 +171,45 @@ func DriverTestCreateFromTemplate(t testing.TB, drivername string, driverOptions
driver := GetDriver(t, drivername, driverOptions...)
defer PutDriver(t)

createBase(t, driver, "Base")
createBase(t, driver, "Base3")
defer func() {
require.NoError(t, driver.Remove("Base"))
require.NoError(t, driver.Remove("Base3"))
}()

err := driver.Create("Snap", "Base", nil)
err := driver.Create("Snap3", "Base3", nil)
require.NoError(t, err)
defer func() {
require.NoError(t, driver.Remove("Snap"))
require.NoError(t, driver.Remove("Snap3"))
}()

content := []byte("test content")
if err := addFile(driver, "Snap", "testfile.txt", content); err != nil {
if err := addFile(driver, "Snap3", "testfile.txt", content); err != nil {
t.Fatal(err)
}

err = driver.CreateFromTemplate("FromTemplate", "Snap", nil, "Base", nil, nil, true)
err = driver.CreateFromTemplate("FromTemplate", "Snap3", nil, "Base3", nil, nil, true)
require.NoError(t, err)
defer func() {
require.NoError(t, driver.Remove("FromTemplate"))
}()

err = driver.CreateFromTemplate("ROFromTemplate", "Snap", nil, "Base", nil, nil, false)
err = driver.CreateFromTemplate("ROFromTemplate", "Snap3", nil, "Base3", nil, nil, false)
require.NoError(t, err)
defer func() {
require.NoError(t, driver.Remove("ROFromTemplate"))
}()

noChanges := []archive.Change{}

changes, err := driver.Changes("FromTemplate", nil, "Snap", nil, "")
changes, err := driver.Changes("FromTemplate", nil, "Snap3", nil, "")
if err != nil {
t.Fatal(err)
}
if err = checkChanges(noChanges, changes); err != nil {
t.Fatal(err)
}

changes, err = driver.Changes("ROFromTemplate", nil, "Snap", nil, "")
changes, err = driver.Changes("ROFromTemplate", nil, "Snap3", nil, "")
if err != nil {
t.Fatal(err)
}
Expand All @@ -223,7 +223,7 @@ func DriverTestCreateFromTemplate(t testing.TB, drivername string, driverOptions
if err := checkFile(driver, "ROFromTemplate", "testfile.txt", content); err != nil {
t.Fatal(err)
}
if err := checkFile(driver, "Snap", "testfile.txt", content); err != nil {
if err := checkFile(driver, "Snap3", "testfile.txt", content); err != nil {
t.Fatal(err)
}

Expand All @@ -232,31 +232,31 @@ func DriverTestCreateFromTemplate(t testing.TB, drivername string, driverOptions
Kind: archive.ChangeAdd,
}}

changes, err = driver.Changes("Snap", nil, "Base", nil, "")
changes, err = driver.Changes("Snap3", nil, "Base3", nil, "")
if err != nil {
t.Fatal(err)
}
if err = checkChanges(expectedChanges, changes); err != nil {
t.Fatal(err)
}

changes, err = driver.Changes("FromTemplate", nil, "Base", nil, "")
changes, err = driver.Changes("FromTemplate", nil, "Base3", nil, "")
if err != nil {
t.Fatal(err)
}
if err = checkChanges(expectedChanges, changes); err != nil {
t.Fatal(err)
}

changes, err = driver.Changes("ROFromTemplate", nil, "Base", nil, "")
changes, err = driver.Changes("ROFromTemplate", nil, "Base3", nil, "")
if err != nil {
t.Fatal(err)
}
if err = checkChanges(expectedChanges, changes); err != nil {
t.Fatal(err)
}

verifyBase(t, driver, "Base", defaultPerms)
verifyBase(t, driver, "Base3", defaultPerms)
}

// DriverTestDeepLayerRead reads a file from a lower layer under a given number of layers
Expand Down Expand Up @@ -429,11 +429,11 @@ func DriverTestSetQuota(t *testing.T, drivername string) {
driver := GetDriver(t, drivername)
defer PutDriver(t)

createBase(t, driver, "Base")
createBase(t, driver, "Base4")
createOpts := &graphdriver.CreateOpts{}
createOpts.StorageOpt = make(map[string]string, 1)
createOpts.StorageOpt["size"] = "50M"
if err := driver.Create("zfsTest", "Base", createOpts); err != nil {
if err := driver.Create("zfsTest", "Base4", createOpts); err != nil {
t.Fatal(err)
}

Expand Down
64 changes: 64 additions & 0 deletions pkg/idtools/idtools.go
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
42 changes: 42 additions & 0 deletions pkg/idtools/idtools_test.go
Expand Up @@ -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{
{
Expand Down

0 comments on commit 754b868

Please sign in to comment.