// Copyright 2018 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 kernel import ( "fmt" "sync" "gvisor.dev/gvisor/pkg/refs" "gvisor.dev/gvisor/pkg/sentry/fs" ) // FSContext contains filesystem context. // // This includes umask and working directory. // // +stateify savable type FSContext struct { refs.AtomicRefCount // mu protects below. mu sync.Mutex `state:"nosave"` // root is the filesystem root. Will be nil iff the FSContext has been // destroyed. root *fs.Dirent // cwd is the current working directory. Will be nil iff the FSContext // has been destroyed. cwd *fs.Dirent // umask is the current file mode creation mask. When a thread using this // context invokes a syscall that creates a file, bits set in umask are // removed from the permissions that the file is created with. umask uint } // newFSContext returns a new filesystem context. func newFSContext(root, cwd *fs.Dirent, umask uint) *FSContext { root.IncRef() cwd.IncRef() return &FSContext{ root: root, cwd: cwd, umask: umask, } } // destroy is the destructor for an FSContext. // // This will call DecRef on both root and cwd Dirents. If either call to // DecRef returns an error, then it will be propigated. If both calls to // DecRef return an error, then the one from root.DecRef will be propigated. // // Note that there may still be calls to WorkingDirectory() or RootDirectory() // (that return nil). This is because valid references may still be held via // proc files or other mechanisms. func (f *FSContext) destroy() { // Hold f.mu so that we don't race with RootDirectory() and // WorkingDirectory(). f.mu.Lock() defer f.mu.Unlock() f.root.DecRef() f.root = nil f.cwd.DecRef() f.cwd = nil } // DecRef implements RefCounter.DecRef with destructor f.destroy. func (f *FSContext) DecRef() { f.DecRefWithDestructor(f.destroy) } // Fork forks this FSContext. // // This is not a valid call after destroy. func (f *FSContext) Fork() *FSContext { f.mu.Lock() defer f.mu.Unlock() f.cwd.IncRef() f.root.IncRef() return &FSContext{ cwd: f.cwd, root: f.root, umask: f.umask, } } // WorkingDirectory returns the current working directory. // // This will return nil if called after destroy(), otherwise it will return a // Dirent with a reference taken. func (f *FSContext) WorkingDirectory() *fs.Dirent { f.mu.Lock() defer f.mu.Unlock() if f.cwd != nil { f.cwd.IncRef() } return f.cwd } // SetWorkingDirectory sets the current working directory. // This will take an extra reference on the Dirent. // // This is not a valid call after destroy. func (f *FSContext) SetWorkingDirectory(d *fs.Dirent) { if d == nil { panic("FSContext.SetWorkingDirectory called with nil dirent") } f.mu.Lock() defer f.mu.Unlock() if f.cwd == nil { panic(fmt.Sprintf("FSContext.SetWorkingDirectory(%v)) called after destroy", d)) } old := f.cwd f.cwd = d d.IncRef() old.DecRef() } // RootDirectory returns the current filesystem root. // // This will return nil if called after destroy(), otherwise it will return a // Dirent with a reference taken. func (f *FSContext) RootDirectory() *fs.Dirent { f.mu.Lock() defer f.mu.Unlock() if f.root != nil { f.root.IncRef() } return f.root } // SetRootDirectory sets the root directory. // This will take an extra reference on the Dirent. // // This is not a valid call after free. func (f *FSContext) SetRootDirectory(d *fs.Dirent) { if d == nil { panic("FSContext.SetRootDirectory called with nil dirent") } f.mu.Lock() defer f.mu.Unlock() if f.root == nil { panic(fmt.Sprintf("FSContext.SetRootDirectory(%v)) called after destroy", d)) } old := f.root f.root = d d.IncRef() old.DecRef() } // Umask returns the current umask. func (f *FSContext) Umask() uint { f.mu.Lock() defer f.mu.Unlock() return f.umask } // SwapUmask atomically sets the current umask and returns the old umask. func (f *FSContext) SwapUmask(mask uint) uint { f.mu.Lock() defer f.mu.Unlock() old := f.umask f.umask = mask return old }