github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/fs_context.go (about) 1 // Copyright 2018 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 kernel 16 17 import ( 18 "fmt" 19 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/sentry/fs" 22 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 23 "github.com/SagerNet/gvisor/pkg/sync" 24 ) 25 26 // FSContext contains filesystem context. 27 // 28 // This includes umask and working directory. 29 // 30 // +stateify savable 31 type FSContext struct { 32 FSContextRefs 33 34 // mu protects below. 35 mu sync.Mutex `state:"nosave"` 36 37 // root is the filesystem root. Will be nil iff the FSContext has been 38 // destroyed. 39 root *fs.Dirent 40 41 // rootVFS2 is the filesystem root. 42 rootVFS2 vfs.VirtualDentry 43 44 // cwd is the current working directory. Will be nil iff the FSContext 45 // has been destroyed. 46 cwd *fs.Dirent 47 48 // cwdVFS2 is the current working directory. 49 cwdVFS2 vfs.VirtualDentry 50 51 // umask is the current file mode creation mask. When a thread using this 52 // context invokes a syscall that creates a file, bits set in umask are 53 // removed from the permissions that the file is created with. 54 umask uint 55 } 56 57 // newFSContext returns a new filesystem context. 58 func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext { 59 root.IncRef() 60 cwd.IncRef() 61 f := FSContext{ 62 root: root, 63 cwd: cwd, 64 umask: umask, 65 } 66 f.InitRefs() 67 return &f 68 } 69 70 // NewFSContextVFS2 returns a new filesystem context. 71 func NewFSContextVFS2(root, cwd vfs.VirtualDentry, umask uint) *FSContext { 72 root.IncRef() 73 cwd.IncRef() 74 f := FSContext{ 75 rootVFS2: root, 76 cwdVFS2: cwd, 77 umask: umask, 78 } 79 f.InitRefs() 80 return &f 81 } 82 83 // DecRef implements RefCounter.DecRef. 84 // 85 // When f reaches zero references, DecRef will be called on both root and cwd 86 // Dirents. 87 // 88 // Note that there may still be calls to WorkingDirectory() or RootDirectory() 89 // (that return nil). This is because valid references may still be held via 90 // proc files or other mechanisms. 91 func (f *FSContext) DecRef(ctx context.Context) { 92 f.FSContextRefs.DecRef(func() { 93 // Hold f.mu so that we don't race with RootDirectory() and 94 // WorkingDirectory(). 95 f.mu.Lock() 96 defer f.mu.Unlock() 97 98 if VFS2Enabled { 99 f.rootVFS2.DecRef(ctx) 100 f.rootVFS2 = vfs.VirtualDentry{} 101 f.cwdVFS2.DecRef(ctx) 102 f.cwdVFS2 = vfs.VirtualDentry{} 103 } else { 104 f.root.DecRef(ctx) 105 f.root = nil 106 f.cwd.DecRef(ctx) 107 f.cwd = nil 108 } 109 }) 110 } 111 112 // Fork forks this FSContext. 113 // 114 // This is not a valid call after f is destroyed. 115 func (f *FSContext) Fork() *FSContext { 116 f.mu.Lock() 117 defer f.mu.Unlock() 118 119 if VFS2Enabled { 120 if !f.cwdVFS2.Ok() { 121 panic("FSContext.Fork() called after destroy") 122 } 123 f.cwdVFS2.IncRef() 124 f.rootVFS2.IncRef() 125 } else { 126 if f.cwd == nil { 127 panic("FSContext.Fork() called after destroy") 128 } 129 f.cwd.IncRef() 130 f.root.IncRef() 131 } 132 133 ctx := &FSContext{ 134 cwd: f.cwd, 135 root: f.root, 136 cwdVFS2: f.cwdVFS2, 137 rootVFS2: f.rootVFS2, 138 umask: f.umask, 139 } 140 ctx.InitRefs() 141 return ctx 142 } 143 144 // WorkingDirectory returns the current working directory. 145 // 146 // This will return nil if called after f is destroyed, otherwise it will return 147 // a Dirent with a reference taken. 148 func (f *FSContext) WorkingDirectory() *fs.Dirent { 149 f.mu.Lock() 150 defer f.mu.Unlock() 151 152 if f.cwd != nil { 153 f.cwd.IncRef() 154 } 155 return f.cwd 156 } 157 158 // WorkingDirectoryVFS2 returns the current working directory. 159 // 160 // This will return an empty vfs.VirtualDentry if called after f is 161 // destroyed, otherwise it will return a Dirent with a reference taken. 162 func (f *FSContext) WorkingDirectoryVFS2() vfs.VirtualDentry { 163 f.mu.Lock() 164 defer f.mu.Unlock() 165 166 if f.cwdVFS2.Ok() { 167 f.cwdVFS2.IncRef() 168 } 169 return f.cwdVFS2 170 } 171 172 // SetWorkingDirectory sets the current working directory. 173 // This will take an extra reference on the Dirent. 174 // 175 // This is not a valid call after f is destroyed. 176 func (f *FSContext) SetWorkingDirectory(ctx context.Context, d *fs.Dirent) { 177 if d == nil { 178 panic("FSContext.SetWorkingDirectory called with nil dirent") 179 } 180 181 f.mu.Lock() 182 defer f.mu.Unlock() 183 184 if f.cwd == nil { 185 panic(fmt.Sprintf("FSContext.SetWorkingDirectory(%v)) called after destroy", d)) 186 } 187 188 old := f.cwd 189 f.cwd = d 190 d.IncRef() 191 old.DecRef(ctx) 192 } 193 194 // SetWorkingDirectoryVFS2 sets the current working directory. 195 // This will take an extra reference on the VirtualDentry. 196 // 197 // This is not a valid call after f is destroyed. 198 func (f *FSContext) SetWorkingDirectoryVFS2(ctx context.Context, d vfs.VirtualDentry) { 199 f.mu.Lock() 200 defer f.mu.Unlock() 201 202 if !f.cwdVFS2.Ok() { 203 panic(fmt.Sprintf("FSContext.SetWorkingDirectoryVFS2(%v)) called after destroy", d)) 204 } 205 206 old := f.cwdVFS2 207 f.cwdVFS2 = d 208 d.IncRef() 209 old.DecRef(ctx) 210 } 211 212 // RootDirectory returns the current filesystem root. 213 // 214 // This will return nil if called after f is destroyed, otherwise it will return 215 // a Dirent with a reference taken. 216 func (f *FSContext) RootDirectory() *fs.Dirent { 217 f.mu.Lock() 218 defer f.mu.Unlock() 219 if f.root != nil { 220 f.root.IncRef() 221 } 222 return f.root 223 } 224 225 // RootDirectoryVFS2 returns the current filesystem root. 226 // 227 // This will return an empty vfs.VirtualDentry if called after f is 228 // destroyed, otherwise it will return a Dirent with a reference taken. 229 func (f *FSContext) RootDirectoryVFS2() vfs.VirtualDentry { 230 f.mu.Lock() 231 defer f.mu.Unlock() 232 233 if f.rootVFS2.Ok() { 234 f.rootVFS2.IncRef() 235 } 236 return f.rootVFS2 237 } 238 239 // SetRootDirectory sets the root directory. 240 // This will take an extra reference on the Dirent. 241 // 242 // This is not a valid call after f is destroyed. 243 func (f *FSContext) SetRootDirectory(ctx context.Context, d *fs.Dirent) { 244 if d == nil { 245 panic("FSContext.SetRootDirectory called with nil dirent") 246 } 247 248 f.mu.Lock() 249 defer f.mu.Unlock() 250 251 if f.root == nil { 252 panic(fmt.Sprintf("FSContext.SetRootDirectory(%v)) called after destroy", d)) 253 } 254 255 old := f.root 256 f.root = d 257 d.IncRef() 258 old.DecRef(ctx) 259 } 260 261 // SetRootDirectoryVFS2 sets the root directory. It takes a reference on vd. 262 // 263 // This is not a valid call after f is destroyed. 264 func (f *FSContext) SetRootDirectoryVFS2(ctx context.Context, vd vfs.VirtualDentry) { 265 if !vd.Ok() { 266 panic("FSContext.SetRootDirectoryVFS2 called with zero-value VirtualDentry") 267 } 268 269 f.mu.Lock() 270 271 if !f.rootVFS2.Ok() { 272 f.mu.Unlock() 273 panic(fmt.Sprintf("FSContext.SetRootDirectoryVFS2(%v)) called after destroy", vd)) 274 } 275 276 old := f.rootVFS2 277 vd.IncRef() 278 f.rootVFS2 = vd 279 f.mu.Unlock() 280 old.DecRef(ctx) 281 } 282 283 // Umask returns the current umask. 284 func (f *FSContext) Umask() uint { 285 f.mu.Lock() 286 defer f.mu.Unlock() 287 return f.umask 288 } 289 290 // SwapUmask atomically sets the current umask and returns the old umask. 291 func (f *FSContext) SwapUmask(mask uint) uint { 292 f.mu.Lock() 293 defer f.mu.Unlock() 294 old := f.umask 295 f.umask = mask 296 return old 297 }