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  }