vfs: establish lock ordering for FilesystemImpl.PrependPath

- Add type parameter Filesystem to vfs/genericfstree, which is required to
  provide `ancestryMu sync.RWMutex`, and add such a RWMutex to all FSImpls that
  use genericfstree.

- Modify genericfstree.PrependPath() and genericfstree.IsDescendant() to use
  ancestryMu to ensure atomicity. For callers of genericfstree.PrependPath(),
  this means that (broader) FSImpl locks no longer need to be held during the
  call. For callers of genericfstree.IsDescendant(), this means that we can
  remove documentation warnings about its non-atomicity.

- Minor cleanup: Remove useless variable `start`, which is always 0, from
  MM.ReadMaps/SmapsDataInto().

PiperOrigin-RevId: 696713993
This commit is contained in:
Jamie Liu
2024-11-14 18:14:27 -08:00
committed by gVisor bot
parent 2dfab3e180
commit 0659b6035a
29 changed files with 239 additions and 209 deletions
+2
View File
@@ -14,6 +14,7 @@ go_template_instance(
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
types = {
"Dentry": "dentry",
"Filesystem": "filesystem",
},
)
@@ -70,6 +71,7 @@ go_library(
"//pkg/sentry/memmap",
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/vfs",
"//pkg/sync",
"//pkg/usermem",
],
)
+4 -1
View File
@@ -19,7 +19,6 @@ import (
"os"
"runtime"
"strconv"
"sync"
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
@@ -30,6 +29,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/memmap"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
)
// Name is the filesystem name. It is part of the interface used by users,
@@ -72,6 +72,9 @@ type filesystem struct {
// reduce the lock contention. Bucket is chosen based on the hash calculation
// on nid in filesystem.inodeBucket.
inodeBuckets []inodeBucket
// ancestryMu is required by genericfstree.
ancestryMu sync.RWMutex `state:"nosave"`
}
// InternalFilesystemOptions may be passed as
+6 -6
View File
@@ -430,15 +430,15 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
// PrependPath implements vfs.FilesystemImpl.PrependPath.
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
return genericPrependPath(fs, vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(fs, vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
// MountOptions implements vfs.FilesystemImpl.MountOptions.
func (fs *filesystem) MountOptions() string {
return fs.mopts
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
+1
View File
@@ -49,6 +49,7 @@ go_template_instance(
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
types = {
"Dentry": "dentry",
"Filesystem": "filesystem",
},
)
+5 -5
View File
@@ -538,14 +538,14 @@ func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptio
inode, err := controlFD.Walk(ctx, d.name)
if err != nil {
if !dt.isDir() || !dt.forMountpoint {
return fmt.Errorf("failed to walk %q of type %x: %w", genericDebugPathname(d), dt.fileType(), err)
return fmt.Errorf("failed to walk %q of type %x: %w", genericDebugPathname(d.fs, d), dt.fileType(), err)
}
// Recreate directories that were created during volume mounting, since
// during restore we don't attempt to remount them.
inode, err = controlFD.MkdirAt(ctx, d.name, linux.FileMode(d.mode.Load()), lisafs.UID(d.uid.Load()), lisafs.GID(d.gid.Load()))
if err != nil {
return fmt.Errorf("failed to create mountpoint directory at %q: %w", genericDebugPathname(d), err)
return fmt.Errorf("failed to create mountpoint directory at %q: %w", genericDebugPathname(d.fs, d), err)
}
}
return dt.restoreFile(ctx, &inode, opts)
@@ -558,13 +558,13 @@ func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptio
})
if err != nil {
if !dt.isDir() || !dt.forMountpoint {
return fmt.Errorf("failed to walk %q of type %x: %w", genericDebugPathname(d), dt.fileType(), err)
return fmt.Errorf("failed to walk %q of type %x: %w", genericDebugPathname(d.fs, d), dt.fileType(), err)
}
// Recreate directories that were created during volume mounting, since
// during restore we don't attempt to remount them.
if err := unix.Mkdirat(controlFD, d.name, d.mode.Load()); err != nil {
return fmt.Errorf("failed to create mountpoint directory at %q: %w", genericDebugPathname(d), err)
return fmt.Errorf("failed to create mountpoint directory at %q: %w", genericDebugPathname(d.fs, d), err)
}
// Try again...
@@ -572,7 +572,7 @@ func (d *dentry) restoreFile(ctx context.Context, opts *vfs.CompleteRestoreOptio
return unix.Openat(controlFD, d.name, flags, 0)
})
if err != nil {
return fmt.Errorf("failed to open %q: %w", genericDebugPathname(d), err)
return fmt.Errorf("failed to open %q: %w", genericDebugPathname(d.fs, d), err)
}
}
return dt.restoreFile(ctx, childFD, opts)
+7 -7
View File
@@ -452,7 +452,7 @@ func (d *directfsDentry) getCreatedChild(name string, uid, gid int, isDir bool)
deleteChild := func() {
// Best effort attempt to remove the newly created child on failure.
if err := unix.Unlinkat(d.controlFD, name, unlinkFlags); err != nil {
log.Warningf("error unlinking newly created child %q after failure: %v", filepath.Join(genericDebugPathname(&d.dentry), name), err)
log.Warningf("error unlinking newly created child %q after failure: %v", filepath.Join(genericDebugPathname(d.fs, &d.dentry), name), err)
}
}
@@ -518,7 +518,7 @@ func (d *directfsDentry) bindAt(ctx context.Context, name string, creds *auth.Cr
hbep := opts.Endpoint.(transport.HostBoundEndpoint)
if err := hbep.SetBoundSocketFD(ctx, boundSocketFD); err != nil {
if err := unix.Unlinkat(d.controlFD, name, 0); err != nil {
log.Warningf("error unlinking newly created socket %q after failure: %v", filepath.Join(genericDebugPathname(&d.dentry), name), err)
log.Warningf("error unlinking newly created socket %q after failure: %v", filepath.Join(genericDebugPathname(d.fs, &d.dentry), name), err)
}
return nil, err
}
@@ -593,7 +593,7 @@ func (d *directfsDentry) getDirentsLocked(recordDirent func(name string, key ino
// TODO(gvisor.dev/issue/6665): Get rid of per-dirent stat.
stat, err := fsutil.StatAt(d.controlFD, name)
if err != nil {
log.Warningf("Getdent64: skipping file %q with failed stat, err: %v", path.Join(genericDebugPathname(&d.dentry), name), err)
log.Warningf("Getdent64: skipping file %q with failed stat, err: %v", path.Join(genericDebugPathname(d.fs, &d.dentry), name), err)
return
}
recordDirent(name, inoKeyFromStat(&stat), ftype)
@@ -650,7 +650,7 @@ func (d *directfsDentry) restoreFile(ctx context.Context, controlFD int, opts *v
var stat unix.Stat_t
if err := unix.Fstat(controlFD, &stat); err != nil {
_ = unix.Close(controlFD)
return fmt.Errorf("failed to stat %q: %w", genericDebugPathname(&d.dentry), err)
return fmt.Errorf("failed to stat %q: %w", genericDebugPathname(d.fs, &d.dentry), err)
}
d.controlFD = controlFD
@@ -672,12 +672,12 @@ func (d *directfsDentry) restoreFile(ctx context.Context, controlFD int, opts *v
if d.isRegularFile() {
if opts.ValidateFileSizes {
if d.size.RacyLoad() != uint64(stat.Size) {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(&d.dentry), d.size.Load(), stat.Size)}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d.fs, &d.dentry), d.size.Load(), stat.Size)}
}
}
if opts.ValidateFileModificationTimestamps {
if want := dentryTimestampFromUnix(stat.Mtim); d.mtime.RacyLoad() != want {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(&d.dentry), linux.NsecToStatxTimestamp(d.mtime.RacyLoad()), linux.NsecToStatxTimestamp(want))}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d.fs, &d.dentry), linux.NsecToStatxTimestamp(d.mtime.RacyLoad()), linux.NsecToStatxTimestamp(want))}
}
}
}
@@ -687,7 +687,7 @@ func (d *directfsDentry) restoreFile(ctx context.Context, controlFD int, opts *v
if rw, ok := d.fs.savedDentryRW[&d.dentry]; ok {
if err := d.ensureSharedHandle(ctx, rw.read, rw.write, false /* trunc */); err != nil {
return fmt.Errorf("failed to restore file handles (read=%t, write=%t) for %q: %w", rw.read, rw.write, genericDebugPathname(&d.dentry), err)
return fmt.Errorf("failed to restore file handles (read=%t, write=%t) for %q: %w", rw.read, rw.write, genericDebugPathname(d.fs, &d.dentry), err)
}
}
+1 -2
View File
@@ -51,8 +51,7 @@ func (d *dentry) isDir() bool {
// +checklocks:d.childrenMu
func (d *dentry) cacheNewChildLocked(child *dentry, name string) {
d.IncRef() // reference held by child on its parent
child.parent.Store(d)
child.name = name
genericSetParentAndName(d.fs, child, d, name)
if d.children == nil {
d.children = make(map[string]*dentry)
} else if c, ok := d.children[name]; ok {
+8 -10
View File
@@ -1418,7 +1418,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
return err
}
if renamed.isDir() {
if renamed == newParent || genericIsAncestorDentry(renamed, newParent) {
if renamed == newParent || genericIsAncestorDentry(fs, renamed, newParent) {
return linuxerr.EINVAL
}
if oldParent != newParent {
@@ -1456,7 +1456,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if !renamed.isDir() {
return linuxerr.EISDIR
}
if genericIsAncestorDentry(replaced, renamed) {
if genericIsAncestorDentry(fs, replaced, renamed) {
return linuxerr.ENOTEMPTY
}
} else {
@@ -1738,9 +1738,12 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
// PrependPath implements vfs.FilesystemImpl.PrependPath.
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
fs.renameMu.RLock()
defer fs.renameMu.RUnlock()
return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
return genericPrependPath(fs, vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(fs, vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
type mopt struct {
@@ -1804,8 +1807,3 @@ func (fs *filesystem) MountOptions() string {
}
return strings.Join(opts, ",")
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
+6 -1
View File
@@ -25,11 +25,12 @@
// dentry.childrenMu
// filesystem.syncMu
// dentry.metadataMu
// *** "memmap.Mappable locks" below this point
// *** "memmap.Mappable/MappingIdentity locks" below this point
// dentry.mapsMu
// *** "memmap.Mappable locks taken by Translate" below this point
// dentry.handleMu
// dentry.dataMu
// filesystem.ancestryMu
// filesystem.inoMu
// specialFileFD.mu
// specialFileFD.bufMu
@@ -218,6 +219,10 @@ type filesystem struct {
// it is reachable from its parent).
renameMu sync.RWMutex `state:"nosave"`
// ancestryMu additionally protects dentry.parent and dentry.name as
// required by genericfstree.
ancestryMu sync.RWMutex `state:"nosave"`
dentryCache *dentryCache
// syncableDentries contains all non-synthetic dentries. specialFileFDs
+5 -5
View File
@@ -547,18 +547,18 @@ func (d *lisafsDentry) restoreFile(ctx context.Context, inode *lisafs.Inode, opt
if d.isRegularFile() {
if opts.ValidateFileSizes {
if inode.Stat.Mask&linux.STATX_SIZE == 0 {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(&d.dentry))}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d.fs, &d.dentry))}
}
if d.size.RacyLoad() != inode.Stat.Size {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(&d.dentry), d.size.Load(), inode.Stat.Size)}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d.fs, &d.dentry), d.size.Load(), inode.Stat.Size)}
}
}
if opts.ValidateFileModificationTimestamps {
if inode.Stat.Mask&linux.STATX_MTIME == 0 {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(&d.dentry))}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d.fs, &d.dentry))}
}
if want := dentryTimestamp(inode.Stat.Mtime); d.mtime.RacyLoad() != want {
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(&d.dentry), linux.NsecToStatxTimestamp(d.mtime.RacyLoad()), linux.NsecToStatxTimestamp(want))}
return vfs.ErrCorruption{fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d.fs, &d.dentry), linux.NsecToStatxTimestamp(d.mtime.RacyLoad()), linux.NsecToStatxTimestamp(want))}
}
}
}
@@ -568,7 +568,7 @@ func (d *lisafsDentry) restoreFile(ctx context.Context, inode *lisafs.Inode, opt
if rw, ok := d.fs.savedDentryRW[&d.dentry]; ok {
if err := d.ensureSharedHandle(ctx, rw.read, rw.write, false /* trunc */); err != nil {
return fmt.Errorf("failed to restore file handles (read=%t, write=%t) for %q: %w", rw.read, rw.write, genericDebugPathname(&d.dentry), err)
return fmt.Errorf("failed to restore file handles (read=%t, write=%t) for %q: %w", rw.read, rw.write, genericDebugPathname(d.fs, &d.dentry), err)
}
}
+3 -3
View File
@@ -130,7 +130,7 @@ func (d *dentry) prepareSaveRecursive(ctx context.Context) error {
// beforeSave is invoked by stateify.
func (d *dentry) beforeSave() {
if d.vfsd.IsDead() {
panic(fmt.Sprintf("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored", genericDebugPathname(d)))
panic(fmt.Sprintf("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored", genericDebugPathname(d.fs, d)))
}
}
@@ -260,7 +260,7 @@ func (fd *specialFileFD) completeRestore(ctx context.Context) error {
d := fd.dentry()
h, err := d.openHandle(ctx, fd.vfsfd.IsReadable(), fd.vfsfd.IsWritable(), false /* trunc */)
if err != nil {
return fmt.Errorf("failed to open handle for specialFileFD for %q: %w", genericDebugPathname(d), err)
return fmt.Errorf("failed to open handle for specialFileFD for %q: %w", genericDebugPathname(d.fs, d), err)
}
fd.handle = h
@@ -268,7 +268,7 @@ func (fd *specialFileFD) completeRestore(ctx context.Context) error {
fd.haveQueue = (ftype == linux.S_IFIFO || ftype == linux.S_IFSOCK) && fd.handle.fd >= 0
if fd.haveQueue {
if err := fdnotifier.AddFD(fd.handle.fd, &fd.queue); err != nil {
return fmt.Errorf("failed to add FD to fdnotified for %q: %w", genericDebugPathname(d), err)
return fmt.Errorf("failed to add FD to fdnotified for %q: %w", genericDebugPathname(d.fs, d), err)
}
}
+12 -3
View File
@@ -26,6 +26,7 @@ go_template_instance(
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
types = {
"Dentry": "Dentry",
"Filesystem": "Filesystem",
},
)
@@ -86,10 +87,10 @@ go_template_instance(
)
declare_rwmutex(
name = "filesystem_mutex",
out = "filesystem_mutex.go",
name = "ancestry_mutex",
out = "ancestry_mutex.go",
package = "kernfs",
prefix = "filesystem",
prefix = "ancestry",
)
declare_mutex(
@@ -99,9 +100,17 @@ declare_mutex(
prefix = "deferredDecRefs",
)
declare_rwmutex(
name = "filesystem_mutex",
out = "filesystem_mutex.go",
package = "kernfs",
prefix = "filesystem",
)
go_library(
name = "kernfs",
srcs = [
"ancestry_mutex.go",
"deferred_dec_refs_mutex.go",
"dentry_list.go",
"dynamic_bytes_file.go",
+6 -8
View File
@@ -1089,9 +1089,12 @@ func (fs *Filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
// PrependPath implements vfs.FilesystemImpl.PrependPath.
func (fs *Filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
fs.mu.RLock()
defer fs.mu.RUnlock()
return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*Dentry), b)
return genericPrependPath(fs, vfsroot, vd.Mount(), vd.Dentry().Impl().(*Dentry), b)
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *Filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(fs, vfsroot.Dentry(), vd.Dentry().Impl().(*Dentry))
}
func (fs *Filesystem) deferDecRefVD(ctx context.Context, vd vfs.VirtualDentry) {
@@ -1106,8 +1109,3 @@ func (fs *Filesystem) deferDecRefVD(ctx context.Context, vd vfs.VirtualDentry) {
vd.DecRef(ctx)
}
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *Filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*Dentry))
}
+5 -1
View File
@@ -111,6 +111,10 @@ type Filesystem struct {
// fs.deferDecRef(dentry)
mu filesystemRWMutex `state:"nosave"`
// ancestryMu additionally protects dentry.parent and dentry.name as
// required by genericfstree.
ancestryMu ancestryRWMutex `state:"nosave"`
// nextInoMinusOne is used to to allocate inode numbers on this
// filesystem. Must be accessed by atomic operations.
nextInoMinusOne atomicbitops.Uint64
@@ -597,7 +601,7 @@ func (d *Dentry) Inode() Inode {
// filesystem.
func (d *Dentry) FSLocalPath() string {
var b fspath.Builder
_ = genericPrependPath(vfs.VirtualDentry{}, nil, d, &b)
_ = genericPrependPath(d.fs, vfs.VirtualDentry{}, nil, d, &b)
b.PrependByte('/')
return b.String()
}
+9
View File
@@ -6,6 +6,13 @@ package(default_applicable_licenses = ["//:license"])
licenses(["notice"])
declare_rwmutex(
name = "ancestry_rwmutex",
out = "ancestry_rwmutex.go",
package = "overlay",
prefix = "ancestry",
)
declare_mutex(
name = "dir_mutex",
out = "dir_mutex.go",
@@ -75,12 +82,14 @@ go_template_instance(
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
types = {
"Dentry": "dentry",
"Filesystem": "filesystem",
},
)
go_library(
name = "overlay",
srcs = [
"ancestry_rwmutex.go",
"copy_up.go",
"data_rwmutex.go",
"dev_mutex.go",
+9 -7
View File
@@ -1152,7 +1152,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
return err
}
if renamed.isDir() {
if renamed == newParent || genericIsAncestorDentry(renamed, newParent) {
if renamed == newParent || genericIsAncestorDentry(fs, renamed, newParent) {
return linuxerr.EINVAL
}
if oldParent != newParent {
@@ -1195,7 +1195,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
if !renamed.isDir() {
return linuxerr.EISDIR
}
if genericIsAncestorDentry(replaced, renamed) {
if genericIsAncestorDentry(fs, replaced, renamed) {
return linuxerr.ENOTEMPTY
}
replaced.dirMu.NestedLock(dirLockReplaced)
@@ -1336,9 +1336,8 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
oldParent.DecRef(ctx)
ds = appendDentry(ds, oldParent)
newParent.IncRef()
renamed.parent.Store(newParent)
}
renamed.name = newName
genericSetParentAndName(fs, renamed, newParent, newName)
if newParent.children == nil {
newParent.children = make(map[string]*dentry)
}
@@ -1879,9 +1878,12 @@ func (fs *filesystem) removeXattrLocked(ctx context.Context, d *dentry, mnt *vfs
// PrependPath implements vfs.FilesystemImpl.PrependPath.
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
fs.renameMu.RLock()
defer fs.renameMu.RUnlock()
return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
return genericPrependPath(fs, vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(fs, vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
// MountOptions implements vfs.FilesystemImpl.MountOptions.
+6 -6
View File
@@ -23,10 +23,11 @@
// dentry.dirMu
// dentry.copyMu
// filesystem.devMu
// *** "memmap.Mappable locks" below this point
// *** "memmap.Mappable/MappingIdentity locks" below this point
// dentry.mapsMu
// *** "memmap.Mappable locks taken by Translate" below this point
// dentry.dataMu
// filesystem.ancestryMu
//
// Locking dentry.dirMu in multiple dentries requires that parent dentries are
// locked before child dentries, and that filesystem.renameMu is locked to
@@ -117,6 +118,10 @@ type filesystem struct {
// dentries.
renameMu renameRWMutex `state:"nosave"`
// ancestryMu additionally protects dentry.parent and dentry.name as
// required by genericfstree.
ancestryMu ancestryRWMutex `state:"nosave"`
// dirInoCache caches overlay-private directory inode numbers by mapped
// bottommost device numbers and inode number. dirInoCache is protected by
// dirInoCacheMu.
@@ -482,11 +487,6 @@ func (fs *filesystem) getLowerDevMinor(layerMajor, layerMinor uint32) (uint32, e
return minor, nil
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
// dentry implements vfs.DentryImpl.
//
// +stateify savable
+27 -18
View File
@@ -26,6 +26,7 @@ go_template_instance(
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
types = {
"Dentry": "dentry",
"Filesystem": "filesystem",
},
)
@@ -40,25 +41,11 @@ go_template_instance(
},
)
declare_mutex(
name = "inode_mutex",
out = "inode_mutex.go",
declare_rwmutex(
name = "ancestry_mutex",
out = "ancestry_mutex.go",
package = "tmpfs",
prefix = "inode",
)
declare_mutex(
name = "pages_used_mutex",
out = "pages_used_mutex.go",
package = "tmpfs",
prefix = "pagesUsed",
)
declare_mutex(
name = "iter_mutex",
out = "iter_mutex.go",
package = "tmpfs",
prefix = "iter",
prefix = "ancestry",
)
declare_rwmutex(
@@ -68,9 +55,31 @@ declare_rwmutex(
prefix = "filesystem",
)
declare_mutex(
name = "inode_mutex",
out = "inode_mutex.go",
package = "tmpfs",
prefix = "inode",
)
declare_mutex(
name = "iter_mutex",
out = "iter_mutex.go",
package = "tmpfs",
prefix = "iter",
)
declare_mutex(
name = "pages_used_mutex",
out = "pages_used_mutex.go",
package = "tmpfs",
prefix = "pagesUsed",
)
go_library(
name = "tmpfs",
srcs = [
"ancestry_mutex.go",
"dentry_list.go",
"device_file.go",
"directory.go",
+1 -2
View File
@@ -60,8 +60,7 @@ func (fs *filesystem) newDirectory(kuid auth.KUID, kgid auth.KGID, mode linux.Fi
// - filesystem.mu must be locked for writing.
// - dir must not already contain a child with the given name.
func (dir *directory) insertChildLocked(child *dentry, name string) {
child.parent.Store(&dir.dentry)
child.name = name
genericSetParentAndName(dir.dentry.inode.fs, child, &dir.dentry, name)
if dir.childMap == nil {
dir.childMap = make(map[string]*dentry)
}
+23 -34
View File
@@ -580,7 +580,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
// mount point then we want to rename the mount point, not anything in the
// mounted filesystem.
if renamed.inode.isDir() {
if renamed == &newParentDir.dentry || genericIsAncestorDentry(renamed, &newParentDir.dentry) {
if renamed == &newParentDir.dentry || genericIsAncestorDentry(fs, renamed, &newParentDir.dentry) {
return linuxerr.EINVAL
}
if oldParentDir != newParentDir {
@@ -936,37 +936,31 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
// PrependPath implements vfs.FilesystemImpl.PrependPath.
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
fs.mu.RLock()
defer fs.mu.RUnlock()
mnt := vd.Mount()
d := vd.Dentry().Impl().(*dentry)
for {
if mnt == vfsroot.Mount() && &d.vfsd == vfsroot.Dentry() {
return vfs.PrependPathAtVFSRootError{}
if d.parent.Load() == nil {
fs.ancestryMu.Lock()
name := d.name
fs.ancestryMu.Unlock()
if name != "" {
// This file must have been created by
// newUnlinkedRegularFileDescription(). In Linux,
// mm/shmem.c:__shmem_file_setup() =>
// fs/file_table.c:alloc_file_pseudo() sets the created
// dentry's dentry_operations to anon_ops, for which d_dname ==
// simple_dname. fs/d_path.c:simple_dname() defines the
// dentry's pathname to be its name, prefixed with "/" and
// suffixed with " (deleted)".
b.PrependComponent("/" + name)
b.AppendString(" (deleted)")
return vfs.PrependPathSyntheticError{}
}
if mnt != nil && &d.vfsd == mnt.Root() {
return nil
}
parent := d.parent.Load()
if parent == nil {
if d.name != "" {
// This file must have been created by
// newUnlinkedRegularFileDescription(). In Linux,
// mm/shmem.c:__shmem_file_setup() =>
// fs/file_table.c:alloc_file_pseudo() sets the created
// dentry's dentry_operations to anon_ops, for which d_dname ==
// simple_dname. fs/d_path.c:simple_dname() defines the
// dentry's pathname to be its name, prefixed with "/" and
// suffixed with " (deleted)".
b.PrependComponent("/" + d.name)
b.AppendString(" (deleted)")
return vfs.PrependPathSyntheticError{}
}
return vfs.PrependPathAtNonMountRootError{}
}
b.PrependComponent(d.name)
d = parent
}
return genericPrependPath(fs, vfsroot, vd.Mount(), d, b)
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(fs, vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
// MountOptions implements vfs.FilesystemImpl.MountOptions.
@@ -974,11 +968,6 @@ func (fs *filesystem) MountOptions() string {
return fs.mopts
}
// IsDescendant implements vfs.FilesystemImpl.IsDescendant.
func (fs *filesystem) IsDescendant(vfsroot, vd vfs.VirtualDentry) bool {
return genericIsDescendant(vfsroot.Dentry(), vd.Dentry().Impl().(*dentry))
}
// adjustPageAcct adjusts the accounting done against filesystem size limit in
// case there is any discrepancy between the number of pages reserved vs the
// number of pages actually allocated.

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