overlay: remove dirInoCache entries from RmdirAt()

Right now, entries are never removed from dirInoCache and if someone creates
and deletes directories in a loop, they observe memory leaks.

PiperOrigin-RevId: 698195540
This commit is contained in:
Andrei Vagin
2024-11-19 17:14:01 -08:00
committed by gVisor bot
parent 6666e9fc85
commit a92fc7b8c9
2 changed files with 24 additions and 7 deletions
+7 -1
View File
@@ -330,7 +330,12 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
// directories. Override them if necessary. We can use RacyLoad() because
// child is still being initialized.
if child.isDir() {
child.ino.Store(fs.newDirIno(child.devMajor.RacyLoad(), child.devMinor.RacyLoad(), child.ino.RacyLoad()))
orig := layerDevNoAndIno{
layerDevNumber: layerDevNumber{child.devMajor.RacyLoad(), child.devMinor.RacyLoad()},
ino: child.ino.RacyLoad(),
}
child.ino.Store(fs.newDirIno(orig))
child.dirInoHash = orig
child.devMajor = atomicbitops.FromUint32(linux.UNNAMED_MAJOR)
child.devMinor = atomicbitops.FromUint32(fs.dirDevMinor)
} else if !child.upperVD.Ok() {
@@ -1486,6 +1491,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
toDecRef = vfsObj.CommitDeleteDentry(ctx, &child.vfsd)
delete(parent.children, name)
fs.releaseDirIno(child.dirInoHash)
ds = appendDentry(ds, child)
parent.dirents = nil
parent.watches.Notify(ctx, name, linux.IN_DELETE|linux.IN_ISDIR, 0 /* cookie */, vfs.InodeEvent, true /* unlinked */)
+17 -6
View File
@@ -365,7 +365,12 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
root.devMinor = atomicbitops.FromUint32(fs.dirDevMinor)
// For root dir, it is okay to use top most level's stat to compute inode
// number because we don't allow copy ups on root dentries.
root.ino.Store(fs.newDirIno(rootStat.DevMajor, rootStat.DevMinor, rootStat.Ino))
orig := layerDevNoAndIno{
layerDevNumber: layerDevNumber{rootStat.DevMajor, rootStat.DevMinor},
ino: rootStat.Ino,
}
root.ino.Store(fs.newDirIno(orig))
root.dirInoHash = orig
} else if !root.upperVD.Ok() {
root.devMajor = atomicbitops.FromUint32(linux.UNNAMED_MAJOR)
rootDevMinor, err := fs.getLowerDevMinor(rootStat.DevMajor, rootStat.DevMinor)
@@ -456,13 +461,9 @@ func (fs *filesystem) statFS(ctx context.Context) (linux.Statfs, error) {
return fsstat, nil
}
func (fs *filesystem) newDirIno(layerMajor, layerMinor uint32, layerIno uint64) uint64 {
func (fs *filesystem) newDirIno(orig layerDevNoAndIno) uint64 {
fs.dirInoCacheMu.Lock()
defer fs.dirInoCacheMu.Unlock()
orig := layerDevNoAndIno{
layerDevNumber: layerDevNumber{layerMajor, layerMinor},
ino: layerIno,
}
if ino, ok := fs.dirInoCache[orig]; ok {
return ino
}
@@ -472,6 +473,12 @@ func (fs *filesystem) newDirIno(layerMajor, layerMinor uint32, layerIno uint64)
return newIno
}
func (fs *filesystem) releaseDirIno(orig layerDevNoAndIno) {
fs.dirInoCacheMu.Lock()
defer fs.dirInoCacheMu.Unlock()
delete(fs.dirInoCache, orig)
}
func (fs *filesystem) getLowerDevMinor(layerMajor, layerMinor uint32) (uint32, error) {
fs.devMu.Lock()
defer fs.devMu.Unlock()
@@ -587,6 +594,10 @@ type dentry struct {
// watches, due to the fact that we do not have inode structures in this
// overlay implementation.
watches vfs.Watches
// dirInoHash is the entry hash in fs.dirInoCache. This is only set for
// directories.
dirInoHash layerDevNoAndIno
}
// newDentry creates a new dentry. The dentry initially has no references; it