github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/vfs/propagation.go (about)

     1  // Copyright 2022 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  	"fmt"
    19  
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    24  )
    25  
    26  // PropagationType is a propagation flavor as described in
    27  // https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt. Child
    28  // and Unbindable are currently unimplemented.
    29  // TODO(b/249777195): Support MS_SLAVE and MS_UNBINDABLE propagation types.
    30  type PropagationType int
    31  
    32  const (
    33  	// Unknown represents an invalid/unknown propagation type.
    34  	Unknown PropagationType = iota
    35  	// Shared represents the shared propagation type.
    36  	Shared
    37  	// Private represents the private propagation type.
    38  	Private
    39  	// Child represents the child propagation type (MS_SLAVE).
    40  	Child
    41  	// Unbindable represents the unbindable propagation type.
    42  	Unbindable
    43  )
    44  
    45  // PropagationTypeFromLinux returns the PropagationType corresponding to a
    46  // linux mount flag, aka MS_SHARED.
    47  func PropagationTypeFromLinux(propFlag uint64) PropagationType {
    48  	switch propFlag {
    49  	case linux.MS_SHARED:
    50  		return Shared
    51  	case linux.MS_PRIVATE:
    52  		return Private
    53  	case linux.MS_SLAVE:
    54  		return Child
    55  	case linux.MS_UNBINDABLE:
    56  		return Unbindable
    57  	default:
    58  		return Unknown
    59  	}
    60  }
    61  
    62  // setPropagation sets the propagation on mnt for a propagation type.
    63  //
    64  // +checklocks:vfs.mountMu
    65  func (vfs *VirtualFilesystem) setPropagation(mnt *Mount, ptype PropagationType) error {
    66  	switch ptype {
    67  	case Shared:
    68  		id, err := vfs.allocateGroupID()
    69  		if err != nil {
    70  			return err
    71  		}
    72  		mnt.groupID = id
    73  		mnt.sharedList = &sharedList{}
    74  		mnt.sharedList.PushBack(mnt)
    75  	case Private:
    76  		if mnt.propType == Shared {
    77  			mnt.sharedList.Remove(mnt)
    78  			if mnt.sharedList.Empty() {
    79  				vfs.freeGroupID(mnt.groupID)
    80  			}
    81  			mnt.sharedList = nil
    82  			mnt.groupID = 0
    83  		}
    84  	default:
    85  		panic(fmt.Sprintf("unsupported propagation type: %v", ptype))
    86  	}
    87  	mnt.propType = ptype
    88  	return nil
    89  }
    90  
    91  // addPeer adds oth to mnt's peer group. Both will have the same groupID
    92  // and sharedList. vfs.mountMu must be locked.
    93  //
    94  // +checklocks:vfs.mountMu
    95  func (vfs *VirtualFilesystem) addPeer(mnt *Mount, oth *Mount) {
    96  	mnt.sharedList.PushBack(oth)
    97  	oth.sharedList = mnt.sharedList
    98  	oth.propType = mnt.propType
    99  	oth.groupID = mnt.groupID
   100  }
   101  
   102  // mergePeerGroup merges oth and all its peers into mnt's peer group. Oth
   103  // must have propagation type shared and vfs.mountMu must be locked.
   104  //
   105  // +checklocks:vfs.mountMu
   106  func (vfs *VirtualFilesystem) mergePeerGroup(mnt *Mount, oth *Mount) {
   107  	peer := oth.sharedList.Front()
   108  	for peer != nil {
   109  		next := peer.sharedEntry.Next()
   110  		vfs.setPropagation(peer, Private)
   111  		vfs.addPeer(mnt, peer)
   112  		peer = next
   113  	}
   114  }
   115  
   116  // preparePropagationTree returns a mapping of propagated mounts to their future
   117  // mountpoints. The new mounts are clones of mnt and are added to mnt's peer
   118  // group if vd.mount and mnt are shared. All the cloned mounts and new
   119  // mountpoints in the tree have an extra reference taken.
   120  //
   121  // +checklocks:vfs.mountMu
   122  // +checklocksalias:mnt.vfs.mountMu=vfs.mountMu
   123  func (vfs *VirtualFilesystem) preparePropagationTree(mnt *Mount, vd VirtualDentry) map[*Mount]VirtualDentry {
   124  	tree := map[*Mount]VirtualDentry{}
   125  	if vd.mount.propType == Private {
   126  		return tree
   127  	}
   128  	if mnt.propType == Private {
   129  		vfs.setPropagation(mnt, Shared)
   130  	}
   131  	var newPeerGroup []*Mount
   132  	for peer := vd.mount.sharedList.Front(); peer != nil; peer = peer.sharedEntry.Next() {
   133  		if peer == vd.mount {
   134  			continue
   135  		}
   136  		peerVd := VirtualDentry{
   137  			mount:  peer,
   138  			dentry: vd.dentry,
   139  		}
   140  		peerVd.IncRef()
   141  		clone := vfs.cloneMount(mnt, mnt.root, nil)
   142  		tree[clone] = peerVd
   143  		newPeerGroup = append(newPeerGroup, clone)
   144  	}
   145  	for _, newPeer := range newPeerGroup {
   146  		vfs.addPeer(mnt, newPeer)
   147  	}
   148  	return tree
   149  }
   150  
   151  // commitPropagationTree attaches to mounts in tree to the mountpoints they
   152  // are mapped to. If there is an error attaching a mount, the method panics.
   153  //
   154  // +checklocks:vfs.mountMu
   155  func (vfs *VirtualFilesystem) commitPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
   156  	// The peer mounts should have no way of being dead if we've reached this
   157  	// point so its safe to connect without checks.
   158  	vfs.mounts.seq.BeginWrite()
   159  	for mnt, vd := range tree {
   160  		vd.dentry.mu.Lock()
   161  		// If mnt isn't connected yet, skip connecting during propagation.
   162  		if mntns := vd.mount.ns; mntns != nil {
   163  			vfs.connectLocked(mnt, vd, mntns)
   164  		}
   165  		vd.dentry.mu.Unlock()
   166  		mnt.DecRef(ctx)
   167  	}
   168  	vfs.mounts.seq.EndWrite()
   169  }
   170  
   171  // abortPropagationTree releases any references held by the mounts and
   172  // mountpoints in the tree and removes the mounts from their peer groups.
   173  //
   174  // +checklocks:vfs.mountMu
   175  func (vfs *VirtualFilesystem) abortPropagationTree(ctx context.Context, tree map[*Mount]VirtualDentry) {
   176  	for mnt, vd := range tree {
   177  		vd.DecRef(ctx)
   178  		vfs.setPropagation(mnt, Private)
   179  		mnt.DecRef(ctx)
   180  	}
   181  }
   182  
   183  // SetMountPropagationAt changes the propagation type of the mount pointed to by
   184  // pop.
   185  func (vfs *VirtualFilesystem) SetMountPropagationAt(ctx context.Context, creds *auth.Credentials, pop *PathOperation, propType PropagationType) error {
   186  	vd, err := vfs.GetDentryAt(ctx, creds, pop, &GetDentryOptions{})
   187  	if err != nil {
   188  		return err
   189  	}
   190  	// See the similar defer in UmountAt for why this is in a closure.
   191  	defer func() {
   192  		vd.DecRef(ctx)
   193  	}()
   194  	if vd.dentry.isMounted() {
   195  		if realmnt := vfs.getMountAt(ctx, vd.mount, vd.dentry); realmnt != nil {
   196  			vd.mount.DecRef(ctx)
   197  			vd.mount = realmnt
   198  		}
   199  	} else if vd.dentry != vd.mount.root {
   200  		return linuxerr.EINVAL
   201  	}
   202  	vfs.SetMountPropagation(vd.mount, propType)
   203  	return nil
   204  }
   205  
   206  // SetMountPropagation changes the propagation type of the mount.
   207  func (vfs *VirtualFilesystem) SetMountPropagation(mnt *Mount, propType PropagationType) {
   208  	vfs.mountMu.Lock()
   209  	defer vfs.mountMu.Unlock()
   210  	if propType != mnt.propType {
   211  		switch propType {
   212  		case Shared, Private:
   213  			vfs.setPropagation(mnt, propType)
   214  		default:
   215  			panic(fmt.Sprintf("unsupported propagation type: %v", propType))
   216  		}
   217  	}
   218  	mnt.propType = propType
   219  }