mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
proc: invalidate task inodes when tasks are destroyed
PiperOrigin-RevId: 705785809
This commit is contained in:
@@ -511,6 +511,7 @@ type dir struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
implStatFS
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -229,6 +229,7 @@ type rootInode struct {
|
||||
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
rootInodeRefs
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -39,6 +39,7 @@ type masterInode struct {
|
||||
kernfs.InodeNotAnonymous
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeFSOwned
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -39,6 +39,7 @@ type replicaInode struct {
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ type inode struct {
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.CachedMappable
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
// the owning filesystem. fs is immutable.
|
||||
fs *filesystem
|
||||
|
||||
@@ -100,6 +100,7 @@ type inode struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
|
||||
kernfs.InodeWatches
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ type DynamicBytesFile struct {
|
||||
InodeNotDirectory
|
||||
InodeNotSymlink
|
||||
InodeWatches
|
||||
InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
// data can additionally implement vfs.WritableDynamicBytesSource to support
|
||||
|
||||
@@ -155,6 +155,14 @@ func (fs *Filesystem) invalidateRemovedChildLocked(ctx context.Context, vfsObj *
|
||||
d := toInvalidate[len(toInvalidate)-1]
|
||||
toInvalidate = toInvalidate[:len(toInvalidate)-1]
|
||||
|
||||
if d.cached {
|
||||
// The dentry is removed from the cache when its
|
||||
// reference counter drops to 0. It can't be removed
|
||||
// from the cache here, because fs.mu isn't locked for
|
||||
// write.
|
||||
d.IncRef()
|
||||
fs.deferDecRef(d)
|
||||
}
|
||||
if d.inode.Keep() {
|
||||
fs.deferDecRef(d)
|
||||
}
|
||||
|
||||
@@ -735,6 +735,7 @@ type StaticDirectory struct {
|
||||
InodeWatches
|
||||
OrderedChildren
|
||||
StaticDirectoryRefs
|
||||
InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
fdOpts GenericDirectoryFDOptions
|
||||
@@ -845,3 +846,15 @@ type InodeNotAnonymous struct{}
|
||||
func (*InodeNotAnonymous) Anonymous() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// InodeFSOwned represents inodes whose lifecycle is entirely managed by the
|
||||
// filesystem.
|
||||
//
|
||||
// +stateify savable
|
||||
type InodeFSOwned struct{}
|
||||
|
||||
// RegisterDentry implements Inode.RegisterDentry.
|
||||
func (*InodeFSOwned) RegisterDentry(d *Dentry) {}
|
||||
|
||||
// UnregisterDentry implements Remove.UnregisterDentry.
|
||||
func (*InodeFSOwned) UnregisterDentry(d *Dentry) {}
|
||||
|
||||
@@ -317,6 +317,27 @@ func (d *Dentry) decRefLocked(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate invalidates the dentry and its children.
|
||||
func (d *Dentry) Invalidate(ctx context.Context) {
|
||||
d.fs.mu.RLock()
|
||||
defer d.fs.processDeferredDecRefs(ctx)
|
||||
defer d.fs.mu.RUnlock()
|
||||
parent := d.parent.Load()
|
||||
if parent == nil {
|
||||
return
|
||||
}
|
||||
parent.dirMu.Lock()
|
||||
child := parent.children[d.name]
|
||||
if child != d {
|
||||
parent.dirMu.Unlock()
|
||||
return
|
||||
}
|
||||
delete(parent.children, d.name)
|
||||
parent.dirMu.Unlock()
|
||||
|
||||
d.fs.invalidateRemovedChildLocked(ctx, d.fs.vfsfs.VirtualFilesystem(), child)
|
||||
}
|
||||
|
||||
// cacheLocked should be called after d's reference count becomes 0. The ref
|
||||
// count check may happen before acquiring d.fs.mu so there might be a race
|
||||
// condition where the ref count is increased again by the time the caller
|
||||
@@ -451,6 +472,7 @@ func (d *Dentry) destroy(ctx context.Context) {
|
||||
panic("dentry.destroy() called with references on the dentry")
|
||||
}
|
||||
|
||||
d.inode.UnregisterDentry(d)
|
||||
d.inode.DecRef(ctx) // IncRef from Init.
|
||||
|
||||
refs.Unregister(d)
|
||||
@@ -505,6 +527,7 @@ func (d *Dentry) Init(fs *Filesystem, inode Inode) {
|
||||
d.flags = atomicbitops.FromUint32(d.flags.RacyLoad() | dflagsIsSymlink)
|
||||
}
|
||||
refs.Register(d)
|
||||
inode.RegisterDentry(d)
|
||||
}
|
||||
|
||||
// VFSDentry returns the generic vfs dentry for this kernfs dentry.
|
||||
@@ -732,6 +755,13 @@ type Inode interface {
|
||||
// Anonymous indicates that the Inode is anonymous. It will never have
|
||||
// a name or parent.
|
||||
Anonymous() bool
|
||||
|
||||
// RegisterDentry is called when a new dentry representing the inode is
|
||||
// created.
|
||||
RegisterDentry(d *Dentry)
|
||||
|
||||
// UnregisterDentry is called when the specified dentry is destroyed.
|
||||
UnregisterDentry(d *Dentry)
|
||||
}
|
||||
|
||||
type inodeRefs interface {
|
||||
|
||||
@@ -109,6 +109,7 @@ type readonlyDir struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
}
|
||||
@@ -146,6 +147,7 @@ type dir struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ type StaticSymlink struct {
|
||||
InodeSymlink
|
||||
InodeNoStatFS
|
||||
InodeWatches
|
||||
InodeFSOwned
|
||||
|
||||
target string
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ type syntheticDirectory struct {
|
||||
InodeWatches
|
||||
OrderedChildren
|
||||
syntheticDirectoryRefs
|
||||
InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ type rootInode struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@ type Inode struct {
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
kernfs.InodeFSOwned
|
||||
inodeRefs
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -95,6 +95,7 @@ type inode struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeWatches
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
locks vfs.FileLocks
|
||||
pipe *pipe.VFSPipe
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
load("//pkg/sync/locking:locking.bzl", "declare_rwmutex")
|
||||
load("//tools:defs.bzl", "go_library", "go_test")
|
||||
load("//tools/go_generics:defs.bzl", "go_template_instance")
|
||||
|
||||
@@ -60,9 +61,17 @@ go_template_instance(
|
||||
},
|
||||
)
|
||||
|
||||
declare_rwmutex(
|
||||
name = "dentries_mutex",
|
||||
out = "dentries_mutex.go",
|
||||
package = "proc",
|
||||
prefix = "dentries",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "proc",
|
||||
srcs = [
|
||||
"dentries_mutex.go",
|
||||
"fd_dir_inode_refs.go",
|
||||
"fd_info_dir_inode_refs.go",
|
||||
"filesystem.go",
|
||||
@@ -106,6 +115,7 @@ go_library(
|
||||
"//pkg/sentry/usage",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/sync/locking",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/usermem",
|
||||
|
||||
@@ -40,6 +40,7 @@ type subtasksInode struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
subtasksInodeRefs
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -45,6 +45,10 @@ type taskInode struct {
|
||||
locks vfs.FileLocks
|
||||
|
||||
task *kernel.Task
|
||||
|
||||
dentriesMu dentriesRWMutex `state:"nosave"`
|
||||
// dentries is a list of dentries to be invalidated when the task is destroyed.
|
||||
dentries map[*kernfs.Dentry]struct{}
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*taskInode)(nil)
|
||||
@@ -99,7 +103,10 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
|
||||
contents["cgroup"] = fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, &taskCgroupData{task: task})
|
||||
}
|
||||
|
||||
taskInode := &taskInode{task: task}
|
||||
taskInode := &taskInode{
|
||||
task: task,
|
||||
dentries: make(map[*kernfs.Dentry]struct{}),
|
||||
}
|
||||
// Note: credentials are overridden by taskOwnedInode.
|
||||
taskInode.InodeAttrs.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
|
||||
taskInode.InitRefs()
|
||||
@@ -113,6 +120,44 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
|
||||
return inode, nil
|
||||
}
|
||||
|
||||
func (i *taskInode) TaskDestroyAction(ctx context.Context) {
|
||||
i.dentriesMu.Lock()
|
||||
dentries := i.dentries
|
||||
i.dentries = nil
|
||||
i.dentriesMu.Unlock()
|
||||
|
||||
for d := range dentries {
|
||||
d.Invalidate(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterDentry implements kernfs.Inode.RegisterDentry.
|
||||
func (i *taskInode) RegisterDentry(d *kernfs.Dentry) {
|
||||
i.dentriesMu.Lock()
|
||||
defer i.dentriesMu.Unlock()
|
||||
|
||||
if i.dentries == nil {
|
||||
return
|
||||
}
|
||||
if len(i.dentries) == 0 && !i.task.RegisterOnDestroyAction(i) {
|
||||
// The task has been destroyed.
|
||||
i.dentries = nil
|
||||
return
|
||||
}
|
||||
i.dentries[d] = struct{}{}
|
||||
}
|
||||
|
||||
// UnregisterDentry implements kernfs.Inode.RegisterDentry.
|
||||
func (i *taskInode) UnregisterDentry(d *kernfs.Dentry) {
|
||||
i.dentriesMu.Lock()
|
||||
defer i.dentriesMu.Unlock()
|
||||
|
||||
delete(i.dentries, d)
|
||||
if len(i.dentries) == 0 {
|
||||
i.task.UnregisterOnDestroyAction(i)
|
||||
}
|
||||
}
|
||||
|
||||
// Valid implements kernfs.Inode.Valid. This inode remains valid as long
|
||||
// as the task is still running. When it's dead, another tasks with the same
|
||||
// PID could replace it.
|
||||
@@ -141,6 +186,17 @@ func (i *taskInode) DecRef(ctx context.Context) {
|
||||
i.taskInodeRefs.DecRef(func() { i.Destroy(ctx) })
|
||||
}
|
||||
|
||||
func (i *taskInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
|
||||
i.dentriesMu.RLock()
|
||||
if i.dentries == nil {
|
||||
// The task has been destroyed and the inode invalidate callback has been executed.
|
||||
i.dentriesMu.RUnlock()
|
||||
return nil, linuxerr.ESRCH
|
||||
}
|
||||
i.dentriesMu.RUnlock()
|
||||
return i.OrderedChildren.Lookup(ctx, name)
|
||||
}
|
||||
|
||||
// taskOwnedInode implements kernfs.Inode and overrides inode owner with task
|
||||
// effective user and group.
|
||||
//
|
||||
|
||||
@@ -118,6 +118,7 @@ type fdDirInode struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*fdDirInode)(nil)
|
||||
@@ -202,6 +203,7 @@ type fdSymlink struct {
|
||||
kernfs.InodeNotAnonymous
|
||||
kernfs.InodeSymlink
|
||||
kernfs.InodeWatches
|
||||
kernfs.InodeFSOwned
|
||||
|
||||
fs *filesystem
|
||||
task *kernel.Task
|
||||
@@ -264,6 +266,7 @@ type fdInfoDirInode struct {
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
kernfs.InodeFSOwned
|
||||
}
|
||||
|
||||
var _ kernfs.Inode = (*fdInfoDirInode)(nil)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user