Skip to content

Commit 10fc9e8

Browse files
nlacasseshentubot
authored andcommitted
runsc: Mount "mandatory" mounts right after mounting the root.
The /proc and /sys mounts are "mandatory" in the sense that they should be mounted in the sandbox even when they are not included in the spec. Runsc treats /tmp similarly, because it is faster to use the internal tmpfs implementation instead of proxying to the host. However, the spec may contain submounts of these mandatory mounts (particularly for /tmp). In those cases, we must mount our mandatory mounts before the submount, otherwise the submount will be masked. Since the mandatory mounts are all top-level directories, we can mount them right after the root. PiperOrigin-RevId: 203145635
1 parent 6f6f14f commit 10fc9e8

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

runsc/boot/fs.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func createMountNamespace(userCtx context.Context, rootCtx context.Context, spec
8383
}
8484

8585
// compileMounts returns the supported mounts from the mount spec, adding any
86-
// additional mounts that are required by the OCI specification.
86+
// mandatory mounts that are required by the OCI specification.
8787
func compileMounts(spec *specs.Spec) []specs.Mount {
8888
// Keep track of whether proc, sys, and tmp were mounted.
8989
var procMounted, sysMounted, tmpMounted bool
@@ -119,14 +119,15 @@ func compileMounts(spec *specs.Spec) []specs.Mount {
119119

120120
// Mount proc and sys even if the user did not ask for it, as the spec
121121
// says we SHOULD.
122+
var mandatoryMounts []specs.Mount
122123
if !procMounted {
123-
mounts = append(mounts, specs.Mount{
124+
mandatoryMounts = append(mandatoryMounts, specs.Mount{
124125
Type: "proc",
125126
Destination: "/proc",
126127
})
127128
}
128129
if !sysMounted {
129-
mounts = append(mounts, specs.Mount{
130+
mandatoryMounts = append(mandatoryMounts, specs.Mount{
130131
Type: "sysfs",
131132
Destination: "/sys",
132133
})
@@ -136,11 +137,20 @@ func compileMounts(spec *specs.Spec) []specs.Mount {
136137
// rely on the host /tmp, but this is a nice optimization, and fixes
137138
// some apps that call mknod in /tmp.
138139
if !tmpMounted {
139-
mounts = append(mounts, specs.Mount{
140+
// TODO: If the host /tmp (or a mount at /tmp) has
141+
// files in it, we should overlay our tmpfs implementation over
142+
// that. Until then, the /tmp mount will always appear empty at
143+
// container creation.
144+
mandatoryMounts = append(mandatoryMounts, specs.Mount{
140145
Type: "tmpfs",
141146
Destination: "/tmp",
142147
})
143148
}
149+
150+
// The mandatory mounts should be ordered right after the root, in case
151+
// there are submounts of these mandatory mounts already in the spec.
152+
mounts = append(mounts[:0], append(mandatoryMounts, mounts[0:]...)...)
153+
144154
return mounts
145155
}
146156

@@ -430,8 +440,8 @@ func addRestoreMount(conf *Config, renv *fs.RestoreEnvironment, m specs.Mount, f
430440
if err != nil {
431441
return err
432442
}
433-
// TODO: Fix this when we support all the mount types and make this a
434-
// fatal error.
443+
// TODO: Fix this when we support all the mount types and
444+
// make this a fatal error.
435445
if fsName == "" {
436446
return nil
437447
}

runsc/boot/loader_test.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,37 @@ func TestCreateMountNamespace(t *testing.T) {
304304
},
305305
expectedPaths: []string{"/proc", "/dev", "/dev/fd-foo", "/dev/foo", "/dev/bar", "/sys"},
306306
},
307+
{
308+
name: "mounts inside mandatory mounts",
309+
spec: specs.Spec{
310+
Root: &specs.Root{
311+
Path: os.TempDir(),
312+
Readonly: true,
313+
},
314+
Mounts: []specs.Mount{
315+
{
316+
Destination: "/proc",
317+
Type: "tmpfs",
318+
},
319+
// We don't include /sys, and /tmp in
320+
// the spec, since they will be added
321+
// automatically.
322+
//
323+
// Instead, add submounts inside these
324+
// directories and make sure they are
325+
// visible under the mandatory mounts.
326+
{
327+
Destination: "/sys/bar",
328+
Type: "tmpfs",
329+
},
330+
{
331+
Destination: "/tmp/baz",
332+
Type: "tmpfs",
333+
},
334+
},
335+
},
336+
expectedPaths: []string{"/proc", "/sys", "/sys/bar", "/tmp", "/tmp/baz"},
337+
},
307338
}
308339

309340
for _, tc := range testCases {
@@ -494,14 +525,14 @@ func TestRestoreEnvironment(t *testing.T) {
494525
},
495526
},
496527
"tmpfs": {
528+
{
529+
Dev: "none",
530+
},
497531
{
498532
Dev: "none",
499533
Flags: fs.MountSourceFlags{NoAtime: true},
500534
Data: "uid=1022",
501535
},
502-
{
503-
Dev: "none",
504-
},
505536
},
506537
"devtmpfs": {
507538
{
@@ -587,7 +618,7 @@ func TestRestoreEnvironment(t *testing.T) {
587618
}
588619
} else {
589620
if !reflect.DeepEqual(*actualRenv, tc.expectedRenv) {
590-
t.Fatalf("restore environments did not match for test:%s", tc.name)
621+
t.Fatalf("restore environments did not match for test:%s\ngot:%+v\nwant:%+v\n", tc.name, *actualRenv, tc.expectedRenv)
591622
}
592623
}
593624
}

0 commit comments

Comments
 (0)