gvisor/pkg/sentry/fs/fs.go

162 lines
4.7 KiB
Go
Raw Normal View History

// 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 fs implements a virtual filesystem layer.
//
// Specific filesystem implementations must implement the InodeOperations
// interface (inode.go).
//
// The MountNamespace (mounts.go) is used to create a collection of mounts in
// a filesystem rooted at a given Inode.
//
// MountSources (mount.go) form a tree, with each mount holding pointers to its
// parent and children.
//
// Dirents (dirents.go) wrap Inodes in a caching layer.
//
// When multiple locks are to be held at the same time, they should be acquired
// in the following order.
//
// Either:
// File.mu
// Locks in FileOperations implementations
// goto Dirent-Locks
//
// Or:
// MountNamespace.mu
// goto Dirent-Locks
//
// Dirent-Locks:
// renameMu
// Dirent.dirMu
// Dirent.mu
// DirentCache.mu
// Locks in InodeOperations implementations or overlayEntry
// Inode.Watches.mu (see `Inotify` for other lock ordering)
// MountSource.mu
//
// If multiple Dirent or MountSource locks must be taken, locks in the parent must be
// taken before locks in their children.
//
// If locks must be taken on multiple unrelated Dirents, renameMu must be taken
// first. See lockForRename.
package fs
import (
"sync"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/context"
)
var (
// workMu is used to synchronize pending asynchronous work. Async work
// runs with the lock held for reading. AsyncBarrier will take the lock
// for writing, thus ensuring that all Async work completes before
// AsyncBarrier returns.
workMu sync.RWMutex
// asyncError is used to store up to one asynchronous execution error.
asyncError = make(chan error, 1)
)
// AsyncBarrier waits for all outstanding asynchronous work to complete.
func AsyncBarrier() {
workMu.Lock()
workMu.Unlock()
}
// Async executes a function asynchronously.
//
// Async must not be called recursively.
func Async(f func()) {
workMu.RLock()
go func() { // S/R-SAFE: AsyncBarrier must be called.
defer workMu.RUnlock() // Ensure RUnlock in case of panic.
f()
}()
}
// AsyncWithContext is just like Async, except that it calls the asynchronous
// function with the given context as argument. This function exists to avoid
// needing to allocate an extra function on the heap in a hot path.
func AsyncWithContext(ctx context.Context, f func(context.Context)) {
workMu.RLock()
go func() { // S/R-SAFE: AsyncBarrier must be called.
defer workMu.RUnlock() // Ensure RUnlock in case of panic.
f(ctx)
}()
}
// AsyncErrorBarrier waits for all outstanding asynchronous work to complete, or
// the first async error to arrive. Other unfinished async executions will
// continue in the background. Other past and future async errors are ignored.
func AsyncErrorBarrier() error {
wait := make(chan struct{}, 1)
go func() { // S/R-SAFE: Does not touch persistent state.
AsyncBarrier()
wait <- struct{}{}
}()
select {
case <-wait:
select {
case err := <-asyncError:
return err
default:
return nil
}
case err := <-asyncError:
return err
}
}
// CatchError tries to capture the potential async error returned by the
// function. At most one async error will be captured globally so excessive
// errors will be dropped.
func CatchError(f func() error) func() {
return func() {
if err := f(); err != nil {
select {
case asyncError <- err:
default:
log.Warningf("excessive async error dropped: %v", err)
}
}
}
}
// ErrSaveRejection indicates a failed save due to unsupported file system state
// such as dangling open fd, etc.
type ErrSaveRejection struct {
// Err is the wrapped error.
Err error
}
// Error returns a sensible description of the save rejection error.
func (e ErrSaveRejection) Error() string {
return "save rejected due to unsupported file system state: " + e.Err.Error()
}
// ErrCorruption indicates a failed restore due to external file system state in
// corruption.
type ErrCorruption struct {
// Err is the wrapped error.
Err error
}
// Error returns a sensible description of the restore error.
func (e ErrCorruption) Error() string {
return "restore failed due to external file system state in corruption: " + e.Err.Error()
}