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 }