Add VFS2 support for /proc/filesystems.

Updates #1195

PiperOrigin-RevId: 287269106
This commit is contained in:
Jamie Liu
2019-12-27 00:13:54 -08:00
committed by gVisor bot
parent 3c125eb219
commit 796f53c0be
11 changed files with 103 additions and 39 deletions
@@ -50,7 +50,9 @@ func setUp(b *testing.B, imagePath string) (context.Context, *vfs.VirtualFilesys
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("extfs", ext.FilesystemType{})
vfsObj.MustRegisterFilesystemType("extfs", ext.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, imagePath, "extfs", &vfs.GetFilesystemOptions{InternalData: int(f.Fd())})
if err != nil {
f.Close()
+3 -1
View File
@@ -66,7 +66,9 @@ func setUp(t *testing.T, imagePath string) (context.Context, *vfs.VirtualFilesys
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("extfs", FilesystemType{})
vfsObj.MustRegisterFilesystemType("extfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, localImagePath, "extfs", &vfs.GetFilesystemOptions{InternalData: int(f.Fd())})
if err != nil {
f.Close()
+3 -1
View File
@@ -59,7 +59,9 @@ func newTestSystem(t *testing.T, rootFn RootDentryFn) *TestSystem {
ctx := contexttest.Context(t)
creds := auth.CredentialsFromContext(ctx)
v := vfs.New()
v.MustRegisterFilesystemType("testfs", &fsType{rootFn: rootFn})
v.MustRegisterFilesystemType("testfs", &fsType{rootFn: rootFn}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mns, err := v.NewMountNamespace(ctx, creds, "", "testfs", &vfs.GetFilesystemOptions{})
if err != nil {
t.Fatalf("Failed to create testfs root mount: %v", err)
+6 -2
View File
@@ -176,7 +176,9 @@ func BenchmarkVFS2MemfsStat(b *testing.B) {
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{})
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "memfs", &vfs.GetFilesystemOptions{})
if err != nil {
b.Fatalf("failed to create tmpfs root mount: %v", err)
@@ -365,7 +367,9 @@ func BenchmarkVFS2MemfsMountStat(b *testing.B) {
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{})
vfsObj.MustRegisterFilesystemType("memfs", memfs.FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "memfs", &vfs.GetFilesystemOptions{})
if err != nil {
b.Fatalf("failed to create tmpfs root mount: %v", err)
+3 -1
View File
@@ -152,7 +152,9 @@ func setup(t *testing.T) (context.Context, *auth.Credentials, *vfs.VirtualFilesy
// Create VFS.
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("memfs", FilesystemType{})
vfsObj.MustRegisterFilesystemType("memfs", FilesystemType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "memfs", &vfs.GetFilesystemOptions{})
if err != nil {
t.Fatalf("failed to create tmpfs root mount: %v", err)
+16 -14
View File
@@ -56,25 +56,25 @@ func checkDots(dirs []vfs.Dirent) ([]vfs.Dirent, error) {
func checkTasksStaticFiles(gots []vfs.Dirent) ([]vfs.Dirent, error) {
wants := map[string]vfs.Dirent{
"loadavg": vfs.Dirent{Type: linux.DT_REG},
"meminfo": vfs.Dirent{Type: linux.DT_REG},
"mounts": vfs.Dirent{Type: linux.DT_LNK},
"self": vfs.Dirent{Type: linux.DT_LNK},
"stat": vfs.Dirent{Type: linux.DT_REG},
"thread-self": vfs.Dirent{Type: linux.DT_LNK},
"version": vfs.Dirent{Type: linux.DT_REG},
"loadavg": {Type: linux.DT_REG},
"meminfo": {Type: linux.DT_REG},
"mounts": {Type: linux.DT_LNK},
"self": {Type: linux.DT_LNK},
"stat": {Type: linux.DT_REG},
"thread-self": {Type: linux.DT_LNK},
"version": {Type: linux.DT_REG},
}
return checkFiles(gots, wants)
}
func checkTaskStaticFiles(gots []vfs.Dirent) ([]vfs.Dirent, error) {
wants := map[string]vfs.Dirent{
"io": vfs.Dirent{Type: linux.DT_REG},
"maps": vfs.Dirent{Type: linux.DT_REG},
"smaps": vfs.Dirent{Type: linux.DT_REG},
"stat": vfs.Dirent{Type: linux.DT_REG},
"statm": vfs.Dirent{Type: linux.DT_REG},
"status": vfs.Dirent{Type: linux.DT_REG},
"io": {Type: linux.DT_REG},
"maps": {Type: linux.DT_REG},
"smaps": {Type: linux.DT_REG},
"stat": {Type: linux.DT_REG},
"statm": {Type: linux.DT_REG},
"status": {Type: linux.DT_REG},
}
return checkFiles(gots, wants)
}
@@ -114,7 +114,9 @@ func setup() (context.Context, *vfs.VirtualFilesystem, vfs.VirtualDentry, error)
creds := auth.CredentialsFromContext(ctx)
vfsObj := vfs.New()
vfsObj.MustRegisterFilesystemType("procfs", &procFSType{})
vfsObj.MustRegisterFilesystemType("procfs", &procFSType{}, &vfs.RegisterFilesystemTypeOptions{
AllowUserMount: true,
})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "procfs", &vfs.GetFilesystemOptions{})
if err != nil {
return nil, nil, vfs.VirtualDentry{}, fmt.Errorf("NewMountNamespace(): %v", err)
@@ -89,7 +89,7 @@ func TestGenCountFD(t *testing.T) {
creds := auth.CredentialsFromContext(ctx)
vfsObj := New() // vfs.New()
vfsObj.MustRegisterFilesystemType("testfs", FDTestFilesystemType{})
vfsObj.MustRegisterFilesystemType("testfs", FDTestFilesystemType{}, &RegisterFilesystemTypeOptions{})
mntns, err := vfsObj.NewMountNamespace(ctx, creds, "", "testfs", &GetFilesystemOptions{})
if err != nil {
t.Fatalf("failed to create testfs root mount: %v", err)
+49 -6
View File
@@ -15,6 +15,7 @@
package vfs
import (
"bytes"
"fmt"
"gvisor.dev/gvisor/pkg/sentry/context"
@@ -43,28 +44,70 @@ type GetFilesystemOptions struct {
InternalData interface{}
}
type registeredFilesystemType struct {
fsType FilesystemType
opts RegisterFilesystemTypeOptions
}
// RegisterFilesystemTypeOptions contains options to
// VirtualFilesystem.RegisterFilesystem().
type RegisterFilesystemTypeOptions struct {
// If AllowUserMount is true, allow calls to VirtualFilesystem.MountAt()
// for which MountOptions.InternalMount == false to use this filesystem
// type.
AllowUserMount bool
// If AllowUserList is true, make this filesystem type visible in
// /proc/filesystems.
AllowUserList bool
// If RequiresDevice is true, indicate that mounting this filesystem
// requires a block device as the mount source in /proc/filesystems.
RequiresDevice bool
}
// RegisterFilesystemType registers the given FilesystemType in vfs with the
// given name.
func (vfs *VirtualFilesystem) RegisterFilesystemType(name string, fsType FilesystemType) error {
func (vfs *VirtualFilesystem) RegisterFilesystemType(name string, fsType FilesystemType, opts *RegisterFilesystemTypeOptions) error {
vfs.fsTypesMu.Lock()
defer vfs.fsTypesMu.Unlock()
if existing, ok := vfs.fsTypes[name]; ok {
return fmt.Errorf("name %q is already registered to filesystem type %T", name, existing)
return fmt.Errorf("name %q is already registered to filesystem type %T", name, existing.fsType)
}
vfs.fsTypes[name] = &registeredFilesystemType{
fsType: fsType,
opts: *opts,
}
vfs.fsTypes[name] = fsType
return nil
}
// MustRegisterFilesystemType is equivalent to RegisterFilesystemType but
// panics on failure.
func (vfs *VirtualFilesystem) MustRegisterFilesystemType(name string, fsType FilesystemType) {
if err := vfs.RegisterFilesystemType(name, fsType); err != nil {
func (vfs *VirtualFilesystem) MustRegisterFilesystemType(name string, fsType FilesystemType, opts *RegisterFilesystemTypeOptions) {
if err := vfs.RegisterFilesystemType(name, fsType, opts); err != nil {
panic(fmt.Sprintf("failed to register filesystem type %T: %v", fsType, err))
}
}
func (vfs *VirtualFilesystem) getFilesystemType(name string) FilesystemType {
func (vfs *VirtualFilesystem) getFilesystemType(name string) *registeredFilesystemType {
vfs.fsTypesMu.RLock()
defer vfs.fsTypesMu.RUnlock()
return vfs.fsTypes[name]
}
// GenerateProcFilesystems emits the contents of /proc/filesystems for vfs to
// buf.
func (vfs *VirtualFilesystem) GenerateProcFilesystems(buf *bytes.Buffer) {
vfs.fsTypesMu.RLock()
defer vfs.fsTypesMu.RUnlock()
for name, rft := range vfs.fsTypes {
if !rft.opts.AllowUserList {
continue
}
var nodev string
if !rft.opts.RequiresDevice {
nodev = "nodev"
}
fmt.Fprintf(buf, "%s\t%s\n", nodev, name)
}
}
+9 -6
View File
@@ -112,11 +112,11 @@ type MountNamespace struct {
// configured by the given arguments. A reference is taken on the returned
// MountNamespace.
func (vfs *VirtualFilesystem) NewMountNamespace(ctx context.Context, creds *auth.Credentials, source, fsTypeName string, opts *GetFilesystemOptions) (*MountNamespace, error) {
fsType := vfs.getFilesystemType(fsTypeName)
if fsType == nil {
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
return nil, syserror.ENODEV
}
fs, root, err := fsType.GetFilesystem(ctx, vfs, creds, source, *opts)
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, *opts)
if err != nil {
return nil, err
}
@@ -136,11 +136,14 @@ func (vfs *VirtualFilesystem) NewMountNamespace(ctx context.Context, creds *auth
// MountAt creates and mounts a Filesystem configured by the given arguments.
func (vfs *VirtualFilesystem) MountAt(ctx context.Context, creds *auth.Credentials, source string, target *PathOperation, fsTypeName string, opts *MountOptions) error {
fsType := vfs.getFilesystemType(fsTypeName)
if fsType == nil {
rft := vfs.getFilesystemType(fsTypeName)
if rft == nil {
return syserror.ENODEV
}
fs, root, err := fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if !opts.InternalMount && !rft.opts.AllowUserMount {
return syserror.ENODEV
}
fs, root, err := rft.fsType.GetFilesystem(ctx, vfs, creds, source, opts.GetFilesystemOptions)
if err != nil {
return err
}
+4
View File
@@ -50,6 +50,10 @@ type MknodOptions struct {
type MountOptions struct {
// GetFilesystemOptions contains options to FilesystemType.GetFilesystem().
GetFilesystemOptions GetFilesystemOptions
// If InternalMount is true, allow the use of filesystem types for which
// RegisterFilesystemTypeOptions.AllowUserMount == false.
InternalMount bool
}
// OpenOptions contains options to VirtualFilesystem.OpenAt() and
+6 -6
View File
@@ -75,23 +75,23 @@ type VirtualFilesystem struct {
// mountpoints is analogous to Linux's mountpoint_hashtable.
mountpoints map[*Dentry]map[*Mount]struct{}
// fsTypes contains all registered FilesystemTypes. fsTypes is protected by
// fsTypesMu.
fsTypesMu sync.RWMutex
fsTypes map[string]*registeredFilesystemType
// filesystems contains all Filesystems. filesystems is protected by
// filesystemsMu.
filesystemsMu sync.Mutex
filesystems map[*Filesystem]struct{}
// fsTypes contains all FilesystemTypes that are usable in the
// VirtualFilesystem. fsTypes is protected by fsTypesMu.
fsTypesMu sync.RWMutex
fsTypes map[string]FilesystemType
}
// New returns a new VirtualFilesystem with no mounts or FilesystemTypes.
func New() *VirtualFilesystem {
vfs := &VirtualFilesystem{
mountpoints: make(map[*Dentry]map[*Mount]struct{}),
fsTypes: make(map[string]*registeredFilesystemType),
filesystems: make(map[*Filesystem]struct{}),
fsTypes: make(map[string]FilesystemType),
}
vfs.mounts.Init()
return vfs