Merge pull request #5890 from artqzn/idmapped_mounts

RFC: Initial support of idmapped mount points
This commit is contained in:
Akihiro Suda
2023-09-05 20:41:05 +09:00
committed by GitHub
10 changed files with 1007 additions and 71 deletions

View File

@@ -45,6 +45,7 @@ type SnapshotterConfig struct {
upperdirLabel bool
ms MetaStore
mountOptions []string
remapIds bool
}
// Opt is an option to configure the overlay snapshotter
@@ -92,12 +93,18 @@ func WithMetaStore(ms MetaStore) Opt {
}
}
func WithRemapIds(config *SnapshotterConfig) error {
config.remapIds = true
return nil
}
type snapshotter struct {
root string
ms MetaStore
asyncRemove bool
upperdirLabel bool
options []string
remapIds bool
}
// NewSnapshotter returns a Snapshotter which uses overlayfs. The overlayfs
@@ -153,6 +160,7 @@ func NewSnapshotter(root string, opts ...Opt) (snapshots.Snapshotter, error) {
asyncRemove: config.asyncRemove,
upperdirLabel: config.upperdirLabel,
options: config.mountOptions,
remapIds: config.remapIds,
}, nil
}
@@ -259,16 +267,22 @@ func (o *snapshotter) View(ctx context.Context, key, parent string, opts ...snap
// This can be used to recover mounts after calling View or Prepare.
func (o *snapshotter) Mounts(ctx context.Context, key string) (_ []mount.Mount, err error) {
var s storage.Snapshot
var info snapshots.Info
if err := o.ms.WithTransaction(ctx, false, func(ctx context.Context) error {
s, err = storage.GetSnapshot(ctx, key)
if err != nil {
return fmt.Errorf("failed to get active mount: %w", err)
}
_, info, _, err = storage.GetInfo(ctx, key)
if err != nil {
return fmt.Errorf("failed to get snapshot info: %w", err)
}
return nil
}); err != nil {
return nil, err
}
return o.mounts(s), nil
return o.mounts(s, info), nil
}
func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
@@ -402,10 +416,46 @@ func (o *snapshotter) getCleanupDirectories(ctx context.Context) ([]string, erro
return cleanup, nil
}
func validateIDMapping(mapping string) error {
var (
hostID int
ctrID int
length int
)
if _, err := fmt.Sscanf(mapping, "%d:%d:%d", &ctrID, &hostID, &length); err != nil {
return err
}
// Almost impossible, but snapshots.WithLabels doesn't check it
if ctrID < 0 || hostID < 0 || length < 0 {
return fmt.Errorf("invalid mapping \"%d:%d:%d\"", ctrID, hostID, length)
}
if ctrID != 0 {
return fmt.Errorf("container mapping of 0 is only supported")
}
return nil
}
func hostID(mapping string) (int, error) {
var (
hostID int
ctrID int
length int
)
if err := validateIDMapping(mapping); err != nil {
return -1, fmt.Errorf("invalid mapping: %w", err)
}
if _, err := fmt.Sscanf(mapping, "%d:%d:%d", &ctrID, &hostID, &length); err != nil {
return -1, err
}
return hostID, nil
}
func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts []snapshots.Opt) (_ []mount.Mount, err error) {
var (
s storage.Snapshot
td, path string
info snapshots.Info
)
defer func() {
@@ -436,14 +486,46 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
return fmt.Errorf("failed to create snapshot: %w", err)
}
if len(s.ParentIDs) > 0 {
st, err := os.Stat(o.upperPath(s.ParentIDs[0]))
if err != nil {
return fmt.Errorf("failed to stat parent: %w", err)
}
_, info, _, err = storage.GetInfo(ctx, key)
if err != nil {
return fmt.Errorf("failed to get snapshot info: %w", err)
}
stat := st.Sys().(*syscall.Stat_t)
if err := os.Lchown(filepath.Join(td, "fs"), int(stat.Uid), int(stat.Gid)); err != nil {
mappedUID := -1
mappedGID := -1
// NOTE: if idmapped mounts' supported by hosted kernel there may be
// no parents at all, so overlayfs will not work and snapshotter
// will use bind mount. To be able to create file objects inside the
// rootfs -- just chown this only bound directory according to provided
// {uid,gid}map. In case of one/multiple parents -- chown upperdir.
if v, ok := info.Labels[snapshots.LabelSnapshotUIDMapping]; ok {
if mappedUID, err = hostID(v); err != nil {
return fmt.Errorf("failed to parse UID mapping: %w", err)
}
}
if v, ok := info.Labels[snapshots.LabelSnapshotGIDMapping]; ok {
if mappedGID, err = hostID(v); err != nil {
return fmt.Errorf("failed to parse GID mapping: %w", err)
}
}
if mappedUID == -1 || mappedGID == -1 {
if len(s.ParentIDs) > 0 {
st, err := os.Stat(o.upperPath(s.ParentIDs[0]))
if err != nil {
return fmt.Errorf("failed to stat parent: %w", err)
}
stat, ok := st.Sys().(*syscall.Stat_t)
if !ok {
return fmt.Errorf("incompatible types after stat call: *syscall.Stat_t expected")
}
mappedUID = int(stat.Uid)
mappedGID = int(stat.Gid)
}
}
if mappedUID != -1 && mappedGID != -1 {
if err := os.Lchown(filepath.Join(td, "fs"), mappedUID, mappedGID); err != nil {
return fmt.Errorf("failed to chown: %w", err)
}
}
@@ -458,8 +540,7 @@ func (o *snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
}); err != nil {
return nil, err
}
return o.mounts(s), nil
return o.mounts(s, info), nil
}
func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string, kind snapshots.Kind) (string, error) {
@@ -481,7 +562,18 @@ func (o *snapshotter) prepareDirectory(ctx context.Context, snapshotDir string,
return td, nil
}
func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
func (o *snapshotter) mounts(s storage.Snapshot, info snapshots.Info) []mount.Mount {
var options []string
if o.remapIds {
if v, ok := info.Labels[snapshots.LabelSnapshotUIDMapping]; ok {
options = append(options, fmt.Sprintf("uidmap=%s", v))
}
if v, ok := info.Labels[snapshots.LabelSnapshotGIDMapping]; ok {
options = append(options, fmt.Sprintf("gidmap=%s", v))
}
}
if len(s.ParentIDs) == 0 {
// if we only have one layer/no parents then just return a bind mount as overlay
// will not work
@@ -489,20 +581,18 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
if s.Kind == snapshots.KindView {
roFlag = "ro"
}
return []mount.Mount{
{
Source: o.upperPath(s.ID),
Type: "bind",
Options: []string{
Options: append(options,
roFlag,
"rbind",
},
),
},
}
}
options := o.options
if s.Kind == snapshots.KindActive {
options = append(options,
fmt.Sprintf("workdir=%s", o.workPath(s.ID)),
@@ -513,10 +603,10 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
{
Source: o.upperPath(s.ParentIDs[0]),
Type: "bind",
Options: []string{
Options: append(options,
"ro",
"rbind",
},
),
},
}
}
@@ -525,8 +615,9 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
for i := range s.ParentIDs {
parentPaths[i] = o.upperPath(s.ParentIDs[i])
}
options = append(options, fmt.Sprintf("lowerdir=%s", strings.Join(parentPaths, ":")))
options = append(options, o.options...)
return []mount.Mount{
{
Type: "overlay",
@@ -534,7 +625,6 @@ func (o *snapshotter) mounts(s storage.Snapshot) []mount.Mount {
Options: options,
},
}
}
func (o *snapshotter) upperPath(id string) string {

View File

@@ -26,12 +26,14 @@ import (
"syscall"
"testing"
"github.com/containerd/containerd"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/pkg/testutil"
"github.com/containerd/containerd/snapshots"
"github.com/containerd/containerd/snapshots/overlay/overlayutils"
"github.com/containerd/containerd/snapshots/storage"
"github.com/containerd/containerd/snapshots/testsuite"
"github.com/opencontainers/runtime-spec/specs-go"
)
func newSnapshotterWithOpts(opts ...Opt) testsuite.SnapshotterFunc {
@@ -51,12 +53,23 @@ func TestOverlay(t *testing.T) {
"no opt": nil,
// default in init()
"AsynchronousRemove": {AsynchronousRemove},
// idmapped mounts enabled
"WithRemapIds": {WithRemapIds},
}
for optsName, opts := range optTestCases {
t.Run(optsName, func(t *testing.T) {
newSnapshotter := newSnapshotterWithOpts(opts...)
testsuite.SnapshotterSuite(t, "overlayfs", newSnapshotter)
t.Run("TestOverlayRemappedBind", func(t *testing.T) {
testOverlayRemappedBind(t, newSnapshotter)
})
t.Run("TestOverlayRemappedActive", func(t *testing.T) {
testOverlayRemappedActive(t, newSnapshotter)
})
t.Run("TestOverlayRemappedInvalidMappings", func(t *testing.T) {
testOverlayRemappedInvalidMapping(t, newSnapshotter)
})
t.Run("TestOverlayMounts", func(t *testing.T) {
testOverlayMounts(t, newSnapshotter)
})
@@ -156,28 +169,28 @@ func testOverlayOverlayMount(t *testing.T, newSnapshotter testsuite.SnapshotterF
t.Errorf("expected source %q but received %q", "overlay", m.Source)
}
var (
bp = getBasePath(ctx, o, root, "/tmp/layer2")
work = "workdir=" + filepath.Join(bp, "work")
upper = "upperdir=" + filepath.Join(bp, "fs")
lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0]
expected []string
bp = getBasePath(ctx, o, root, "/tmp/layer2")
work = "workdir=" + filepath.Join(bp, "work")
upper = "upperdir=" + filepath.Join(bp, "fs")
lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0]
)
expected := []string{
"index=off",
}
if !supportsIndex() {
expected = expected[1:]
expected = append(expected, []string{
work,
upper,
lower,
}...)
if supportsIndex() {
expected = append(expected, "index=off")
}
if userxattr, err := overlayutils.NeedsUserXAttr(root); err != nil {
t.Fatal(err)
} else if userxattr {
expected = append(expected, "userxattr")
}
expected = append(expected, []string{
work,
upper,
lower,
}...)
for i, v := range expected {
if m.Options[i] != v {
t.Errorf("expected %q but received %q", v, m.Options[i])
@@ -185,6 +198,241 @@ func testOverlayOverlayMount(t *testing.T, newSnapshotter testsuite.SnapshotterF
}
}
func testOverlayRemappedBind(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
var (
opts []snapshots.Opt
mounts []mount.Mount
)
ctx := context.TODO()
root := t.TempDir()
o, _, err := newSnapshotter(ctx, root)
if err != nil {
t.Fatal(err)
}
if sn, ok := o.(*snapshotter); !ok || !sn.remapIds {
t.Skip("overlayfs doesn't support idmapped mounts")
}
hostID := uint32(666)
contID := uint32(0)
length := uint32(65536)
uidMap := specs.LinuxIDMapping{
ContainerID: contID,
HostID: hostID,
Size: length,
}
gidMap := specs.LinuxIDMapping{
ContainerID: contID,
HostID: hostID,
Size: length,
}
opts = append(opts, containerd.WithRemapperLabels(
uidMap.ContainerID, uidMap.HostID,
gidMap.ContainerID, gidMap.HostID,
length),
)
key := "/tmp/test"
if mounts, err = o.Prepare(ctx, key, "", opts...); err != nil {
t.Fatal(err)
}
bp := getBasePath(ctx, o, root, key)
expected := []string{
fmt.Sprintf("uidmap=%d:%d:%d", uidMap.ContainerID, uidMap.HostID, uidMap.Size),
fmt.Sprintf("gidmap=%d:%d:%d", gidMap.ContainerID, gidMap.HostID, gidMap.Size),
"rw",
"rbind",
}
checkMountOpts := func() {
if len(mounts) != 1 {
t.Errorf("should only have 1 mount but received %d", len(mounts))
}
if len(mounts[0].Options) != len(expected) {
t.Errorf("expected %d options, but received %d", len(expected), len(mounts[0].Options))
}
m := mounts[0]
for i, v := range expected {
if m.Options[i] != v {
t.Errorf("mount option %q is not valid, expected %q", m.Options[i], v)
}
}
st, err := os.Stat(filepath.Join(bp, "fs"))
if err != nil {
t.Errorf("failed to stat %s", filepath.Join(bp, "fs"))
}
if stat, ok := st.Sys().(*syscall.Stat_t); !ok {
t.Errorf("incompatible types after stat call: *syscall.Stat_t expected")
} else if stat.Uid != uidMap.HostID || stat.Gid != gidMap.HostID {
t.Errorf("bad mapping: expected {uid: %d, gid: %d}; real {uid: %d, gid: %d}", uidMap.HostID, gidMap.HostID, int(stat.Uid), int(stat.Gid))
}
}
checkMountOpts()
expected[2] = "ro"
if err = o.Commit(ctx, "base", key, opts...); err != nil {
t.Fatal(err)
}
if mounts, err = o.View(ctx, key, "base", opts...); err != nil {
t.Fatal(err)
}
bp = getBasePath(ctx, o, root, key)
checkMountOpts()
key = "/tmp/test1"
if mounts, err = o.Prepare(ctx, key, ""); err != nil {
t.Fatal(err)
}
bp = getBasePath(ctx, o, root, key)
expected = expected[2:]
expected[0] = "rw"
uidMap.HostID = 0
gidMap.HostID = 0
checkMountOpts()
}
func testOverlayRemappedActive(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
var (
opts []snapshots.Opt
mounts []mount.Mount
)
ctx := context.TODO()
root := t.TempDir()
o, _, err := newSnapshotter(ctx, root)
if err != nil {
t.Fatal(err)
}
if sn, ok := o.(*snapshotter); !ok || !sn.remapIds {
t.Skip("overlayfs doesn't support idmapped mounts")
}
hostID := uint32(666)
contID := uint32(0)
length := uint32(65536)
uidMap := specs.LinuxIDMapping{
ContainerID: contID,
HostID: hostID,
Size: length,
}
gidMap := specs.LinuxIDMapping{
ContainerID: contID,
HostID: hostID,
Size: length,
}
opts = append(opts, containerd.WithRemapperLabels(
uidMap.ContainerID, uidMap.HostID,
gidMap.ContainerID, gidMap.HostID,
length),
)
key := "/tmp/test"
if _, err = o.Prepare(ctx, key, "", opts...); err != nil {
t.Fatal(err)
}
if err = o.Commit(ctx, "base", key, opts...); err != nil {
t.Fatal(err)
}
if mounts, err = o.Prepare(ctx, key, "base", opts...); err != nil {
t.Fatal(err)
}
if len(mounts) != 1 {
t.Errorf("should only have 1 mount but received %d", len(mounts))
}
bp := getBasePath(ctx, o, root, key)
expected := []string{
fmt.Sprintf("uidmap=%d:%d:%d", uidMap.ContainerID, uidMap.HostID, uidMap.Size),
fmt.Sprintf("gidmap=%d:%d:%d", gidMap.ContainerID, gidMap.HostID, gidMap.Size),
fmt.Sprintf("workdir=%s", filepath.Join(bp, "work")),
fmt.Sprintf("upperdir=%s", filepath.Join(bp, "fs")),
fmt.Sprintf("lowerdir=%s", getParents(ctx, o, root, key)[0]),
}
m := mounts[0]
for i, v := range expected {
if m.Options[i] != v {
t.Errorf("mount option %q is invalid, expected %q", m.Options[i], v)
}
}
st, err := os.Stat(filepath.Join(bp, "fs"))
if err != nil {
t.Errorf("failed to stat %s", filepath.Join(bp, "fs"))
}
if stat, ok := st.Sys().(*syscall.Stat_t); !ok {
t.Errorf("incompatible types after stat call: *syscall.Stat_t expected")
} else if stat.Uid != uidMap.HostID || stat.Gid != gidMap.HostID {
t.Errorf("bad mapping: expected {uid: %d, gid: %d}; received {uid: %d, gid: %d}", uidMap.HostID, gidMap.HostID, int(stat.Uid), int(stat.Gid))
}
}
func testOverlayRemappedInvalidMapping(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
ctx := context.TODO()
root := t.TempDir()
o, _, err := newSnapshotter(ctx, root)
if err != nil {
t.Fatal(err)
}
if sn, ok := o.(*snapshotter); !ok || !sn.remapIds {
t.Skip("overlayfs doesn't support idmapped mounts")
}
key := "/tmp/test"
for desc, opts := range map[string][]snapshots.Opt{
"WithLabels: negative UID mapping must fail": {
snapshots.WithLabels(map[string]string{
snapshots.LabelSnapshotUIDMapping: "-1:-1:-2",
snapshots.LabelSnapshotGIDMapping: "0:0:66666",
}),
},
"WithLabels: negative GID mapping must fail": {
snapshots.WithLabels(map[string]string{
snapshots.LabelSnapshotUIDMapping: "0:0:66666",
snapshots.LabelSnapshotGIDMapping: "-1:-1:-2",
}),
},
"WithLabels: negative GID/UID mappings must fail": {
snapshots.WithLabels(map[string]string{
snapshots.LabelSnapshotUIDMapping: "-666:-666:-666",
snapshots.LabelSnapshotGIDMapping: "-666:-666:-666",
}),
},
"WithRemapperLabels: container ID (GID/UID) other than 0 must fail": {
containerd.WithRemapperLabels(666, 666, 666, 666, 666),
},
"WithRemapperLabels: container ID (UID) other than 0 must fail": {
containerd.WithRemapperLabels(666, 0, 0, 0, 65536),
},
"WithRemapperLabels: container ID (GID) other than 0 must fail": {
containerd.WithRemapperLabels(0, 0, 666, 0, 4294967295),
},
} {
t.Log(desc)
if _, err = o.Prepare(ctx, key, "", opts...); err == nil {
t.Fatalf("snapshots with invalid mappings must fail")
}
// remove may fail, but it doesn't matter
_ = o.Remove(ctx, key)
}
}
func getBasePath(ctx context.Context, sn snapshots.Snapshotter, root, key string) string {
o := sn.(*snapshotter)
ctx, t, err := o.ms.TransactionContext(ctx, false)
@@ -306,6 +554,7 @@ func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
if m.Source != expected {
t.Errorf("expected source %q but received %q", expected, m.Source)
}
if m.Options[0] != "ro" {
t.Errorf("expected mount option ro but received %q", m.Options[0])
}
@@ -345,18 +594,13 @@ func testOverlayView(t *testing.T, newSnapshotter testsuite.SnapshotterFunc) {
t.Errorf("expected %d additional mount option but got %d", expectedOptions, len(m.Options))
}
lowers := getParents(ctx, o, root, "/tmp/view2")
expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1])
optIdx := 2
if !supportsIndex {
optIdx--
if m.Options[0] != expected {
t.Errorf("expected option %q but received %q", expected, m.Options[0])
}
if userxattr {
optIdx++
}
if m.Options[0] != "volatile" {
if m.Options[1] != "volatile" {
t.Error("expected option first option to be provided option \"volatile\"")
}
if m.Options[optIdx] != expected {
t.Errorf("expected option %q but received %q", expected, m.Options[optIdx])
}
}

View File

@@ -24,6 +24,8 @@ import (
"path/filepath"
"syscall"
"golang.org/x/sys/unix"
kernel "github.com/containerd/containerd/contrib/seccomp/kernelversion"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
@@ -198,3 +200,98 @@ func NeedsUserXAttr(d string) (bool, error) {
}
return true, nil
}
// SupportsIDMappedMounts tells if this kernel supports idmapped mounts for overlayfs
// or not.
//
// This function returns error whether the kernel supports idmapped mounts
// for overlayfs or not, i.e. if e.g. -ENOSYS may be returned as well as -EPERM.
// So, caller should check for (true, err == nil), otherwise treat it as there's
// no support from the kernel side.
func SupportsIDMappedMounts() (bool, error) {
// Fast path
fiveDotNineteen := kernel.KernelVersion{Kernel: 5, Major: 19}
if ok, err := kernel.GreaterEqualThan(fiveDotNineteen); err == nil && ok {
return true, nil
}
// Do slow path, because idmapped mounts may be backported to older kernels.
uidMap := syscall.SysProcIDMap{
ContainerID: 0,
HostID: 666,
Size: 1,
}
gidMap := syscall.SysProcIDMap{
ContainerID: 0,
HostID: 666,
Size: 1,
}
td, err := os.MkdirTemp("", "ovl-idmapped-check")
if err != nil {
return false, fmt.Errorf("failed to create check directory: %w", err)
}
defer func() {
if err := os.RemoveAll(td); err != nil {
log.L.WithError(err).Warnf("failed to remove check directory %s", td)
}
}()
for _, dir := range []string{"lower", "upper", "work", "merged"} {
if err = os.Mkdir(filepath.Join(td, dir), 0755); err != nil {
return false, fmt.Errorf("failed to create %s directory: %w", dir, err)
}
}
defer func() {
if err = os.RemoveAll(td); err != nil {
log.L.WithError(err).Warnf("failed remove overlay check directory %s", td)
}
}()
if err = os.Lchown(filepath.Join(td, "upper"), uidMap.HostID, gidMap.HostID); err != nil {
return false, fmt.Errorf("failed to chown upper directory %s: %w", filepath.Join(td, "upper"), err)
}
lowerDir := filepath.Join(td, "lower")
uidmap := fmt.Sprintf("%d:%d:%d", uidMap.ContainerID, uidMap.HostID, uidMap.Size)
gidmap := fmt.Sprintf("%d:%d:%d", gidMap.ContainerID, gidMap.HostID, gidMap.Size)
usernsFd, childProcCleanUp, err := mount.GetUsernsFD(uidmap, gidmap)
if err != nil {
return false, err
}
defer childProcCleanUp()
if err = mount.IDMapMount(lowerDir, lowerDir, usernsFd); err != nil {
return false, fmt.Errorf("failed to remap lowerdir %s: %w", lowerDir, err)
}
defer func() {
if err = unix.Unmount(lowerDir, 0); err != nil {
log.L.WithError(err).Warnf("failed to unmount lowerdir %s", lowerDir)
}
}()
opts := fmt.Sprintf("index=off,lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, filepath.Join(td, "upper"), filepath.Join(td, "work"))
if err = unix.Mount("", filepath.Join(td, "merged"), "overlay", uintptr(unix.MS_RDONLY), opts); err != nil {
return false, fmt.Errorf("failed to mount idmapped overlay to %s: %w", filepath.Join(td, "merged"), err)
}
defer func() {
if err = unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
log.L.WithError(err).Warnf("failed to unmount overlay check directory %s", filepath.Join(td, "merged"))
}
}()
// NOTE: we can't just return true if mount didn't fail since overlay supports
// idmappings for {lower,upper}dir. That means we need to check merged directory
// to make sure it completely supports idmapped mounts.
st, err := os.Stat(filepath.Join(td, "merged"))
if err != nil {
return false, fmt.Errorf("failed to stat %s: %w", filepath.Join(td, "merged"), err)
}
if stat, ok := st.Sys().(*syscall.Stat_t); !ok {
return false, fmt.Errorf("incompatible types after stat call: *syscall.Stat_t expected")
} else if int(stat.Uid) != uidMap.HostID || int(stat.Gid) != gidMap.HostID {
return false, fmt.Errorf("bad mapping: expected {uid: %d, gid: %d}; real {uid: %d, gid: %d}", uidMap.HostID, gidMap.HostID, int(stat.Uid), int(stat.Gid))
}
return true, nil
}

View File

@@ -24,6 +24,11 @@ import (
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshots/overlay"
"github.com/containerd/containerd/snapshots/overlay/overlayutils"
)
const (
capaRemapIds = "remap-ids"
)
// Config represents configuration for the overlay plugin.
@@ -66,6 +71,10 @@ func init() {
if len(config.MountOptions) > 0 {
oOpts = append(oOpts, overlay.WithMountOptions(config.MountOptions))
}
if ok, err := overlayutils.SupportsIDMappedMounts(); err == nil && ok {
oOpts = append(oOpts, overlay.WithRemapIds)
ic.Meta.Capabilities = append(ic.Meta.Capabilities, capaRemapIds)
}
ic.Meta.Exports["root"] = root
return overlay.NewSnapshotter(root, oOpts...)