Browse code

c8d: "unremap" the user namespace on commit

We remap the snapshot when we create a container, we have to to the
inverse when we commit the container into an image

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>

Djordje Lukic authored on 2023/11/08 20:23:55
Showing 3 changed files
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	cerrdefs "github.com/containerd/containerd/errdefs"
17 17
 	"github.com/containerd/containerd/images"
18 18
 	"github.com/containerd/containerd/leases"
19
+	"github.com/containerd/containerd/mount"
19 20
 	"github.com/containerd/containerd/rootfs"
20 21
 	"github.com/containerd/containerd/snapshots"
21 22
 	"github.com/containerd/log"
... ...
@@ -90,7 +91,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
90 90
 	if diffLayerDesc != nil {
91 91
 		rootfsID := identity.ChainID(imageConfig.RootFS.DiffIDs).String()
92 92
 
93
-		if err := applyDiffLayer(ctx, rootfsID, parentImage, sn, differ, *diffLayerDesc); err != nil {
93
+		if err := i.applyDiffLayer(ctx, rootfsID, cc.ContainerID, sn, differ, *diffLayerDesc); err != nil {
94 94
 			return "", fmt.Errorf("failed to apply diff: %w", err)
95 95
 		}
96 96
 
... ...
@@ -291,13 +292,19 @@ func createDiff(ctx context.Context, name string, sn snapshots.Snapshotter, cs c
291 291
 }
292 292
 
293 293
 // applyDiffLayer will apply diff layer content created by createDiff into the snapshotter.
294
-func applyDiffLayer(ctx context.Context, name string, baseImg imagespec.DockerOCIImage, sn snapshots.Snapshotter, differ diff.Applier, diffDesc ocispec.Descriptor) (retErr error) {
294
+func (i *ImageService) applyDiffLayer(ctx context.Context, name string, containerID string, sn snapshots.Snapshotter, differ diff.Applier, diffDesc ocispec.Descriptor) (retErr error) {
295 295
 	var (
296 296
 		key    = uniquePart() + "-" + name
297
-		parent = identity.ChainID(baseImg.RootFS.DiffIDs).String()
297
+		mounts []mount.Mount
298
+		err    error
298 299
 	)
299 300
 
300
-	mount, err := sn.Prepare(ctx, key, parent)
301
+	info, err := sn.Stat(ctx, containerID)
302
+	if err != nil {
303
+		return err
304
+	}
305
+
306
+	mounts, err = sn.Prepare(ctx, key, info.Parent)
301 307
 	if err != nil {
302 308
 		return fmt.Errorf("failed to prepare snapshot: %w", err)
303 309
 	}
... ...
@@ -312,16 +319,46 @@ func applyDiffLayer(ctx context.Context, name string, baseImg imagespec.DockerOC
312 312
 		}
313 313
 	}()
314 314
 
315
-	if _, err = differ.Apply(ctx, diffDesc, mount); err != nil {
315
+	if _, err = differ.Apply(ctx, diffDesc, mounts); err != nil {
316 316
 		return err
317 317
 	}
318 318
 
319
+	if !i.idMapping.Empty() {
320
+		// The rootfs of the container is remapped if an id mapping exists, we
321
+		// need to "unremap" it before committing the snapshot
322
+		rootPair := i.idMapping.RootPair()
323
+		usernsID := fmt.Sprintf("%s-%d-%d", key, rootPair.UID, rootPair.GID)
324
+		remappedID := usernsID + remapSuffix
325
+
326
+		if err = sn.Commit(ctx, name+"-pre", key); err != nil {
327
+			if cerrdefs.IsAlreadyExists(err) {
328
+				return nil
329
+			}
330
+			return err
331
+		}
332
+
333
+		mounts, err = sn.Prepare(ctx, remappedID, name+"-pre")
334
+		if err != nil {
335
+			return err
336
+		}
337
+
338
+		if err := i.unremapRootFS(ctx, mounts); err != nil {
339
+			return err
340
+		}
341
+
342
+		if err := sn.Commit(ctx, name, remappedID); err != nil {
343
+			return err
344
+		}
345
+		key = remappedID
346
+	}
347
+
319 348
 	if err = sn.Commit(ctx, name, key); err != nil {
320 349
 		if cerrdefs.IsAlreadyExists(err) {
321 350
 			return nil
322 351
 		}
323 352
 		return err
324 353
 	}
354
+
325 355
 	return nil
326 356
 }
327 357
 
... ...
@@ -67,3 +67,25 @@ func (i *ImageService) remapRootFS(ctx context.Context, mounts []mount.Mount) er
67 67
 		})
68 68
 	})
69 69
 }
70
+
71
+func (i *ImageService) unremapRootFS(ctx context.Context, mounts []mount.Mount) error {
72
+	return mount.WithTempMount(ctx, mounts, func(root string) error {
73
+		return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
74
+			if err != nil {
75
+				return err
76
+			}
77
+
78
+			stat := info.Sys().(*syscall.Stat_t)
79
+			if stat == nil {
80
+				return fmt.Errorf("cannot get underlying data for %s", path)
81
+			}
82
+
83
+			uid, gid, err := i.idMapping.ToContainer(idtools.Identity{UID: int(stat.Uid), GID: int(stat.Gid)})
84
+			if err != nil {
85
+				return err
86
+			}
87
+
88
+			return os.Lchown(path, uid, gid)
89
+		})
90
+	})
91
+}
... ...
@@ -3,9 +3,14 @@ package containerd
3 3
 import (
4 4
 	"context"
5 5
 
6
+	"github.com/containerd/containerd/mount"
6 7
 	"github.com/containerd/containerd/snapshots"
7 8
 )
8 9
 
9 10
 func (i *ImageService) remapSnapshot(ctx context.Context, snapshotter snapshots.Snapshotter, id string, parentSnapshot string) error {
10 11
 	return nil
11 12
 }
13
+
14
+func (i *ImageService) unremapRootFS(ctx context.Context, mounts []mount.Mount) error {
15
+	return nil
16
+}