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
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"sync/atomic"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/refs"
|
|
|
|
"gvisor.dev/gvisor/pkg/sentry/context"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
2019-06-27 21:22:40 +00:00
|
|
|
// DirentOperations provide file systems greater control over how long a Dirent
|
|
|
|
// stays pinned in core. Implementations must not take Dirent.mu.
|
2018-04-27 17:37:02 +00:00
|
|
|
type DirentOperations interface {
|
2018-08-27 21:25:21 +00:00
|
|
|
// Revalidate is called during lookup each time we encounter a Dirent
|
|
|
|
// in the cache. Implementations may update stale properties of the
|
|
|
|
// child Inode. If Revalidate returns true, then the entire Inode will
|
|
|
|
// be reloaded.
|
|
|
|
//
|
|
|
|
// Revalidate will never be called on a Inode that is mounted.
|
|
|
|
Revalidate(ctx context.Context, name string, parent, child *Inode) bool
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2018-07-19 21:56:42 +00:00
|
|
|
// Keep returns true if the Dirent should be kept in memory for as long
|
|
|
|
// as possible beyond any active references.
|
2018-04-27 17:37:02 +00:00
|
|
|
Keep(dirent *Dirent) bool
|
2019-06-27 21:22:40 +00:00
|
|
|
|
|
|
|
// CacheReaddir returns true if directory entries returned by
|
|
|
|
// FileOperations.Readdir may be cached for future use.
|
|
|
|
//
|
|
|
|
// Postconditions: This method must always return the same value.
|
|
|
|
CacheReaddir() bool
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MountSourceOperations contains filesystem specific operations.
|
|
|
|
type MountSourceOperations interface {
|
|
|
|
// DirentOperations provide optional extra management of Dirents.
|
|
|
|
DirentOperations
|
|
|
|
|
|
|
|
// Destroy destroys the MountSource.
|
|
|
|
Destroy()
|
|
|
|
|
|
|
|
// Below are MountSourceOperations that do not conform to Linux.
|
|
|
|
|
|
|
|
// ResetInodeMappings clears all mappings of Inodes before SaveInodeMapping
|
|
|
|
// is called.
|
|
|
|
ResetInodeMappings()
|
|
|
|
|
|
|
|
// SaveInodeMappings is called during saving to store, for each reachable
|
|
|
|
// Inode in the mounted filesystem, a mapping of Inode.StableAttr.InodeID
|
|
|
|
// to the Inode's path relative to its mount point. If an Inode is
|
|
|
|
// reachable at more than one path due to hard links, it is unspecified
|
|
|
|
// which path is mapped. Filesystems that do not use this information to
|
|
|
|
// restore inodes can make SaveInodeMappings a no-op.
|
|
|
|
SaveInodeMapping(inode *Inode, path string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// InodeMappings defines a fmt.Stringer MountSource Inode mappings.
|
|
|
|
type InodeMappings map[uint64]string
|
|
|
|
|
|
|
|
// String implements fmt.Stringer.String.
|
|
|
|
func (i InodeMappings) String() string {
|
|
|
|
var mappingsBuf bytes.Buffer
|
|
|
|
mappingsBuf.WriteString("\n")
|
|
|
|
for ino, name := range i {
|
|
|
|
mappingsBuf.WriteString(fmt.Sprintf("\t%q\t\tinode number %d\n", name, ino))
|
|
|
|
}
|
|
|
|
return mappingsBuf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// MountSource represents a source of file objects.
|
|
|
|
//
|
|
|
|
// MountSource corresponds to struct super_block in Linux.
|
|
|
|
//
|
|
|
|
// A mount source may represent a physical device (or a partition of a physical
|
|
|
|
// device) or a virtual source of files such as procfs for a specific PID
|
|
|
|
// namespace. There should be only one mount source per logical device. E.g.
|
|
|
|
// there should be only procfs mount source for a given PID namespace.
|
|
|
|
//
|
|
|
|
// A mount source represents files as inodes. Every inode belongs to exactly
|
|
|
|
// one mount source. Each file object may only be represented using one inode
|
|
|
|
// object in a sentry instance.
|
|
|
|
//
|
2019-05-23 11:15:18 +00:00
|
|
|
// TODO(b/63601033): Move Flags out of MountSource to Mount.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type MountSource struct {
|
|
|
|
refs.AtomicRefCount
|
|
|
|
|
|
|
|
// MountSourceOperations defines filesystem specific behavior.
|
|
|
|
MountSourceOperations
|
|
|
|
|
2019-05-08 21:34:01 +00:00
|
|
|
// FilesystemType is the type of the filesystem backing this mount.
|
|
|
|
FilesystemType string
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// Flags are the flags that this filesystem was mounted with.
|
|
|
|
Flags MountSourceFlags
|
|
|
|
|
|
|
|
// fscache keeps Dirents pinned beyond application references to them.
|
|
|
|
// It must be flushed before kernel.SaveTo.
|
2019-04-10 18:26:10 +00:00
|
|
|
fscache *DirentCache
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// direntRefs is the sum of references on all Dirents in this MountSource.
|
|
|
|
//
|
|
|
|
// direntRefs is increased when a Dirent in MountSource is IncRef'd, and
|
|
|
|
// decreased when a Dirent in MountSource is DecRef'd.
|
|
|
|
//
|
|
|
|
// To cleanly unmount a MountSource, one must check that no direntRefs are
|
|
|
|
// held anymore. To check, one must hold root.parent.dirMu of the
|
|
|
|
// MountSource's root Dirent before reading direntRefs to prevent further
|
|
|
|
// walks to Dirents in this MountSource.
|
|
|
|
//
|
|
|
|
// direntRefs must be atomically changed.
|
|
|
|
direntRefs uint64
|
|
|
|
}
|
|
|
|
|
2019-04-17 19:56:23 +00:00
|
|
|
// DefaultDirentCacheSize is the number of Dirents that the VFS can hold an
|
|
|
|
// extra reference on.
|
|
|
|
const DefaultDirentCacheSize uint64 = 1000
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// NewMountSource returns a new MountSource. Filesystem may be nil if there is no
|
|
|
|
// filesystem backing the mount.
|
2019-06-14 01:39:43 +00:00
|
|
|
func NewMountSource(ctx context.Context, mops MountSourceOperations, filesystem Filesystem, flags MountSourceFlags) *MountSource {
|
2019-05-08 21:34:01 +00:00
|
|
|
fsType := "none"
|
|
|
|
if filesystem != nil {
|
|
|
|
fsType = filesystem.Name()
|
|
|
|
}
|
2019-06-29 03:06:33 +00:00
|
|
|
msrc := MountSource{
|
2018-04-27 17:37:02 +00:00
|
|
|
MountSourceOperations: mops,
|
2018-08-02 22:55:19 +00:00
|
|
|
Flags: flags,
|
2019-05-08 21:34:01 +00:00
|
|
|
FilesystemType: fsType,
|
2019-04-17 19:56:23 +00:00
|
|
|
fscache: NewDirentCache(DefaultDirentCacheSize),
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-06-29 03:06:33 +00:00
|
|
|
msrc.EnableLeakCheck("fs.MountSource")
|
|
|
|
return &msrc
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// DirentRefs returns the current mount direntRefs.
|
|
|
|
func (msrc *MountSource) DirentRefs() uint64 {
|
|
|
|
return atomic.LoadUint64(&msrc.direntRefs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IncDirentRefs increases direntRefs.
|
|
|
|
func (msrc *MountSource) IncDirentRefs() {
|
|
|
|
atomic.AddUint64(&msrc.direntRefs, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecDirentRefs decrements direntRefs.
|
|
|
|
func (msrc *MountSource) DecDirentRefs() {
|
|
|
|
if atomic.AddUint64(&msrc.direntRefs, ^uint64(0)) == ^uint64(0) {
|
|
|
|
panic("Decremented zero mount reference direntRefs")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (msrc *MountSource) destroy() {
|
|
|
|
if c := msrc.DirentRefs(); c != 0 {
|
|
|
|
panic(fmt.Sprintf("MountSource with non-zero direntRefs is being destroyed: %d", c))
|
|
|
|
}
|
|
|
|
msrc.MountSourceOperations.Destroy()
|
|
|
|
}
|
|
|
|
|
|
|
|
// DecRef drops a reference on the MountSource.
|
|
|
|
func (msrc *MountSource) DecRef() {
|
|
|
|
msrc.DecRefWithDestructor(msrc.destroy)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FlushDirentRefs drops all references held by the MountSource on Dirents.
|
|
|
|
func (msrc *MountSource) FlushDirentRefs() {
|
|
|
|
msrc.fscache.Invalidate()
|
|
|
|
}
|
|
|
|
|
2019-04-17 19:56:23 +00:00
|
|
|
// SetDirentCacheMaxSize sets the max size to the dirent cache associated with
|
|
|
|
// this mount source.
|
|
|
|
func (msrc *MountSource) SetDirentCacheMaxSize(max uint64) {
|
|
|
|
msrc.fscache.setMaxSize(max)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDirentCacheLimiter sets the limiter objcet to the dirent cache associated
|
|
|
|
// with this mount source.
|
|
|
|
func (msrc *MountSource) SetDirentCacheLimiter(l *DirentCacheLimiter) {
|
|
|
|
msrc.fscache.limit = l
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// NewCachingMountSource returns a generic mount that will cache dirents
|
2019-01-15 04:33:29 +00:00
|
|
|
// aggressively.
|
2019-06-14 01:39:43 +00:00
|
|
|
func NewCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
|
|
|
|
return NewMountSource(ctx, &SimpleMountSourceOperations{
|
2019-06-27 21:22:40 +00:00
|
|
|
keep: true,
|
|
|
|
revalidate: false,
|
|
|
|
cacheReaddir: true,
|
2018-04-27 17:37:02 +00:00
|
|
|
}, filesystem, flags)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNonCachingMountSource returns a generic mount that will never cache dirents.
|
2019-06-14 01:39:43 +00:00
|
|
|
func NewNonCachingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
|
|
|
|
return NewMountSource(ctx, &SimpleMountSourceOperations{
|
2019-06-27 21:22:40 +00:00
|
|
|
keep: false,
|
|
|
|
revalidate: false,
|
|
|
|
cacheReaddir: false,
|
2018-08-11 00:15:27 +00:00
|
|
|
}, filesystem, flags)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRevalidatingMountSource returns a generic mount that will cache dirents,
|
2019-06-27 21:22:40 +00:00
|
|
|
// but will revalidate them on each lookup and always perform uncached readdir.
|
2019-06-14 01:39:43 +00:00
|
|
|
func NewRevalidatingMountSource(ctx context.Context, filesystem Filesystem, flags MountSourceFlags) *MountSource {
|
|
|
|
return NewMountSource(ctx, &SimpleMountSourceOperations{
|
2019-06-27 21:22:40 +00:00
|
|
|
keep: true,
|
|
|
|
revalidate: true,
|
|
|
|
cacheReaddir: false,
|
2018-04-27 17:37:02 +00:00
|
|
|
}, filesystem, flags)
|
|
|
|
}
|
|
|
|
|
2019-01-15 04:33:29 +00:00
|
|
|
// NewPseudoMountSource returns a "pseudo" mount source that is not backed by
|
|
|
|
// an actual filesystem. It is always non-caching.
|
2019-06-14 01:39:43 +00:00
|
|
|
func NewPseudoMountSource(ctx context.Context) *MountSource {
|
|
|
|
return NewMountSource(ctx, &SimpleMountSourceOperations{
|
2019-06-27 21:22:40 +00:00
|
|
|
keep: false,
|
|
|
|
revalidate: false,
|
|
|
|
cacheReaddir: false,
|
2019-01-15 04:33:29 +00:00
|
|
|
}, nil, MountSourceFlags{})
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// SimpleMountSourceOperations implements MountSourceOperations.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type SimpleMountSourceOperations struct {
|
2019-06-27 21:22:40 +00:00
|
|
|
keep bool
|
|
|
|
revalidate bool
|
|
|
|
cacheReaddir bool
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Revalidate implements MountSourceOperations.Revalidate.
|
2018-08-27 21:25:21 +00:00
|
|
|
func (smo *SimpleMountSourceOperations) Revalidate(context.Context, string, *Inode, *Inode) bool {
|
2018-08-11 00:15:27 +00:00
|
|
|
return smo.revalidate
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Keep implements MountSourceOperations.Keep.
|
|
|
|
func (smo *SimpleMountSourceOperations) Keep(*Dirent) bool {
|
|
|
|
return smo.keep
|
|
|
|
}
|
|
|
|
|
2019-06-27 21:22:40 +00:00
|
|
|
// CacheReaddir implements MountSourceOperations.CacheReaddir.
|
|
|
|
func (smo *SimpleMountSourceOperations) CacheReaddir() bool {
|
|
|
|
return smo.cacheReaddir
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// ResetInodeMappings implements MountSourceOperations.ResetInodeMappings.
|
|
|
|
func (*SimpleMountSourceOperations) ResetInodeMappings() {}
|
|
|
|
|
|
|
|
// SaveInodeMapping implements MountSourceOperations.SaveInodeMapping.
|
|
|
|
func (*SimpleMountSourceOperations) SaveInodeMapping(*Inode, string) {}
|
|
|
|
|
|
|
|
// Destroy implements MountSourceOperations.Destroy.
|
|
|
|
func (*SimpleMountSourceOperations) Destroy() {}
|
|
|
|
|
|
|
|
// Info defines attributes of a filesystem.
|
|
|
|
type Info struct {
|
|
|
|
// Type is the filesystem type magic value.
|
|
|
|
Type uint64
|
|
|
|
|
|
|
|
// TotalBlocks is the total data blocks in the filesystem.
|
|
|
|
TotalBlocks uint64
|
|
|
|
|
|
|
|
// FreeBlocks is the number of free blocks available.
|
|
|
|
FreeBlocks uint64
|
|
|
|
|
|
|
|
// TotalFiles is the total file nodes in the filesystem.
|
|
|
|
TotalFiles uint64
|
|
|
|
|
|
|
|
// FreeFiles is the number of free file nodes.
|
|
|
|
FreeFiles uint64
|
|
|
|
}
|