2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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)
|
2018-11-28 02:16:18 +00:00
|
|
|
// MountSource.mu
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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"
|
2018-05-08 18:36:11 +00:00
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/log"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
2018-05-08 18:36:11 +00:00
|
|
|
var (
|
2018-11-28 02:16:18 +00:00
|
|
|
// 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
|
2018-05-08 18:36:11 +00:00
|
|
|
|
|
|
|
// asyncError is used to store up to one asynchronous execution error.
|
|
|
|
asyncError = make(chan error, 1)
|
|
|
|
)
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// AsyncBarrier waits for all outstanding asynchronous work to complete.
|
|
|
|
func AsyncBarrier() {
|
2018-11-28 02:16:18 +00:00
|
|
|
workMu.Lock()
|
|
|
|
workMu.Unlock()
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Async executes a function asynchronously.
|
2018-11-28 02:16:18 +00:00
|
|
|
//
|
|
|
|
// Async must not be called recursively.
|
2018-04-27 17:37:02 +00:00
|
|
|
func Async(f func()) {
|
2018-11-28 02:16:18 +00:00
|
|
|
workMu.RLock()
|
|
|
|
go func() { // S/R-SAFE: AsyncBarrier must be called.
|
|
|
|
defer workMu.RUnlock() // Ensure RUnlock in case of panic.
|
2018-04-27 17:37:02 +00:00
|
|
|
f()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2019-02-08 23:53:16 +00:00
|
|
|
// 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)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2018-05-08 18:36:11 +00:00
|
|
|
// 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.
|
2018-11-28 02:16:18 +00:00
|
|
|
AsyncBarrier()
|
2018-05-08 18:36:11 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// 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()
|
|
|
|
}
|
2018-05-08 18:36:11 +00:00
|
|
|
|
|
|
|
// ErrCorruption indicates a failed restore due to external file system state in
|
|
|
|
// corruption.
|
|
|
|
type ErrCorruption struct {
|
|
|
|
// Err is the wrapped error.
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:17:52 +00:00
|
|
|
// Error returns a sensible description of the restore error.
|
2018-05-08 18:36:11 +00:00
|
|
|
func (e ErrCorruption) Error() string {
|
|
|
|
return "restore failed due to external file system state in corruption: " + e.Err.Error()
|
|
|
|
}
|