github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/vfs/namespace.go (about)

     1  // Copyright 2023 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vfs
    16  
    17  import (
    18  	"github.com/MerlinKodo/gvisor/pkg/context"
    19  	"github.com/MerlinKodo/gvisor/pkg/errors/linuxerr"
    20  	"github.com/MerlinKodo/gvisor/pkg/refs"
    21  	"github.com/MerlinKodo/gvisor/pkg/sentry/kernel/auth"
    22  )
    23  
    24  // A MountNamespace is a collection of Mounts.//
    25  // MountNamespaces are reference-counted. Unless otherwise specified, all
    26  // MountNamespace methods require that a reference is held.
    27  //
    28  // MountNamespace is analogous to Linux's struct mnt_namespace.
    29  //
    30  // +stateify savable
    31  type MountNamespace struct {
    32  	// Refs is the reference count for this mount namespace.
    33  	Refs refs.TryRefCounter
    34  
    35  	// Owner is the usernamespace that owns this mount namespace.
    36  	Owner *auth.UserNamespace
    37  
    38  	// root is the MountNamespace's root mount.
    39  	root *Mount
    40  
    41  	// mountpoints maps all Dentries which are mount points in this namespace
    42  	// to the number of Mounts for which they are mount points. mountpoints is
    43  	// protected by VirtualFilesystem.mountMu.
    44  	//
    45  	// mountpoints is used to determine if a Dentry can be moved or removed
    46  	// (which requires that the Dentry is not a mount point in the calling
    47  	// namespace).
    48  	//
    49  	// mountpoints is maintained even if there are no references held on the
    50  	// MountNamespace; this is required to ensure that
    51  	// VFS.PrepareDeleteDentry() and VFS.PrepareRemoveDentry() operate
    52  	// correctly on unreferenced MountNamespaces.
    53  	mountpoints map[*Dentry]uint32
    54  
    55  	// mounts is the total number of mounts in this mount namespace.
    56  	mounts uint32
    57  }
    58  
    59  // Namespace is the namespace interface.
    60  type Namespace interface {
    61  	Type() string
    62  	Destroy(ctx context.Context)
    63  }
    64  
    65  // NewMountNamespace returns a new mount namespace with a root filesystem
    66  // configured by the given arguments. A reference is taken on the returned
    67  // MountNamespace.
    68  //
    69  // If nsfs is nil, the default reference counter is used.
    70  func (vfs *VirtualFilesystem) NewMountNamespace(
    71  	ctx context.Context,
    72  	creds *auth.Credentials,
    73  	source, fsTypeName string,
    74  	opts *MountOptions,
    75  	nsfs NamespaceInodeGetter,
    76  ) (*MountNamespace, error) {
    77  	rft := vfs.getFilesystemType(fsTypeName)
    78  	if rft == nil {
    79  		ctx.Warningf("Unknown filesystem type: %s", fsTypeName)
    80  		return nil, linuxerr.ENODEV
    81  	}
    82  	fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return vfs.NewMountNamespaceFrom(ctx, creds, fs, root, opts, nsfs), nil
    87  }
    88  
    89  type namespaceDefaultRefs struct {
    90  	namespaceRefs
    91  	destroy func(ctx context.Context)
    92  }
    93  
    94  func (r *namespaceDefaultRefs) DecRef(ctx context.Context) {
    95  	r.namespaceRefs.DecRef(
    96  		func() {
    97  			r.destroy(ctx)
    98  		},
    99  	)
   100  }
   101  
   102  // NewMountNamespaceFrom constructs a new mount namespace from an existing
   103  // filesystem and its root dentry. This is similar to NewMountNamespace, but
   104  // uses an existing filesystem instead of constructing a new one.
   105  func (vfs *VirtualFilesystem) NewMountNamespaceFrom(
   106  	ctx context.Context,
   107  	creds *auth.Credentials,
   108  	fs *Filesystem,
   109  	root *Dentry,
   110  	opts *MountOptions,
   111  	nsfs NamespaceInodeGetter,
   112  ) *MountNamespace {
   113  	mntns := &MountNamespace{
   114  		Owner:       creds.UserNamespace,
   115  		mountpoints: make(map[*Dentry]uint32),
   116  	}
   117  	if nsfs == nil {
   118  		refs := &namespaceDefaultRefs{destroy: mntns.Destroy}
   119  		refs.InitRefs()
   120  		mntns.Refs = refs
   121  	} else {
   122  		mntns.Refs = nsfs.GetNamespaceInode(ctx, mntns)
   123  	}
   124  	mntns.root = newMount(vfs, fs, root, mntns, opts)
   125  	return mntns
   126  }
   127  
   128  type cloneEntry struct {
   129  	prevMount   *Mount
   130  	parentMount *Mount
   131  }
   132  
   133  // +checklocks:vfs.mountMu
   134  func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) {
   135  	if root.mount == src {
   136  		vfs.delayDecRef(root.mount)
   137  		root.mount = dst
   138  		root.mount.IncRef()
   139  	}
   140  	if cwd.mount == src {
   141  		vfs.delayDecRef(cwd.mount)
   142  		cwd.mount = dst
   143  		cwd.mount.IncRef()
   144  	}
   145  	for srcChild := range src.children {
   146  		dstChild := vfs.mounts.Lookup(dst, srcChild.point())
   147  		vfs.updateRootAndCWD(ctx, root, cwd, srcChild, dstChild)
   148  	}
   149  }
   150  
   151  // NamespaceInodeGetter is an interface that provides the GetNamespaceInode method.
   152  type NamespaceInodeGetter interface {
   153  	GetNamespaceInode(ctx context.Context, ns Namespace) refs.TryRefCounter
   154  }
   155  
   156  // CloneMountNamespace makes a copy of the specified mount namespace.
   157  //
   158  // If `root` or `cwd` have mounts in the old namespace, they will be replaced
   159  // with proper mounts from the new namespace.
   160  func (vfs *VirtualFilesystem) CloneMountNamespace(
   161  	ctx context.Context,
   162  	creds *auth.Credentials,
   163  	ns *MountNamespace,
   164  	root *VirtualDentry,
   165  	cwd *VirtualDentry,
   166  	nsfs NamespaceInodeGetter,
   167  ) (*MountNamespace, error) {
   168  	newns := &MountNamespace{
   169  		Owner:       creds.UserNamespace,
   170  		mountpoints: make(map[*Dentry]uint32),
   171  	}
   172  
   173  	newns.Refs = nsfs.GetNamespaceInode(ctx, newns)
   174  	vfs.lockMounts()
   175  	defer vfs.unlockMounts(ctx)
   176  
   177  	newRoot, err := vfs.cloneMountTree(ctx, ns.root, ns.root.root)
   178  	if err != nil {
   179  		newns.DecRef(ctx)
   180  		vfs.abortTree(ctx, newRoot)
   181  		return nil, err
   182  	}
   183  	newns.root = newRoot
   184  	newns.root.ns = newns
   185  	vfs.commitPendingTree(ctx, newRoot)
   186  	vfs.updateRootAndCWD(ctx, root, cwd, ns.root, newns.root)
   187  	return newns, nil
   188  }
   189  
   190  // Destroy implements nsfs.Namespace.Destroy.
   191  func (mntns *MountNamespace) Destroy(ctx context.Context) {
   192  	vfs := mntns.root.fs.VirtualFilesystem()
   193  	vfs.lockMounts()
   194  	vfs.mounts.seq.BeginWrite()
   195  	vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{
   196  		disconnectHierarchy: true,
   197  	})
   198  	vfs.mounts.seq.EndWrite()
   199  	vfs.unlockMounts(ctx)
   200  }
   201  
   202  // Type implements nsfs.Namespace.Type.
   203  func (mntns *MountNamespace) Type() string {
   204  	return "mnt"
   205  }
   206  
   207  // IncRef increments mntns' refcount.
   208  func (mntns *MountNamespace) IncRef() {
   209  	mntns.Refs.IncRef()
   210  }
   211  
   212  // DecRef decrements mntns' reference count.
   213  func (mntns *MountNamespace) DecRef(ctx context.Context) {
   214  	mntns.Refs.DecRef(ctx)
   215  }
   216  
   217  // TryIncRef attempts to increment mntns' reference count.
   218  func (mntns *MountNamespace) TryIncRef() bool {
   219  	return mntns.Refs.TryIncRef()
   220  }
   221  
   222  // Root returns mntns' root. If the root is over-mounted, it returns the top
   223  // mount.
   224  func (mntns *MountNamespace) Root(ctx context.Context) VirtualDentry {
   225  	vfs := mntns.root.fs.VirtualFilesystem()
   226  	vd := VirtualDentry{
   227  		mount:  mntns.root,
   228  		dentry: mntns.root.root,
   229  	}
   230  	vd.IncRef()
   231  	if !vd.dentry.isMounted() {
   232  		return vd
   233  	}
   234  	m := vfs.getMountAt(ctx, vd.mount, vd.dentry)
   235  	if m == nil {
   236  		return vd
   237  	}
   238  	vd.DecRef(ctx)
   239  	vd.mount = m
   240  	vd.dentry = m.root
   241  	vd.dentry.IncRef()
   242  	return vd
   243  }