diff --git a/pkg/sentry/vfs/mount.go b/pkg/sentry/vfs/mount.go index af571a7b1..02cf2c540 100644 --- a/pkg/sentry/vfs/mount.go +++ b/pkg/sentry/vfs/mount.go @@ -108,10 +108,6 @@ type Mount struct { // Mount.EndWrite(). The MSB of writers is set if MS_RDONLY is in effect. // writers is accessed using atomic memory operations. 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 { @@ -308,39 +304,6 @@ func (vfs *VirtualFilesystem) cloneMount(mnt *Mount, root *Dentry, mopts *MountO 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 // the target path. The new mount's root dentry is one pointed to by the source // path. diff --git a/pkg/sentry/vfs/namespace.go b/pkg/sentry/vfs/namespace.go index c11d09063..131193fff 100644 --- a/pkg/sentry/vfs/namespace.go +++ b/pkg/sentry/vfs/namespace.go @@ -125,6 +125,11 @@ func (vfs *VirtualFilesystem) NewMountNamespaceFrom( return mntns } +type cloneEntry struct { + prevMount *Mount + parentMount *Mount +} + // +checklocks:vfs.mountMu func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) { if root.mount == src { @@ -137,10 +142,6 @@ func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *Virtua cwd.mount = dst cwd.mount.IncRef() } - for srcChild := range src.children { - dstChild := vfs.mounts.Lookup(dst, srcChild.point()) - vfs.updateRootAndCWD(ctx, root, cwd, srcChild, dstChild) - } } // NamespaceInodeGetter is an interface that provides the GetNamespaceInode method. @@ -169,16 +170,38 @@ func (vfs *VirtualFilesystem) CloneMountNamespace( vfs.lockMounts() defer vfs.unlockMounts(ctx) - newRoot, err := vfs.cloneMountTree(ctx, ns.root, ns.root.root) - if err != nil { - newns.DecRef(ctx) - vfs.abortTree(ctx, newRoot) - return nil, err + 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.isShared { + vfs.addPeer(ns.root, newns.root) } - newns.root = newRoot - newns.root.ns = newns - vfs.commitTree(ctx, newRoot) 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 } diff --git a/pkg/sentry/vfs/propagation.go b/pkg/sentry/vfs/propagation.go index 8f70af2b9..1d34d9740 100644 --- a/pkg/sentry/vfs/propagation.go +++ b/pkg/sentry/vfs/propagation.go @@ -153,43 +153,6 @@ 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 // pop. func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, propFlags uint32) error {