mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement setns for mount namespaces
PiperOrigin-RevId: 552859231
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user