github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/mount.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
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"sync/atomic"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/refs"
    24  )
    25  
    26  // DirentOperations provide file systems greater control over how long a Dirent
    27  // stays pinned in core. Implementations must not take Dirent.mu.
    28  type DirentOperations interface {
    29  	// Revalidate is called during lookup each time we encounter a Dirent
    30  	// in the cache. Implementations may update stale properties of the
    31  	// child Inode. If Revalidate returns true, then the entire Inode will
    32  	// be reloaded.
    33  	//
    34  	// Revalidate will never be called on a Inode that is mounted.
    35  	Revalidate(ctx context.Context, name string, parent, child *Inode) bool
    36  
    37  	// Keep returns true if the Dirent should be kept in memory for as long
    38  	// as possible beyond any active references.
    39  	Keep(dirent *Dirent) bool
    40  
    41  	// CacheReaddir returns true if directory entries returned by
    42  	// FileOperations.Readdir may be cached for future use.
    43  	//
    44  	// Postconditions: This method must always return the same value.
    45  	CacheReaddir() bool
    46  }
    47  
    48  // MountSourceOperations contains filesystem specific operations.
    49  type MountSourceOperations interface {
    50  	// DirentOperations provide optional extra management of Dirents.
    51  	DirentOperations
    52  
    53  	// Destroy destroys the MountSource.
    54  	Destroy(ctx context.Context)
    55  
    56  	// Below are MountSourceOperations that do not conform to Linux.
    57  
    58  	// ResetInodeMappings clears all mappings of Inodes before SaveInodeMapping
    59  	// is called.
    60  	ResetInodeMappings()
    61  
    62  	// SaveInodeMappings is called during saving to store, for each reachable
    63  	// Inode in the mounted filesystem, a mapping of Inode.StableAttr.InodeID
    64  	// to the Inode's path relative to its mount point. If an Inode is
    65  	// reachable at more than one path due to hard links, it is unspecified
    66  	// which path is mapped. Filesystems that do not use this information to
    67  	// restore inodes can make SaveInodeMappings a no-op.
    68  	SaveInodeMapping(inode *Inode, path string)
    69  }
    70  
    71  // InodeMappings defines a fmt.Stringer MountSource Inode mappings.
    72  type InodeMappings map[uint64]string
    73  
    74  // String implements fmt.Stringer.String.
    75  func (i InodeMappings) String() string {
    76  	var mappingsBuf bytes.Buffer
    77  	mappingsBuf.WriteString("\n")
    78  	for ino, name := range i {
    79  		mappingsBuf.WriteString(fmt.Sprintf("\t%q\t\tinode number %d\n", name, ino))
    80  	}
    81  	return mappingsBuf.String()
    82  }
    83  
    84  // MountSource represents a source of file objects.
    85  //
    86  // MountSource corresponds to struct super_block in Linux.
    87  //
    88  // A mount source may represent a physical device (or a partition of a physical
    89  // device) or a virtual source of files such as procfs for a specific PID
    90  // namespace. There should be only one mount source per logical device. E.g.
    91  // there should be only procfs mount source for a given PID namespace.
    92  //
    93  // A mount source represents files as inodes. Every inode belongs to exactly
    94  // one mount source. Each file object may only be represented using one inode
    95  // object in a sentry instance.
    96  //
    97  // TODO(b/63601033): Move Flags out of MountSource to Mount.
    98  //
    99  // +stateify savable
   100  type MountSource struct {
   101  	refs.AtomicRefCount
   102  
   103  	// MountSourceOperations defines filesystem specific behavior.
   104  	MountSourceOperations
   105  
   106  	// FilesystemType is the type of the filesystem backing this mount.
   107  	FilesystemType string
   108  
   109  	// Flags are the flags that this filesystem was mounted with.
   110  	Flags MountSourceFlags
   111  
   112  	// fscache keeps Dirents pinned beyond application references to them.
   113  	// It must be flushed before kernel.SaveTo.
   114  	fscache *DirentCache
   115  
   116  	// direntRefs is the sum of references on all Dirents in this MountSource.
   117  	//
   118  	// direntRefs is increased when a Dirent in MountSource is IncRef'd, and
   119  	// decreased when a Dirent in MountSource is DecRef'd.
   120  	//
   121  	// To cleanly unmount a MountSource, one must check that no direntRefs are
   122  	// held anymore. To check, one must hold root.parent.dirMu of the
   123  	// MountSource's root Dirent before reading direntRefs to prevent further
   124  	// walks to Dirents in this MountSource.
   125  	//
   126  	// direntRefs must be atomically changed.
   127  	direntRefs uint64
   128  }
   129  
   130  // DefaultDirentCacheSize is the number of Dirents that the VFS can hold an
   131  // extra reference on.
   132  const DefaultDirentCacheSize uint64 = 1000
   133  
   134  // NewMountSource returns a new MountSource. Filesystem may be nil if there is no
   135  // filesystem backing the mount.
   136  func NewMountSource(ctx context.Context, mops MountSourceOperations, filesystem Filesystem, flags MountSourceFlags) *MountSource {
   137  	fsType := "none"
   138  	if filesystem != nil {
   139  		fsType = filesystem.Name()
   140  	}
   141  	msrc := MountSource{
   142  		MountSourceOperations: mops,
   143  		Flags:                 flags,
   144  		FilesystemType:        fsType,
   145  		fscache:               NewDirentCache(DefaultDirentCacheSize),
   146  	}
   147  	msrc.EnableLeakCheck("fs.MountSource")
   148  	return &msrc
   149  }
   150  
   151  // DirentRefs returns the current mount direntRefs.
   152  func (msrc *MountSource) DirentRefs() uint64 {
   153  	return atomic.LoadUint64(&msrc.direntRefs)
   154  }
   155  
   156  // IncDirentRefs increases direntRefs.
   157  func (msrc *MountSource) IncDirentRefs() {
   158  	atomic.AddUint64(&msrc.direntRefs, 1)
   159  }
   160  
   161  // DecDirentRefs decrements direntRefs.
   162  func (msrc *MountSource) DecDirentRefs() {
   163  	if atomic.AddUint64(&msrc.direntRefs, ^uint64(0)) == ^uint64(0) {
   164  		panic("Decremented zero mount reference direntRefs")
   165  	}
   166  }
   167  
   168  func (msrc *MountSource) destroy(ctx context.Context) {
   169  	if c := msrc.DirentRefs(); c != 0 {
   170  		panic(fmt.Sprintf("MountSource with non-zero direntRefs is being destroyed: %d", c))
   171  	}
   172  	msrc.MountSourceOperations.Destroy(ctx)
   173  }
   174  
   175  // DecRef drops a reference on the MountSource.
   176  func (msrc *MountSource) DecRef(ctx context.Context) {
   177  	msrc.DecRefWithDestructor(ctx, msrc.destroy)
   178  }
   179  
   180  // FlushDirentRefs drops all references held by the MountSource on Dirents.
   181  func (msrc *MountSource) FlushDirentRefs() {
   182  	msrc.fscache.Invalidate()
   183  }
   184  
   185  // SetDirentCacheMaxSize sets the max size to the dirent cache associated with
   186  // this mount source.
   187  func (msrc *MountSource) SetDirentCacheMaxSize(max uint64) {
   188  	msrc.fscache.setMaxSize(max)
   189  }
   190  
   191  // SetDirentCacheLimiter sets the limiter objcet to the dirent cache associated
   192  // with this mount source.
   193  func (msrc *MountSource) SetDirentCacheLimiter(l *DirentCacheLimiter) {
   194  	msrc.fscache.limit = l
   195  }
   196  
   197  // NewCachingMountSource returns a generic mount that will cache dirents
   198  // aggressively.
   199  func NewCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
   200  	return NewMountSource(ctx, &SimpleMountSourceOperations{
   201  		keep:         true,
   202  		revalidate:   false,
   203  		cacheReaddir: true,
   204  	}, filesystem, flags)
   205  }
   206  
   207  // NewNonCachingMountSource returns a generic mount that will never cache dirents.
   208  func NewNonCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
   209  	return NewMountSource(ctx, &SimpleMountSourceOperations{
   210  		keep:         false,
   211  		revalidate:   false,
   212  		cacheReaddir: false,
   213  	}, filesystem, flags)
   214  }
   215  
   216  // NewRevalidatingMountSource returns a generic mount that will cache dirents,
   217  // but will revalidate them on each lookup and always perform uncached readdir.
   218  func NewRevalidatingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
   219  	return NewMountSource(ctx, &SimpleMountSourceOperations{
   220  		keep:         true,
   221  		revalidate:   true,
   222  		cacheReaddir: false,
   223  	}, filesystem, flags)
   224  }
   225  
   226  // NewPseudoMountSource returns a "pseudo" mount source that is not backed by
   227  // an actual filesystem. It is always non-caching.
   228  func NewPseudoMountSource(ctx context.Context) *MountSource {
   229  	return NewMountSource(ctx, &SimpleMountSourceOperations{
   230  		keep:         false,
   231  		revalidate:   false,
   232  		cacheReaddir: false,
   233  	}, nil, MountSourceFlags{})
   234  }
   235  
   236  // SimpleMountSourceOperations implements MountSourceOperations.
   237  //
   238  // +stateify savable
   239  type SimpleMountSourceOperations struct {
   240  	keep         bool
   241  	revalidate   bool
   242  	cacheReaddir bool
   243  }
   244  
   245  // Revalidate implements MountSourceOperations.Revalidate.
   246  func (smo *SimpleMountSourceOperations) Revalidate(context.Context, string, *Inode, *Inode) bool {
   247  	return smo.revalidate
   248  }
   249  
   250  // Keep implements MountSourceOperations.Keep.
   251  func (smo *SimpleMountSourceOperations) Keep(*Dirent) bool {
   252  	return smo.keep
   253  }
   254  
   255  // CacheReaddir implements MountSourceOperations.CacheReaddir.
   256  func (smo *SimpleMountSourceOperations) CacheReaddir() bool {
   257  	return smo.cacheReaddir
   258  }
   259  
   260  // ResetInodeMappings implements MountSourceOperations.ResetInodeMappings.
   261  func (*SimpleMountSourceOperations) ResetInodeMappings() {}
   262  
   263  // SaveInodeMapping implements MountSourceOperations.SaveInodeMapping.
   264  func (*SimpleMountSourceOperations) SaveInodeMapping(*Inode, string) {}
   265  
   266  // Destroy implements MountSourceOperations.Destroy.
   267  func (*SimpleMountSourceOperations) Destroy(context.Context) {}
   268  
   269  // Info defines attributes of a filesystem.
   270  type Info struct {
   271  	// Type is the filesystem type magic value.
   272  	Type uint64
   273  
   274  	// TotalBlocks is the total data blocks in the filesystem.
   275  	TotalBlocks uint64
   276  
   277  	// FreeBlocks is the number of free blocks available.
   278  	FreeBlocks uint64
   279  
   280  	// TotalFiles is the total file nodes in the filesystem.
   281  	TotalFiles uint64
   282  
   283  	// FreeFiles is the number of free file nodes.
   284  	FreeFiles uint64
   285  }