summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/ramfs
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/ramfs')
-rw-r--r--pkg/sentry/fs/ramfs/BUILD6
-rw-r--r--pkg/sentry/fs/ramfs/dir.go223
-rw-r--r--pkg/sentry/fs/ramfs/file.go150
-rw-r--r--pkg/sentry/fs/ramfs/ramfs.go441
-rw-r--r--pkg/sentry/fs/ramfs/socket.go48
-rw-r--r--pkg/sentry/fs/ramfs/symlink.go67
-rw-r--r--pkg/sentry/fs/ramfs/test/BUILD16
-rw-r--r--pkg/sentry/fs/ramfs/test/test.go46
-rw-r--r--pkg/sentry/fs/ramfs/tree.go3
-rw-r--r--pkg/sentry/fs/ramfs/tree_test.go2
10 files changed, 265 insertions, 737 deletions
diff --git a/pkg/sentry/fs/ramfs/BUILD b/pkg/sentry/fs/ramfs/BUILD
index a93ad6240..a476c9cce 100644
--- a/pkg/sentry/fs/ramfs/BUILD
+++ b/pkg/sentry/fs/ramfs/BUILD
@@ -6,8 +6,6 @@ go_library(
name = "ramfs",
srcs = [
"dir.go",
- "file.go",
- "ramfs.go",
"socket.go",
"symlink.go",
"tree.go",
@@ -15,14 +13,12 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs",
visibility = ["//pkg/sentry:internal"],
deps = [
- "//pkg/secio",
+ "//pkg/abi/linux",
"//pkg/sentry/context",
"//pkg/sentry/fs",
"//pkg/sentry/fs/anon",
"//pkg/sentry/fs/fsutil",
"//pkg/sentry/kernel/time",
- "//pkg/sentry/memmap",
- "//pkg/sentry/safemem",
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/usermem",
"//pkg/syserror",
diff --git a/pkg/sentry/fs/ramfs/dir.go b/pkg/sentry/fs/ramfs/dir.go
index 0a911b155..729f37694 100644
--- a/pkg/sentry/fs/ramfs/dir.go
+++ b/pkg/sentry/fs/ramfs/dir.go
@@ -18,10 +18,12 @@ import (
"sync"
"syscall"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
+ ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
)
@@ -47,7 +49,17 @@ type CreateOps struct {
//
// +stateify savable
type Dir struct {
- Entry
+ fsutil.InodeGenericChecker `state:"nosave"`
+ fsutil.InodeIsDirTruncate `state:"nosave"`
+ fsutil.InodeNoopRelease `state:"nosave"`
+ fsutil.InodeNoopWriteOut `state:"nosave"`
+ fsutil.InodeNotMappable `state:"nosave"`
+ fsutil.InodeNotSocket `state:"nosave"`
+ fsutil.InodeNotSymlink `state:"nosave"`
+ fsutil.InodeVirtual `state:"nosave"`
+
+ fsutil.InodeSimpleAttributes
+ fsutil.InodeSimpleExtendedAttributes
// CreateOps may be provided.
//
@@ -64,17 +76,23 @@ type Dir struct {
children map[string]*fs.Inode
// dentryMap is a sortedDentryMap containing entries for all children.
- // Its entries ar kept up-to-date with d.children.
+ // Its entries are kept up-to-date with d.children.
dentryMap *fs.SortedDentryMap
}
-// InitDir initializes a directory.
-func (d *Dir) InitDir(ctx context.Context, contents map[string]*fs.Inode, owner fs.FileOwner, perms fs.FilePermissions) {
- d.InitEntry(ctx, owner, perms)
+var _ fs.InodeOperations = (*Dir)(nil)
+
+// NewDir returns a new Dir with the given contents and attributes.
+func NewDir(ctx context.Context, contents map[string]*fs.Inode, owner fs.FileOwner, perms fs.FilePermissions) *Dir {
+ d := &Dir{
+ InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, perms, linux.RAMFS_MAGIC),
+ }
+
if contents == nil {
contents = make(map[string]*fs.Inode)
}
d.children = contents
+
// Build the entries map ourselves, rather than calling addChildLocked,
// because it will be faster.
entries := make(map[string]fs.DentAttr, len(contents))
@@ -88,6 +106,8 @@ func (d *Dir) InitDir(ctx context.Context, contents map[string]*fs.Inode, owner
// Directories have an extra link, corresponding to '.'.
d.AddLink()
+
+ return d
}
// addChildLocked add the child inode, inheriting its reference.
@@ -124,17 +144,24 @@ func (d *Dir) FindChild(name string) (*fs.Inode, bool) {
return child, ok
}
+// Children returns the names and DentAttrs of all children. It can be used to
+// implement Readdir for types that embed ramfs.Dir.
+func (d *Dir) Children() ([]string, map[string]fs.DentAttr) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ return d.dentryMap.GetAll()
+}
+
// removeChildLocked attempts to remove an entry from this directory.
-// This Entry's mutex must be held. It returns the removed Inode.
func (d *Dir) removeChildLocked(ctx context.Context, name string) (*fs.Inode, error) {
inode, ok := d.children[name]
if !ok {
- return nil, ErrNotFound
+ return nil, syserror.EACCES
}
delete(d.children, name)
d.dentryMap.Remove(name)
- d.Entry.NotifyModification(ctx)
+ d.NotifyModification(ctx)
// If the child was a subdirectory, then we must decrement this dir's
// link count which was the child's ".." directory entry.
@@ -143,7 +170,7 @@ func (d *Dir) removeChildLocked(ctx context.Context, name string) (*fs.Inode, er
}
// Update ctime.
- inode.NotifyStatusChange(ctx)
+ inode.InodeOperations.NotifyStatusChange(ctx)
// Given we're now removing this inode to the directory we must also
// decrease its link count. Similarly it is increased in addChildLocked.
@@ -152,8 +179,8 @@ func (d *Dir) removeChildLocked(ctx context.Context, name string) (*fs.Inode, er
return inode, nil
}
-// RemoveEntry attempts to remove an entry from this directory.
-func (d *Dir) RemoveEntry(ctx context.Context, name string) error {
+// Remove removes the named non-directory.
+func (d *Dir) Remove(ctx context.Context, _ *fs.Inode, name string) error {
d.mu.Lock()
defer d.mu.Unlock()
inode, err := d.removeChildLocked(ctx, name)
@@ -166,27 +193,23 @@ func (d *Dir) RemoveEntry(ctx context.Context, name string) error {
return nil
}
-// Remove removes the named non-directory.
-func (d *Dir) Remove(ctx context.Context, dir *fs.Inode, name string) error {
- return d.RemoveEntry(ctx, name)
-}
-
// RemoveDirectory removes the named directory.
-func (d *Dir) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) error {
+func (d *Dir) RemoveDirectory(ctx context.Context, _ *fs.Inode, name string) error {
d.mu.Lock()
defer d.mu.Unlock()
- n, err := d.walkLocked(ctx, name)
+ // Get the child and make sure it is not empty.
+ childInode, err := d.walkLocked(ctx, name)
if err != nil {
return err
}
- dirCtx := &fs.DirCtx{}
- if _, err := n.HandleOps().DeprecatedReaddir(ctx, dirCtx, 0); err != nil {
+ if ok, err := hasChildren(ctx, childInode); err != nil {
return err
+ } else if ok {
+ return syserror.ENOTEMPTY
}
- if len(dirCtx.DentAttrs()) > 0 {
- return ErrNotEmpty
- }
+
+ // Child was empty. Proceed with removal.
inode, err := d.removeChildLocked(ctx, name)
if err != nil {
return err
@@ -195,11 +218,11 @@ func (d *Dir) RemoveDirectory(ctx context.Context, dir *fs.Inode, name string) e
// Remove our reference on the inode.
inode.DecRef()
- return err
+ return nil
}
// Lookup loads an inode at p into a Dirent.
-func (d *Dir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent, error) {
+func (d *Dir) Lookup(ctx context.Context, _ *fs.Inode, p string) (*fs.Dirent, error) {
d.mu.Lock()
defer d.mu.Unlock()
@@ -214,9 +237,9 @@ func (d *Dir) Lookup(ctx context.Context, dir *fs.Inode, p string) (*fs.Dirent,
return fs.NewDirent(inode, p), nil
}
-// walkLocked must be called with this Entry's mutex held.
+// walkLocked must be called with d.mu held.
func (d *Dir) walkLocked(ctx context.Context, p string) (*fs.Inode, error) {
- d.Entry.NotifyAccess(ctx)
+ d.NotifyAccess(ctx)
// Lookup a child node.
if inode, ok := d.children[p]; ok {
@@ -244,7 +267,7 @@ func (d *Dir) createInodeOperationsCommon(ctx context.Context, name string, make
}
d.addChildLocked(name, inode)
- d.Entry.NotifyModification(ctx)
+ d.NotifyModification(ctx)
return inode, nil
}
@@ -252,7 +275,7 @@ func (d *Dir) createInodeOperationsCommon(ctx context.Context, name string, make
// Create creates a new Inode with the given name and returns its File.
func (d *Dir) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.FileFlags, perms fs.FilePermissions) (*fs.File, error) {
if d.CreateOps == nil || d.CreateOps.NewFile == nil {
- return nil, ErrDenied
+ return nil, syserror.EACCES
}
inode, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
@@ -274,7 +297,7 @@ func (d *Dir) Create(ctx context.Context, dir *fs.Inode, name string, flags fs.F
// CreateLink returns a new link.
func (d *Dir) CreateLink(ctx context.Context, dir *fs.Inode, oldname, newname string) error {
if d.CreateOps == nil || d.CreateOps.NewSymlink == nil {
- return ErrDenied
+ return syserror.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, newname, func() (*fs.Inode, error) {
return d.NewSymlink(ctx, dir, oldname)
@@ -292,10 +315,10 @@ func (d *Dir) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inod
// The link count will be incremented in addChildLocked.
d.addChildLocked(name, target)
- d.Entry.NotifyModification(ctx)
+ d.NotifyModification(ctx)
// Update ctime.
- target.NotifyStatusChange(ctx)
+ target.InodeOperations.NotifyStatusChange(ctx)
return nil
}
@@ -303,7 +326,7 @@ func (d *Dir) CreateHardLink(ctx context.Context, dir *fs.Inode, target *fs.Inod
// CreateDirectory returns a new subdirectory.
func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error {
if d.CreateOps == nil || d.CreateOps.NewDir == nil {
- return ErrDenied
+ return syserror.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewDir(ctx, dir, perms)
@@ -316,7 +339,7 @@ func (d *Dir) CreateDirectory(ctx context.Context, dir *fs.Inode, name string, p
// Bind implements fs.InodeOperations.Bind.
func (d *Dir) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport.BoundEndpoint, perms fs.FilePermissions) (*fs.Dirent, error) {
if d.CreateOps == nil || d.CreateOps.NewBoundEndpoint == nil {
- return nil, ErrDenied
+ return nil, syserror.EACCES
}
inode, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewBoundEndpoint(ctx, dir, ep, perms)
@@ -335,7 +358,7 @@ func (d *Dir) Bind(ctx context.Context, dir *fs.Inode, name string, ep transport
// CreateFifo implements fs.InodeOperations.CreateFifo.
func (d *Dir) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perms fs.FilePermissions) error {
if d.CreateOps == nil || d.CreateOps.NewFifo == nil {
- return ErrDenied
+ return syserror.EACCES
}
_, err := d.createInodeOperationsCommon(ctx, name, func() (*fs.Inode, error) {
return d.NewFifo(ctx, dir, perms)
@@ -343,29 +366,125 @@ func (d *Dir) CreateFifo(ctx context.Context, dir *fs.Inode, name string, perms
return err
}
-func (d *Dir) readdirLocked(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
- // Serialize the entries in dentryMap.
- n, err := fs.GenericReaddir(dirCtx, d.dentryMap)
+// GetFile implements fs.InodeOperations.GetFile.
+func (d *Dir) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ flags.Pread = true
+ return fs.NewFile(ctx, dirent, flags, &dirFileOperations{dir: d}), nil
+}
- // Touch the access time.
- d.Entry.NotifyAccess(ctx)
+// Rename implements fs.InodeOperations.Rename.
+func (*Dir) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
+ return Rename(ctx, oldParent.InodeOperations, oldName, newParent.InodeOperations, newName)
+}
+// dirFileOperations implements fs.FileOperations for a ramfs directory.
+//
+// +stateify savable
+type dirFileOperations struct {
+ fsutil.DirFileOperations `state:"nosave"`
+
+ // dirCursor contains the name of the last directory entry that was
+ // serialized.
+ dirCursor string
+
+ // dir is the ramfs dir that this file corresponds to.
+ dir *Dir
+}
+
+var _ fs.FileOperations = (*dirFileOperations)(nil)
+
+// Seek implements fs.FileOperations.Seek.
+func (dfo *dirFileOperations) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) {
+ return fsutil.SeekWithDirCursor(ctx, file, whence, offset, &dfo.dirCursor)
+}
+
+// IterateDir implements DirIterator.IterateDir.
+func (dfo *dirFileOperations) IterateDir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
+ dfo.dir.mu.Lock()
+ defer dfo.dir.mu.Unlock()
+
+ n, err := fs.GenericReaddir(dirCtx, dfo.dir.dentryMap)
return offset + n, err
}
-// DeprecatedReaddir emits the entries contained in this directory.
-func (d *Dir) DeprecatedReaddir(ctx context.Context, dirCtx *fs.DirCtx, offset int) (int, error) {
- d.mu.Lock()
- defer d.mu.Unlock()
- return d.readdirLocked(ctx, dirCtx, offset)
+// Readdir implements FileOperations.Readdir.
+func (dfo *dirFileOperations) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) {
+ root := fs.RootFromContext(ctx)
+ defer root.DecRef()
+ dirCtx := &fs.DirCtx{
+ Serializer: serializer,
+ DirCursor: &dfo.dirCursor,
+ }
+ dfo.dir.mu.Lock()
+ dfo.dir.InodeSimpleAttributes.Unstable.AccessTime = ktime.NowFromContext(ctx)
+ dfo.dir.mu.Unlock()
+ return fs.DirentReaddir(ctx, file.Dirent, dfo, root, dirCtx, file.Offset())
}
-// DeprecatedPreadv always returns ErrIsDirectory
-func (*Dir) DeprecatedPreadv(context.Context, usermem.IOSequence, int64) (int64, error) {
- return 0, ErrIsDirectory
+// hasChildren is a helper method that determines whether an arbitrary inode
+// (not necessarily ramfs) has any children.
+func hasChildren(ctx context.Context, inode *fs.Inode) (bool, error) {
+ // Take an extra ref on inode which will be given to the dirent and
+ // dropped when that dirent is destroyed.
+ inode.IncRef()
+ d := fs.NewTransientDirent(inode)
+ defer d.DecRef()
+
+ file, err := inode.GetFile(ctx, d, fs.FileFlags{Read: true})
+ if err != nil {
+ return false, err
+ }
+ defer file.DecRef()
+
+ ser := &fs.CollectEntriesSerializer{}
+ if err := file.Readdir(ctx, ser); err != nil {
+ return false, err
+ }
+ // We will always write "." and "..", so ignore those two.
+ if ser.Written() > 2 {
+ return true, nil
+ }
+ return false, nil
}
-// DeprecatedPwritev always returns ErrIsDirectory
-func (*Dir) DeprecatedPwritev(context.Context, usermem.IOSequence, int64) (int64, error) {
- return 0, ErrIsDirectory
+// Rename renames from a *ramfs.Dir to another *ramfs.Dir.
+func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, newParent fs.InodeOperations, newName string) error {
+ op, ok := oldParent.(*Dir)
+ if !ok {
+ return syserror.EXDEV
+ }
+ np, ok := newParent.(*Dir)
+ if !ok {
+ return syserror.EXDEV
+ }
+
+ np.mu.Lock()
+ defer np.mu.Unlock()
+
+ // Check whether the ramfs entry to be replaced is a non-empty directory.
+ if replaced, ok := np.children[newName]; ok {
+ if fs.IsDir(replaced.StableAttr) {
+ if ok, err := hasChildren(ctx, replaced); err != nil {
+ return err
+ } else if ok {
+ return syserror.ENOTEMPTY
+ }
+ }
+ }
+
+ // Be careful, we may have already grabbed this mutex above.
+ if op != np {
+ op.mu.Lock()
+ defer op.mu.Unlock()
+ }
+
+ // Do the swap.
+ n := op.children[oldName]
+ op.removeChildLocked(ctx, oldName)
+ np.addChildLocked(newName, n)
+
+ // Update ctime.
+ n.InodeOperations.NotifyStatusChange(ctx)
+
+ return nil
}
diff --git a/pkg/sentry/fs/ramfs/file.go b/pkg/sentry/fs/ramfs/file.go
deleted file mode 100644
index b7fc98ffc..000000000
--- a/pkg/sentry/fs/ramfs/file.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// 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 ramfs
-
-import (
- "io"
- "sync"
-
- "gvisor.googlesource.com/gvisor/pkg/secio"
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/safemem"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
- "gvisor.googlesource.com/gvisor/pkg/syserror"
-)
-
-// File represents a unique file. It uses a simple byte slice as storage, and
-// thus should only be used for small files.
-//
-// A File is not mappable.
-//
-// +stateify savable
-type File struct {
- Entry
-
- // mu protects the fields below.
- mu sync.Mutex `state:"nosave"`
-
- // data tracks backing data for the file.
- data []byte
-}
-
-// InitFile initializes a file.
-func (f *File) InitFile(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions) {
- f.InitEntry(ctx, owner, perms)
-}
-
-// UnstableAttr returns unstable attributes of this ramfs file.
-func (f *File) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
- f.mu.Lock()
- defer f.mu.Unlock()
-
- uattr, _ := f.Entry.UnstableAttr(ctx, inode)
- uattr.Size = int64(len(f.data))
- uattr.Usage = f.usageLocked()
-
- return uattr, nil
-}
-
-// usageLocked returns the disk usage. Caller must hold f.mu.
-func (f *File) usageLocked() int64 {
- return int64(len(f.data))
-}
-
-// Append appends the given data. This is for internal use.
-func (f *File) Append(data []byte) {
- f.mu.Lock()
- defer f.mu.Unlock()
- f.data = append(f.data, data...)
-}
-
-// Truncate truncates this node.
-func (f *File) Truncate(ctx context.Context, inode *fs.Inode, l int64) error {
- f.mu.Lock()
- defer f.mu.Unlock()
- if l < int64(len(f.data)) {
- // Remove excess bytes.
- f.data = f.data[:l]
- return nil
- } else if l > int64(len(f.data)) {
- // Create a new slice with size l, and copy f.data into it.
- d := make([]byte, l)
- copy(d, f.data)
- f.data = d
- }
- f.Entry.NotifyModification(ctx)
- return nil
-}
-
-// ReadAt implements io.ReaderAt.
-func (f *File) ReadAt(data []byte, offset int64) (int, error) {
- if offset < 0 {
- return 0, ErrInvalidOp
- }
- if offset >= int64(len(f.data)) {
- return 0, io.EOF
- }
- n := copy(data, f.data[offset:])
- // Did we read past the end?
- if offset+int64(len(data)) >= int64(len(f.data)) {
- return n, io.EOF
- }
- return n, nil
-}
-
-// DeprecatedPreadv reads into a collection of slices from a given offset.
-func (f *File) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
- f.mu.Lock()
- defer f.mu.Unlock()
- if offset >= int64(len(f.data)) {
- return 0, io.EOF
- }
- n, err := dst.CopyOut(ctx, f.data[offset:])
- if n > 0 {
- f.Entry.NotifyAccess(ctx)
- }
- return int64(n), err
-}
-
-// WriteAt implements io.WriterAt.
-func (f *File) WriteAt(data []byte, offset int64) (int, error) {
- if offset < 0 {
- return 0, ErrInvalidOp
- }
- newLen := offset + int64(len(data))
- if newLen < 0 {
- // Overflow.
- return 0, syserror.EINVAL
- }
- if newLen > int64(len(f.data)) {
- // Copy f.data into new slice with expanded length.
- d := make([]byte, newLen)
- copy(d, f.data)
- f.data = d
- }
- return copy(f.data[offset:], data), nil
-}
-
-// DeprecatedPwritev writes from a collection of slices at a given offset.
-func (f *File) DeprecatedPwritev(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
- f.mu.Lock()
- defer f.mu.Unlock()
- n, err := src.CopyInTo(ctx, safemem.FromIOWriter{secio.NewOffsetWriter(f, offset)})
- if n > 0 {
- f.Entry.NotifyModification(ctx)
- }
- return n, err
-}
diff --git a/pkg/sentry/fs/ramfs/ramfs.go b/pkg/sentry/fs/ramfs/ramfs.go
deleted file mode 100644
index d77688a34..000000000
--- a/pkg/sentry/fs/ramfs/ramfs.go
+++ /dev/null
@@ -1,441 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// 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 ramfs implements an in-memory file system that can be associated with
-// any device.
-package ramfs
-
-import (
- "errors"
- "sync"
- "syscall"
-
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
- ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
- "gvisor.googlesource.com/gvisor/pkg/sentry/memmap"
- "gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
- "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
- "gvisor.googlesource.com/gvisor/pkg/syserror"
- "gvisor.googlesource.com/gvisor/pkg/waiter"
-)
-
-var (
- // ErrInvalidOp indicates the operation is not valid.
- ErrInvalidOp = errors.New("invalid operation")
-
- // ErrDenied indicates the operation was denied.
- ErrDenied = errors.New("operation denied")
-
- // ErrNotFound indicates that a node was not found on a walk.
- ErrNotFound = errors.New("node not found")
-
- // ErrCrossDevice indicates a cross-device link or rename.
- ErrCrossDevice = errors.New("can't link across filesystems")
-
- // ErrIsDirectory indicates that the operation failed because
- // the node is a directory.
- ErrIsDirectory = errors.New("is a directory")
-
- // ErrNotDirectory indicates that the operation failed because
- // the node is a not directory.
- ErrNotDirectory = errors.New("not a directory")
-
- // ErrNotEmpty indicates that the operation failed because the
- // directory is not empty.
- ErrNotEmpty = errors.New("directory not empty")
-)
-
-// Entry represents common internal state for file and directory nodes.
-// This may be used by other packages to easily create ramfs files.
-//
-// +stateify savable
-type Entry struct {
- waiter.AlwaysReady `state:"nosave"`
- fsutil.NoMappable `state:"nosave"`
- fsutil.NoopWriteOut `state:"nosave"`
- fsutil.InodeNotSocket `state:"nosave"`
-
- // mu protects the fields below.
- mu sync.Mutex `state:"nosave"`
-
- // unstable is unstable attributes.
- unstable fs.UnstableAttr
-
- // xattrs are the extended attributes of the Entry.
- xattrs map[string][]byte
-}
-
-// InitEntry initializes an entry.
-func (e *Entry) InitEntry(ctx context.Context, owner fs.FileOwner, p fs.FilePermissions) {
- e.InitEntryWithAttr(ctx, fs.WithCurrentTime(ctx, fs.UnstableAttr{
- Owner: owner,
- Perms: p,
- // Always start unlinked.
- Links: 0,
- }))
-}
-
-// InitEntryWithAttr initializes an entry with a complete set of attributes.
-func (e *Entry) InitEntryWithAttr(ctx context.Context, uattr fs.UnstableAttr) {
- e.unstable = uattr
- e.xattrs = make(map[string][]byte)
-}
-
-// UnstableAttr implements fs.InodeOperations.UnstableAttr.
-func (e *Entry) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
- e.mu.Lock()
- attr := e.unstable
- e.mu.Unlock()
- return attr, nil
-}
-
-// Check implements fs.InodeOperations.Check.
-func (*Entry) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
- return fs.ContextCanAccessFile(ctx, inode, p)
-}
-
-// Getxattr implements fs.InodeOperations.Getxattr.
-func (e *Entry) Getxattr(inode *fs.Inode, name string) ([]byte, error) {
- // Hot path. Avoid defers.
- e.mu.Lock()
- value, ok := e.xattrs[name]
- e.mu.Unlock()
- if ok {
- return value, nil
- }
- return nil, syserror.ENOATTR
-}
-
-// Setxattr implements fs.InodeOperations.Setxattr.
-func (e *Entry) Setxattr(inode *fs.Inode, name string, value []byte) error {
- e.mu.Lock()
- e.xattrs[name] = value
- e.mu.Unlock()
- return nil
-}
-
-// Listxattr implements fs.InodeOperations.Listxattr.
-func (e *Entry) Listxattr(inode *fs.Inode) (map[string]struct{}, error) {
- e.mu.Lock()
- names := make(map[string]struct{}, len(e.xattrs))
- for name := range e.xattrs {
- names[name] = struct{}{}
- }
- e.mu.Unlock()
- return names, nil
-}
-
-// GetFile returns a fs.File backed by the dirent argument and flags.
-func (*Entry) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
- return fsutil.NewHandle(ctx, d, flags, d.Inode.HandleOps()), nil
-}
-
-// SetPermissions always sets the permissions.
-func (e *Entry) SetPermissions(ctx context.Context, inode *fs.Inode, p fs.FilePermissions) bool {
- e.mu.Lock()
- e.unstable.Perms = p
- e.unstable.StatusChangeTime = ktime.NowFromContext(ctx)
- e.mu.Unlock()
- return true
-}
-
-// SetOwner always sets ownership.
-func (e *Entry) SetOwner(ctx context.Context, inode *fs.Inode, owner fs.FileOwner) error {
- e.mu.Lock()
- if owner.UID.Ok() {
- e.unstable.Owner.UID = owner.UID
- }
- if owner.GID.Ok() {
- e.unstable.Owner.GID = owner.GID
- }
- e.mu.Unlock()
- return nil
-}
-
-// SetTimestamps sets the timestamps.
-func (e *Entry) SetTimestamps(ctx context.Context, inode *fs.Inode, ts fs.TimeSpec) error {
- if ts.ATimeOmit && ts.MTimeOmit {
- return nil
- }
-
- e.mu.Lock()
- now := ktime.NowFromContext(ctx)
- if !ts.ATimeOmit {
- if ts.ATimeSetSystemTime {
- e.unstable.AccessTime = now
- } else {
- e.unstable.AccessTime = ts.ATime
- }
- }
- if !ts.MTimeOmit {
- if ts.MTimeSetSystemTime {
- e.unstable.ModificationTime = now
- } else {
- e.unstable.ModificationTime = ts.MTime
- }
- }
- e.unstable.StatusChangeTime = now
- e.mu.Unlock()
- return nil
-}
-
-// NotifyStatusChange updates the status change time (ctime).
-func (e *Entry) NotifyStatusChange(ctx context.Context) {
- e.mu.Lock()
- e.unstable.StatusChangeTime = ktime.NowFromContext(ctx)
- e.mu.Unlock()
-}
-
-// StatusChangeTime returns the last status change time for this node.
-func (e *Entry) StatusChangeTime() ktime.Time {
- e.mu.Lock()
- t := e.unstable.StatusChangeTime
- e.mu.Unlock()
- return t
-}
-
-// NotifyModification updates the modification time and the status change time.
-func (e *Entry) NotifyModification(ctx context.Context) {
- e.mu.Lock()
- now := ktime.NowFromContext(ctx)
- e.unstable.ModificationTime = now
- e.unstable.StatusChangeTime = now
- e.mu.Unlock()
-}
-
-// ModificationTime returns the last modification time for this node.
-func (e *Entry) ModificationTime() ktime.Time {
- e.mu.Lock()
- t := e.unstable.ModificationTime
- e.mu.Unlock()
- return t
-}
-
-// NotifyAccess updates the access time.
-func (e *Entry) NotifyAccess(ctx context.Context) {
- e.mu.Lock()
- now := ktime.NowFromContext(ctx)
- e.unstable.AccessTime = now
- e.mu.Unlock()
-}
-
-// AccessTime returns the last access time for this node.
-func (e *Entry) AccessTime() ktime.Time {
- e.mu.Lock()
- t := e.unstable.AccessTime
- e.mu.Unlock()
- return t
-}
-
-// Permissions returns permissions on this entry.
-func (e *Entry) Permissions() fs.FilePermissions {
- e.mu.Lock()
- p := e.unstable.Perms
- e.mu.Unlock()
- return p
-}
-
-// Lookup is not supported by default.
-func (*Entry) Lookup(context.Context, *fs.Inode, string) (*fs.Dirent, error) {
- return nil, ErrInvalidOp
-}
-
-// Create is not supported by default.
-func (*Entry) Create(context.Context, *fs.Inode, string, fs.FileFlags, fs.FilePermissions) (*fs.File, error) {
- return nil, ErrInvalidOp
-}
-
-// CreateLink is not supported by default.
-func (*Entry) CreateLink(context.Context, *fs.Inode, string, string) error {
- return ErrInvalidOp
-}
-
-// CreateHardLink is not supported by default.
-func (*Entry) CreateHardLink(context.Context, *fs.Inode, *fs.Inode, string) error {
- return ErrInvalidOp
-}
-
-// IsVirtual returns true.
-func (*Entry) IsVirtual() bool {
- return true
-}
-
-// CreateDirectory is not supported by default.
-func (*Entry) CreateDirectory(context.Context, *fs.Inode, string, fs.FilePermissions) error {
- return ErrInvalidOp
-}
-
-// Bind is not supported by default.
-func (*Entry) Bind(context.Context, *fs.Inode, string, transport.BoundEndpoint, fs.FilePermissions) (*fs.Dirent, error) {
- return nil, ErrInvalidOp
-}
-
-// CreateFifo implements fs.InodeOperations.CreateFifo. CreateFifo is not supported by
-// default.
-func (*Entry) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error {
- return ErrInvalidOp
-}
-
-// Remove is not supported by default.
-func (*Entry) Remove(context.Context, *fs.Inode, string) error {
- return ErrInvalidOp
-}
-
-// RemoveDirectory is not supported by default.
-func (*Entry) RemoveDirectory(context.Context, *fs.Inode, string) error {
- return ErrInvalidOp
-}
-
-// StatFS always returns ENOSYS.
-func (*Entry) StatFS(context.Context) (fs.Info, error) {
- return fs.Info{}, syscall.ENOSYS
-}
-
-// Rename implements fs.InodeOperations.Rename.
-func (e *Entry) Rename(ctx context.Context, oldParent *fs.Inode, oldName string, newParent *fs.Inode, newName string) error {
- return Rename(ctx, oldParent.InodeOperations, oldName, newParent.InodeOperations, newName)
-}
-
-// Rename renames from a *ramfs.Dir to another *ramfs.Dir.
-func Rename(ctx context.Context, oldParent fs.InodeOperations, oldName string, newParent fs.InodeOperations, newName string) error {
- op, ok := oldParent.(*Dir)
- if !ok {
- return ErrCrossDevice
- }
- np, ok := newParent.(*Dir)
- if !ok {
- return ErrCrossDevice
- }
-
- np.mu.Lock()
- defer np.mu.Unlock()
-
- // Check whether the ramfs entry to be replaced is a non-empty directory.
- if replaced, ok := np.children[newName]; ok {
- if fs.IsDir(replaced.StableAttr) {
- // FIXME: simplify by pinning children of ramfs-backed directories
- // in the Dirent tree: this allows us to generalize ramfs operations without
- // relying on an implementation of Readdir (which may do anything, like require
- // that the file be open ... which would be reasonable).
- dirCtx := &fs.DirCtx{}
- _, err := replaced.HandleOps().DeprecatedReaddir(ctx, dirCtx, 0)
- if err != nil {
- return err
- }
- attrs := dirCtx.DentAttrs()
-
- // ramfs-backed directories should not contain "." and "..", but we do this
- // just in case.
- delete(attrs, ".")
- delete(attrs, "..")
-
- // If the directory to be replaced is not empty, reject the rename.
- if len(attrs) != 0 {
- return ErrNotEmpty
- }
- }
- }
-
- // Be careful, we may have already grabbed this mutex above.
- if op != np {
- op.mu.Lock()
- defer op.mu.Unlock()
- }
-
- // Do the swap.
- n := op.children[oldName]
- op.removeChildLocked(ctx, oldName)
- np.addChildLocked(newName, n)
-
- // Update ctime.
- n.NotifyStatusChange(ctx)
-
- return nil
-}
-
-// Truncate is not supported by default.
-func (*Entry) Truncate(context.Context, *fs.Inode, int64) error {
- return ErrInvalidOp
-}
-
-// Readlink always returns ENOLINK.
-func (*Entry) Readlink(context.Context, *fs.Inode) (string, error) {
- return "", syscall.ENOLINK
-}
-
-// Getlink always returns ENOLINK.
-func (*Entry) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
- return nil, syscall.ENOLINK
-}
-
-// Release is a no-op.
-func (e *Entry) Release(context.Context) {}
-
-// AddLink implements InodeOperationss.AddLink.
-func (e *Entry) AddLink() {
- e.mu.Lock()
- e.unstable.Links++
- e.mu.Unlock()
-}
-
-// DropLink implements InodeOperationss.DropLink.
-func (e *Entry) DropLink() {
- e.mu.Lock()
- e.unstable.Links--
- e.mu.Unlock()
-}
-
-// DeprecatedReaddir is not supported by default.
-func (*Entry) DeprecatedReaddir(context.Context, *fs.DirCtx, int) (int, error) {
- return 0, ErrNotDirectory
-}
-
-// DeprecatedPreadv always returns ErrInvalidOp.
-func (*Entry) DeprecatedPreadv(context.Context, usermem.IOSequence, int64) (int64, error) {
- return 0, ErrInvalidOp
-}
-
-// DeprecatedPwritev always returns ErrInvalidOp.
-func (*Entry) DeprecatedPwritev(context.Context, usermem.IOSequence, int64) (int64, error) {
- return 0, ErrInvalidOp
-}
-
-// DeprecatedFsync is a noop.
-func (*Entry) DeprecatedFsync() error {
- // Ignore, this is in memory.
- return nil
-}
-
-// DeprecatedFlush always returns nil.
-func (*Entry) DeprecatedFlush() error {
- return nil
-}
-
-// DeprecatedMappable implements fs.InodeOperations.DeprecatedMappable.
-func (*Entry) DeprecatedMappable(context.Context, *fs.Inode) (memmap.Mappable, bool) {
- return nil, false
-}
-
-func init() {
- // Register ramfs errors.
- syserror.AddErrorTranslation(ErrInvalidOp, syscall.EINVAL)
- syserror.AddErrorTranslation(ErrDenied, syscall.EACCES)
- syserror.AddErrorTranslation(ErrNotFound, syscall.ENOENT)
- syserror.AddErrorTranslation(ErrCrossDevice, syscall.EXDEV)
- syserror.AddErrorTranslation(ErrIsDirectory, syscall.EISDIR)
- syserror.AddErrorTranslation(ErrNotDirectory, syscall.ENOTDIR)
- syserror.AddErrorTranslation(ErrNotEmpty, syscall.ENOTEMPTY)
-}
diff --git a/pkg/sentry/fs/ramfs/socket.go b/pkg/sentry/fs/ramfs/socket.go
index 8c81478c8..2c1295897 100644
--- a/pkg/sentry/fs/ramfs/socket.go
+++ b/pkg/sentry/fs/ramfs/socket.go
@@ -15,25 +15,42 @@
package ramfs
import (
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
+ "gvisor.googlesource.com/gvisor/pkg/waiter"
)
// Socket represents a socket.
//
// +stateify savable
type Socket struct {
- Entry
+ fsutil.InodeGenericChecker `state:"nosave"`
+ fsutil.InodeNoopRelease `state:"nosave"`
+ fsutil.InodeNoopWriteOut `state:"nosave"`
+ fsutil.InodeNotDirectory `state:"nosave"`
+ fsutil.InodeNotMappable `state:"nosave"`
+ fsutil.InodeNotSymlink `state:"nosave"`
+ fsutil.InodeNotTruncatable `state:"nosave"`
+ fsutil.InodeVirtual `state:"nosave"`
+
+ fsutil.InodeSimpleAttributes
+ fsutil.InodeSimpleExtendedAttributes
// ep is the bound endpoint.
ep transport.BoundEndpoint
}
-// InitSocket initializes a socket.
-func (s *Socket) InitSocket(ctx context.Context, ep transport.BoundEndpoint, owner fs.FileOwner, perms fs.FilePermissions) {
- s.InitEntry(ctx, owner, perms)
- s.ep = ep
+var _ fs.InodeOperations = (*Socket)(nil)
+
+// NewSocket returns a new Socket.
+func NewSocket(ctx context.Context, ep transport.BoundEndpoint, owner fs.FileOwner, perms fs.FilePermissions) *Socket {
+ return &Socket{
+ InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, perms, linux.SOCKFS_MAGIC),
+ ep: ep,
+ }
}
// BoundEndpoint returns the socket data.
@@ -42,3 +59,24 @@ func (s *Socket) BoundEndpoint(*fs.Inode, string) transport.BoundEndpoint {
// care about the path argument.
return s.ep
}
+
+// GetFile implements fs.FileOperations.GetFile.
+func (s *Socket) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &socketFileOperations{}), nil
+}
+
+// +stateify savable
+type socketFileOperations struct {
+ waiter.AlwaysReady `state:"nosave"`
+ fsutil.FileNoIoctl `state:"nosave"`
+ fsutil.FileNoMMap `state:"nosave"`
+ fsutil.FileNoopFlush `state:"nosave"`
+ fsutil.FileNoopFsync `state:"nosave"`
+ fsutil.FileNoopRelease `state:"nosave"`
+ fsutil.FileNoRead `state:"nosave"`
+ fsutil.FileNoSeek `state:"nosave"`
+ fsutil.FileNotDirReaddir `state:"nosave"`
+ fsutil.FileNoWrite `state:"nosave"`
+}
+
+var _ fs.FileOperations = (*socketFileOperations)(nil)
diff --git a/pkg/sentry/fs/ramfs/symlink.go b/pkg/sentry/fs/ramfs/symlink.go
index a21fac2c7..47dae380b 100644
--- a/pkg/sentry/fs/ramfs/symlink.go
+++ b/pkg/sentry/fs/ramfs/symlink.go
@@ -15,44 +15,55 @@
package ramfs
import (
- "sync"
-
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
+ "gvisor.googlesource.com/gvisor/pkg/waiter"
)
// Symlink represents a symlink.
//
// +stateify savable
type Symlink struct {
- Entry
+ fsutil.InodeGenericChecker `state:"nosave"`
+ fsutil.InodeNoopRelease `state:"nosave"`
+ fsutil.InodeNoopWriteOut `state:"nosave"`
+ fsutil.InodeNotDirectory `state:"nosave"`
+ fsutil.InodeNotMappable `state:"nosave"`
+ fsutil.InodeNotTruncatable `state:"nosave"`
+ fsutil.InodeNotSocket `state:"nosave"`
+ fsutil.InodeVirtual `state:"nosave"`
- mu sync.Mutex `state:"nosave"`
+ fsutil.InodeSimpleAttributes
+ fsutil.InodeSimpleExtendedAttributes
// Target is the symlink target.
Target string
}
-// InitSymlink initializes a symlink, pointing to the given target.
-// A symlink is assumed to always have permissions 0777.
-func (s *Symlink) InitSymlink(ctx context.Context, owner fs.FileOwner, target string) {
- s.InitEntry(ctx, owner, fs.FilePermsFromMode(0777))
- s.Target = target
+var _ fs.InodeOperations = (*Symlink)(nil)
+
+// NewSymlink returns a new Symlink.
+func NewSymlink(ctx context.Context, owner fs.FileOwner, target string) *Symlink {
+ // A symlink is assumed to always have permissions 0777.
+ return &Symlink{
+ InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, owner, fs.FilePermsFromMode(0777), linux.RAMFS_MAGIC),
+ Target: target,
+ }
}
// UnstableAttr returns all attributes of this ramfs symlink.
func (s *Symlink) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
- uattr, _ := s.Entry.UnstableAttr(ctx, inode)
+ uattr, err := s.InodeSimpleAttributes.UnstableAttr(ctx, inode)
+ if err != nil {
+ return fs.UnstableAttr{}, err
+ }
uattr.Size = int64(len(s.Target))
uattr.Usage = uattr.Size
return uattr, nil
}
-// Check implements InodeOperations.Check.
-func (s *Symlink) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
- return fs.ContextCanAccessFile(ctx, inode, p)
-}
-
// SetPermissions on a symlink is always rejected.
func (s *Symlink) SetPermissions(context.Context, *fs.Inode, fs.FilePermissions) bool {
return false
@@ -60,10 +71,7 @@ func (s *Symlink) SetPermissions(context.Context, *fs.Inode, fs.FilePermissions)
// Readlink reads the symlink value.
func (s *Symlink) Readlink(ctx context.Context, _ *fs.Inode) (string, error) {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- s.Entry.NotifyAccess(ctx)
+ s.NotifyAccess(ctx)
return s.Target, nil
}
@@ -72,3 +80,24 @@ func (s *Symlink) Readlink(ctx context.Context, _ *fs.Inode) (string, error) {
func (*Symlink) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) {
return nil, fs.ErrResolveViaReadlink
}
+
+// GetFile implements fs.FileOperations.GetFile.
+func (s *Symlink) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
+ return fs.NewFile(ctx, dirent, flags, &symlinkFileOperations{}), nil
+}
+
+// +stateify savable
+type symlinkFileOperations struct {
+ waiter.AlwaysReady `state:"nosave"`
+ fsutil.FileNoIoctl `state:"nosave"`
+ fsutil.FileNoMMap `state:"nosave"`
+ fsutil.FileNoopFlush `state:"nosave"`
+ fsutil.FileNoopFsync `state:"nosave"`
+ fsutil.FileNoopRelease `state:"nosave"`
+ fsutil.FileNoRead `state:"nosave"`
+ fsutil.FileNoSeek `state:"nosave"`
+ fsutil.FileNotDirReaddir `state:"nosave"`
+ fsutil.FileNoWrite `state:"nosave"`
+}
+
+var _ fs.FileOperations = (*symlinkFileOperations)(nil)
diff --git a/pkg/sentry/fs/ramfs/test/BUILD b/pkg/sentry/fs/ramfs/test/BUILD
deleted file mode 100644
index 187eac49d..000000000
--- a/pkg/sentry/fs/ramfs/test/BUILD
+++ /dev/null
@@ -1,16 +0,0 @@
-package(licenses = ["notice"]) # Apache 2.0
-
-load("//tools/go_stateify:defs.bzl", "go_library")
-
-go_library(
- name = "test",
- testonly = 1,
- srcs = ["test.go"],
- importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test",
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/sentry/context",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/ramfs",
- ],
-)
diff --git a/pkg/sentry/fs/ramfs/test/test.go b/pkg/sentry/fs/ramfs/test/test.go
deleted file mode 100644
index 11bff7729..000000000
--- a/pkg/sentry/fs/ramfs/test/test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2018 Google LLC
-//
-// 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 test provides a simple ramfs-based filesystem for use in testing.
-package test
-
-import (
- "gvisor.googlesource.com/gvisor/pkg/sentry/context"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
- "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs"
-)
-
-// Dir is a simple ramfs.Dir that supports save/restore as-is.
-type Dir struct {
- ramfs.Dir
-}
-
-// NewDir returns a simple ramfs directory with the passed contents.
-func NewDir(ctx context.Context, contents map[string]*fs.Inode, perms fs.FilePermissions) *Dir {
- d := &Dir{}
- d.InitDir(ctx, contents, fs.RootOwner, perms)
- return d
-}
-
-// File is a simple ramfs.File that supports save/restore as-is.
-type File struct {
- ramfs.File
-}
-
-// NewFile returns a simple ramfs File.
-func NewFile(ctx context.Context, perms fs.FilePermissions) *File {
- f := &File{}
- f.InitFile(ctx, fs.RootOwner, perms)
- return f
-}
diff --git a/pkg/sentry/fs/ramfs/tree.go b/pkg/sentry/fs/ramfs/tree.go
index 29a70f698..f6d5ffdec 100644
--- a/pkg/sentry/fs/ramfs/tree.go
+++ b/pkg/sentry/fs/ramfs/tree.go
@@ -60,8 +60,7 @@ func makeSubdir(ctx context.Context, msrc *fs.MountSource, root *Dir, subdir str
// emptyDir returns an empty *ramfs.Dir that is traversable but not writable.
func emptyDir(ctx context.Context, msrc *fs.MountSource) *fs.Inode {
- dir := &Dir{}
- dir.InitDir(ctx, make(map[string]*fs.Inode), fs.RootOwner, fs.FilePermsFromMode(0555))
+ dir := NewDir(ctx, make(map[string]*fs.Inode), fs.RootOwner, fs.FilePermsFromMode(0555))
return fs.NewInode(dir, msrc, fs.StableAttr{
DeviceID: anon.PseudoDevice.DeviceID(),
InodeID: anon.PseudoDevice.NextIno(),
diff --git a/pkg/sentry/fs/ramfs/tree_test.go b/pkg/sentry/fs/ramfs/tree_test.go
index 54df2143c..8bee9cfc1 100644
--- a/pkg/sentry/fs/ramfs/tree_test.go
+++ b/pkg/sentry/fs/ramfs/tree_test.go
@@ -22,7 +22,7 @@ import (
)
func TestMakeDirectoryTree(t *testing.T) {
- mount := fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{})
+ mount := fs.NewPseudoMountSource()
for _, test := range []struct {
name string