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:
Ayush Ranjan
2022-07-22 09:45:24 -07:00
committed by gVisor bot
parent c8f981f9b2
commit bf4d27a6ca
29 changed files with 133 additions and 37 deletions
+1
View File
@@ -493,6 +493,7 @@ type dir struct {
kernfs.InodeAttrs
kernfs.InodeNotSymlink
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeWatches
kernfs.OrderedChildren
implStatFS
+1
View File
@@ -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
+1
View File
@@ -38,6 +38,7 @@ type masterInode struct {
kernfs.InodeNoopRefCount
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeWatches
locks vfs.FileLocks
+1
View File
@@ -37,6 +37,7 @@ type replicaInode struct {
kernfs.InodeNoopRefCount
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeWatches
locks vfs.FileLocks
+1
View File
@@ -305,6 +305,7 @@ type inode struct {
kernfs.InodeAttrs
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeNotSymlink
kernfs.InodeWatches
kernfs.OrderedChildren
// the owning filesystem. fs is immutable.
+1
View File
@@ -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
+26 -2
View File
@@ -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
}
+32 -7
View File
@@ -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 {
+2
View File
@@ -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
+1
View File
@@ -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
+1
View File
@@ -33,6 +33,7 @@ type rootInode struct {
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeNotSymlink
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
locks vfs.FileLocks
+1
View File
@@ -92,6 +92,7 @@ type inode struct {
kernfs.InodeNotDirectory
kernfs.InodeNotSymlink
kernfs.InodeNoopRefCount
kernfs.InodeWatches
locks vfs.FileLocks
pipe *pipe.VFSPipe
+1
View File
@@ -37,6 +37,7 @@ type subtasksInode struct {
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeNotSymlink
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
subtasksInodeRefs
+1
View File
@@ -37,6 +37,7 @@ type taskInode struct {
kernfs.InodeDirectoryNoNewChildren
kernfs.InodeNotSymlink
kernfs.InodeTemporary
kernfs.InodeWatches
kernfs.OrderedChildren
taskInodeRefs
+3
View File
@@ -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
}
+5
View File
@@ -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
}
+1
View File
@@ -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