mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ go_template_instance(
|
||||
template = "//pkg/sentry/vfs/genericfstree:generic_fstree",
|
||||
types = {
|
||||
"Dentry": "dentry",
|
||||
"Filesystem": "filesystem",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user