From f4b851067a3ad746990a73d9d772717e32394a25 Mon Sep 17 00:00:00 2001 From: Jamie Liu Date: Thu, 7 Dec 2023 23:07:37 -0800 Subject: [PATCH] Un-delete devtmpfs. This partially reverts cl/578969556, but implements devtmpfs using fsimpl/dev (added by that CL). Note that the behavior of "devtmpfs" differs compared to before cl/578969556. Container runtime specs typically specify a tmpfs mount at /dev, which the container runtime is required to prepopulate with certain device files (https://github.com/opencontainers/runtime-spec/blob/main/config-linux.md#default-devices). Prior to cl/578969556, runsc did so by silently replacing tmpfs mounts at /dev with devtmpfs mounts. After that CL, runsc does so by silently replacing tmpfs mounts at /dev with fsimpl/dev mounts. The distinction is that devtmpfs is a singleton whereas fsimpl/dev is not, so in a multi-container sandbox, all containers shared the contents of /dev before cl/578969556, but not after that CL; this change was necessary due to differences in GPU/TPU accessibility between containers, and is also more consistent with runc (which also creates separate tmpfs mounts for /dev as specified). As a side effect, explicit devtmpfs mounts shared the same filesystem as every container's /dev before cl/578969556. Since the change to container /dev won't be reverted, our options now are: - All devtmpfs mounts share a single filesystem, which is distinct from any container's /dev. - Each devtmpfs mount gets a distinct filesystem. This CL chooses the former option for greater similarity to Linux devtmpfs semantics. (In runc, explicit devtmpfs mounts share a single filesystem, which is the *host's* /dev.) PiperOrigin-RevId: 589021563 --- pkg/sentry/fsimpl/devtmpfs/BUILD | 21 ++++++ pkg/sentry/fsimpl/devtmpfs/devtmpfs.go | 74 ++++++++++++++++++++++ pkg/sentry/fsimpl/devtmpfs/save_restore.go | 23 +++++++ runsc/boot/BUILD | 1 + runsc/boot/vfs.go | 5 ++ 5 files changed, 124 insertions(+) create mode 100644 pkg/sentry/fsimpl/devtmpfs/BUILD create mode 100644 pkg/sentry/fsimpl/devtmpfs/devtmpfs.go create mode 100644 pkg/sentry/fsimpl/devtmpfs/save_restore.go diff --git a/pkg/sentry/fsimpl/devtmpfs/BUILD b/pkg/sentry/fsimpl/devtmpfs/BUILD new file mode 100644 index 000000000..886ae937f --- /dev/null +++ b/pkg/sentry/fsimpl/devtmpfs/BUILD @@ -0,0 +1,21 @@ +load("//tools:defs.bzl", "go_library") + +package(default_applicable_licenses = ["//:license"]) + +licenses(["notice"]) + +go_library( + name = "devtmpfs", + srcs = [ + "devtmpfs.go", + "save_restore.go", + ], + visibility = ["//pkg/sentry:internal"], + deps = [ + "//pkg/context", + "//pkg/sentry/fsimpl/dev", + "//pkg/sentry/kernel/auth", + "//pkg/sentry/vfs", + "//pkg/sync", + ], +) diff --git a/pkg/sentry/fsimpl/devtmpfs/devtmpfs.go b/pkg/sentry/fsimpl/devtmpfs/devtmpfs.go new file mode 100644 index 000000000..e1ab3cfee --- /dev/null +++ b/pkg/sentry/fsimpl/devtmpfs/devtmpfs.go @@ -0,0 +1,74 @@ +// Copyright 2020 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package devtmpfs provides a singleton fsimpl/dev filesystem instance, +// analogous to Linux's devtmpfs. +package devtmpfs + +import ( + "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/dev" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/vfs" + "gvisor.dev/gvisor/pkg/sync" +) + +// Name is the default filesystem name. +const Name = "devtmpfs" + +// FilesystemType implements vfs.FilesystemType. +// +// +stateify savable +type FilesystemType struct { + initOnce sync.Once `state:"nosave"` + initErr error + + // fs is the tmpfs filesystem that backs all mounts of this FilesystemType. + // root is fs' root. fs and root are immutable. + fs *vfs.Filesystem + root *vfs.Dentry +} + +// Name implements vfs.FilesystemType.Name. +func (*FilesystemType) Name() string { + return Name +} + +// GetFilesystem implements vfs.FilesystemType.GetFilesystem. +func (fst *FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) { + fst.initOnce.Do(func() { + fs, root, err := dev.FilesystemType{}.GetFilesystem(ctx, vfsObj, creds, source, opts) + if err != nil { + fst.initErr = err + return + } + fst.fs = fs + fst.root = root + }) + if fst.initErr != nil { + return nil, nil, fst.initErr + } + fst.fs.IncRef() + fst.root.IncRef() + return fst.fs, fst.root, nil +} + +// Release implements vfs.FilesystemType.Release. +func (fst *FilesystemType) Release(ctx context.Context) { + if fst.fs != nil { + // Release the original reference obtained when creating the filesystem. + fst.root.DecRef(ctx) + fst.fs.DecRef(ctx) + } +} diff --git a/pkg/sentry/fsimpl/devtmpfs/save_restore.go b/pkg/sentry/fsimpl/devtmpfs/save_restore.go new file mode 100644 index 000000000..28832d850 --- /dev/null +++ b/pkg/sentry/fsimpl/devtmpfs/save_restore.go @@ -0,0 +1,23 @@ +// Copyright 2020 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package devtmpfs + +// afterLoad is invoked by stateify. +func (fst *FilesystemType) afterLoad() { + if fst.fs != nil { + // Ensure that we don't create another filesystem. + fst.initOnce.Do(func() {}) + } +} diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD index 8f905d461..8a2d1aa6c 100644 --- a/runsc/boot/BUILD +++ b/runsc/boot/BUILD @@ -64,6 +64,7 @@ go_library( "//pkg/sentry/fsimpl/cgroupfs", "//pkg/sentry/fsimpl/dev", "//pkg/sentry/fsimpl/devpts", + "//pkg/sentry/fsimpl/devtmpfs", "//pkg/sentry/fsimpl/erofs", "//pkg/sentry/fsimpl/fuse", "//pkg/sentry/fsimpl/gofer", diff --git a/runsc/boot/vfs.go b/runsc/boot/vfs.go index 7a0e9a508..f00767854 100644 --- a/runsc/boot/vfs.go +++ b/runsc/boot/vfs.go @@ -44,6 +44,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/fsimpl/cgroupfs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/dev" "gvisor.dev/gvisor/pkg/sentry/fsimpl/devpts" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/devtmpfs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/erofs" "gvisor.dev/gvisor/pkg/sentry/fsimpl/fuse" "gvisor.dev/gvisor/pkg/sentry/fsimpl/gofer" @@ -108,6 +109,10 @@ func registerFilesystems(k *kernel.Kernel, info *containerInfo) error { AllowUserMount: true, }) vfsObj.MustRegisterFilesystemType(dev.Name, &dev.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{}) + vfsObj.MustRegisterFilesystemType(devtmpfs.Name, &devtmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ + AllowUserMount: true, + AllowUserList: true, + }) vfsObj.MustRegisterFilesystemType(erofs.Name, &erofs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{ AllowUserList: true, })