mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add inotify support to kernfs and anonfs.
This is consistent with Linux. Some applications recursively add files to inotify FD. They fail if they run into kernfs files because inotify_add_watch(2) returns EPERM in that case. PiperOrigin-RevId: 462642204
This commit is contained in:
@@ -493,6 +493,7 @@ type dir struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
implStatFS
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ type rootInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
rootInodeRefs
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ type masterInode struct {
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ type replicaInode struct {
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -305,6 +305,7 @@ type inode struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
|
||||
// the owning filesystem. fs is immutable.
|
||||
|
||||
@@ -99,6 +99,7 @@ type inode struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.CachedMappable
|
||||
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ type DynamicBytesFile struct {
|
||||
InodeNoopRefCount
|
||||
InodeNotDirectory
|
||||
InodeNotSymlink
|
||||
InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
// data can additionally implement vfs.WritableDynamicBytesSource to support
|
||||
|
||||
@@ -395,6 +395,8 @@ func (fs *Filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE, 0, vfs.InodeEvent, false /* unlinked */)
|
||||
d.inode.Watches().Notify(ctx, "", linux.IN_ATTRIB, 0, vfs.InodeEvent, false /* unlinked */)
|
||||
var child Dentry
|
||||
child.Init(fs, childI)
|
||||
parent.insertChildLocked(pc, &child)
|
||||
@@ -433,6 +435,7 @@ func (fs *Filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
|
||||
}
|
||||
var child Dentry
|
||||
child.Init(fs, childI)
|
||||
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE|linux.IN_ISDIR, 0, vfs.InodeEvent, false /* unlinked */)
|
||||
parent.insertChildLocked(pc, &child)
|
||||
return nil
|
||||
}
|
||||
@@ -467,6 +470,7 @@ func (fs *Filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE, 0, vfs.InodeEvent, false /* unlinked */)
|
||||
var newD Dentry
|
||||
newD.Init(fs, newI)
|
||||
parent.insertChildLocked(pc, &newD)
|
||||
@@ -559,6 +563,9 @@ afterTrailingSymlink:
|
||||
if len(pc) > linux.NAME_MAX {
|
||||
return nil, linuxerr.ENAMETOOLONG
|
||||
}
|
||||
if parent.VFSDentry().IsDead() {
|
||||
return nil, linuxerr.ENOENT
|
||||
}
|
||||
// Determine whether or not we need to create a file.
|
||||
child, err := fs.stepExistingLocked(ctx, rp, parent, false /* mayFollowSymlinks */)
|
||||
if linuxerr.Equals(linuxerr.ENOENT, err) {
|
||||
@@ -582,6 +589,7 @@ afterTrailingSymlink:
|
||||
// its destruction while fs.mu is unlocked.
|
||||
child.IncRef()
|
||||
unlock()
|
||||
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE, 0, vfs.PathEvent, false /* unlinked */)
|
||||
fd, err := child.inode.Open(ctx, rp, &child, opts)
|
||||
child.DecRef(ctx)
|
||||
return fd, err
|
||||
@@ -765,7 +773,9 @@ func (fs *Filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
||||
// deferDecRef so that fs.mu and dstDir.mu are unlocked by then.
|
||||
fs.deferDecRef(replaced)
|
||||
replaceVFSD = replaced.VFSDentry()
|
||||
replaced.setDeleted()
|
||||
}
|
||||
vfs.InotifyRename(ctx, src.inode.Watches(), srcDir.inode.Watches(), dstDir.inode.Watches(), oldName, newName, src.isDir())
|
||||
virtfs.CommitRenameReplaceDentry(ctx, srcVFSD, replaceVFSD) // +checklocksforce: to may be nil, that's okay.
|
||||
return nil
|
||||
}
|
||||
@@ -810,9 +820,11 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
return err
|
||||
}
|
||||
delete(parentDentry.children, d.name)
|
||||
parentDentry.inode.Watches().Notify(ctx, d.name, linux.IN_DELETE|linux.IN_ISDIR, 0, vfs.InodeEvent, true /* unlinked */)
|
||||
// Defer decref so that fs.mu and parentDentry.dirMu are unlocked by then.
|
||||
fs.deferDecRef(d)
|
||||
virtfs.CommitDeleteDentry(ctx, vfsd)
|
||||
d.setDeleted()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -820,15 +832,24 @@ func (fs *Filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
func (fs *Filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.processDeferredDecRefs(ctx)
|
||||
defer fs.mu.RUnlock()
|
||||
d, err := fs.walkExistingLocked(ctx, rp)
|
||||
if err != nil {
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
if opts.Stat.Mask == 0 {
|
||||
fs.mu.RUnlock()
|
||||
return nil
|
||||
}
|
||||
return d.inode.SetStat(ctx, fs.VFSFilesystem(), rp.Credentials(), opts)
|
||||
err = d.inode.SetStat(ctx, fs.VFSFilesystem(), rp.Credentials(), opts)
|
||||
fs.mu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
d.InotifyWithParent(ctx, ev, 0, vfs.InodeEvent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StatAt implements vfs.FilesystemImpl.StatAt.
|
||||
@@ -885,6 +906,7 @@ func (fs *Filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, targ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent.inode.Watches().Notify(ctx, pc, linux.IN_CREATE, 0, vfs.InodeEvent, false /* unlinked */)
|
||||
var child Dentry
|
||||
child.Init(fs, childI)
|
||||
parent.insertChildLocked(pc, &child)
|
||||
@@ -926,9 +948,11 @@ func (fs *Filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
return err
|
||||
}
|
||||
delete(parentDentry.children, d.name)
|
||||
vfs.InotifyRemoveChild(ctx, d.inode.Watches(), parentDentry.inode.Watches(), d.name)
|
||||
// Defer decref so that fs.mu and parentDentry.dirMu are unlocked by then.
|
||||
fs.deferDecRef(d)
|
||||
virtfs.CommitDeleteDentry(ctx, vfsd)
|
||||
d.setDeleted()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -716,6 +716,7 @@ type StaticDirectory struct {
|
||||
InodeNoStatFS
|
||||
InodeNotSymlink
|
||||
InodeTemporary
|
||||
InodeWatches
|
||||
OrderedChildren
|
||||
StaticDirectoryRefs
|
||||
|
||||
@@ -796,3 +797,15 @@ type InodeNoStatFS struct{}
|
||||
func (*InodeNoStatFS) StatFS(context.Context, *vfs.Filesystem) (linux.Statfs, error) {
|
||||
return linux.Statfs{}, linuxerr.ENOSYS
|
||||
}
|
||||
|
||||
// InodeWatches partially implements Inode.
|
||||
//
|
||||
// +stateify savable
|
||||
type InodeWatches struct {
|
||||
watches vfs.Watches
|
||||
}
|
||||
|
||||
// Watches implements Inode.Watches.
|
||||
func (i *InodeWatches) Watches() *vfs.Watches {
|
||||
return &i.watches
|
||||
}
|
||||
|
||||
@@ -244,6 +244,10 @@ type Dentry struct {
|
||||
children map[string]*Dentry
|
||||
|
||||
inode Inode
|
||||
|
||||
// If deleted is non-zero, the file represented by this dentry has been
|
||||
// deleted. deleted is accessed using atomic memory operations.
|
||||
deleted atomicbitops.Uint32
|
||||
}
|
||||
|
||||
// IncRef implements vfs.DentryImpl.IncRef.
|
||||
@@ -339,6 +343,9 @@ func (d *Dentry) cacheLocked(ctx context.Context) {
|
||||
d.fs.cachedDentriesLen--
|
||||
d.cached = false
|
||||
}
|
||||
if d.isDeleted() {
|
||||
d.inode.Watches().HandleDeletion(ctx)
|
||||
}
|
||||
d.destroyLocked(ctx)
|
||||
return
|
||||
}
|
||||
@@ -420,7 +427,6 @@ func (d *Dentry) destroyLocked(ctx context.Context) {
|
||||
}
|
||||
|
||||
d.inode.DecRef(ctx) // IncRef from Init.
|
||||
d.inode = nil
|
||||
|
||||
if d.parent != nil {
|
||||
d.parent.decRefLocked(ctx)
|
||||
@@ -485,6 +491,14 @@ func (d *Dentry) VFSDentry() *vfs.Dentry {
|
||||
return &d.vfsd
|
||||
}
|
||||
|
||||
func (d *Dentry) isDeleted() bool {
|
||||
return d.deleted.Load() != 0
|
||||
}
|
||||
|
||||
func (d *Dentry) setDeleted() {
|
||||
d.deleted.Store(1)
|
||||
}
|
||||
|
||||
// isDir checks whether the dentry points to a directory inode.
|
||||
func (d *Dentry) isDir() bool {
|
||||
return d.flags.Load()&dflagsIsDir != 0
|
||||
@@ -496,15 +510,23 @@ func (d *Dentry) isSymlink() bool {
|
||||
}
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
//
|
||||
// Although Linux technically supports inotify on pseudo filesystems (inotify
|
||||
// is implemented at the vfs layer), it is not particularly useful. It is left
|
||||
// unimplemented until someone actually needs it.
|
||||
func (d *Dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {}
|
||||
func (d *Dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
|
||||
if d.isDir() {
|
||||
events |= linux.IN_ISDIR
|
||||
}
|
||||
|
||||
d.fs.mu.RLock()
|
||||
defer d.fs.mu.RUnlock()
|
||||
// The ordering below is important, Linux always notifies the parent first.
|
||||
if d.parent != nil {
|
||||
d.parent.inode.Watches().Notify(ctx, d.name, events, cookie, et, d.isDeleted())
|
||||
}
|
||||
d.inode.Watches().Notify(ctx, "", events, cookie, et, d.isDeleted())
|
||||
}
|
||||
|
||||
// Watches implements vfs.DentryImpl.Watches.
|
||||
func (d *Dentry) Watches() *vfs.Watches {
|
||||
return nil
|
||||
return d.inode.Watches()
|
||||
}
|
||||
|
||||
// OnZeroWatches implements vfs.Dentry.OnZeroWatches.
|
||||
@@ -680,6 +702,9 @@ type Inode interface {
|
||||
// Valid should return true if this inode is still valid, or needs to
|
||||
// be resolved again by a call to Lookup.
|
||||
Valid(ctx context.Context) bool
|
||||
|
||||
// Watches returns the set of inotify watches associated with this inode.
|
||||
Watches() *vfs.Watches
|
||||
}
|
||||
|
||||
type inodeRefs interface {
|
||||
|
||||
@@ -106,6 +106,7 @@ type readonlyDir struct {
|
||||
kernfs.InodeNoStatFS
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
|
||||
locks vfs.FileLocks
|
||||
@@ -141,6 +142,7 @@ type dir struct {
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeNoStatFS
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -31,6 +31,7 @@ type StaticSymlink struct {
|
||||
InodeNoopRefCount
|
||||
InodeSymlink
|
||||
InodeNoStatFS
|
||||
InodeWatches
|
||||
|
||||
target string
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type syntheticDirectory struct {
|
||||
InodeAttrs
|
||||
InodeNoStatFS
|
||||
InodeNotSymlink
|
||||
InodeWatches
|
||||
OrderedChildren
|
||||
syntheticDirectoryRefs
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ type rootInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
@@ -92,6 +92,7 @@ type inode struct {
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
pipe *pipe.VFSPipe
|
||||
|
||||
@@ -37,6 +37,7 @@ type subtasksInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
subtasksInodeRefs
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ type taskInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
taskInodeRefs
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ type fdDirInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
}
|
||||
|
||||
@@ -197,6 +198,7 @@ type fdSymlink struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
fs *filesystem
|
||||
task *kernel.Task
|
||||
@@ -256,6 +258,7 @@ type fdInfoDirInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,7 @@ type memInode struct {
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
task *kernel.Task
|
||||
locks vfs.FileLocks
|
||||
@@ -675,6 +676,7 @@ type statusInode struct {
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
task *kernel.Task
|
||||
pidns *kernel.PIDNamespace
|
||||
@@ -906,6 +908,7 @@ type exeSymlink struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
fs *filesystem
|
||||
task *kernel.Task
|
||||
@@ -978,6 +981,7 @@ type cwdSymlink struct {
|
||||
kernfs.InodeAttrs
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
fs *filesystem
|
||||
task *kernel.Task
|
||||
@@ -1152,6 +1156,7 @@ type namespaceInode struct {
|
||||
kernfs.InodeNoopRefCount
|
||||
kernfs.InodeNotDirectory
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeWatches
|
||||
|
||||
locks vfs.FileLocks
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ type tasksInode struct {
|
||||
kernfs.InodeDirectoryNoNewChildren
|
||||
kernfs.InodeNotSymlink
|
||||
kernfs.InodeTemporary // This holds no meaning as this inode can't be Looked up and is always valid.
|
||||
kernfs.InodeWatches
|
||||
kernfs.OrderedChildren
|
||||
tasksInodeRefs
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user