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

chown: use overflow id as fallback when chowning #1220

Merged
merged 3 commits into from Apr 28, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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