Support inotify in overlayfs.

Fixes #1479, #317.

PiperOrigin-RevId: 334258052
This commit is contained in:
Dean Deng
2020-09-28 16:11:16 -07:00
committed by gVisor bot
parent fa995da840
commit a0e0ba690f
7 changed files with 130 additions and 35 deletions
+9 -9
View File
@@ -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
+3 -1
View File
@@ -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 {
+45 -5
View File
@@ -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
}
+60 -10
View File
@@ -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.
+9 -9
View File
@@ -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
View File
@@ -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",
)