Implement setns for mount namespaces

PiperOrigin-RevId: 552859231
This commit is contained in:
Andrei Vagin
2023-08-01 11:12:29 -07:00
committed by gVisor bot
parent 1f14891734
commit aa2c8c33c6
21 changed files with 369 additions and 205 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ type Accessor struct {
// NewAccessor returns an Accessor that supports creation of device special
// files in the devtmpfs instance registered with name fsTypeName in vfsObj.
func NewAccessor(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, fsTypeName string) (*Accessor, error) {
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "devtmpfs" /* source */, fsTypeName, &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "devtmpfs" /* source */, fsTypeName, &vfs.MountOptions{}, nil)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -48,7 +48,7 @@ func setupDevtmpfs(t *testing.T) (context.Context, *auth.Credentials, *vfs.Virtu
})
// Create a test mount namespace with devtmpfs mounted at "/dev".
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "tmpfs" /* source */, "tmpfs" /* fsTypeName */, &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "tmpfs" /* source */, "tmpfs" /* fsTypeName */, &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("failed to create tmpfs root mount: %v", err)
}
+1 -1
View File
@@ -38,7 +38,7 @@ func setup(t *testing.T) *testutil.System {
AllowUserMount: true,
})
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("NewMountNamespace(): %v", err)
}
+1 -1
View File
@@ -53,7 +53,7 @@ func newTestSystem(t *testing.T, rootFn RootDentryFn) *testutil.System {
v.MustRegisterFilesystemType("testfs", &fsType{rootFn: rootFn}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mns, err := v.NewMountNamespace(ctx, creds, "", "testfs", &vfs.MountOptions{})
mns, err := v.NewMountNamespace(ctx, creds, "", "testfs", &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("Failed to create testfs root mount: %v", err)
}
+3 -9
View File
@@ -86,7 +86,7 @@ type Inode struct {
inodeRefs
locks vfs.FileLocks
namespace Namespace
namespace vfs.Namespace
mnt *vfs.Mount
}
@@ -101,14 +101,8 @@ func (i *Inode) Keep() bool {
return false
}
// Namespace is the namespace interface.
type Namespace interface {
Type() string
Destroy(ctx context.Context)
}
// NewInode creates a new nsfs inode.
func NewInode(ctx context.Context, mnt *vfs.Mount, namespace Namespace) *Inode {
func NewInode(ctx context.Context, mnt *vfs.Mount, namespace vfs.Namespace) *Inode {
fs := mnt.Filesystem().Impl().(*filesystem)
creds := auth.CredentialsFromContext(ctx)
i := &Inode{
@@ -123,7 +117,7 @@ func NewInode(ctx context.Context, mnt *vfs.Mount, namespace Namespace) *Inode {
const nsfsMode = linux.S_IFREG | linux.ModeUserRead | linux.ModeGroupRead | linux.ModeOtherRead
// Namespace returns the namespace associated with the inode.
func (i *Inode) Namespace() Namespace {
func (i *Inode) Namespace() vfs.Namespace {
return i.namespace
}
+1
View File
@@ -73,6 +73,7 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
"net": fs.newTaskNetDir(ctx, task),
"ns": fs.newTaskOwnedDir(ctx, task, fs.NextIno(), 0511, map[string]kernfs.Inode{
"net": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWNET),
"mnt": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWNS),
"pid": fs.newPIDNamespaceSymlink(ctx, task, fs.NextIno()),
"user": fs.newFakeNamespaceSymlink(ctx, task, fs.NextIno(), "user"),
"ipc": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWIPC),
+12 -1
View File
@@ -1273,12 +1273,23 @@ func (fs *filesystem) newFakeNamespaceSymlink(ctx context.Context, task *kernel.
func (s *namespaceSymlink) getInode(t *kernel.Task) *nsfs.Inode {
switch s.nsType {
case linux.CLONE_NEWNET:
return t.GetNetworkNamespace().GetInode()
netns := t.GetNetworkNamespace()
if netns == nil {
return nil
}
return netns.GetInode()
case linux.CLONE_NEWIPC:
if ipcns := t.GetIPCNamespace(); ipcns != nil {
return ipcns.GetInode()
}
return nil
case linux.CLONE_NEWNS:
mntns := t.GetMountNamespace()
if mntns == nil {
return nil
}
inode, _ := mntns.Refs.(*nsfs.Inode)
return inode
default:
panic("unknown namespace")
}
+1 -1
View File
@@ -110,7 +110,7 @@ func setup(t *testing.T) *testutil.System {
AllowUserMount: true,
})
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", tmpfs.Name, &vfs.MountOptions{})
mntns, err := k.VFS().NewMountNamespace(ctx, creds, "", tmpfs.Name, &vfs.MountOptions{}, k)
if err != nil {
t.Fatalf("NewMountNamespace(): %v", err)
}
+1 -1
View File
@@ -38,7 +38,7 @@ func newTestSystem(t *testing.T) *testutil.System {
AllowUserMount: true,
})
mns, err := k.VFS().NewMountNamespace(ctx, creds, "", sys.Name, &vfs.MountOptions{})
mns, err := k.VFS().NewMountNamespace(ctx, creds, "", sys.Name, &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("Failed to create new mount namespace: %v", err)
}
+2 -2
View File
@@ -62,7 +62,7 @@ func BenchmarkTmpfsStat(b *testing.B) {
vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
b.Fatalf("failed to create tmpfs root mount: %v", err)
}
@@ -154,7 +154,7 @@ func BenchmarkTmpfsMountStat(b *testing.B) {
vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
b.Fatalf("failed to create tmpfs root mount: %v", err)
}
+1 -1
View File
@@ -158,7 +158,7 @@ func setup(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesy
vfsObj.MustRegisterFilesystemType("tmpfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("failed to create tmpfs root mount: %v", err)
}
+1 -1
View File
@@ -42,7 +42,7 @@ func newTmpfsRoot(ctx context.Context) (*vfs.VirtualFilesystem, vfs.VirtualDentr
vfsObj.MustRegisterFilesystemType("tmpfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
return nil, vfs.VirtualDentry{}, nil, fmt.Errorf("failed to create tmpfs root mount: %v", err)
}
+1 -1
View File
@@ -120,7 +120,7 @@ func TestGetExecUserHome(t *testing.T) {
vfsObj.MustRegisterFilesystemType("tmpfs", tmpfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{})
mns, err := vfsObj.NewMountNamespace(ctx, creds, "", "tmpfs", &vfs.MountOptions{}, nil)
if err != nil {
t.Fatalf("failed to create tmpfs root mount: %v", err)
}
+7 -7
View File
@@ -46,6 +46,7 @@ import (
"gvisor.dev/gvisor/pkg/eventchannel"
"gvisor.dev/gvisor/pkg/fspath"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
@@ -448,10 +449,9 @@ func (k *Kernel) Init(args InitKernelArgs) error {
return fmt.Errorf("failed to create nsfs filesystem: %v", err)
}
defer nsfsFilesystem.DecRef(ctx)
nsfsMount := k.vfs.NewDisconnectedMount(nsfsFilesystem, nil, &vfs.MountOptions{})
k.nsfsMount = nsfsMount
k.rootNetworkNamespace.SetInode(nsfs.NewInode(ctx, nsfsMount, k.rootNetworkNamespace))
k.rootIPCNamespace.SetInode(nsfs.NewInode(ctx, nsfsMount, k.rootIPCNamespace))
k.nsfsMount = k.vfs.NewDisconnectedMount(nsfsFilesystem, nil, &vfs.MountOptions{})
k.rootNetworkNamespace.SetInode(nsfs.NewInode(ctx, k.nsfsMount, k.rootNetworkNamespace))
k.rootIPCNamespace.SetInode(nsfs.NewInode(ctx, k.nsfsMount, k.rootIPCNamespace))
tmpfsOpts := vfs.GetFilesystemOptions{
InternalData: tmpfs.FilesystemOpts{
@@ -1621,9 +1621,9 @@ func (k *Kernel) PipeMount() *vfs.Mount {
return k.pipeMount
}
// NsfsMount returns the nsfs mount.
func (k *Kernel) NsfsMount() *vfs.Mount {
return k.nsfsMount
// GetNamespaceInode returns a new nsfs inode which serves as a reference counter for the namespace.
func (k *Kernel) GetNamespaceInode(ctx context.Context, ns vfs.Namespace) refs.TryRefCounter {
return nsfs.NewInode(ctx, k.nsfsMount, ns)
}
// ShmMount returns the tmpfs mount.
+10 -2
View File
@@ -783,14 +783,22 @@ func (t *Task) WithMuLocked(f func(*Task)) {
t.mu.Unlock()
}
// MountNamespace returns t's MountNamespace. A reference is taken on the
// returned mount namespace.
// MountNamespace returns t's MountNamespace.
func (t *Task) MountNamespace() *vfs.MountNamespace {
t.mu.Lock()
defer t.mu.Unlock()
return t.mountNamespace
}
// GetMountNamespace returns t's MountNamespace. A reference is taken on the
// returned mount namespace.
func (t *Task) GetMountNamespace() *vfs.MountNamespace {
t.mu.Lock()
defer t.mu.Unlock()
t.mountNamespace.IncRef()
return t.mountNamespace
}
// AbstractSockets returns t's AbstractSocketNamespace.
func (t *Task) AbstractSockets() *AbstractSocketNamespace {
return t.abstractSockets
+34 -2
View File
@@ -173,7 +173,7 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
mntns := t.mountNamespace
if args.Flags&linux.CLONE_NEWNS != 0 {
var err error
mntns, err = t.k.vfs.CloneMountNamespace(t, creds, mntns, &fsContext.root, &fsContext.cwd)
mntns, err = t.k.vfs.CloneMountNamespace(t, creds, mntns, &fsContext.root, &fsContext.cwd, t.k)
if err != nil {
return 0, nil, err
}
@@ -463,6 +463,38 @@ func (t *Task) Setns(fd *vfs.FileDescription, flags int32) error {
t.mu.Unlock()
oldNS.DecRef(t)
return nil
case *vfs.MountNamespace:
if flags != 0 && flags != linux.CLONE_NEWNS {
return linuxerr.EINVAL
}
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, ns.Owner) ||
!t.Credentials().HasCapability(linux.CAP_SYS_CHROOT) ||
!t.Credentials().HasCapability(linux.CAP_SYS_ADMIN) {
return linuxerr.EPERM
}
oldFSContext := t.fsContext
// The current task has to be an exclusive owner of its fs context.
if oldFSContext.ReadRefs() != 1 {
return linuxerr.EINVAL
}
fsContext := oldFSContext.Fork()
fsContext.root.DecRef(t)
fsContext.cwd.DecRef(t)
vd := ns.Root()
vd.IncRef()
fsContext.root = vd
vd.IncRef()
fsContext.cwd = vd
oldNS := t.mountNamespace
ns.IncRef()
t.mu.Lock()
t.mountNamespace = ns
t.fsContext = fsContext
t.mu.Unlock()
oldNS.DecRef(t)
oldFSContext.DecRef(t)
return nil
default:
return linuxerr.EINVAL
}
@@ -584,7 +616,7 @@ func (t *Task) Unshare(flags int32) error {
return linuxerr.EPERM
}
oldMountNS := t.mountNamespace
mntns, err := t.k.vfs.CloneMountNamespace(t, creds, oldMountNS, &t.fsContext.root, &t.fsContext.cwd)
mntns, err := t.k.vfs.CloneMountNamespace(t, creds, oldMountNS, &t.fsContext.root, &t.fsContext.cwd, t.k)
if err != nil {
return err
}
+2 -1
View File
@@ -93,7 +93,7 @@ go_template_instance(
name = "mount_namespace_refs",
out = "mount_namespace_refs.go",
package = "vfs",
prefix = "MountNamespace",
prefix = "namespace",
template = "//pkg/refs:refs_template",
types = {
"T": "MountNamespace",
@@ -145,6 +145,7 @@ go_library(
"mount.go",
"mount_namespace_refs.go",
"mount_unsafe.go",
"namespace.go",
"opath.go",
"options.go",
"pathname.go",
-171
View File
@@ -159,147 +159,6 @@ func (mnt *Mount) generateOptionalTags() string {
return optional
}
// A MountNamespace is a collection of Mounts.//
// MountNamespaces are reference-counted. Unless otherwise specified, all
// MountNamespace methods require that a reference is held.
//
// MountNamespace is analogous to Linux's struct mnt_namespace.
//
// +stateify savable
type MountNamespace struct {
MountNamespaceRefs
// Owner is the usernamespace that owns this mount namespace.
Owner *auth.UserNamespace
// root is the MountNamespace's root mount.
root *Mount
// mountpoints maps all Dentries which are mount points in this namespace
// to the number of Mounts for which they are mount points. mountpoints is
// protected by VirtualFilesystem.mountMu.
//
// mountpoints is used to determine if a Dentry can be moved or removed
// (which requires that the Dentry is not a mount point in the calling
// namespace).
//
// mountpoints is maintained even if there are no references held on the
// MountNamespace; this is required to ensure that
// VFS.PrepareDeleteDentry() and VFS.PrepareRemoveDentry() operate
// correctly on unreferenced MountNamespaces.
mountpoints map[*Dentry]uint32
// mounts is the total number of mounts in this mount namespace.
mounts uint32
}
// NewMountNamespace returns a new mount namespace with a root filesystem
// configured by the given arguments. A reference is taken on the returned
// MountNamespace.
func (vfs *VirtualFilesystem) NewMountNamespace(ctx context.Context, creds *auth.Credentials, source, fsTypeName string, opts *MountOptions) (*MountNamespace, error) {
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
ctx.Warningf("Unknown filesystem type: %s", fsTypeName)
return nil, linuxerr.ENODEV
}
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if err != nil {
return nil, err
}
return vfs.NewMountNamespaceFrom(ctx, creds, fs, root, opts), nil
}
// NewMountNamespaceFrom constructs a new mount namespace from an existing
// filesystem and its root dentry. This is similar to NewMountNamespace, but
// uses an existing filesystem instead of constructing a new one.
func (vfs *VirtualFilesystem) NewMountNamespaceFrom(ctx context.Context, creds *auth.Credentials, fs *Filesystem, root *Dentry, opts *MountOptions) *MountNamespace {
mntns := &MountNamespace{
Owner: creds.UserNamespace,
mountpoints: make(map[*Dentry]uint32),
}
mntns.InitRefs()
mntns.root = newMount(vfs, fs, root, mntns, opts)
return mntns
}
type cloneEntry struct {
prevMount *Mount
parentMount *Mount
}
func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) {
if root.mount == src {
root.mount.DecRef(ctx)
root.mount = dst
root.mount.IncRef()
}
if cwd.mount == src {
cwd.mount.DecRef(ctx)
cwd.mount = dst
cwd.mount.IncRef()
}
}
// CloneMountNamespace makes a copy of the specified mount namespace.
//
// If `root` or `cwd` have mounts in the old namespace, they will be replaced
// with proper mounts from the new namespace.
func (vfs *VirtualFilesystem) CloneMountNamespace(ctx context.Context, creds *auth.Credentials, ns *MountNamespace, root *VirtualDentry, cwd *VirtualDentry) (*MountNamespace, error) {
newns := &MountNamespace{
Owner: creds.UserNamespace,
mountpoints: make(map[*Dentry]uint32),
}
newns.InitRefs()
vdsToDecRef := []VirtualDentry{}
defer func() {
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
}()
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
ns.root.root.IncRef()
ns.root.fs.IncRef()
newns.root = newMount(vfs, ns.root.fs, ns.root.root, newns, &MountOptions{Flags: ns.root.Flags, ReadOnly: ns.root.ReadOnly()})
if ns.root.propType == Shared {
vfs.addPeer(ns.root, newns.root)
}
vfs.updateRootAndCWD(ctx, root, cwd, ns.root, newns.root)
queue := []cloneEntry{cloneEntry{ns.root, newns.root}}
for len(queue) != 0 {
p := queue[0]
queue = queue[1:]
for c := range p.prevMount.children {
m := vfs.cloneMount(c, c.root, nil)
vd := VirtualDentry{
mount: p.parentMount,
dentry: c.point(),
}
vd.IncRef()
vds, err := vfs.connectMountAtLocked(ctx, m, vd)
m.DecRef(ctx)
vdsToDecRef = append(vdsToDecRef, vds...)
if err != nil {
newns.DecRef(ctx)
return nil, err
}
if c.propType == Shared {
vfs.addPeer(c, m)
}
vfs.updateRootAndCWD(ctx, root, cwd, c, m)
if len(c.children) != 0 {
queue = append(queue, cloneEntry{c, m})
}
}
}
return newns, nil
}
// NewFilesystem creates a new filesystem object not yet associated with any
// mounts. It can be installed into the filesystem tree with ConnectMountAt.
// Note that only the filesystem-specific mount options from opts are used by
@@ -852,26 +711,6 @@ func (mnt *Mount) LogRefs() bool {
return false
}
// DecRef decrements mntns' reference count.
func (mntns *MountNamespace) DecRef(ctx context.Context) {
vfs := mntns.root.fs.VirtualFilesystem()
mntns.MountNamespaceRefs.DecRef(func() {
vfs.mountMu.Lock()
vfs.mounts.seq.BeginWrite()
vdsToDecRef, mountsToDecRef := vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{
disconnectHierarchy: true,
}, nil, nil)
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
for _, mnt := range mountsToDecRef {
mnt.DecRef(ctx)
}
})
}
// getMountAt returns the last Mount in the stack mounted at (mnt, d). It takes
// a reference on the returned Mount. If (mnt, d) is not a mount point,
// getMountAt returns nil.
@@ -1158,16 +997,6 @@ func (mnt *Mount) Root() *Dentry {
return mnt.root
}
// Root returns mntns' root. It does not take a reference on the returned
// Dentry.
func (mntns *MountNamespace) Root() VirtualDentry {
vd := VirtualDentry{
mount: mntns.root,
dentry: mntns.root.root,
}
return vd
}
// GenerateProcMounts emits the contents of /proc/[pid]/mounts for vfs to buf.
//
// Preconditions: taskRootDir.Ok().
+264
View File
@@ -0,0 +1,264 @@
// Copyright 2023 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 vfs
import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
)
// A MountNamespace is a collection of Mounts.//
// MountNamespaces are reference-counted. Unless otherwise specified, all
// MountNamespace methods require that a reference is held.
//
// MountNamespace is analogous to Linux's struct mnt_namespace.
//
// +stateify savable
type MountNamespace struct {
// Refs is the reference count for this mount namespace.
Refs refs.TryRefCounter
// Owner is the usernamespace that owns this mount namespace.
Owner *auth.UserNamespace
// root is the MountNamespace's root mount.
root *Mount
// mountpoints maps all Dentries which are mount points in this namespace
// to the number of Mounts for which they are mount points. mountpoints is
// protected by VirtualFilesystem.mountMu.
//
// mountpoints is used to determine if a Dentry can be moved or removed
// (which requires that the Dentry is not a mount point in the calling
// namespace).
//
// mountpoints is maintained even if there are no references held on the
// MountNamespace; this is required to ensure that
// VFS.PrepareDeleteDentry() and VFS.PrepareRemoveDentry() operate
// correctly on unreferenced MountNamespaces.
mountpoints map[*Dentry]uint32
// mounts is the total number of mounts in this mount namespace.
mounts uint32
}
// Namespace is the namespace interface.
type Namespace interface {
Type() string
Destroy(ctx context.Context)
}
// NewMountNamespace returns a new mount namespace with a root filesystem
// configured by the given arguments. A reference is taken on the returned
// MountNamespace.
//
// If nsfs is nil, the default reference counter is used.
func (vfs *VirtualFilesystem) NewMountNamespace(
ctx context.Context,
creds *auth.Credentials,
source, fsTypeName string,
opts *MountOptions,
nsfs NamespaceInodeGetter,
) (*MountNamespace, error) {
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
ctx.Warningf("Unknown filesystem type: %s", fsTypeName)
return nil, linuxerr.ENODEV
}
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if err != nil {
return nil, err
}
return vfs.NewMountNamespaceFrom(ctx, creds, fs, root, opts, nsfs), nil
}
type namespaceDefaultRefs struct {
namespaceRefs
destroy func(ctx context.Context)
}
func (r *namespaceDefaultRefs) DecRef(ctx context.Context) {
r.namespaceRefs.DecRef(
func() {
r.destroy(ctx)
},
)
}
// NewMountNamespaceFrom constructs a new mount namespace from an existing
// filesystem and its root dentry. This is similar to NewMountNamespace, but
// uses an existing filesystem instead of constructing a new one.
func (vfs *VirtualFilesystem) NewMountNamespaceFrom(
ctx context.Context,
creds *auth.Credentials,
fs *Filesystem,
root *Dentry,
opts *MountOptions,
nsfs NamespaceInodeGetter,
) *MountNamespace {
mntns := &MountNamespace{
Owner: creds.UserNamespace,
mountpoints: make(map[*Dentry]uint32),
}
if nsfs == nil {
refs := &namespaceDefaultRefs{destroy: mntns.Destroy}
refs.InitRefs()
mntns.Refs = refs
} else {
mntns.Refs = nsfs.GetNamespaceInode(ctx, mntns)
}
mntns.root = newMount(vfs, fs, root, mntns, opts)
return mntns
}
type cloneEntry struct {
prevMount *Mount
parentMount *Mount
}
func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) {
if root.mount == src {
root.mount.DecRef(ctx)
root.mount = dst
root.mount.IncRef()
}
if cwd.mount == src {
cwd.mount.DecRef(ctx)
cwd.mount = dst
cwd.mount.IncRef()
}
}
// NamespaceInodeGetter is an interface that provides the GetNamespaceInode method.
type NamespaceInodeGetter interface {
GetNamespaceInode(ctx context.Context, ns Namespace) refs.TryRefCounter
}
// CloneMountNamespace makes a copy of the specified mount namespace.
//
// If `root` or `cwd` have mounts in the old namespace, they will be replaced
// with proper mounts from the new namespace.
func (vfs *VirtualFilesystem) CloneMountNamespace(
ctx context.Context,
creds *auth.Credentials,
ns *MountNamespace,
root *VirtualDentry,
cwd *VirtualDentry,
nsfs NamespaceInodeGetter,
) (*MountNamespace, error) {
newns := &MountNamespace{
Owner: creds.UserNamespace,
mountpoints: make(map[*Dentry]uint32),
}
newns.Refs = nsfs.GetNamespaceInode(ctx, newns)
vdsToDecRef := []VirtualDentry{}
defer func() {
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
}()
vfs.mountMu.Lock()
defer vfs.mountMu.Unlock()
ns.root.root.IncRef()
ns.root.fs.IncRef()
newns.root = newMount(vfs, ns.root.fs, ns.root.root, newns, &MountOptions{Flags: ns.root.Flags, ReadOnly: ns.root.ReadOnly()})
if ns.root.propType == Shared {
vfs.addPeer(ns.root, newns.root)
}
vfs.updateRootAndCWD(ctx, root, cwd, ns.root, newns.root)
queue := []cloneEntry{cloneEntry{ns.root, newns.root}}
for len(queue) != 0 {
p := queue[0]
queue = queue[1:]
for c := range p.prevMount.children {
m := vfs.cloneMount(c, c.root, nil)
vd := VirtualDentry{
mount: p.parentMount,
dentry: c.point(),
}
vd.IncRef()
vds, err := vfs.connectMountAtLocked(ctx, m, vd)
m.DecRef(ctx)
vdsToDecRef = append(vdsToDecRef, vds...)
if err != nil {
newns.DecRef(ctx)
return nil, err
}
if c.propType == Shared {
vfs.addPeer(c, m)
}
vfs.updateRootAndCWD(ctx, root, cwd, c, m)
if len(c.children) != 0 {
queue = append(queue, cloneEntry{c, m})
}
}
}
return newns, nil
}
// Destroy implements nsfs.Namespace.Destroy.
func (mntns *MountNamespace) Destroy(ctx context.Context) {
vfs := mntns.root.fs.VirtualFilesystem()
vfs.mountMu.Lock()
vfs.mounts.seq.BeginWrite()
vdsToDecRef, mountsToDecRef := vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{
disconnectHierarchy: true,
}, nil, nil)
vfs.mounts.seq.EndWrite()
vfs.mountMu.Unlock()
for _, vd := range vdsToDecRef {
vd.DecRef(ctx)
}
for _, mnt := range mountsToDecRef {
mnt.DecRef(ctx)
}
}
// Type implements nsfs.Namespace.Type.
func (mntns *MountNamespace) Type() string {
return "mnt"
}
// IncRef increments mntns' refcount.
func (mntns *MountNamespace) IncRef() {
mntns.Refs.IncRef()
}
// DecRef decrements mntns' reference count.
func (mntns *MountNamespace) DecRef(ctx context.Context) {
mntns.Refs.DecRef(ctx)
}
// TryIncRef attempts to increment mntns' reference count.
func (mntns *MountNamespace) TryIncRef() bool {
return mntns.Refs.TryIncRef()
}
// Root returns mntns' root. It does not take a reference on the returned
// Dentry.
func (mntns *MountNamespace) Root() VirtualDentry {
vd := VirtualDentry{
mount: mntns.root,
dentry: mntns.root.root,
}
return vd
}
+1 -1
View File
@@ -504,7 +504,7 @@ func (c *containerMounter) createMountNamespace(ctx context.Context, conf *confi
fsName = overlay.Name
}
mns, err := c.k.VFS().NewMountNamespace(ctx, creds, "", fsName, opts)
mns, err := c.k.VFS().NewMountNamespace(ctx, creds, "", fsName, opts, c.k)
if err != nil {
return nil, fmt.Errorf("setting up mount namespace: %w", err)
}

Some files were not shown because too many files have changed in this diff Show More