mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Separate propagation methods/constants into their own file.
mount.go is becoming bloated, we should break it into smaller files where it makes sense. PiperOrigin-RevId: 491955373
This commit is contained in:
committed by
gVisor bot
parent
62ddad6119
commit
3dca16ed35
@@ -140,6 +140,7 @@ go_library(
|
||||
"options.go",
|
||||
"pathname.go",
|
||||
"permissions.go",
|
||||
"propagation.go",
|
||||
"resolving_path.go",
|
||||
"save_restore.go",
|
||||
"shared_list.go",
|
||||
|
||||
+4
-199
@@ -30,46 +30,10 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
)
|
||||
|
||||
// PropagationType is a propagation flavor as described in
|
||||
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt. Child
|
||||
// and Unbindable are currently unimplemented.
|
||||
// TODO(b/249777195): Support MS_SLAVE and MS_UNBINDABLE propagation types.
|
||||
type PropagationType int
|
||||
|
||||
const (
|
||||
// Unknown represents an invalid/unknown propagation type.
|
||||
Unknown PropagationType = iota
|
||||
// Shared represents the shared propagation type.
|
||||
Shared
|
||||
// Private represents the private propagation type.
|
||||
Private
|
||||
// Child represents the child propagation type (MS_SLAVE).
|
||||
Child
|
||||
// Unbindable represents the unbindable propagation type.
|
||||
Unbindable
|
||||
|
||||
// MountMax is the maximum number of mounts allowed. In Linux this can be
|
||||
// configured by the user at /proc/sys/fs/mount-max, but the default is
|
||||
// 100,000. We set the gVisor limit to 10,000.
|
||||
MountMax = 10000
|
||||
)
|
||||
|
||||
// PropagationTypeFromLinux returns the PropagationType corresponding to a
|
||||
// linux mount flag, aka MS_SHARED.
|
||||
func PropagationTypeFromLinux(propFlag uint64) PropagationType {
|
||||
switch propFlag {
|
||||
case linux.MS_SHARED:
|
||||
return Shared
|
||||
case linux.MS_PRIVATE:
|
||||
return Private
|
||||
case linux.MS_SLAVE:
|
||||
return Child
|
||||
case linux.MS_UNBINDABLE:
|
||||
return Unbindable
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
// MountMax is the maximum number of mounts allowed. In Linux this can be
|
||||
// configured by the user at /proc/sys/fs/mount-max, but the default is
|
||||
// 100,000. We set the gVisor limit to 10,000.
|
||||
const MountMax = 10000
|
||||
|
||||
// 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
|
||||
@@ -196,60 +160,6 @@ func (mnt *Mount) generateOptionalTags() string {
|
||||
return optional
|
||||
}
|
||||
|
||||
// addPeer adds oth to mnt's peer group. Both will have the same groupID
|
||||
// and sharedList. vfs.mountMu must be locked.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) addPeer(mnt *Mount, oth *Mount) {
|
||||
mnt.sharedList.PushBack(oth)
|
||||
oth.sharedList = mnt.sharedList
|
||||
oth.propType = mnt.propType
|
||||
oth.groupID = mnt.groupID
|
||||
}
|
||||
|
||||
// mergePeerGroup merges oth and all its peers into mnt's peer group. Oth
|
||||
// must have propagation type shared and vfs.mountMu must be locked.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) mergePeerGroup(mnt *Mount, oth *Mount) {
|
||||
peer := oth.sharedList.Front()
|
||||
for peer != nil {
|
||||
next := peer.sharedEntry.Next()
|
||||
vfs.setPropagation(peer, Private)
|
||||
vfs.addPeer(mnt, peer)
|
||||
peer = next
|
||||
}
|
||||
}
|
||||
|
||||
// setPropagation sets the propagation on mnt for a propagation type.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, ptype PropagationType) error {
|
||||
switch ptype {
|
||||
case Shared:
|
||||
id, err := vfs.allocateGroupID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mnt.groupID = id
|
||||
mnt.sharedList = &sharedList{}
|
||||
mnt.sharedList.PushBack(mnt)
|
||||
case Private:
|
||||
if mnt.propType == Shared {
|
||||
mnt.sharedList.Remove(mnt)
|
||||
if mnt.sharedList.Empty() {
|
||||
vfs.freeGroupID(mnt.groupID)
|
||||
}
|
||||
mnt.sharedList = nil
|
||||
mnt.groupID = 0
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported propagation type: %v", ptype))
|
||||
}
|
||||
mnt.propType = ptype
|
||||
return nil
|
||||
}
|
||||
|
||||
// A MountNamespace is a collection of Mounts.//
|
||||
// MountNamespaces are reference-counted. Unless otherwise specified, all
|
||||
// MountNamespace methods require that a reference is held.
|
||||
@@ -375,73 +285,6 @@ func (vfs *VirtualFilesystem) ConnectMountAt(ctx context.Context, creds *auth.Cr
|
||||
return nil
|
||||
}
|
||||
|
||||
// preparePropagationTree returns a mapping of propagated mounts to their future
|
||||
// mountpoints. The new mounts are clones of mnt and are added to mnt's peer
|
||||
// group if vd.mount and mnt are shared. All the cloned mounts and new
|
||||
// mountpoints in the tree have an extra reference taken.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
// +checklocksalias:mnt.vfs.mountMu=vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) preparePropagationTree(mnt *Mount, vd VirtualDentry) map[*Mount]VirtualDentry {
|
||||
tree := map[*Mount]VirtualDentry{}
|
||||
if vd.mount.propType == Private {
|
||||
return tree
|
||||
}
|
||||
if mnt.propType == Private {
|
||||
vfs.setPropagation(mnt, Shared)
|
||||
}
|
||||
var newPeerGroup []*Mount
|
||||
for peer := vd.mount.sharedList.Front(); peer != nil; peer = peer.sharedEntry.Next() {
|
||||
if peer == vd.mount {
|
||||
continue
|
||||
}
|
||||
peerVd := VirtualDentry{
|
||||
mount: peer,
|
||||
dentry: vd.dentry,
|
||||
}
|
||||
peerVd.IncRef()
|
||||
clone := vfs.cloneMount(mnt, mnt.root, nil)
|
||||
tree[clone] = peerVd
|
||||
newPeerGroup = append(newPeerGroup, clone)
|
||||
}
|
||||
for _, newPeer := range newPeerGroup {
|
||||
vfs.addPeer(mnt, newPeer)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// commitPropagationTree attaches to mounts in tree to the mountpoints they
|
||||
// are mapped to. If there is an error attaching a mount, the method panics.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) commitPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
|
||||
// The peer mounts should have no way of being dead if we've reached this
|
||||
// point so its safe to connect without checks.
|
||||
vfs.mounts.seq.BeginWrite()
|
||||
for mnt, vd := range tree {
|
||||
vd.dentry.mu.Lock()
|
||||
// If mnt isn't connected yet, skip connecting during propagation.
|
||||
if mntns := vd.mount.ns; mntns != nil {
|
||||
vfs.connectLocked(mnt, vd, mntns)
|
||||
}
|
||||
vd.dentry.mu.Unlock()
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
vfs.mounts.seq.EndWrite()
|
||||
}
|
||||
|
||||
// abortPropagationTree releases any references held by the mounts and
|
||||
// mountpoints in the tree and removes the mounts from their peer groups.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) abortPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
|
||||
for mnt, vd := range tree {
|
||||
vd.DecRef(ctx)
|
||||
vfs.setPropagation(mnt, Private)
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// connectMountAtLocked attaches mnt at vd. It returns the new mountpoint of mnt
|
||||
// if no error occurred.
|
||||
//
|
||||
@@ -496,44 +339,6 @@ func (vfs *VirtualFilesystem) connectMountAt(ctx context.Context, mnt *Mount, vd
|
||||
return 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, propType PropagationType) error {
|
||||
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// See the similar defer in UmountAt for why this is in a closure.
|
||||
defer func() {
|
||||
vd.DecRef(ctx)
|
||||
}()
|
||||
if vd.dentry.isMounted() {
|
||||
if realmnt := vfs.getMountAt(ctx, vd.mount, vd.dentry); realmnt != nil {
|
||||
vd.mount.DecRef(ctx)
|
||||
vd.mount = realmnt
|
||||
}
|
||||
} else if vd.dentry != vd.mount.root {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
vfs.SetMountPropagation(vd.mount, propType)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMountPropagation changes the propagation type of the mount.
|
||||
func (vfs *VirtualFilesystem) SetMountPropagation(mnt *Mount, propType PropagationType) {
|
||||
vfs.mountMu.Lock()
|
||||
defer vfs.mountMu.Unlock()
|
||||
if propType != mnt.propType {
|
||||
switch propType {
|
||||
case Shared, Private:
|
||||
vfs.setPropagation(mnt, propType)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported propagation type: %v", propType))
|
||||
}
|
||||
}
|
||||
mnt.propType = propType
|
||||
}
|
||||
|
||||
// CloneMountAt returns a new mount with the same fs, specified root and
|
||||
// mount options. If mnt's propagation type is shared the new mount is
|
||||
// automatically made a peer of mnt. If mount options are nil, mnt's
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
// Copyright 2022 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 (
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
)
|
||||
|
||||
// PropagationType is a propagation flavor as described in
|
||||
// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt. Child
|
||||
// and Unbindable are currently unimplemented.
|
||||
// TODO(b/249777195): Support MS_SLAVE and MS_UNBINDABLE propagation types.
|
||||
type PropagationType int
|
||||
|
||||
const (
|
||||
// Unknown represents an invalid/unknown propagation type.
|
||||
Unknown PropagationType = iota
|
||||
// Shared represents the shared propagation type.
|
||||
Shared
|
||||
// Private represents the private propagation type.
|
||||
Private
|
||||
// Child represents the child propagation type (MS_SLAVE).
|
||||
Child
|
||||
// Unbindable represents the unbindable propagation type.
|
||||
Unbindable
|
||||
)
|
||||
|
||||
// PropagationTypeFromLinux returns the PropagationType corresponding to a
|
||||
// linux mount flag, aka MS_SHARED.
|
||||
func PropagationTypeFromLinux(propFlag uint64) PropagationType {
|
||||
switch propFlag {
|
||||
case linux.MS_SHARED:
|
||||
return Shared
|
||||
case linux.MS_PRIVATE:
|
||||
return Private
|
||||
case linux.MS_SLAVE:
|
||||
return Child
|
||||
case linux.MS_UNBINDABLE:
|
||||
return Unbindable
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// setPropagation sets the propagation on mnt for a propagation type.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, ptype PropagationType) error {
|
||||
switch ptype {
|
||||
case Shared:
|
||||
id, err := vfs.allocateGroupID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mnt.groupID = id
|
||||
mnt.sharedList = &sharedList{}
|
||||
mnt.sharedList.PushBack(mnt)
|
||||
case Private:
|
||||
if mnt.propType == Shared {
|
||||
mnt.sharedList.Remove(mnt)
|
||||
if mnt.sharedList.Empty() {
|
||||
vfs.freeGroupID(mnt.groupID)
|
||||
}
|
||||
mnt.sharedList = nil
|
||||
mnt.groupID = 0
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported propagation type: %v", ptype))
|
||||
}
|
||||
mnt.propType = ptype
|
||||
return nil
|
||||
}
|
||||
|
||||
// addPeer adds oth to mnt's peer group. Both will have the same groupID
|
||||
// and sharedList. vfs.mountMu must be locked.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) addPeer(mnt *Mount, oth *Mount) {
|
||||
mnt.sharedList.PushBack(oth)
|
||||
oth.sharedList = mnt.sharedList
|
||||
oth.propType = mnt.propType
|
||||
oth.groupID = mnt.groupID
|
||||
}
|
||||
|
||||
// mergePeerGroup merges oth and all its peers into mnt's peer group. Oth
|
||||
// must have propagation type shared and vfs.mountMu must be locked.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) mergePeerGroup(mnt *Mount, oth *Mount) {
|
||||
peer := oth.sharedList.Front()
|
||||
for peer != nil {
|
||||
next := peer.sharedEntry.Next()
|
||||
vfs.setPropagation(peer, Private)
|
||||
vfs.addPeer(mnt, peer)
|
||||
peer = next
|
||||
}
|
||||
}
|
||||
|
||||
// preparePropagationTree returns a mapping of propagated mounts to their future
|
||||
// mountpoints. The new mounts are clones of mnt and are added to mnt's peer
|
||||
// group if vd.mount and mnt are shared. All the cloned mounts and new
|
||||
// mountpoints in the tree have an extra reference taken.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
// +checklocksalias:mnt.vfs.mountMu=vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) preparePropagationTree(mnt *Mount, vd VirtualDentry) map[*Mount]VirtualDentry {
|
||||
tree := map[*Mount]VirtualDentry{}
|
||||
if vd.mount.propType == Private {
|
||||
return tree
|
||||
}
|
||||
if mnt.propType == Private {
|
||||
vfs.setPropagation(mnt, Shared)
|
||||
}
|
||||
var newPeerGroup []*Mount
|
||||
for peer := vd.mount.sharedList.Front(); peer != nil; peer = peer.sharedEntry.Next() {
|
||||
if peer == vd.mount {
|
||||
continue
|
||||
}
|
||||
peerVd := VirtualDentry{
|
||||
mount: peer,
|
||||
dentry: vd.dentry,
|
||||
}
|
||||
peerVd.IncRef()
|
||||
clone := vfs.cloneMount(mnt, mnt.root, nil)
|
||||
tree[clone] = peerVd
|
||||
newPeerGroup = append(newPeerGroup, clone)
|
||||
}
|
||||
for _, newPeer := range newPeerGroup {
|
||||
vfs.addPeer(mnt, newPeer)
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
// commitPropagationTree attaches to mounts in tree to the mountpoints they
|
||||
// are mapped to. If there is an error attaching a mount, the method panics.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) commitPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
|
||||
// The peer mounts should have no way of being dead if we've reached this
|
||||
// point so its safe to connect without checks.
|
||||
vfs.mounts.seq.BeginWrite()
|
||||
for mnt, vd := range tree {
|
||||
vd.dentry.mu.Lock()
|
||||
// If mnt isn't connected yet, skip connecting during propagation.
|
||||
if mntns := vd.mount.ns; mntns != nil {
|
||||
vfs.connectLocked(mnt, vd, mntns)
|
||||
}
|
||||
vd.dentry.mu.Unlock()
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
vfs.mounts.seq.EndWrite()
|
||||
}
|
||||
|
||||
// abortPropagationTree releases any references held by the mounts and
|
||||
// mountpoints in the tree and removes the mounts from their peer groups.
|
||||
//
|
||||
// +checklocks:vfs.mountMu
|
||||
func (vfs *VirtualFilesystem) abortPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
|
||||
for mnt, vd := range tree {
|
||||
vd.DecRef(ctx)
|
||||
vfs.setPropagation(mnt, Private)
|
||||
mnt.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// SetMountPropagationAt changes the propagation type of the mount pointed to by
|
||||
// pop.
|
||||
func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, propType PropagationType) error {
|
||||
vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// See the similar defer in UmountAt for why this is in a closure.
|
||||
defer func() {
|
||||
vd.DecRef(ctx)
|
||||
}()
|
||||
if vd.dentry.isMounted() {
|
||||
if realmnt := vfs.getMountAt(ctx, vd.mount, vd.dentry); realmnt != nil {
|
||||
vd.mount.DecRef(ctx)
|
||||
vd.mount = realmnt
|
||||
}
|
||||
} else if vd.dentry != vd.mount.root {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
vfs.SetMountPropagation(vd.mount, propType)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMountPropagation changes the propagation type of the mount.
|
||||
func (vfs *VirtualFilesystem) SetMountPropagation(mnt *Mount, propType PropagationType) {
|
||||
vfs.mountMu.Lock()
|
||||
defer vfs.mountMu.Unlock()
|
||||
if propType != mnt.propType {
|
||||
switch propType {
|
||||
case Shared, Private:
|
||||
vfs.setPropagation(mnt, propType)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported propagation type: %v", propType))
|
||||
}
|
||||
}
|
||||
mnt.propType = propType
|
||||
}
|
||||
Reference in New Issue
Block a user