mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Abstract mount tree cloning into its own mount method.
When copying a mount tree, new child mounts are added to a mount's pending list. Each of these mounts has its pending mountpoint already set with setKey. Eventually this method will be used to enable recursive binds. PiperOrigin-RevId: 563236298
This commit is contained in:
committed by
gVisor bot
parent
2319f958fc
commit
6ceceae938
@@ -108,6 +108,10 @@ type Mount struct {
|
|||||||
// Mount.EndWrite(). The MSB of writers is set if MS_RDONLY is in effect.
|
// Mount.EndWrite(). The MSB of writers is set if MS_RDONLY is in effect.
|
||||||
// writers is accessed using atomic memory operations.
|
// writers is accessed using atomic memory operations.
|
||||||
writers atomicbitops.Int64
|
writers atomicbitops.Int64
|
||||||
|
|
||||||
|
// pendingChildren is a list of new child mounts that have not yet been
|
||||||
|
// connected to this mount as the parent.
|
||||||
|
pendingChildren []*Mount
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *MountNamespace, opts *MountOptions) *Mount {
|
func newMount(vfs *VirtualFilesystem, fs *Filesystem, root *Dentry, mntns *MountNamespace, opts *MountOptions) *Mount {
|
||||||
@@ -304,6 +308,39 @@ func (vfs *VirtualFilesystem) cloneMount(mnt *Mount, root *Dentry, mopts *MountO
|
|||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cloneTreeNode struct {
|
||||||
|
prevMount *Mount
|
||||||
|
parentMount *Mount
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneMountTree creates a copy of mnt's tree with the specified root
|
||||||
|
// dentry at root. The new descendents are added to mnt's pending mount list.
|
||||||
|
//
|
||||||
|
// +checklocks:vfs.mountMu
|
||||||
|
func (vfs *VirtualFilesystem) cloneMountTree(ctx context.Context, mnt *Mount, root *Dentry) (*Mount, error) {
|
||||||
|
clone := vfs.cloneMount(mnt, root, nil)
|
||||||
|
queue := []cloneTreeNode{{mnt, clone}}
|
||||||
|
for len(queue) != 0 {
|
||||||
|
p := queue[len(queue)-1]
|
||||||
|
queue = queue[:len(queue)-1]
|
||||||
|
for c := range p.prevMount.children {
|
||||||
|
m := vfs.cloneMount(c, c.root, nil)
|
||||||
|
vfs.delayDecRef(m)
|
||||||
|
mp := VirtualDentry{
|
||||||
|
mount: p.parentMount,
|
||||||
|
dentry: c.point(),
|
||||||
|
}
|
||||||
|
mp.IncRef()
|
||||||
|
m.setKey(mp)
|
||||||
|
p.parentMount.pendingChildren = append(p.parentMount.pendingChildren, m)
|
||||||
|
if len(c.children) != 0 {
|
||||||
|
queue = append(queue, cloneTreeNode{c, m})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clone, nil
|
||||||
|
}
|
||||||
|
|
||||||
// BindAt creates a clone of the source path's parent mount and mounts it at
|
// BindAt creates a clone of the source path's parent mount and mounts it at
|
||||||
// the target path. The new mount's root dentry is one pointed to by the source
|
// the target path. The new mount's root dentry is one pointed to by the source
|
||||||
// path.
|
// path.
|
||||||
|
|||||||
+30
-44
@@ -15,6 +15,8 @@
|
|||||||
package vfs
|
package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"gvisor.dev/gvisor/pkg/context"
|
"gvisor.dev/gvisor/pkg/context"
|
||||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||||
"gvisor.dev/gvisor/pkg/refs"
|
"gvisor.dev/gvisor/pkg/refs"
|
||||||
@@ -125,22 +127,28 @@ func (vfs *VirtualFilesystem) NewMountNamespaceFrom(
|
|||||||
return mntns
|
return mntns
|
||||||
}
|
}
|
||||||
|
|
||||||
type cloneEntry struct {
|
|
||||||
prevMount *Mount
|
|
||||||
parentMount *Mount
|
|
||||||
}
|
|
||||||
|
|
||||||
// +checklocks:vfs.mountMu
|
// +checklocks:vfs.mountMu
|
||||||
func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) {
|
func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, srcRoot *Mount, dstRoot *Mount) {
|
||||||
if root.mount == src {
|
// The mount trees are exact copies of each other so submountsLocked will
|
||||||
vfs.delayDecRef(root.mount)
|
// return corresponding mounts in the same order.
|
||||||
root.mount = dst
|
srcMounts := srcRoot.submountsLocked()
|
||||||
root.mount.IncRef()
|
dstMounts := dstRoot.submountsLocked()
|
||||||
|
if len(srcMounts) != len(dstMounts) {
|
||||||
|
panic(fmt.Sprintf("mount trees are not the same size: len(srcTree) = %d, len(dstTree) = %d", len(srcMounts), len(dstMounts)))
|
||||||
}
|
}
|
||||||
if cwd.mount == src {
|
for i := 0; i < len(srcMounts); i++ {
|
||||||
vfs.delayDecRef(cwd.mount)
|
old := srcMounts[i]
|
||||||
cwd.mount = dst
|
new := dstMounts[i]
|
||||||
cwd.mount.IncRef()
|
if root.mount == old {
|
||||||
|
vfs.delayDecRef(root.mount)
|
||||||
|
root.mount = new
|
||||||
|
root.mount.IncRef()
|
||||||
|
}
|
||||||
|
if cwd.mount == old {
|
||||||
|
vfs.delayDecRef(cwd.mount)
|
||||||
|
cwd.mount = new
|
||||||
|
cwd.mount.IncRef()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,38 +178,16 @@ func (vfs *VirtualFilesystem) CloneMountNamespace(
|
|||||||
vfs.lockMounts()
|
vfs.lockMounts()
|
||||||
defer vfs.unlockMounts(ctx)
|
defer vfs.unlockMounts(ctx)
|
||||||
|
|
||||||
ns.root.root.IncRef()
|
newRoot, err := vfs.cloneMountTree(ctx, ns.root, ns.root.root)
|
||||||
ns.root.fs.IncRef()
|
if err != nil {
|
||||||
newns.root = newMount(vfs, ns.root.fs, ns.root.root, newns, &MountOptions{Flags: ns.root.Flags, ReadOnly: ns.root.ReadOnly()})
|
newns.DecRef(ctx)
|
||||||
if ns.root.isShared {
|
vfs.abortTree(ctx, newRoot)
|
||||||
vfs.addPeer(ns.root, newns.root)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
newns.root = newRoot
|
||||||
|
newns.root.ns = newns
|
||||||
|
vfs.commitTree(ctx, newRoot)
|
||||||
vfs.updateRootAndCWD(ctx, root, cwd, 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()
|
|
||||||
|
|
||||||
err := vfs.connectMountAtLocked(ctx, m, vd)
|
|
||||||
vfs.delayDecRef(m)
|
|
||||||
if err != nil {
|
|
||||||
newns.DecRef(ctx)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
vfs.updateRootAndCWD(ctx, root, cwd, c, m)
|
|
||||||
if len(c.children) != 0 {
|
|
||||||
queue = append(queue, cloneEntry{c, m})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newns, nil
|
return newns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,43 @@ func (vfs *VirtualFilesystem) abortPropagationTree(ctx context.Context, tree map
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// +checklocks:vfs.mountMu
|
||||||
|
func (vfs *VirtualFilesystem) commitTree(ctx context.Context, mnt *Mount) {
|
||||||
|
vfs.mounts.seq.BeginWrite()
|
||||||
|
for _, c := range mnt.pendingChildren {
|
||||||
|
vfs.commitTreeSeqed(ctx, c)
|
||||||
|
}
|
||||||
|
mnt.pendingChildren = nil
|
||||||
|
vfs.mounts.seq.EndWrite()
|
||||||
|
}
|
||||||
|
|
||||||
|
// +checklocks:vfs.mountMu
|
||||||
|
func (vfs *VirtualFilesystem) commitTreeSeqed(ctx context.Context, mnt *Mount) {
|
||||||
|
mp := mnt.getKey()
|
||||||
|
mp.dentry.mu.Lock()
|
||||||
|
vfs.connectLocked(mnt, mp, mp.mount.ns)
|
||||||
|
mp.dentry.mu.Unlock()
|
||||||
|
for _, c := range mnt.pendingChildren {
|
||||||
|
vfs.commitTreeSeqed(ctx, c)
|
||||||
|
}
|
||||||
|
mnt.pendingChildren = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// abortTree releases references on a pending mount and all its pending
|
||||||
|
// descendants.
|
||||||
|
//
|
||||||
|
// +checklocks:vfs.mountMu
|
||||||
|
func (vfs *VirtualFilesystem) abortTree(ctx context.Context, mnt *Mount) {
|
||||||
|
mp := mnt.getKey()
|
||||||
|
vfs.delayDecRef(mnt)
|
||||||
|
vfs.delayDecRef(mp.dentry)
|
||||||
|
vfs.setPropagation(mnt, linux.MS_PRIVATE)
|
||||||
|
for _, c := range mnt.pendingChildren {
|
||||||
|
vfs.abortTree(ctx, c)
|
||||||
|
}
|
||||||
|
mnt.pendingChildren = nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetMountPropagationAt changes the propagation type of the mount pointed to by
|
// SetMountPropagationAt changes the propagation type of the mount pointed to by
|
||||||
// pop.
|
// pop.
|
||||||
func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, propFlags uint32) error {
|
func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, propFlags uint32) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user