github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fs.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 fs implements a virtual filesystem layer. 16 // 17 // Specific filesystem implementations must implement the InodeOperations 18 // interface (inode.go). 19 // 20 // The MountNamespace (mounts.go) is used to create a collection of mounts in 21 // a filesystem rooted at a given Inode. 22 // 23 // MountSources (mount.go) form a tree, with each mount holding pointers to its 24 // parent and children. 25 // 26 // Dirents (dirents.go) wrap Inodes in a caching layer. 27 // 28 // When multiple locks are to be held at the same time, they should be acquired 29 // in the following order. 30 // 31 // Either: 32 // File.mu 33 // Locks in FileOperations implementations 34 // goto Dirent-Locks 35 // 36 // Or: 37 // MountNamespace.mu 38 // goto Dirent-Locks 39 // 40 // Dirent-Locks: 41 // renameMu 42 // Dirent.dirMu 43 // Dirent.mu 44 // DirentCache.mu 45 // Inode.Watches.mu (see `Inotify` for other lock ordering) 46 // MountSource.mu 47 // Inode.appendMu 48 // Locks in InodeOperations implementations or overlayEntry 49 // 50 // If multiple Dirent or MountSource locks must be taken, locks in the parent must be 51 // taken before locks in their children. 52 // 53 // If locks must be taken on multiple unrelated Dirents, renameMu must be taken 54 // first. See lockForRename. 55 package fs 56 57 import ( 58 "github.com/SagerNet/gvisor/pkg/context" 59 "github.com/SagerNet/gvisor/pkg/log" 60 "github.com/SagerNet/gvisor/pkg/sync" 61 ) 62 63 var ( 64 // workMu is used to synchronize pending asynchronous work. Async work 65 // runs with the lock held for reading. AsyncBarrier will take the lock 66 // for writing, thus ensuring that all Async work completes before 67 // AsyncBarrier returns. 68 workMu sync.CrossGoroutineRWMutex 69 70 // asyncError is used to store up to one asynchronous execution error. 71 asyncError = make(chan error, 1) 72 ) 73 74 // AsyncBarrier waits for all outstanding asynchronous work to complete. 75 func AsyncBarrier() { 76 workMu.Lock() 77 workMu.Unlock() 78 } 79 80 // Async executes a function asynchronously. 81 // 82 // Async must not be called recursively. 83 // +checklocksignore 84 func Async(f func()) { 85 workMu.RLock() 86 go asyncWork(f) // S/R-SAFE: AsyncBarrier must be called. 87 } 88 89 // +checklocksignore 90 func asyncWork(f func()) { 91 // Ensure RUnlock in case of panic. 92 defer workMu.RUnlock() 93 f() 94 } 95 96 // AsyncWithContext is just like Async, except that it calls the asynchronous 97 // function with the given context as argument. This function exists to avoid 98 // needing to allocate an extra function on the heap in a hot path. 99 // +checklocksignore 100 func AsyncWithContext(ctx context.Context, f func(context.Context)) { 101 workMu.RLock() 102 go asyncWorkWithContext(ctx, f) 103 } 104 105 // +checklocksignore 106 func asyncWorkWithContext(ctx context.Context, f func(context.Context)) { 107 // Ensure RUnlock in case of panic. 108 defer workMu.RUnlock() 109 f(ctx) 110 } 111 112 // AsyncErrorBarrier waits for all outstanding asynchronous work to complete, or 113 // the first async error to arrive. Other unfinished async executions will 114 // continue in the background. Other past and future async errors are ignored. 115 func AsyncErrorBarrier() error { 116 wait := make(chan struct{}, 1) 117 go func() { // S/R-SAFE: Does not touch persistent state. 118 AsyncBarrier() 119 wait <- struct{}{} 120 }() 121 select { 122 case <-wait: 123 select { 124 case err := <-asyncError: 125 return err 126 default: 127 return nil 128 } 129 case err := <-asyncError: 130 return err 131 } 132 } 133 134 // CatchError tries to capture the potential async error returned by the 135 // function. At most one async error will be captured globally so excessive 136 // errors will be dropped. 137 func CatchError(f func() error) func() { 138 return func() { 139 if err := f(); err != nil { 140 select { 141 case asyncError <- err: 142 default: 143 log.Warningf("excessive async error dropped: %v", err) 144 } 145 } 146 } 147 } 148 149 // ErrSaveRejection indicates a failed save due to unsupported file system state 150 // such as dangling open fd, etc. 151 type ErrSaveRejection struct { 152 // Err is the wrapped error. 153 Err error 154 } 155 156 // Error returns a sensible description of the save rejection error. 157 func (e *ErrSaveRejection) Error() string { 158 return "save rejected due to unsupported file system state: " + e.Err.Error() 159 } 160 161 // ErrCorruption indicates a failed restore due to external file system state in 162 // corruption. 163 type ErrCorruption struct { 164 // Err is the wrapped error. 165 Err error 166 } 167 168 // Error returns a sensible description of the restore error. 169 func (e ErrCorruption) Error() string { 170 return "restore failed due to external file system state in corruption: " + e.Err.Error() 171 }