From 06102af65ad2d0e5a89c5e7dfe281afd5346ed4f Mon Sep 17 00:00:00 2001 From: Jamie Liu Date: Thu, 8 Aug 2019 11:45:33 -0700 Subject: [PATCH] memfs fixes. - Unexport Filesystem/Dentry/Inode. - Support SEEK_CUR in directoryFD.Seek(). - Hold Filesystem.mu before touching directoryFD.off in directoryFD.Seek(). - Remove deleted Dentries from their parent directory.childLists. - Remove invalid FIXMEs. PiperOrigin-RevId: 262400633 --- pkg/sentry/fsimpl/memfs/BUILD | 4 +- pkg/sentry/fsimpl/memfs/directory.go | 55 ++++++++------- pkg/sentry/fsimpl/memfs/filesystem.go | 68 +++++++++--------- pkg/sentry/fsimpl/memfs/memfs.go | 92 ++++++++++++------------- pkg/sentry/fsimpl/memfs/regular_file.go | 6 +- pkg/sentry/fsimpl/memfs/symlink.go | 4 +- 6 files changed, 120 insertions(+), 109 deletions(-) diff --git a/pkg/sentry/fsimpl/memfs/BUILD b/pkg/sentry/fsimpl/memfs/BUILD index d5d4f68df..d2450e810 100644 --- a/pkg/sentry/fsimpl/memfs/BUILD +++ b/pkg/sentry/fsimpl/memfs/BUILD @@ -11,8 +11,8 @@ go_template_instance( prefix = "dentry", template = "//pkg/ilist:generic_list", types = { - "Element": "*Dentry", - "Linker": "*Dentry", + "Element": "*dentry", + "Linker": "*dentry", }, ) diff --git a/pkg/sentry/fsimpl/memfs/directory.go b/pkg/sentry/fsimpl/memfs/directory.go index b0c3ea39a..c52dc781c 100644 --- a/pkg/sentry/fsimpl/memfs/directory.go +++ b/pkg/sentry/fsimpl/memfs/directory.go @@ -23,23 +23,23 @@ import ( ) type directory struct { - inode Inode + inode inode // childList is a list containing (1) child Dentries and (2) fake Dentries // (with inode == nil) that represent the iteration position of // directoryFDs. childList is used to support directoryFD.IterDirents() - // efficiently. childList is protected by Filesystem.mu. + // efficiently. childList is protected by filesystem.mu. childList dentryList } -func (fs *Filesystem) newDirectory(creds *auth.Credentials, mode uint16) *Inode { +func (fs *filesystem) newDirectory(creds *auth.Credentials, mode uint16) *inode { dir := &directory{} dir.inode.init(dir, fs, creds, mode) dir.inode.nlink = 2 // from "." and parent directory or ".." for root return &dir.inode } -func (i *Inode) isDir() bool { +func (i *inode) isDir() bool { _, ok := i.impl.(*directory) return ok } @@ -48,8 +48,8 @@ type directoryFD struct { fileDescription vfs.DirectoryFileDescriptionDefaultImpl - // Protected by Filesystem.mu. - iter *Dentry + // Protected by filesystem.mu. + iter *dentry off int64 } @@ -68,7 +68,7 @@ func (fd *directoryFD) Release() { // IterDirents implements vfs.FileDescriptionImpl.IterDirents. func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error { fs := fd.filesystem() - d := fd.vfsfd.VirtualDentry().Dentry() + vfsd := fd.vfsfd.VirtualDentry().Dentry() fs.mu.Lock() defer fs.mu.Unlock() @@ -77,7 +77,7 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba if !cb.Handle(vfs.Dirent{ Name: ".", Type: linux.DT_DIR, - Ino: d.Impl().(*Dentry).inode.ino, + Ino: vfsd.Impl().(*dentry).inode.ino, Off: 0, }) { return nil @@ -85,7 +85,7 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba fd.off++ } if fd.off == 1 { - parentInode := d.ParentOrSelf().Impl().(*Dentry).inode + parentInode := vfsd.ParentOrSelf().Impl().(*dentry).inode if !cb.Handle(vfs.Dirent{ Name: "..", Type: parentInode.direntType(), @@ -97,12 +97,12 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba fd.off++ } - dir := d.Impl().(*Dentry).inode.impl.(*directory) - var child *Dentry + dir := vfsd.Impl().(*dentry).inode.impl.(*directory) + var child *dentry if fd.iter == nil { // Start iteration at the beginning of dir. child = dir.childList.Front() - fd.iter = &Dentry{} + fd.iter = &dentry{} } else { // Continue iteration from where we left off. child = fd.iter.Next() @@ -130,32 +130,41 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba // Seek implements vfs.FileDescriptionImpl.Seek. func (fd *directoryFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { - if whence != linux.SEEK_SET { - // TODO: Linux also allows SEEK_CUR. + fs := fd.filesystem() + fs.mu.Lock() + defer fs.mu.Unlock() + + switch whence { + case linux.SEEK_SET: + // Use offset as given. + case linux.SEEK_CUR: + offset += fd.off + default: return 0, syserror.EINVAL } if offset < 0 { return 0, syserror.EINVAL } + // If the offset isn't changing (e.g. due to lseek(0, SEEK_CUR)), don't + // seek even if doing so might reposition the iterator due to concurrent + // mutation of the directory. Compare fs/libfs.c:dcache_dir_lseek(). + if fd.off == offset { + return offset, nil + } + fd.off = offset // Compensate for "." and "..". - var remChildren int64 - if offset < 2 { - remChildren = 0 - } else { + remChildren := int64(0) + if offset >= 2 { remChildren = offset - 2 } - fs := fd.filesystem() dir := fd.inode().impl.(*directory) - fs.mu.Lock() - defer fs.mu.Unlock() - // Ensure that fd.iter exists and is not linked into dir.childList. if fd.iter == nil { - fd.iter = &Dentry{} + fd.iter = &dentry{} } else { dir.childList.Remove(fd.iter) } diff --git a/pkg/sentry/fsimpl/memfs/filesystem.go b/pkg/sentry/fsimpl/memfs/filesystem.go index 4d989eeaf..f79e2d9c8 100644 --- a/pkg/sentry/fsimpl/memfs/filesystem.go +++ b/pkg/sentry/fsimpl/memfs/filesystem.go @@ -28,9 +28,9 @@ import ( // // stepLocked is loosely analogous to fs/namei.c:walk_component(). // -// Preconditions: Filesystem.mu must be locked. !rp.Done(). inode == -// vfsd.Impl().(*Dentry).inode. -func stepLocked(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, inode *Inode) (*vfs.Dentry, *Inode, error) { +// Preconditions: filesystem.mu must be locked. !rp.Done(). inode == +// vfsd.Impl().(*dentry).inode. +func stepLocked(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, inode *inode) (*vfs.Dentry, *inode, error) { if !inode.isDir() { return nil, nil, syserror.ENOTDIR } @@ -47,7 +47,7 @@ afterSymlink: // not in the Dentry tree, it doesn't exist. return nil, nil, syserror.ENOENT } - nextInode := nextVFSD.Impl().(*Dentry).inode + nextInode := nextVFSD.Impl().(*dentry).inode if symlink, ok := nextInode.impl.(*symlink); ok && rp.ShouldFollowSymlink() { // TODO: symlink traversals update access time if err := rp.HandleSymlink(symlink.target); err != nil { @@ -64,10 +64,10 @@ afterSymlink: // walkExistingLocked is loosely analogous to Linux's // fs/namei.c:path_lookupat(). // -// Preconditions: Filesystem.mu must be locked. -func walkExistingLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *Inode, error) { +// Preconditions: filesystem.mu must be locked. +func walkExistingLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *inode, error) { vfsd := rp.Start() - inode := vfsd.Impl().(*Dentry).inode + inode := vfsd.Impl().(*dentry).inode for !rp.Done() { var err error vfsd, inode, err = stepLocked(rp, vfsd, inode) @@ -88,10 +88,10 @@ func walkExistingLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *Inode, error) { // walkParentDirLocked is loosely analogous to Linux's // fs/namei.c:path_parentat(). // -// Preconditions: Filesystem.mu must be locked. !rp.Done(). -func walkParentDirLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *Inode, error) { +// Preconditions: filesystem.mu must be locked. !rp.Done(). +func walkParentDirLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *inode, error) { vfsd := rp.Start() - inode := vfsd.Impl().(*Dentry).inode + inode := vfsd.Impl().(*dentry).inode for !rp.Final() { var err error vfsd, inode, err = stepLocked(rp, vfsd, inode) @@ -108,9 +108,9 @@ func walkParentDirLocked(rp *vfs.ResolvingPath) (*vfs.Dentry, *Inode, error) { // checkCreateLocked checks that a file named rp.Component() may be created in // directory parentVFSD, then returns rp.Component(). // -// Preconditions: Filesystem.mu must be locked. parentInode == -// parentVFSD.Impl().(*Dentry).inode. parentInode.isDir() == true. -func checkCreateLocked(rp *vfs.ResolvingPath, parentVFSD *vfs.Dentry, parentInode *Inode) (string, error) { +// Preconditions: filesystem.mu must be locked. parentInode == +// parentVFSD.Impl().(*dentry).inode. parentInode.isDir() == true. +func checkCreateLocked(rp *vfs.ResolvingPath, parentVFSD *vfs.Dentry, parentInode *inode) (string, error) { if err := parentInode.checkPermissions(rp.Credentials(), vfs.MayWrite|vfs.MayExec, true); err != nil { return "", err } @@ -144,7 +144,7 @@ func checkDeleteLocked(vfsd *vfs.Dentry) error { } // GetDentryAt implements vfs.FilesystemImpl.GetDentryAt. -func (fs *Filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) { +func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) { fs.mu.RLock() defer fs.mu.RUnlock() vfsd, inode, err := walkExistingLocked(rp) @@ -164,7 +164,7 @@ func (fs *Filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, op } // LinkAt implements vfs.FilesystemImpl.LinkAt. -func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error { +func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error { if rp.Done() { return syserror.EEXIST } @@ -185,7 +185,7 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs. return err } defer rp.Mount().EndWrite() - d := vd.Dentry().Impl().(*Dentry) + d := vd.Dentry().Impl().(*dentry) if d.inode.isDir() { return syserror.EPERM } @@ -197,7 +197,7 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs. } // MkdirAt implements vfs.FilesystemImpl.MkdirAt. -func (fs *Filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error { +func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error { if rp.Done() { return syserror.EEXIST } @@ -223,7 +223,7 @@ func (fs *Filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v } // MknodAt implements vfs.FilesystemImpl.MknodAt. -func (fs *Filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error { +func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error { if rp.Done() { return syserror.EEXIST } @@ -246,7 +246,7 @@ func (fs *Filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v } // OpenAt implements vfs.FilesystemImpl.OpenAt. -func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) { +func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) { // Filter out flags that are not supported by memfs. O_DIRECTORY and // O_NOFOLLOW have no effect here (they're handled by VFS by setting // appropriate bits in rp), but are returned by @@ -265,11 +265,10 @@ func (fs *Filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf mustCreate := opts.Flags&linux.O_EXCL != 0 vfsd := rp.Start() - inode := vfsd.Impl().(*Dentry).inode + inode := vfsd.Impl().(*dentry).inode fs.mu.Lock() defer fs.mu.Unlock() if rp.Done() { - // FIXME: ??? if rp.MustBeDir() { return nil, syserror.EISDIR } @@ -327,7 +326,7 @@ afterTrailingSymlink: if mustCreate { return nil, syserror.EEXIST } - childInode := childVFSD.Impl().(*Dentry).inode + childInode := childVFSD.Impl().(*dentry).inode if symlink, ok := childInode.impl.(*symlink); ok && rp.ShouldFollowSymlink() { // TODO: symlink traversals update access time if err := rp.HandleSymlink(symlink.target); err != nil { @@ -340,7 +339,7 @@ afterTrailingSymlink: return childInode.open(rp, childVFSD, opts.Flags, false) } -func (i *Inode) open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32, afterCreate bool) (*vfs.FileDescription, error) { +func (i *inode) open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32, afterCreate bool) (*vfs.FileDescription, error) { ats := vfs.AccessTypesForOpenFlags(flags) if !afterCreate { if err := i.checkPermissions(rp.Credentials(), ats, i.isDir()); err != nil { @@ -385,7 +384,7 @@ func (i *Inode) open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, flags uint32, afte } // ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt. -func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) { +func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) { fs.mu.RLock() _, inode, err := walkExistingLocked(rp) fs.mu.RUnlock() @@ -400,9 +399,8 @@ func (fs *Filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (st } // RenameAt implements vfs.FilesystemImpl.RenameAt. -func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry, opts vfs.RenameOptions) error { +func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry, opts vfs.RenameOptions) error { if rp.Done() { - // FIXME return syserror.ENOENT } fs.mu.Lock() @@ -424,7 +422,7 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, vd vf } // RmdirAt implements vfs.FilesystemImpl.RmdirAt. -func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error { +func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error { fs.mu.Lock() defer fs.mu.Unlock() vfsd, inode, err := walkExistingLocked(rp) @@ -447,12 +445,14 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error if err := rp.VirtualFilesystem().DeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil { return err } + // Remove from parent directory's childList. + vfsd.Parent().Impl().(*dentry).inode.impl.(*directory).childList.Remove(vfsd.Impl().(*dentry)) inode.decRef() return nil } // SetStatAt implements vfs.FilesystemImpl.SetStatAt. -func (fs *Filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error { +func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error { fs.mu.RLock() _, _, err := walkExistingLocked(rp) fs.mu.RUnlock() @@ -462,12 +462,12 @@ func (fs *Filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts if opts.Stat.Mask == 0 { return nil } - // TODO: implement Inode.setStat + // TODO: implement inode.setStat return syserror.EPERM } // StatAt implements vfs.FilesystemImpl.StatAt. -func (fs *Filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) { +func (fs *filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) { fs.mu.RLock() _, inode, err := walkExistingLocked(rp) fs.mu.RUnlock() @@ -480,7 +480,7 @@ func (fs *Filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf } // StatFSAt implements vfs.FilesystemImpl.StatFSAt. -func (fs *Filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) { +func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) { fs.mu.RLock() _, _, err := walkExistingLocked(rp) fs.mu.RUnlock() @@ -492,7 +492,7 @@ func (fs *Filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linu } // SymlinkAt implements vfs.FilesystemImpl.SymlinkAt. -func (fs *Filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error { +func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error { if rp.Done() { return syserror.EEXIST } @@ -517,7 +517,7 @@ func (fs *Filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, targ } // UnlinkAt implements vfs.FilesystemImpl.UnlinkAt. -func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error { +func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error { fs.mu.Lock() defer fs.mu.Unlock() vfsd, inode, err := walkExistingLocked(rp) @@ -537,6 +537,8 @@ func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error if err := rp.VirtualFilesystem().DeleteDentry(vfs.MountNamespaceFromContext(ctx), vfsd); err != nil { return err } + // Remove from parent directory's childList. + vfsd.Parent().Impl().(*dentry).inode.impl.(*directory).childList.Remove(vfsd.Impl().(*dentry)) inode.decLinksLocked() return nil } diff --git a/pkg/sentry/fsimpl/memfs/memfs.go b/pkg/sentry/fsimpl/memfs/memfs.go index f381e1a88..59612da14 100644 --- a/pkg/sentry/fsimpl/memfs/memfs.go +++ b/pkg/sentry/fsimpl/memfs/memfs.go @@ -21,10 +21,10 @@ // // Lock order: // -// Filesystem.mu +// filesystem.mu // regularFileFD.offMu // regularFile.mu -// Inode.mu +// inode.mu package memfs import ( @@ -42,8 +42,8 @@ import ( // FilesystemType implements vfs.FilesystemType. type FilesystemType struct{} -// Filesystem implements vfs.FilesystemImpl. -type Filesystem struct { +// filesystem implements vfs.FilesystemImpl. +type filesystem struct { vfsfs vfs.Filesystem // mu serializes changes to the Dentry tree. @@ -54,44 +54,44 @@ type Filesystem struct { // NewFilesystem implements vfs.FilesystemType.NewFilesystem. func (fstype FilesystemType) NewFilesystem(ctx context.Context, creds *auth.Credentials, source string, opts vfs.NewFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) { - var fs Filesystem + var fs filesystem fs.vfsfs.Init(&fs) root := fs.newDentry(fs.newDirectory(creds, 01777)) return &fs.vfsfs, &root.vfsd, nil } // Release implements vfs.FilesystemImpl.Release. -func (fs *Filesystem) Release() { +func (fs *filesystem) Release() { } // Sync implements vfs.FilesystemImpl.Sync. -func (fs *Filesystem) Sync(ctx context.Context) error { +func (fs *filesystem) Sync(ctx context.Context) error { // All filesystem state is in-memory. return nil } -// Dentry implements vfs.DentryImpl. -type Dentry struct { +// dentry implements vfs.DentryImpl. +type dentry struct { vfsd vfs.Dentry - // inode is the inode represented by this Dentry. Multiple Dentries may - // share a single non-directory Inode (with hard links). inode is + // inode is the inode represented by this dentry. Multiple Dentries may + // share a single non-directory inode (with hard links). inode is // immutable. - inode *Inode + inode *inode - // memfs doesn't count references on Dentries; because the Dentry tree is + // memfs doesn't count references on dentries; because the dentry tree is // the sole source of truth, it is by definition always consistent with the - // state of the filesystem. However, it does count references on Inodes, - // because Inode resources are released when all references are dropped. + // state of the filesystem. However, it does count references on inodes, + // because inode resources are released when all references are dropped. // (memfs doesn't really have resources to release, but we implement // reference counting because tmpfs regular files will.) - // dentryEntry (ugh) links Dentries into their parent directory.childList. + // dentryEntry (ugh) links dentries into their parent directory.childList. dentryEntry } -func (fs *Filesystem) newDentry(inode *Inode) *Dentry { - d := &Dentry{ +func (fs *filesystem) newDentry(inode *inode) *dentry { + d := &dentry{ inode: inode, } d.vfsd.Init(d) @@ -99,37 +99,37 @@ func (fs *Filesystem) newDentry(inode *Inode) *Dentry { } // IncRef implements vfs.DentryImpl.IncRef. -func (d *Dentry) IncRef(vfsfs *vfs.Filesystem) { +func (d *dentry) IncRef(vfsfs *vfs.Filesystem) { d.inode.incRef() } // TryIncRef implements vfs.DentryImpl.TryIncRef. -func (d *Dentry) TryIncRef(vfsfs *vfs.Filesystem) bool { +func (d *dentry) TryIncRef(vfsfs *vfs.Filesystem) bool { return d.inode.tryIncRef() } // DecRef implements vfs.DentryImpl.DecRef. -func (d *Dentry) DecRef(vfsfs *vfs.Filesystem) { +func (d *dentry) DecRef(vfsfs *vfs.Filesystem) { d.inode.decRef() } -// Inode represents a filesystem object. -type Inode struct { +// inode represents a filesystem object. +type inode struct { // refs is a reference count. refs is accessed using atomic memory // operations. // - // A reference is held on all Inodes that are reachable in the filesystem + // A reference is held on all inodes that are reachable in the filesystem // tree. For non-directories (which may have multiple hard links), this // means that a reference is dropped when nlink reaches 0. For directories, // nlink never reaches 0 due to the "." entry; instead, - // Filesystem.RmdirAt() drops the reference. + // filesystem.RmdirAt() drops the reference. refs int64 // Inode metadata; protected by mu and accessed using atomic memory // operations unless otherwise specified. mu sync.RWMutex mode uint32 // excluding file type bits, which are based on impl - nlink uint32 // protected by Filesystem.mu instead of Inode.mu + nlink uint32 // protected by filesystem.mu instead of inode.mu uid uint32 // auth.KUID, but stored as raw uint32 for sync/atomic gid uint32 // auth.KGID, but ... ino uint64 // immutable @@ -137,7 +137,7 @@ type Inode struct { impl interface{} // immutable } -func (i *Inode) init(impl interface{}, fs *Filesystem, creds *auth.Credentials, mode uint16) { +func (i *inode) init(impl interface{}, fs *filesystem, creds *auth.Credentials, mode uint16) { i.refs = 1 i.mode = uint32(mode) i.uid = uint32(creds.EffectiveKUID) @@ -147,29 +147,29 @@ func (i *Inode) init(impl interface{}, fs *Filesystem, creds *auth.Credentials, i.impl = impl } -// Preconditions: Filesystem.mu must be locked for writing. -func (i *Inode) incLinksLocked() { +// Preconditions: filesystem.mu must be locked for writing. +func (i *inode) incLinksLocked() { if atomic.AddUint32(&i.nlink, 1) <= 1 { - panic("memfs.Inode.incLinksLocked() called with no existing links") + panic("memfs.inode.incLinksLocked() called with no existing links") } } -// Preconditions: Filesystem.mu must be locked for writing. -func (i *Inode) decLinksLocked() { +// Preconditions: filesystem.mu must be locked for writing. +func (i *inode) decLinksLocked() { if nlink := atomic.AddUint32(&i.nlink, ^uint32(0)); nlink == 0 { i.decRef() } else if nlink == ^uint32(0) { // negative overflow - panic("memfs.Inode.decLinksLocked() called with no existing links") + panic("memfs.inode.decLinksLocked() called with no existing links") } } -func (i *Inode) incRef() { +func (i *inode) incRef() { if atomic.AddInt64(&i.refs, 1) <= 1 { - panic("memfs.Inode.incRef() called without holding a reference") + panic("memfs.inode.incRef() called without holding a reference") } } -func (i *Inode) tryIncRef() bool { +func (i *inode) tryIncRef() bool { for { refs := atomic.LoadInt64(&i.refs) if refs == 0 { @@ -181,7 +181,7 @@ func (i *Inode) tryIncRef() bool { } } -func (i *Inode) decRef() { +func (i *inode) decRef() { if refs := atomic.AddInt64(&i.refs, -1); refs == 0 { // This is unnecessary; it's mostly to simulate what tmpfs would do. if regfile, ok := i.impl.(*regularFile); ok { @@ -191,18 +191,18 @@ func (i *Inode) decRef() { regfile.mu.Unlock() } } else if refs < 0 { - panic("memfs.Inode.decRef() called without holding a reference") + panic("memfs.inode.decRef() called without holding a reference") } } -func (i *Inode) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes, isDir bool) error { +func (i *inode) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes, isDir bool) error { return vfs.GenericCheckPermissions(creds, ats, isDir, uint16(atomic.LoadUint32(&i.mode)), auth.KUID(atomic.LoadUint32(&i.uid)), auth.KGID(atomic.LoadUint32(&i.gid))) } // Go won't inline this function, and returning linux.Statx (which is quite // big) means spending a lot of time in runtime.duffcopy(), so instead it's an // output parameter. -func (i *Inode) statTo(stat *linux.Statx) { +func (i *inode) statTo(stat *linux.Statx) { stat.Mask = linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_NLINK | linux.STATX_UID | linux.STATX_GID | linux.STATX_INO stat.Blksize = 1 // usermem.PageSize in tmpfs stat.Nlink = atomic.LoadUint32(&i.nlink) @@ -241,7 +241,7 @@ func allocatedBlocksForSize(size uint64) uint64 { return (size + 511) / 512 } -func (i *Inode) direntType() uint8 { +func (i *inode) direntType() uint8 { switch i.impl.(type) { case *regularFile: return linux.DT_REG @@ -262,12 +262,12 @@ type fileDescription struct { flags uint32 // status flags; immutable } -func (fd *fileDescription) filesystem() *Filesystem { - return fd.vfsfd.VirtualDentry().Mount().Filesystem().Impl().(*Filesystem) +func (fd *fileDescription) filesystem() *filesystem { + return fd.vfsfd.VirtualDentry().Mount().Filesystem().Impl().(*filesystem) } -func (fd *fileDescription) inode() *Inode { - return fd.vfsfd.VirtualDentry().Dentry().Impl().(*Dentry).inode +func (fd *fileDescription) inode() *inode { + return fd.vfsfd.VirtualDentry().Dentry().Impl().(*dentry).inode } // StatusFlags implements vfs.FileDescriptionImpl.StatusFlags. @@ -294,6 +294,6 @@ func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) if opts.Stat.Mask == 0 { return nil } - // TODO: implement Inode.setStat + // TODO: implement inode.setStat return syserror.EPERM } diff --git a/pkg/sentry/fsimpl/memfs/regular_file.go b/pkg/sentry/fsimpl/memfs/regular_file.go index 4a3603cc8..7a16d5719 100644 --- a/pkg/sentry/fsimpl/memfs/regular_file.go +++ b/pkg/sentry/fsimpl/memfs/regular_file.go @@ -28,16 +28,16 @@ import ( ) type regularFile struct { - inode Inode + inode inode mu sync.RWMutex data []byte // dataLen is len(data), but accessed using atomic memory operations to - // avoid locking in Inode.stat(). + // avoid locking in inode.stat(). dataLen int64 } -func (fs *Filesystem) newRegularFile(creds *auth.Credentials, mode uint16) *Inode { +func (fs *filesystem) newRegularFile(creds *auth.Credentials, mode uint16) *inode { file := ®ularFile{} file.inode.init(file, fs, creds, mode) file.inode.nlink = 1 // from parent directory diff --git a/pkg/sentry/fsimpl/memfs/symlink.go b/pkg/sentry/fsimpl/memfs/symlink.go index e002d1727..b2ac2cbeb 100644 --- a/pkg/sentry/fsimpl/memfs/symlink.go +++ b/pkg/sentry/fsimpl/memfs/symlink.go @@ -19,11 +19,11 @@ import ( ) type symlink struct { - inode Inode + inode inode target string // immutable } -func (fs *Filesystem) newSymlink(creds *auth.Credentials, target string) *Inode { +func (fs *filesystem) newSymlink(creds *auth.Credentials, target string) *inode { link := &symlink{ target: target, }