// 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 // Inode.Watches.mu (see `Inotify` for other lock ordering) // MountSource.mu // Inode.appendMu // Locks in InodeOperations implementations or overlayEntry // // 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 ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sync" ) 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.CrossGoroutineRWMutex // 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() }