Delete VFS1 filesystem implementations.

Updates #1624

PiperOrigin-RevId: 488986080
This commit is contained in:
Ayush Ranjan
2022-11-16 11:05:10 -08:00
committed by gVisor bot
parent 681c7ddd5a
commit 7eeeb796f8
178 changed files with 29 additions and 24241 deletions
+1 -1
View File
@@ -34,8 +34,8 @@ go_library(
"//pkg/fspath",
"//pkg/log",
"//pkg/sentry/fdimport",
"//pkg/sentry/fs/user",
"//pkg/sentry/fsimpl/host",
"//pkg/sentry/fsimpl/user",
"//pkg/sentry/fsmetric",
"//pkg/sentry/kernel",
"//pkg/sentry/kernel/auth",
+1 -1
View File
@@ -24,7 +24,7 @@ import (
"gvisor.dev/gvisor/pkg/log"
pb "gvisor.dev/gvisor/pkg/sentry/control/control_go_proto"
"gvisor.dev/gvisor/pkg/sentry/fdimport"
"gvisor.dev/gvisor/pkg/sentry/fs/user"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/user"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/limits"
+1 -1
View File
@@ -26,8 +26,8 @@ import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/sentry/fdimport"
"gvisor.dev/gvisor/pkg/sentry/fs/user"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/user"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
+2 -41
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "go_library", "go_test")
load("//tools:defs.bzl", "go_library")
load("//tools/go_generics:defs.bzl", "go_template_instance")
load("//pkg/sync/locking:locking.bzl", "declare_mutex")
@@ -60,7 +60,7 @@ go_library(
"//pkg/secio",
"//pkg/sentry/arch",
"//pkg/sentry/device",
"//pkg/sentry/fs/lock",
"//pkg/sentry/fsimpl/lock",
"//pkg/sentry/fsmetric",
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
@@ -123,42 +123,3 @@ declare_mutex(
package = "fs",
prefix = "inotify",
)
go_test(
name = "fs_x_test",
size = "small",
srcs = [
"copy_up_test.go",
"file_overlay_test.go",
"inode_overlay_test.go",
"mounts_test.go",
],
deps = [
":fs",
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/fs/ramfs",
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/kernel/contexttest",
"//pkg/sync",
"//pkg/usermem",
],
)
go_test(
name = "fs_test",
size = "small",
srcs = [
"dirent_cache_test.go",
"dirent_refs_test.go",
"mount_test.go",
"path_test.go",
],
library = ":fs",
deps = [
"//pkg/context",
"//pkg/sentry/contexttest",
"@org_golang_x_sys//unix:go_default_library",
],
)
-20
View File
@@ -1,20 +0,0 @@
load("//tools:defs.bzl", "go_library")
package(licenses = ["notice"])
go_library(
name = "anon",
srcs = [
"anon.go",
"device.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/context",
"//pkg/hostarch",
"//pkg/sentry/device",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
],
)
-42
View File
@@ -1,42 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 anon implements an anonymous inode, useful for implementing
// inodes for pseudo filesystems.
package anon
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
)
// NewInode constructs an anonymous Inode that is not associated
// with any real filesystem. Some types depend on completely pseudo
// "anon" inodes (eventfds, epollfds, etc).
func NewInode(ctx context.Context) *fs.Inode {
iops := &fsutil.SimpleFileInode{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, fs.RootOwner, fs.FilePermissions{
User: fs.PermMask{Read: true, Write: true},
}, linux.ANON_INODE_FS_MAGIC),
}
return fs.NewInode(ctx, iops, fs.NewPseudoMountSource(ctx), fs.StableAttr{
Type: fs.Anonymous,
DeviceID: PseudoDevice.DeviceID(),
InodeID: PseudoDevice.NextIno(),
BlockSize: hostarch.PageSize,
})
}
-22
View File
@@ -1,22 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 anon
import (
"gvisor.dev/gvisor/pkg/sentry/device"
)
// PseudoDevice is the device on which all anonymous inodes reside.
var PseudoDevice = device.NewAnonDevice()
-183
View File
@@ -1,183 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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_test
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"testing"
"gvisor.dev/gvisor/pkg/sentry/fs"
_ "gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/contexttest"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/usermem"
)
const (
// origFileSize is the original file size. This many bytes should be
// copied up before the test file is modified.
origFileSize = 4096
// truncatedFileSize is the size to truncate all test files.
truncateFileSize = 10
)
// TestConcurrentCopyUp is a copy up stress test for an overlay.
//
// It creates a 64-level deep directory tree in the lower filesystem and
// populates the last subdirectory with 64 files containing random content:
//
// /lower
// /sudir0/.../subdir63/
// /file0
// ...
// /file63
//
// The files are truncated concurrently by 4 goroutines per file.
// These goroutines contend with copying up all parent 64 subdirectories
// as well as the final file content.
//
// At the end of the test, we assert that the files respect the new truncated
// size and contain the content we expect.
func TestConcurrentCopyUp(t *testing.T) {
ctx := contexttest.Context(t)
files := makeOverlayTestFiles(t)
var wg sync.WaitGroup
for _, file := range files {
for i := 0; i < 4; i++ {
wg.Add(1)
go func(o *overlayTestFile) {
if err := o.File.Dirent.Inode.Truncate(ctx, o.File.Dirent, truncateFileSize); err != nil {
t.Errorf("failed to copy up: %v", err)
}
wg.Done()
}(file)
}
}
wg.Wait()
for _, file := range files {
got := make([]byte, origFileSize)
n, err := file.File.Readv(ctx, usermem.BytesIOSequence(got))
if int(n) != truncateFileSize {
t.Fatalf("read %d bytes from file, want %d", n, truncateFileSize)
}
if err != nil && err != io.EOF {
t.Fatalf("read got error %v, want nil", err)
}
if !bytes.Equal(got[:n], file.content[:truncateFileSize]) {
t.Fatalf("file content is %v, want %v", got[:n], file.content[:truncateFileSize])
}
}
}
type overlayTestFile struct {
File *fs.File
name string
content []byte
}
func makeOverlayTestFiles(t *testing.T) []*overlayTestFile {
ctx := contexttest.Context(t)
// Create a lower tmpfs mount.
fsys, _ := fs.FindFilesystem("tmpfs")
lower, err := fsys.Mount(contexttest.Context(t), "", fs.MountSourceFlags{}, "", nil)
if err != nil {
t.Fatalf("failed to mount tmpfs: %v", err)
}
lowerRoot := fs.NewDirent(ctx, lower, "")
// Make a deep set of subdirectories that everyone shares.
next := lowerRoot
for i := 0; i < 64; i++ {
name := fmt.Sprintf("subdir%d", i)
err := next.CreateDirectory(ctx, lowerRoot, name, fs.FilePermsFromMode(0777))
if err != nil {
t.Fatalf("failed to create dir %q: %v", name, err)
}
next, err = next.Walk(ctx, lowerRoot, name)
if err != nil {
t.Fatalf("failed to walk to %q: %v", name, err)
}
}
// Make a bunch of files in the last directory.
var files []*overlayTestFile
for i := 0; i < 64; i++ {
name := fmt.Sprintf("file%d", i)
f, err := next.Create(ctx, next, name, fs.FileFlags{Read: true, Write: true}, fs.FilePermsFromMode(0666))
if err != nil {
t.Fatalf("failed to create file %q: %v", name, err)
}
defer f.DecRef(ctx)
relname, _ := f.Dirent.FullName(lowerRoot)
o := &overlayTestFile{
name: relname,
content: make([]byte, origFileSize),
}
if _, err := rand.Read(o.content); err != nil {
t.Fatalf("failed to read from /dev/urandom: %v", err)
}
if _, err := f.Writev(ctx, usermem.BytesIOSequence(o.content)); err != nil {
t.Fatalf("failed to write content to file %q: %v", name, err)
}
files = append(files, o)
}
// Create an empty upper tmpfs mount which we will copy up into.
upper, err := fsys.Mount(ctx, "", fs.MountSourceFlags{}, "", nil)
if err != nil {
t.Fatalf("failed to mount tmpfs: %v", err)
}
// Construct an overlay root.
overlay, err := fs.NewOverlayRoot(ctx, upper, lower, fs.MountSourceFlags{})
if err != nil {
t.Fatalf("failed to construct overlay root: %v", err)
}
// Create a MountNamespace to traverse the file system.
mns, err := fs.NewMountNamespace(ctx, overlay)
if err != nil {
t.Fatalf("failed to construct mount manager: %v", err)
}
// Walk to all of the files in the overlay, open them readable.
for _, f := range files {
maxTraversals := uint(0)
d, err := mns.FindInode(ctx, mns.Root(), mns.Root(), f.name, &maxTraversals)
if err != nil {
t.Fatalf("failed to find %q: %v", f.name, err)
}
defer d.DecRef(ctx)
f.File, err = d.Inode.GetFile(ctx, d, fs.FileFlags{Read: true})
if err != nil {
t.Fatalf("failed to open file %q readable: %v", f.name, err)
}
}
return files
}
-43
View File
@@ -1,43 +0,0 @@
load("//tools:defs.bzl", "go_library")
package(licenses = ["notice"])
go_library(
name = "dev",
srcs = [
"dev.go",
"device.go",
"fs.go",
"full.go",
"net_tun.go",
"null.go",
"random.go",
"tty.go",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/bufferv2",
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/hostarch",
"//pkg/rand",
"//pkg/safemem",
"//pkg/sentry/arch",
"//pkg/sentry/device",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/fs/ramfs",
"//pkg/sentry/fs/tmpfs",
"//pkg/sentry/inet",
"//pkg/sentry/kernel",
"//pkg/sentry/memmap",
"//pkg/sentry/mm",
"//pkg/sentry/pgalloc",
"//pkg/sentry/socket/netstack",
"//pkg/tcpip/link/tun",
"//pkg/usermem",
"//pkg/waiter",
"@org_golang_x_sys//unix:go_default_library",
],
)
-158
View File
@@ -1,158 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev provides a filesystem with simple devices.
package dev
import (
"fmt"
"math"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/ramfs"
"gvisor.dev/gvisor/pkg/sentry/fs/tmpfs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/usermem"
)
// Memory device numbers are from Linux's drivers/char/mem.c
const (
// Mem device major.
memDevMajor uint16 = 1
// Mem device minors.
nullDevMinor uint32 = 3
zeroDevMinor uint32 = 5
fullDevMinor uint32 = 7
randomDevMinor uint32 = 8
urandomDevMinor uint32 = 9
)
// TTY major device number comes from include/uapi/linux/major.h.
const (
ttyDevMinor = 0
ttyDevMajor = 5
)
func newCharacterDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, major uint16, minor uint32) *fs.Inode {
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
DeviceID: devDevice.DeviceID(),
InodeID: devDevice.NextIno(),
BlockSize: hostarch.PageSize,
Type: fs.CharacterDevice,
DeviceFileMajor: major,
DeviceFileMinor: minor,
})
}
func newMemDevice(ctx context.Context, iops fs.InodeOperations, msrc *fs.MountSource, minor uint32) *fs.Inode {
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
DeviceID: devDevice.DeviceID(),
InodeID: devDevice.NextIno(),
BlockSize: hostarch.PageSize,
Type: fs.CharacterDevice,
DeviceFileMajor: memDevMajor,
DeviceFileMinor: minor,
})
}
func newDirectory(ctx context.Context, contents map[string]*fs.Inode, msrc *fs.MountSource) *fs.Inode {
iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555))
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
DeviceID: devDevice.DeviceID(),
InodeID: devDevice.NextIno(),
BlockSize: hostarch.PageSize,
Type: fs.Directory,
})
}
func newSymlink(ctx context.Context, target string, msrc *fs.MountSource) *fs.Inode {
iops := ramfs.NewSymlink(ctx, fs.RootOwner, target)
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
DeviceID: devDevice.DeviceID(),
InodeID: devDevice.NextIno(),
BlockSize: hostarch.PageSize,
Type: fs.Symlink,
})
}
// New returns the root node of a device filesystem.
func New(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
shm, err := tmpfs.NewDir(ctx, nil, fs.RootOwner, fs.FilePermsFromMode(0777), msrc, nil /* parent */)
if err != nil {
panic(fmt.Sprintf("tmpfs.NewDir failed: %v", err))
}
contents := map[string]*fs.Inode{
"fd": newSymlink(ctx, "/proc/self/fd", msrc),
"stdin": newSymlink(ctx, "/proc/self/fd/0", msrc),
"stdout": newSymlink(ctx, "/proc/self/fd/1", msrc),
"stderr": newSymlink(ctx, "/proc/self/fd/2", msrc),
"null": newMemDevice(ctx, newNullDevice(ctx, fs.RootOwner, 0666), msrc, nullDevMinor),
"zero": newMemDevice(ctx, newZeroDevice(ctx, fs.RootOwner, 0666), msrc, zeroDevMinor),
"full": newMemDevice(ctx, newFullDevice(ctx, fs.RootOwner, 0666), msrc, fullDevMinor),
// This is not as good as /dev/random in linux because go
// runtime uses sys_random and /dev/urandom internally.
// According to 'man 4 random', this will be sufficient unless
// application uses this to generate long-lived GPG/SSL/SSH
// keys.
"random": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, randomDevMinor),
"urandom": newMemDevice(ctx, newRandomDevice(ctx, fs.RootOwner, 0444), msrc, urandomDevMinor),
"shm": shm,
// A devpts is typically mounted at /dev/pts to provide
// pseudoterminal support. Place an empty directory there for
// the devpts to be mounted over.
"pts": newDirectory(ctx, nil, msrc),
// Similarly, applications expect a ptmx device at /dev/ptmx
// connected to the terminals provided by /dev/pts/. Rather
// than creating a device directly (which requires a hairy
// lookup on open to determine if a devpts exists), just create
// a symlink to the ptmx provided by devpts. (The Linux devpts
// documentation recommends this).
//
// If no devpts is mounted, this will simply be a dangling
// symlink, which is fine.
"ptmx": newSymlink(ctx, "pts/ptmx", msrc),
"tty": newCharacterDevice(ctx, newTTYDevice(ctx, fs.RootOwner, 0666), msrc, ttyDevMajor, ttyDevMinor),
}
if isNetTunSupported(inet.StackFromContext(ctx)) {
contents["net"] = newDirectory(ctx, map[string]*fs.Inode{
"tun": newCharacterDevice(ctx, newNetTunDevice(ctx, fs.RootOwner, 0666), msrc, netTunDevMajor, netTunDevMinor),
}, msrc)
}
iops := ramfs.NewDir(ctx, contents, fs.RootOwner, fs.FilePermsFromMode(0555))
return fs.NewInode(ctx, iops, msrc, fs.StableAttr{
DeviceID: devDevice.DeviceID(),
InodeID: devDevice.NextIno(),
BlockSize: hostarch.PageSize,
Type: fs.Directory,
})
}
// readZeros implements fs.FileOperations.Read with infinite null bytes.
type readZeros struct{}
// Read implements fs.FileOperations.Read.
func (*readZeros) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
return dst.ZeroOut(ctx, math.MaxInt64)
}
-20
View File
@@ -1,20 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev
import "gvisor.dev/gvisor/pkg/sentry/device"
// devDevice is the pseudo-filesystem device.
var devDevice = device.NewAnonDevice()
-64
View File
@@ -1,64 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev
import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
)
// filesystem is a devtmpfs.
//
// +stateify savable
type filesystem struct{}
var _ fs.Filesystem = (*filesystem)(nil)
func init() {
fs.RegisterFilesystem(&filesystem{})
}
// FilesystemName is the name under which the filesystem is registered.
// Name matches drivers/base/devtmpfs.c:dev_fs_type.name.
const FilesystemName = "devtmpfs"
// Name is the name of the file system.
func (*filesystem) Name() string {
return FilesystemName
}
// AllowUserMount allows users to mount(2) this file system.
func (*filesystem) AllowUserMount() bool {
return true
}
// AllowUserList allows this filesystem to be listed in /proc/filesystems.
func (*filesystem) AllowUserList() bool {
return true
}
// Flags returns that there is nothing special about this file system.
//
// In Linux, devtmpfs does the same thing.
func (*filesystem) Flags() fs.FilesystemFlags {
return 0
}
// Mount returns a devtmpfs root that can be positioned in the vfs.
func (f *filesystem) Mount(ctx context.Context, device string, flags fs.MountSourceFlags, data string, _ any) (*fs.Inode, error) {
// devtmpfs backed by ramfs ignores bad options. See fs/ramfs/inode.c:ramfs_parse_options.
// -> we should consider parsing the mode and backing devtmpfs by this.
return New(ctx, fs.NewNonCachingMountSource(ctx, f, flags)), nil
}
-81
View File
@@ -1,81 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
// fullDevice is used to implement /dev/full.
//
// +stateify savable
type fullDevice struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
fsutil.InodeNotDirectory `state:"nosave"`
fsutil.InodeNotMappable `state:"nosave"`
fsutil.InodeNotSocket `state:"nosave"`
fsutil.InodeNotSymlink `state:"nosave"`
fsutil.InodeVirtual `state:"nosave"`
fsutil.InodeSimpleAttributes
}
var _ fs.InodeOperations = (*fullDevice)(nil)
func newFullDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *fullDevice {
f := &fullDevice{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
}
return f
}
// GetFile implements fs.InodeOperations.GetFile.
func (f *fullDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
flags.Pread = true
return fs.NewFile(ctx, dirent, flags, &fullFileOperations{}), nil
}
// +stateify savable
type fullFileOperations struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
readZeros `state:"nosave"`
}
var _ fs.FileOperations = (*fullFileOperations)(nil)
// Write implements FileOperations.Write.
func (*fullFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
return 0, linuxerr.ENOSPC
}
-185
View File
@@ -1,185 +0,0 @@
// Copyright 2020 The gVisor Authors.
//
// 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 dev
import (
"io"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/sentry/arch"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket/netstack"
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
netTunDevMajor = 10
netTunDevMinor = 200
)
// +stateify savable
type netTunInodeOperations struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
fsutil.InodeNotDirectory `state:"nosave"`
fsutil.InodeNotMappable `state:"nosave"`
fsutil.InodeNotSocket `state:"nosave"`
fsutil.InodeNotSymlink `state:"nosave"`
fsutil.InodeVirtual `state:"nosave"`
fsutil.InodeSimpleAttributes
}
var _ fs.InodeOperations = (*netTunInodeOperations)(nil)
func newNetTunDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *netTunInodeOperations {
return &netTunInodeOperations{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
}
}
// GetFile implements fs.InodeOperations.GetFile.
func (*netTunInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
return fs.NewFile(ctx, d, flags, &netTunFileOperations{}), nil
}
// +stateify savable
type netTunFileOperations struct {
fsutil.FileNoSeek `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
device tun.Device
}
var _ fs.FileOperations = (*netTunFileOperations)(nil)
// Release implements fs.FileOperations.Release.
func (n *netTunFileOperations) Release(ctx context.Context) {
n.device.Release(ctx)
}
// Ioctl implements fs.FileOperations.Ioctl.
func (n *netTunFileOperations) Ioctl(ctx context.Context, file *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
request := args[1].Uint()
data := args[2].Pointer()
t := kernel.TaskFromContext(ctx)
if t == nil {
panic("Ioctl should be called from a task context")
}
switch request {
case linux.TUNSETIFF:
if !t.HasCapability(linux.CAP_NET_ADMIN) {
return 0, linuxerr.EPERM
}
stack, ok := t.NetworkContext().(*netstack.Stack)
if !ok {
return 0, linuxerr.EINVAL
}
var req linux.IFReq
if _, err := req.CopyIn(t, data); err != nil {
return 0, err
}
// Validate flags.
flags, err := netstack.LinuxToTUNFlags(hostarch.ByteOrder.Uint16(req.Data[:]))
if err != nil {
return 0, err
}
return 0, n.device.SetIff(stack.Stack, req.Name(), flags)
case linux.TUNGETIFF:
var req linux.IFReq
copy(req.IFName[:], n.device.Name())
hostarch.ByteOrder.PutUint16(req.Data[:], netstack.TUNFlagsToLinux(n.device.Flags()))
_, err := req.CopyOut(t, data)
return 0, err
default:
return 0, linuxerr.ENOTTY
}
}
// Write implements fs.FileOperations.Write.
func (n *netTunFileOperations) Write(ctx context.Context, file *fs.File, src usermem.IOSequence, offset int64) (int64, error) {
if src.NumBytes() == 0 {
return 0, unix.EINVAL
}
data := bufferv2.NewView(int(src.NumBytes()))
defer data.Release()
if _, err := io.CopyN(data, src.Reader(ctx), src.NumBytes()); err != nil {
return 0, err
}
return n.device.Write(data)
}
// Read implements fs.FileOperations.Read.
func (n *netTunFileOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
data, err := n.device.Read()
if err != nil {
return 0, err
}
defer data.Release()
dataSize := data.Size()
bytesCopied, err := io.CopyN(dst.Writer(ctx), data, dst.NumBytes())
if bytesCopied > 0 && bytesCopied < int64(dataSize) {
// Not an error for partial copying. Packet truncated.
err = nil
}
return int64(bytesCopied), err
}
// Readiness implements watier.Waitable.Readiness.
func (n *netTunFileOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
return n.device.Readiness(mask)
}
// EventRegister implements watier.Waitable.EventRegister.
func (n *netTunFileOperations) EventRegister(e *waiter.Entry) error {
n.device.EventRegister(e)
return nil
}
// EventUnregister implements watier.Waitable.EventUnregister.
func (n *netTunFileOperations) EventUnregister(e *waiter.Entry) {
n.device.EventUnregister(e)
}
// isNetTunSupported returns whether /dev/net/tun device is supported for s.
func isNetTunSupported(s inet.Stack) bool {
_, ok := s.(*netstack.Stack)
return ok
}
-131
View File
@@ -1,131 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/mm"
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
"gvisor.dev/gvisor/pkg/waiter"
)
// +stateify savable
type nullDevice struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
fsutil.InodeNotDirectory `state:"nosave"`
fsutil.InodeNotMappable `state:"nosave"`
fsutil.InodeNotSocket `state:"nosave"`
fsutil.InodeNotSymlink `state:"nosave"`
fsutil.InodeVirtual `state:"nosave"`
fsutil.InodeSimpleAttributes
}
var _ fs.InodeOperations = (*nullDevice)(nil)
func newNullDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *nullDevice {
n := &nullDevice{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
}
return n
}
// GetFile implements fs.FileOperations.GetFile.
func (n *nullDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
flags.Pread = true
flags.Pwrite = true
return fs.NewFile(ctx, dirent, flags, &nullFileOperations{}), nil
}
// +stateify savable
type nullFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRead `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}
var _ fs.FileOperations = (*nullFileOperations)(nil)
// +stateify savable
type zeroDevice struct {
nullDevice
}
var _ fs.InodeOperations = (*zeroDevice)(nil)
func newZeroDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *zeroDevice {
zd := &zeroDevice{
nullDevice: nullDevice{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
},
}
return zd
}
// GetFile implements fs.FileOperations.GetFile.
func (zd *zeroDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
flags.Pread = true
flags.Pwrite = true
flags.NonSeekable = true
return fs.NewFile(ctx, dirent, flags, &zeroFileOperations{}), nil
}
// +stateify savable
type zeroFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
readZeros `state:"nosave"`
}
var _ fs.FileOperations = (*zeroFileOperations)(nil)
// ConfigureMMap implements fs.FileOperations.ConfigureMMap.
func (*zeroFileOperations) ConfigureMMap(ctx context.Context, file *fs.File, opts *memmap.MMapOpts) error {
m, err := mm.NewSharedAnonMappable(opts.Length, pgalloc.MemoryFileProviderFromContext(ctx))
if err != nil {
return err
}
opts.MappingIdentity = m
opts.Mappable = m
return nil
}
-79
View File
@@ -1,79 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 dev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/rand"
"gvisor.dev/gvisor/pkg/safemem"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/pkg/waiter"
)
// +stateify savable
type randomDevice struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
fsutil.InodeNotDirectory `state:"nosave"`
fsutil.InodeNotMappable `state:"nosave"`
fsutil.InodeNotSocket `state:"nosave"`
fsutil.InodeNotSymlink `state:"nosave"`
fsutil.InodeVirtual `state:"nosave"`
fsutil.InodeSimpleAttributes
}
var _ fs.InodeOperations = (*randomDevice)(nil)
func newRandomDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *randomDevice {
r := &randomDevice{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
}
return r
}
// GetFile implements fs.InodeOperations.GetFile.
func (*randomDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
return fs.NewFile(ctx, dirent, flags, &randomFileOperations{}), nil
}
// +stateify savable
type randomFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}
var _ fs.FileOperations = (*randomFileOperations)(nil)
// Read implements fs.FileOperations.Read.
func (*randomFileOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) {
return dst.CopyOutFrom(ctx, safemem.FromIOReader{rand.Reader})
}
-67
View File
@@ -1,67 +0,0 @@
// Copyright 2019 The gVisor Authors.
//
// 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 dev
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/waiter"
)
// +stateify savable
type ttyInodeOperations struct {
fsutil.InodeGenericChecker `state:"nosave"`
fsutil.InodeNoExtendedAttributes `state:"nosave"`
fsutil.InodeNoopAllocate `state:"nosave"`
fsutil.InodeNoopRelease `state:"nosave"`
fsutil.InodeNoopTruncate `state:"nosave"`
fsutil.InodeNoopWriteOut `state:"nosave"`
fsutil.InodeNotDirectory `state:"nosave"`
fsutil.InodeNotMappable `state:"nosave"`
fsutil.InodeNotOpenable `state:"nosave"`
fsutil.InodeNotSocket `state:"nosave"`
fsutil.InodeNotSymlink `state:"nosave"`
fsutil.InodeVirtual `state:"nosave"`
fsutil.InodeSimpleAttributes
}
var _ fs.InodeOperations = (*ttyInodeOperations)(nil)
func newTTYDevice(ctx context.Context, owner fs.FileOwner, mode linux.FileMode) *ttyInodeOperations {
return &ttyInodeOperations{
InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(mode), linux.TMPFS_MAGIC),
}
}
// +stateify savable
type ttyFileOperations struct {
fsutil.FileNoSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNoopRead `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}
var _ fs.FileOperations = (*ttyFileOperations)(nil)
-247
View File
@@ -1,247 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 (
"testing"
)
func TestDirentCache(t *testing.T) {
const maxSize = 5
c := NewDirentCache(maxSize)
// Size starts at 0.
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// Create a Dirent d.
d := NewNegativeDirent("")
// c does not contain d.
if got, want := c.contains(d), false; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// Add d to the cache.
c.Add(d)
// Size is now 1.
if got, want := c.Size(), uint64(1); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// c contains d.
if got, want := c.contains(d), true; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// Add maxSize-1 more elements. d should be oldest element.
for i := 0; i < maxSize-1; i++ {
c.Add(NewNegativeDirent(""))
}
// Size is maxSize.
if got, want := c.Size(), uint64(maxSize); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// c contains d.
if got, want := c.contains(d), true; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// "Bump" d to the front by re-adding it.
c.Add(d)
// Size is maxSize.
if got, want := c.Size(), uint64(maxSize); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// c contains d.
if got, want := c.contains(d), true; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// Add maxSize-1 more elements. d should again be oldest element.
for i := 0; i < maxSize-1; i++ {
c.Add(NewNegativeDirent(""))
}
// Size is maxSize.
if got, want := c.Size(), uint64(maxSize); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// c contains d.
if got, want := c.contains(d), true; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// Add one more element, which will bump d from the cache.
c.Add(NewNegativeDirent(""))
// Size is maxSize.
if got, want := c.Size(), uint64(maxSize); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// c does not contain d.
if got, want := c.contains(d), false; got != want {
t.Errorf("c.contains(d) got %v want %v", got, want)
}
// Invalidating causes size to be 0 and list to be empty.
c.Invalidate()
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
if got, want := c.list.Empty(), true; got != want {
t.Errorf("c.list.Empty() got %v, want %v", got, want)
}
// Fill cache with maxSize dirents.
for i := 0; i < maxSize; i++ {
c.Add(NewNegativeDirent(""))
}
}
func TestDirentCacheLimiter(t *testing.T) {
const (
globalMaxSize = 5
maxSize = 3
)
limit := NewDirentCacheLimiter(globalMaxSize)
c1 := NewDirentCache(maxSize)
c1.limit = limit
c2 := NewDirentCache(maxSize)
c2.limit = limit
// Create a Dirent d.
d := NewNegativeDirent("")
// Add d to the cache.
c1.Add(d)
if got, want := c1.Size(), uint64(1); got != want {
t.Errorf("c1.Size() got %v, want %v", got, want)
}
// Add maxSize-1 more elements. d should be oldest element.
for i := 0; i < maxSize-1; i++ {
c1.Add(NewNegativeDirent(""))
}
if got, want := c1.Size(), uint64(maxSize); got != want {
t.Errorf("c1.Size() got %v, want %v", got, want)
}
// Check that d is still there.
if got, want := c1.contains(d), true; got != want {
t.Errorf("c1.contains(d) got %v want %v", got, want)
}
// Fill up the other cache, it will start dropping old entries from the cache
// when the global limit is reached.
for i := 0; i < maxSize; i++ {
c2.Add(NewNegativeDirent(""))
}
// Check is what's remaining from global max.
if got, want := c2.Size(), globalMaxSize-maxSize; int(got) != want {
t.Errorf("c2.Size() got %v, want %v", got, want)
}
// Check that d was not dropped.
if got, want := c1.contains(d), true; got != want {
t.Errorf("c1.contains(d) got %v want %v", got, want)
}
// Add an entry that will eventually be dropped. Check is done later...
drop := NewNegativeDirent("")
c1.Add(drop)
// Check that d is bumped to front even when global limit is reached.
c1.Add(d)
if got, want := c1.contains(d), true; got != want {
t.Errorf("c1.contains(d) got %v want %v", got, want)
}
// Add 2 more element and check that:
// - d is still in the list: to verify that d was bumped
// - d2/d3 are in the list: older entries are dropped when global limit is
// reached.
// - drop is not in the list: indeed older elements are dropped.
d2 := NewNegativeDirent("")
c1.Add(d2)
d3 := NewNegativeDirent("")
c1.Add(d3)
if got, want := c1.contains(d), true; got != want {
t.Errorf("c1.contains(d) got %v want %v", got, want)
}
if got, want := c1.contains(d2), true; got != want {
t.Errorf("c1.contains(d2) got %v want %v", got, want)
}
if got, want := c1.contains(d3), true; got != want {
t.Errorf("c1.contains(d3) got %v want %v", got, want)
}
if got, want := c1.contains(drop), false; got != want {
t.Errorf("c1.contains(drop) got %v want %v", got, want)
}
// Drop all entries from one cache. The other will be allowed to grow.
c1.Invalidate()
c2.Add(NewNegativeDirent(""))
if got, want := c2.Size(), uint64(maxSize); got != want {
t.Errorf("c2.Size() got %v, want %v", got, want)
}
}
// TestNilDirentCache tests that a nil cache supports all cache operations, but
// treats them as noop.
func TestNilDirentCache(t *testing.T) {
// Create a nil cache.
var c *DirentCache
// Size is zero.
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// Call Add.
c.Add(NewNegativeDirent(""))
// Size is zero.
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// Call Remove.
c.Remove(NewNegativeDirent(""))
// Size is zero.
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
// Call Invalidate.
c.Invalidate()
// Size is zero.
if got, want := c.Size(), uint64(0); got != want {
t.Errorf("c.Size() got %v, want %v", got, want)
}
}
-418
View File
@@ -1,418 +0,0 @@
// Copyright 2018 The gVisor Authors.
//
// 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 (
"testing"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
)
func newMockDirInode(ctx context.Context, cache *DirentCache) *Inode {
return NewMockInode(ctx, NewMockMountSource(cache), StableAttr{Type: Directory})
}
func TestWalkPositive(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
ctx := contexttest.Context(t)
root := NewDirent(ctx, newMockDirInode(ctx, nil), "root")
if got := root.ReadRefs(); got != 1 {
t.Fatalf("root has a ref count of %d, want %d", got, 1)
}
name := "d"
d, err := root.walk(ctx, root, name, false)
if err != nil {
t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
}
if got := root.ReadRefs(); got != 2 {
t.Fatalf("root has a ref count of %d, want %d", got, 2)
}
if got := d.ReadRefs(); got != 1 {
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 1)
}
d.DecRef(ctx)
if got := root.ReadRefs(); got != 1 {
t.Fatalf("root has a ref count of %d, want %d", got, 1)
}
if got := d.ReadRefs(); got != 0 {
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 0)
}
root.flush(ctx)
if got := len(root.children); got != 0 {
t.Fatalf("root has %d children, want %d", got, 0)
}
}
func TestWalkNegative(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
ctx := contexttest.Context(t)
root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root")
mn := root.Inode.InodeOperations.(*mockInodeOperationsLookupNegative)
if got := root.ReadRefs(); got != 1 {
t.Fatalf("root has a ref count of %d, want %d", got, 1)
}
name := "d"
for i := 0; i < 100; i++ {
_, err := root.walk(ctx, root, name, false)
if err != unix.ENOENT {
t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT)
}
}
if got := root.ReadRefs(); got != 1 {
t.Fatalf("root has a ref count of %d, want %d", got, 1)
}
if got := len(root.children); got != 1 {
t.Fatalf("root has %d children, want %d", got, 1)
}
w, ok := root.children[name]
if !ok {
t.Fatalf("root wants child at %q", name)
}
child := w.Get()
if child == nil {
t.Fatalf("root wants to resolve weak reference")
}
if !child.(*Dirent).IsNegative() {
t.Fatalf("root found positive child at %q, want negative", name)
}
if got := child.(*Dirent).ReadRefs(); got != 2 {
t.Fatalf("child has a ref count of %d, want %d", got, 2)
}
child.DecRef(ctx)
if got := child.(*Dirent).ReadRefs(); got != 1 {
t.Fatalf("child has a ref count of %d, want %d", got, 1)
}
if got := len(root.children); got != 1 {
t.Fatalf("root has %d children, want %d", got, 1)
}
root.DecRef(ctx)
if got := root.ReadRefs(); got != 0 {
t.Fatalf("root has a ref count of %d, want %d", got, 0)
}
AsyncBarrier()
if got := mn.releaseCalled; got != true {
t.Fatalf("root.Close was called %v, want true", got)
}
}
type mockInodeOperationsLookupNegative struct {
*MockInodeOperations
releaseCalled bool
}
func NewEmptyDir(ctx context.Context, cache *DirentCache) *Inode {
m := NewMockMountSource(cache)
return NewInode(ctx, &mockInodeOperationsLookupNegative{
MockInodeOperations: NewMockInodeOperations(ctx),
}, m, StableAttr{Type: Directory})
}
func (m *mockInodeOperationsLookupNegative) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) {
return NewNegativeDirent(p), nil
}
func (m *mockInodeOperationsLookupNegative) Release(context.Context) {
m.releaseCalled = true
}
func TestHashNegativeToPositive(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
ctx := contexttest.Context(t)
root := NewDirent(ctx, NewEmptyDir(ctx, nil), "root")
name := "d"
_, err := root.walk(ctx, root, name, false)
if err != unix.ENOENT {
t.Fatalf("root.walk(root, %q) got %v, want %v", name, err, unix.ENOENT)
}
if got := root.exists(ctx, root, name); got != false {
t.Fatalf("got %q exists, want does not exist", name)
}
f, err := root.Create(ctx, root, name, FileFlags{}, FilePermissions{})
if err != nil {
t.Fatalf("root.Create(%q, _), got error %v, want nil", name, err)
}
d := f.Dirent
if d.IsNegative() {
t.Fatalf("got negative Dirent, want positive")
}
if got := d.ReadRefs(); got != 1 {
t.Fatalf("child %q has a ref count of %d, want %d", name, got, 1)
}
if got := root.ReadRefs(); got != 2 {
t.Fatalf("root has a ref count of %d, want %d", got, 2)
}
if got := len(root.children); got != 1 {
t.Fatalf("got %d children, want %d", got, 1)
}
w, ok := root.children[name]
if !ok {
t.Fatalf("failed to find weak reference to %q", name)
}
child := w.Get()
if child == nil {
t.Fatalf("want to resolve weak reference")
}
if child.(*Dirent) != d {
t.Fatalf("got foreign child")
}
}
func TestRevalidate(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
for _, test := range []struct {
// desc is the test's description.
desc string
// Whether to make negative Dirents.
makeNegative bool
}{
{
desc: "Revalidate negative Dirent",
makeNegative: true,
},
{
desc: "Revalidate positive Dirent",
makeNegative: false,
},
} {
t.Run(test.desc, func(t *testing.T) {
ctx := contexttest.Context(t)
root := NewDirent(ctx, NewMockInodeRevalidate(ctx, test.makeNegative), "root")
name := "d"
d1, err := root.walk(ctx, root, name, false)
if !test.makeNegative && err != nil {
t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
}
d2, err := root.walk(ctx, root, name, false)
if !test.makeNegative && err != nil {
t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
}
if !test.makeNegative && d1 == d2 {
t.Fatalf("revalidating walk got same *Dirent, want different")
}
if got := len(root.children); got != 1 {
t.Errorf("revalidating walk got %d children, want %d", got, 1)
}
})
}
}
type MockInodeOperationsRevalidate struct {
*MockInodeOperations
makeNegative bool
}
func NewMockInodeRevalidate(ctx context.Context, makeNegative bool) *Inode {
mn := NewMockInodeOperations(ctx)
m := NewMockMountSource(nil)
m.MountSourceOperations.(*MockMountSourceOps).revalidate = true
return NewInode(ctx, &MockInodeOperationsRevalidate{MockInodeOperations: mn, makeNegative: makeNegative}, m, StableAttr{Type: Directory})
}
func (m *MockInodeOperationsRevalidate) Lookup(ctx context.Context, dir *Inode, p string) (*Dirent, error) {
if !m.makeNegative {
return m.MockInodeOperations.Lookup(ctx, dir, p)
}
return NewNegativeDirent(p), nil
}
func TestCreateExtraRefs(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
ctx := contexttest.Context(t)
for _, test := range []struct {
// desc is the test's description.
desc string
// root is the Dirent to create from.
root *Dirent
// expected references on walked Dirent.
refs int64
}{
{
desc: "Create caching",
root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"),
refs: 2,
},
{
desc: "Create not caching",
root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"),
refs: 1,
},
} {
t.Run(test.desc, func(t *testing.T) {
name := "d"
f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{})
if err != nil {
t.Fatalf("root.Create(root, %q) failed: %v", name, err)
}
d := f.Dirent
if got := d.ReadRefs(); got != test.refs {
t.Errorf("dirent has a ref count of %d, want %d", got, test.refs)
}
})
}
}
func TestRemoveExtraRefs(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
ctx := contexttest.Context(t)
for _, test := range []struct {
// desc is the test's description.
desc string
// root is the Dirent to make and remove from.
root *Dirent
}{
{
desc: "Remove caching",
root: NewDirent(ctx, NewEmptyDir(ctx, NewDirentCache(1)), "root"),
},
{
desc: "Remove not caching",
root: NewDirent(ctx, NewEmptyDir(ctx, nil), "root"),
},
} {
t.Run(test.desc, func(t *testing.T) {
name := "d"
f, err := test.root.Create(ctx, test.root, name, FileFlags{}, FilePermissions{})
if err != nil {
t.Fatalf("root.Create(%q, _) failed: %v", name, err)
}
d := f.Dirent
if err := test.root.Remove(contexttest.Context(t), test.root, name, false /* dirPath */); err != nil {
t.Fatalf("root.Remove(root, %q) failed: %v", name, err)
}
if got := d.ReadRefs(); got != 1 {
t.Fatalf("dirent has a ref count of %d, want %d", got, 1)
}
d.DecRef(ctx)
test.root.flush(ctx)
if got := len(test.root.children); got != 0 {
t.Errorf("root has %d children, want %d", got, 0)
}
})
}
}
func TestRenameExtraRefs(t *testing.T) {
// refs == 0 -> one reference.
// refs == -1 -> has been destroyed.
for _, test := range []struct {
// desc is the test's description.
desc string
// cache of extra Dirent references, may be nil.
cache *DirentCache
}{
{
desc: "Rename no caching",
cache: nil,
},
{
desc: "Rename caching",
cache: NewDirentCache(5),
},
} {
t.Run(test.desc, func(t *testing.T) {
ctx := contexttest.Context(t)
dirAttr := StableAttr{Type: Directory}
oldParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "old_parent")
newParent := NewDirent(ctx, NewMockInode(ctx, NewMockMountSource(test.cache), dirAttr), "new_parent")
renamed, err := oldParent.Walk(ctx, oldParent, "old_child")
if err != nil {
t.Fatalf("Walk(oldParent, %q) got error %v, want nil", "old_child", err)
}
replaced, err := newParent.Walk(ctx, oldParent, "new_child")
if err != nil {
t.Fatalf("Walk(newParent, %q) got error %v, want nil", "new_child", err)
}
if err := Rename(contexttest.RootContext(t), oldParent /*root */, oldParent, "old_child", newParent, "new_child"); err != nil {
t.Fatalf("Rename got error %v, want nil", err)
}
oldParent.flush(ctx)
newParent.flush(ctx)
// Expect to have only active references.
if got := renamed.ReadRefs(); got != 1 {
t.Errorf("renamed has ref count %d, want only active references %d", got, 1)
}
if got := replaced.ReadRefs(); got != 1 {
t.Errorf("replaced has ref count %d, want only active references %d", got, 1)
}
})
}
}
-52
View File
@@ -1,52 +0,0 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_library(
name = "fdpipe",
srcs = [
"pipe.go",
"pipe_opener.go",
"pipe_state.go",
],
imports = ["gvisor.dev/gvisor/pkg/sentry/fs"],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/log",
"//pkg/safemem",
"//pkg/secio",
"//pkg/sentry/fs",
"//pkg/sentry/fs/fsutil",
"//pkg/sync",
"//pkg/usermem",
"//pkg/waiter",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_test(
name = "fdpipe_test",
size = "small",
srcs = [
"pipe_opener_test.go",
"pipe_test.go",
],
library = ":fdpipe",
deps = [
"//pkg/context",
"//pkg/errors",
"//pkg/errors/linuxerr",
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/hostarch",
"//pkg/sentry/contexttest",
"//pkg/sentry/fs",
"//pkg/usermem",
"@com_github_google_uuid//:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)

Some files were not shown because too many files have changed in this diff Show More