mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Split VFS.MountAt into VFS.MountDisconnected and VFS.ConnectMountAt.
This makes it straightforward to create bind mounts internally in VFS2: Given a bind mount root represented by vfs.VirtualDentry vd: - Create a new mount with VFS.NewDisconnectedMount(vd.Mount().Filesystem(), vd.Dentry()). - Connect the resulting mount in the appropriate namespace with VFS.ConnectMountAt(). Note that the resulting bind mount is non-recursive; recursive bind mounting requires explicitly duplicating all children of the original mount, which is best handled internally by VFS. Updates #179 PiperOrigin-RevId: 313703963
This commit is contained in:
+41
-15
@@ -28,9 +28,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// lastMountID is used to allocate mount ids. Must be accessed atomically.
|
||||
var lastMountID uint64
|
||||
|
||||
// A Mount is a replacement of a Dentry (Mount.key.point) from one Filesystem
|
||||
// (Mount.key.parent.fs) with a Dentry (Mount.root) from another Filesystem
|
||||
// (Mount.fs), which applies to path resolution in the context of a particular
|
||||
@@ -97,7 +94,7 @@ type Mount struct {
|
||||
|
||||
func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *MountNamespace, opts *MountOptions) *Mount {
|
||||
mnt := &Mount{
|
||||
ID: atomic.AddUint64(&lastMountID, 1),
|
||||
ID: atomic.AddUint64(&vfs.lastMountID, 1),
|
||||
vfs: vfs,
|
||||
fs: fs,
|
||||
root: root,
|
||||
@@ -111,6 +108,16 @@ func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *Mount
|
||||
return mnt
|
||||
}
|
||||
|
||||
// Options returns a copy of the MountOptions currently applicable to mnt.
|
||||
func (mnt *Mount) Options() MountOptions {
|
||||
mnt.vfs.mountMu.Lock()
|
||||
defer mnt.vfs.mountMu.Unlock()
|
||||
return MountOptions{
|
||||
Flags: mnt.flags,
|
||||
ReadOnly: mnt.readOnly(),
|
||||
}
|
||||
}
|
||||
|
||||
// A MountNamespace is a collection of Mounts.
|
||||
//
|
||||
// MountNamespaces are reference-counted. Unless otherwise specified, all
|
||||
@@ -148,7 +155,7 @@ type MountNamespace struct {
|
||||
func (vfs *VirtualFilesystem) NewMountNamespace(ctx context.Context, creds *auth.Credentials, source, fsTypeName string, opts *GetFilesystemOptions) (*MountNamespace, error) {
|
||||
rft := vfs.getFilesystemType(fsTypeName)
|
||||
if rft == nil {
|
||||
ctx.Warningf("Unknown filesystem: %s", fsTypeName)
|
||||
ctx.Warningf("Unknown filesystem type: %s", fsTypeName)
|
||||
return nil, syserror.ENODEV
|
||||
}
|
||||
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, *opts)
|
||||
@@ -175,26 +182,34 @@ func (vfs *VirtualFilesystem) NewDisconnectedMount(fs *Filesystem, root *Dentry,
|
||||
return newMount(vfs, fs, root, nil /* mntns */, opts), nil
|
||||
}
|
||||
|
||||
// MountAt creates and mounts a Filesystem configured by the given arguments.
|
||||
func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentials, source string, target *PathOperation, fsTypeName string, opts *MountOptions) error {
|
||||
// MountDisconnected creates a Filesystem configured by the given arguments,
|
||||
// then returns a Mount representing it. The new Mount is not associated with
|
||||
// any MountNamespace and is not connected to any other Mounts.
|
||||
func (vfs *VirtualFilesystem) MountDisconnected(ctx context.Context, creds *auth.Credentials, source string, fsTypeName string, opts *MountOptions) (*Mount, error) {
|
||||
rft := vfs.getFilesystemType(fsTypeName)
|
||||
if rft == nil {
|
||||
return syserror.ENODEV
|
||||
return nil, syserror.ENODEV
|
||||
}
|
||||
if !opts.InternalMount && !rft.opts.AllowUserMount {
|
||||
return syserror.ENODEV
|
||||
return nil, syserror.ENODEV
|
||||
}
|
||||
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer root.DecRef()
|
||||
defer fs.DecRef()
|
||||
return vfs.NewDisconnectedMount(fs, root, opts)
|
||||
}
|
||||
|
||||
// ConnectMountAt connects mnt at the path represented by target.
|
||||
//
|
||||
// Preconditions: mnt must be disconnected.
|
||||
func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Credentials, mnt *Mount, target *PathOperation) error {
|
||||
// We can't hold vfs.mountMu while calling FilesystemImpl methods due to
|
||||
// lock ordering.
|
||||
vd, err := vfs.GetDentryAt(ctx, creds, target, &GetDentryOptions{})
|
||||
if err != nil {
|
||||
root.DecRef()
|
||||
fs.DecRef()
|
||||
return err
|
||||
}
|
||||
vfs.mountMu.Lock()
|
||||
@@ -204,8 +219,6 @@ func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentia
|
||||
vd.dentry.mu.Unlock()
|
||||
vfs.mountMu.Unlock()
|
||||
vd.DecRef()
|
||||
root.DecRef()
|
||||
fs.DecRef()
|
||||
return syserror.ENOENT
|
||||
}
|
||||
// vd might have been mounted over between vfs.GetDentryAt() and
|
||||
@@ -238,7 +251,6 @@ func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentia
|
||||
// point and the mount root are directories, or neither are, and returns
|
||||
// ENOTDIR if this is not the case.
|
||||
mntns := vd.mount.ns
|
||||
mnt := newMount(vfs, fs, root, mntns, opts)
|
||||
vfs.mounts.seq.BeginWrite()
|
||||
vfs.connectLocked(mnt, vd, mntns)
|
||||
vfs.mounts.seq.EndWrite()
|
||||
@@ -247,6 +259,19 @@ func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentia
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountAt creates and mounts a Filesystem configured by the given arguments.
|
||||
func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentials, source string, target *PathOperation, fsTypeName string, opts *MountOptions) error {
|
||||
mnt, err := vfs.MountDisconnected(ctx, creds, source, fsTypeName, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := vfs.ConnectMountAt(ctx, creds, mnt, target); err != nil {
|
||||
mnt.DecRef()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UmountAt removes the Mount at the given path.
|
||||
func (vfs *VirtualFilesystem) UmountAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, opts *UmountOptions) error {
|
||||
if opts.Flags&^(linux.MNT_FORCE|linux.MNT_DETACH) != 0 {
|
||||
@@ -377,6 +402,7 @@ func (vfs *VirtualFilesystem) connectLocked(mnt *Mount, vd VirtualDentry, mntns
|
||||
}
|
||||
vd.mount.children[mnt] = struct{}{}
|
||||
atomic.AddUint32(&vd.dentry.mounts, 1)
|
||||
mnt.ns = mntns
|
||||
mntns.mountpoints[vd.dentry]++
|
||||
vfs.mounts.insertSeqed(mnt)
|
||||
vfsmpmounts, ok := vfs.mountpoints[vd.dentry]
|
||||
|
||||
@@ -82,6 +82,10 @@ type VirtualFilesystem struct {
|
||||
// mountpoints is analogous to Linux's mountpoint_hashtable.
|
||||
mountpoints map[*Dentry]map[*Mount]struct{}
|
||||
|
||||
// lastMountID is the last allocated mount ID. lastMountID is accessed
|
||||
// using atomic memory operations.
|
||||
lastMountID uint64
|
||||
|
||||
// anonMount is a Mount, not included in mounts or mountpoints,
|
||||
// representing an anonFilesystem. anonMount is used to back
|
||||
// VirtualDentries returned by VirtualFilesystem.NewAnonVirtualDentry().
|
||||
|
||||
Reference in New Issue
Block a user