mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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
This commit is contained in:
@@ -11,8 +11,8 @@ go_template_instance(
|
||||
prefix = "dentry",
|
||||
template = "//pkg/ilist:generic_list",
|
||||
types = {
|
||||
"Element": "*Dentry",
|
||||
"Linker": "*Dentry",
|
||||
"Element": "*dentry",
|
||||
"Linker": "*dentry",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user