github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/sentry/vfs/namespace.go (about) 1 // Copyright 2023 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 "github.com/MerlinKodo/gvisor/pkg/context" 19 "github.com/MerlinKodo/gvisor/pkg/errors/linuxerr" 20 "github.com/MerlinKodo/gvisor/pkg/refs" 21 "github.com/MerlinKodo/gvisor/pkg/sentry/kernel/auth" 22 ) 23 24 // A MountNamespace is a collection of Mounts.// 25 // MountNamespaces are reference-counted. Unless otherwise specified, all 26 // MountNamespace methods require that a reference is held. 27 // 28 // MountNamespace is analogous to Linux's struct mnt_namespace. 29 // 30 // +stateify savable 31 type MountNamespace struct { 32 // Refs is the reference count for this mount namespace. 33 Refs refs.TryRefCounter 34 35 // Owner is the usernamespace that owns this mount namespace. 36 Owner *auth.UserNamespace 37 38 // root is the MountNamespace's root mount. 39 root *Mount 40 41 // mountpoints maps all Dentries which are mount points in this namespace 42 // to the number of Mounts for which they are mount points. mountpoints is 43 // protected by VirtualFilesystem.mountMu. 44 // 45 // mountpoints is used to determine if a Dentry can be moved or removed 46 // (which requires that the Dentry is not a mount point in the calling 47 // namespace). 48 // 49 // mountpoints is maintained even if there are no references held on the 50 // MountNamespace; this is required to ensure that 51 // VFS.PrepareDeleteDentry() and VFS.PrepareRemoveDentry() operate 52 // correctly on unreferenced MountNamespaces. 53 mountpoints map[*Dentry]uint32 54 55 // mounts is the total number of mounts in this mount namespace. 56 mounts uint32 57 } 58 59 // Namespace is the namespace interface. 60 type Namespace interface { 61 Type() string 62 Destroy(ctx context.Context) 63 } 64 65 // NewMountNamespace returns a new mount namespace with a root filesystem 66 // configured by the given arguments. A reference is taken on the returned 67 // MountNamespace. 68 // 69 // If nsfs is nil, the default reference counter is used. 70 func (vfs *VirtualFilesystem) NewMountNamespace( 71 ctx context.Context, 72 creds *auth.Credentials, 73 source, fsTypeName string, 74 opts *MountOptions, 75 nsfs NamespaceInodeGetter, 76 ) (*MountNamespace, error) { 77 rft := vfs.getFilesystemType(fsTypeName) 78 if rft == nil { 79 ctx.Warningf("Unknown filesystem type: %s", fsTypeName) 80 return nil, linuxerr.ENODEV 81 } 82 fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions) 83 if err != nil { 84 return nil, err 85 } 86 return vfs.NewMountNamespaceFrom(ctx, creds, fs, root, opts, nsfs), nil 87 } 88 89 type namespaceDefaultRefs struct { 90 namespaceRefs 91 destroy func(ctx context.Context) 92 } 93 94 func (r *namespaceDefaultRefs) DecRef(ctx context.Context) { 95 r.namespaceRefs.DecRef( 96 func() { 97 r.destroy(ctx) 98 }, 99 ) 100 } 101 102 // NewMountNamespaceFrom constructs a new mount namespace from an existing 103 // filesystem and its root dentry. This is similar to NewMountNamespace, but 104 // uses an existing filesystem instead of constructing a new one. 105 func (vfs *VirtualFilesystem) NewMountNamespaceFrom( 106 ctx context.Context, 107 creds *auth.Credentials, 108 fs *Filesystem, 109 root *Dentry, 110 opts *MountOptions, 111 nsfs NamespaceInodeGetter, 112 ) *MountNamespace { 113 mntns := &MountNamespace{ 114 Owner: creds.UserNamespace, 115 mountpoints: make(map[*Dentry]uint32), 116 } 117 if nsfs == nil { 118 refs := &namespaceDefaultRefs{destroy: mntns.Destroy} 119 refs.InitRefs() 120 mntns.Refs = refs 121 } else { 122 mntns.Refs = nsfs.GetNamespaceInode(ctx, mntns) 123 } 124 mntns.root = newMount(vfs, fs, root, mntns, opts) 125 return mntns 126 } 127 128 type cloneEntry struct { 129 prevMount *Mount 130 parentMount *Mount 131 } 132 133 // +checklocks:vfs.mountMu 134 func (vfs *VirtualFilesystem) updateRootAndCWD(ctx context.Context, root *VirtualDentry, cwd *VirtualDentry, src *Mount, dst *Mount) { 135 if root.mount == src { 136 vfs.delayDecRef(root.mount) 137 root.mount = dst 138 root.mount.IncRef() 139 } 140 if cwd.mount == src { 141 vfs.delayDecRef(cwd.mount) 142 cwd.mount = dst 143 cwd.mount.IncRef() 144 } 145 for srcChild := range src.children { 146 dstChild := vfs.mounts.Lookup(dst, srcChild.point()) 147 vfs.updateRootAndCWD(ctx, root, cwd, srcChild, dstChild) 148 } 149 } 150 151 // NamespaceInodeGetter is an interface that provides the GetNamespaceInode method. 152 type NamespaceInodeGetter interface { 153 GetNamespaceInode(ctx context.Context, ns Namespace) refs.TryRefCounter 154 } 155 156 // CloneMountNamespace makes a copy of the specified mount namespace. 157 // 158 // If `root` or `cwd` have mounts in the old namespace, they will be replaced 159 // with proper mounts from the new namespace. 160 func (vfs *VirtualFilesystem) CloneMountNamespace( 161 ctx context.Context, 162 creds *auth.Credentials, 163 ns *MountNamespace, 164 root *VirtualDentry, 165 cwd *VirtualDentry, 166 nsfs NamespaceInodeGetter, 167 ) (*MountNamespace, error) { 168 newns := &MountNamespace{ 169 Owner: creds.UserNamespace, 170 mountpoints: make(map[*Dentry]uint32), 171 } 172 173 newns.Refs = nsfs.GetNamespaceInode(ctx, newns) 174 vfs.lockMounts() 175 defer vfs.unlockMounts(ctx) 176 177 newRoot, err := vfs.cloneMountTree(ctx, ns.root, ns.root.root) 178 if err != nil { 179 newns.DecRef(ctx) 180 vfs.abortTree(ctx, newRoot) 181 return nil, err 182 } 183 newns.root = newRoot 184 newns.root.ns = newns 185 vfs.commitPendingTree(ctx, newRoot) 186 vfs.updateRootAndCWD(ctx, root, cwd, ns.root, newns.root) 187 return newns, nil 188 } 189 190 // Destroy implements nsfs.Namespace.Destroy. 191 func (mntns *MountNamespace) Destroy(ctx context.Context) { 192 vfs := mntns.root.fs.VirtualFilesystem() 193 vfs.lockMounts() 194 vfs.mounts.seq.BeginWrite() 195 vfs.umountRecursiveLocked(mntns.root, &umountRecursiveOptions{ 196 disconnectHierarchy: true, 197 }) 198 vfs.mounts.seq.EndWrite() 199 vfs.unlockMounts(ctx) 200 } 201 202 // Type implements nsfs.Namespace.Type. 203 func (mntns *MountNamespace) Type() string { 204 return "mnt" 205 } 206 207 // IncRef increments mntns' refcount. 208 func (mntns *MountNamespace) IncRef() { 209 mntns.Refs.IncRef() 210 } 211 212 // DecRef decrements mntns' reference count. 213 func (mntns *MountNamespace) DecRef(ctx context.Context) { 214 mntns.Refs.DecRef(ctx) 215 } 216 217 // TryIncRef attempts to increment mntns' reference count. 218 func (mntns *MountNamespace) TryIncRef() bool { 219 return mntns.Refs.TryIncRef() 220 } 221 222 // Root returns mntns' root. If the root is over-mounted, it returns the top 223 // mount. 224 func (mntns *MountNamespace) Root(ctx context.Context) VirtualDentry { 225 vfs := mntns.root.fs.VirtualFilesystem() 226 vd := VirtualDentry{ 227 mount: mntns.root, 228 dentry: mntns.root.root, 229 } 230 vd.IncRef() 231 if !vd.dentry.isMounted() { 232 return vd 233 } 234 m := vfs.getMountAt(ctx, vd.mount, vd.dentry) 235 if m == nil { 236 return vd 237 } 238 vd.DecRef(ctx) 239 vd.mount = m 240 vd.dentry = m.root 241 vd.dentry.IncRef() 242 return vd 243 }