mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Support inotify in overlayfs.
Fixes #1479, #317. PiperOrigin-RevId: 334258052
This commit is contained in:
@@ -1416,11 +1416,11 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
if err := d.setStat(ctx, rp.Credentials(), &opts, rp.Mount()); err != nil {
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
err = d.setStat(ctx, rp.Credentials(), &opts, rp.Mount())
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
d.InotifyWithParent(ctx, ev, 0, vfs.InodeEvent)
|
||||
@@ -1556,11 +1556,11 @@ func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
if err := d.setXattr(ctx, rp.Credentials(), &opts); err != nil {
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
err = d.setXattr(ctx, rp.Credentials(), &opts)
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
@@ -1575,11 +1575,11 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
if err := d.removeXattr(ctx, rp.Credentials(), name); err != nil {
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
err = d.removeXattr(ctx, rp.Credentials(), name)
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.renameMuRUnlockAndCheckCaching(ctx, &ds)
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
|
||||
@@ -117,10 +117,12 @@ func (fd *directoryFD) Release(ctx context.Context) {
|
||||
|
||||
// IterDirents implements vfs.FileDescriptionImpl.IterDirents.
|
||||
func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
|
||||
d := fd.dentry()
|
||||
defer d.InotifyWithParent(ctx, linux.IN_ACCESS, 0, vfs.PathEvent)
|
||||
|
||||
fd.mu.Lock()
|
||||
defer fd.mu.Unlock()
|
||||
|
||||
d := fd.dentry()
|
||||
if fd.dirents == nil {
|
||||
ds, err := d.getDirents(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -499,7 +499,13 @@ func (fs *filesystem) doCreateAt(ctx context.Context, rp *vfs.ResolvingPath, dir
|
||||
if err := create(parent, name, childLayer == lookupLayerUpperWhiteout); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parent.dirents = nil
|
||||
ev := linux.IN_CREATE
|
||||
if dir {
|
||||
ev |= linux.IN_ISDIR
|
||||
}
|
||||
parent.watches.Notify(ctx, name, uint32(ev), 0 /* cookie */, vfs.InodeEvent, false /* unlinked */)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -631,6 +637,7 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
|
||||
}
|
||||
return err
|
||||
}
|
||||
old.watches.Notify(ctx, "", linux.IN_ATTRIB, 0 /* cookie */, vfs.InodeEvent, false /* unlinked */)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -975,6 +982,7 @@ func (fs *filesystem) createAndOpenLocked(ctx context.Context, rp *vfs.Resolving
|
||||
// just can't open it anymore for some reason.
|
||||
return nil, err
|
||||
}
|
||||
parent.watches.Notify(ctx, childName, linux.IN_CREATE, 0 /* cookie */, vfs.PathEvent, false /* unlinked */)
|
||||
return &fd.vfsfd, nil
|
||||
}
|
||||
|
||||
@@ -1236,6 +1244,7 @@ func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldPa
|
||||
}
|
||||
}
|
||||
|
||||
vfs.InotifyRename(ctx, &renamed.watches, &oldParent.watches, &newParent.watches, oldName, newName, renamed.isDir())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1352,6 +1361,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
delete(parent.children, name)
|
||||
ds = appendDentry(ds, child)
|
||||
parent.dirents = nil
|
||||
parent.watches.Notify(ctx, name, linux.IN_DELETE|linux.IN_ISDIR, 0 /* cookie */, vfs.InodeEvent, true /* unlinked */)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1359,12 +1369,25 @@ 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 {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
err = d.setStatLocked(ctx, rp, opts)
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
d.InotifyWithParent(ctx, ev, 0 /* cookie */, vfs.InodeEvent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precondition: d.fs.renameMu must be held for reading.
|
||||
func (d *dentry) setStatLocked(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
|
||||
mode := linux.FileMode(atomic.LoadUint32(&d.mode))
|
||||
if err := vfs.CheckSetStat(ctx, rp.Credentials(), &opts, mode, auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid))); err != nil {
|
||||
return err
|
||||
@@ -1555,11 +1578,14 @@ func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error
|
||||
panic(fmt.Sprintf("unrecoverable overlayfs inconsistency: failed to create whiteout during UnlinkAt: %v", err))
|
||||
}
|
||||
|
||||
var cw *vfs.Watches
|
||||
if child != nil {
|
||||
vfsObj.CommitDeleteDentry(ctx, &child.vfsd)
|
||||
delete(parent.children, name)
|
||||
ds = appendDentry(ds, child)
|
||||
cw = &child.watches
|
||||
}
|
||||
vfs.InotifyRemoveChild(ctx, cw, &parent.watches, name)
|
||||
parent.dirents = nil
|
||||
return nil
|
||||
}
|
||||
@@ -1636,13 +1662,20 @@ func (fs *filesystem) getXattr(ctx context.Context, d *dentry, creds *auth.Crede
|
||||
func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetXattrOptions) error {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
|
||||
err = fs.setXattrLocked(ctx, d, rp.Mount(), rp.Credentials(), &opts)
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fs.setXattrLocked(ctx, d, rp.Mount(), rp.Credentials(), &opts)
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0 /* cookie */, vfs.InodeEvent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precondition: fs.renameMu must be locked.
|
||||
@@ -1673,13 +1706,20 @@ func (fs *filesystem) setXattrLocked(ctx context.Context, d *dentry, mnt *vfs.Mo
|
||||
func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
return err
|
||||
}
|
||||
|
||||
err = fs.removeXattrLocked(ctx, d, rp.Mount(), rp.Credentials(), name)
|
||||
fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fs.removeXattrLocked(ctx, d, rp.Mount(), rp.Credentials(), name)
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0 /* cookie */, vfs.InodeEvent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precondition: fs.renameMu must be locked.
|
||||
|
||||
@@ -184,6 +184,9 @@ func (fd *nonDirectoryFD) SetStat(ctx context.Context, opts vfs.SetStatOptions)
|
||||
return err
|
||||
}
|
||||
d.updateAfterSetStatLocked(&opts)
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
d.InotifyWithParent(ctx, ev, 0, vfs.InodeEvent)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -462,6 +462,13 @@ type dentry struct {
|
||||
isMappable uint32
|
||||
|
||||
locks vfs.FileLocks
|
||||
|
||||
// watches is the set of inotify watches on the file repesented by this dentry.
|
||||
//
|
||||
// Note that hard links to the same file will not share the same set of
|
||||
// watches, due to the fact that we do not have inode structures in this
|
||||
// overlay implementation.
|
||||
watches vfs.Watches
|
||||
}
|
||||
|
||||
// newDentry creates a new dentry. The dentry initially has no references; it
|
||||
@@ -521,6 +528,14 @@ func (d *dentry) checkDropLocked(ctx context.Context) {
|
||||
if atomic.LoadInt64(&d.refs) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure that we do not lose watches on dentries that have not been
|
||||
// deleted. Note that overlayfs never calls VFS.InvalidateDentry(), so
|
||||
// d.vfsd.IsDead() indicates that d was deleted.
|
||||
if !d.vfsd.IsDead() && d.watches.Size() > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Refs is still zero; destroy it.
|
||||
d.destroyLocked(ctx)
|
||||
return
|
||||
@@ -549,6 +564,8 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
lowerVD.DecRef(ctx)
|
||||
}
|
||||
|
||||
d.watches.HandleDeletion(ctx)
|
||||
|
||||
if d.parent != nil {
|
||||
d.parent.dirMu.Lock()
|
||||
if !d.vfsd.IsDead() {
|
||||
@@ -567,19 +584,36 @@ func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
func (d *dentry) InotifyWithParent(ctx context.Context, events uint32, cookie uint32, et vfs.EventType) {
|
||||
// TODO(gvisor.dev/issue/1479): Implement inotify.
|
||||
if d.isDir() {
|
||||
events |= linux.IN_ISDIR
|
||||
}
|
||||
|
||||
// overlayfs never calls VFS.InvalidateDentry(), so d.vfsd.IsDead() indicates
|
||||
// that d was deleted.
|
||||
deleted := d.vfsd.IsDead()
|
||||
|
||||
d.fs.renameMu.RLock()
|
||||
// The ordering below is important, Linux always notifies the parent first.
|
||||
if d.parent != nil {
|
||||
d.parent.watches.Notify(ctx, d.name, events, cookie, et, deleted)
|
||||
}
|
||||
d.watches.Notify(ctx, "", events, cookie, et, deleted)
|
||||
d.fs.renameMu.RUnlock()
|
||||
}
|
||||
|
||||
// Watches implements vfs.DentryImpl.Watches.
|
||||
func (d *dentry) Watches() *vfs.Watches {
|
||||
// TODO(gvisor.dev/issue/1479): Implement inotify.
|
||||
return nil
|
||||
return &d.watches
|
||||
}
|
||||
|
||||
// OnZeroWatches implements vfs.DentryImpl.OnZeroWatches.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1479): Implement inotify.
|
||||
func (d *dentry) OnZeroWatches(context.Context) {}
|
||||
func (d *dentry) OnZeroWatches(ctx context.Context) {
|
||||
if atomic.LoadInt64(&d.refs) == 0 {
|
||||
d.fs.renameMu.Lock()
|
||||
d.checkDropLocked(ctx)
|
||||
d.fs.renameMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// iterLayers invokes yield on each layer comprising d, from top to bottom. If
|
||||
// any call to yield returns false, iterLayer stops iteration.
|
||||
@@ -689,17 +723,33 @@ func (fd *fileDescription) GetXattr(ctx context.Context, opts vfs.GetXattrOption
|
||||
// SetXattr implements vfs.FileDescriptionImpl.SetXattr.
|
||||
func (fd *fileDescription) SetXattr(ctx context.Context, opts vfs.SetXattrOptions) error {
|
||||
fs := fd.filesystem()
|
||||
d := fd.dentry()
|
||||
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMu.RUnlock()
|
||||
return fs.setXattrLocked(ctx, fd.dentry(), fd.vfsfd.Mount(), auth.CredentialsFromContext(ctx), &opts)
|
||||
err := fs.setXattrLocked(ctx, d, fd.vfsfd.Mount(), auth.CredentialsFromContext(ctx), &opts)
|
||||
fs.renameMu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveXattr implements vfs.FileDescriptionImpl.RemoveXattr.
|
||||
func (fd *fileDescription) RemoveXattr(ctx context.Context, name string) error {
|
||||
fs := fd.filesystem()
|
||||
d := fd.dentry()
|
||||
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMu.RUnlock()
|
||||
return fs.removeXattrLocked(ctx, fd.dentry(), fd.vfsfd.Mount(), auth.CredentialsFromContext(ctx), name)
|
||||
err := fs.removeXattrLocked(ctx, d, fd.vfsfd.Mount(), auth.CredentialsFromContext(ctx), name)
|
||||
fs.renameMu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
|
||||
@@ -673,11 +673,11 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
if err := d.inode.setStat(ctx, rp.Credentials(), &opts); err != nil {
|
||||
fs.mu.RUnlock()
|
||||
err = d.inode.setStat(ctx, rp.Credentials(), &opts)
|
||||
fs.mu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.mu.RUnlock()
|
||||
|
||||
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
|
||||
d.InotifyWithParent(ctx, ev, 0, vfs.InodeEvent)
|
||||
@@ -822,11 +822,11 @@ func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
if err := d.inode.setXattr(rp.Credentials(), &opts); err != nil {
|
||||
fs.mu.RUnlock()
|
||||
err = d.inode.setXattr(rp.Credentials(), &opts)
|
||||
fs.mu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.mu.RUnlock()
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
@@ -840,11 +840,11 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
|
||||
fs.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
if err := d.inode.removeXattr(rp.Credentials(), name); err != nil {
|
||||
fs.mu.RUnlock()
|
||||
err = d.inode.removeXattr(rp.Credentials(), name)
|
||||
fs.mu.RUnlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fs.mu.RUnlock()
|
||||
|
||||
d.InotifyWithParent(ctx, linux.IN_ATTRIB, 0, vfs.InodeEvent)
|
||||
return nil
|
||||
|
||||
+1
-1
@@ -238,7 +238,7 @@ syscall_test(
|
||||
|
||||
syscall_test(
|
||||
size = "medium",
|
||||
add_overlay = False, # TODO(gvisor.dev/issue/317): enable when fixed.
|
||||
add_overlay = True,
|
||||
test = "//test/syscalls/linux:inotify_test",
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user