summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry
diff options
context:
space:
mode:
authorDean Deng <deandeng@google.com>2020-05-29 12:27:15 -0700
committergVisor bot <gvisor-bot@google.com>2020-05-29 12:28:49 -0700
commitccf69bdd7e05a4e5f404fbef89a7f49f218645e2 (patch)
treea3ceba80d016b650cea25791c5fd9fbe36a702b9 /pkg/sentry
parent0baba92ad9fc3c92347cada47364514a85603462 (diff)
Implement IN_EXCL_UNLINK inotify option in vfs2.
Limited to tmpfs. Inotify support in other filesystem implementations to follow. Updates #1479 PiperOrigin-RevId: 313828648
Diffstat (limited to 'pkg/sentry')
-rw-r--r--pkg/sentry/fsimpl/ext/dentry.go2
-rw-r--r--pkg/sentry/fsimpl/gofer/gofer.go2
-rw-r--r--pkg/sentry/fsimpl/kernfs/kernfs.go2
-rw-r--r--pkg/sentry/fsimpl/tmpfs/directory.go3
-rw-r--r--pkg/sentry/fsimpl/tmpfs/filesystem.go14
-rw-r--r--pkg/sentry/fsimpl/tmpfs/tmpfs.go18
-rw-r--r--pkg/sentry/kernel/fd_table.go2
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/read_write.go24
-rw-r--r--pkg/sentry/vfs/anonfs.go2
-rw-r--r--pkg/sentry/vfs/dentry.go6
-rw-r--r--pkg/sentry/vfs/inotify.go38
-rw-r--r--pkg/sentry/vfs/vfs.go2
12 files changed, 71 insertions, 44 deletions
diff --git a/pkg/sentry/fsimpl/ext/dentry.go b/pkg/sentry/fsimpl/ext/dentry.go
index 4d0deaf03..6bd1a9fc6 100644
--- a/pkg/sentry/fsimpl/ext/dentry.go
+++ b/pkg/sentry/fsimpl/ext/dentry.go
@@ -64,7 +64,7 @@ func (d *dentry) DecRef() {
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
//
// TODO(gvisor.dev/issue/1479): Implement inotify.
-func (d *dentry) InotifyWithParent(events uint32, cookie uint32) {}
+func (d *dentry) InotifyWithParent(events uint32, cookie uint32, et vfs.EventType) {}
// Watches implements vfs.DentryImpl.Watches.
//
diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go
index 850482a19..3f3bd56f0 100644
--- a/pkg/sentry/fsimpl/gofer/gofer.go
+++ b/pkg/sentry/fsimpl/gofer/gofer.go
@@ -1042,7 +1042,7 @@ func (d *dentry) decRefLocked() {
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
//
// TODO(gvisor.dev/issue/1479): Implement inotify.
-func (d *dentry) InotifyWithParent(events uint32, cookie uint32) {}
+func (d *dentry) InotifyWithParent(events uint32, cookie uint32, et vfs.EventType) {}
// Watches implements vfs.DentryImpl.Watches.
//
diff --git a/pkg/sentry/fsimpl/kernfs/kernfs.go b/pkg/sentry/fsimpl/kernfs/kernfs.go
index 3f1220791..bbee8ccda 100644
--- a/pkg/sentry/fsimpl/kernfs/kernfs.go
+++ b/pkg/sentry/fsimpl/kernfs/kernfs.go
@@ -228,7 +228,7 @@ func (d *Dentry) destroy() {
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
//
// TODO(gvisor.dev/issue/1479): Implement inotify.
-func (d *Dentry) InotifyWithParent(events uint32, cookie uint32) {}
+func (d *Dentry) InotifyWithParent(events uint32, cookie uint32, et vfs.EventType) {}
// Watches implements vfs.DentryImpl.Watches.
//
diff --git a/pkg/sentry/fsimpl/tmpfs/directory.go b/pkg/sentry/fsimpl/tmpfs/directory.go
index 8bc475f88..70387cb9c 100644
--- a/pkg/sentry/fsimpl/tmpfs/directory.go
+++ b/pkg/sentry/fsimpl/tmpfs/directory.go
@@ -79,6 +79,7 @@ func (dir *directory) removeChildLocked(child *dentry) {
dir.iterMu.Lock()
dir.childList.Remove(child)
dir.iterMu.Unlock()
+ child.unlinked = true
}
type directoryFD struct {
@@ -112,7 +113,7 @@ func (fd *directoryFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallba
dir.iterMu.Lock()
defer dir.iterMu.Unlock()
- fd.dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ fd.dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
fd.inode().touchAtime(fd.vfsfd.Mount())
if fd.off == 0 {
diff --git a/pkg/sentry/fsimpl/tmpfs/filesystem.go b/pkg/sentry/fsimpl/tmpfs/filesystem.go
index b4159f904..183eb975c 100644
--- a/pkg/sentry/fsimpl/tmpfs/filesystem.go
+++ b/pkg/sentry/fsimpl/tmpfs/filesystem.go
@@ -182,7 +182,7 @@ func (fs *filesystem) doCreateAt(rp *vfs.ResolvingPath, dir bool, create func(pa
if dir {
ev |= linux.IN_ISDIR
}
- parentDir.inode.watches.Notify(name, uint32(ev), 0)
+ parentDir.inode.watches.Notify(name, uint32(ev), 0, vfs.InodeEvent)
parentDir.inode.touchCMtime()
return nil
}
@@ -247,7 +247,7 @@ func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.
return syserror.EMLINK
}
d.inode.incLinksLocked()
- d.inode.watches.Notify("", linux.IN_ATTRIB, 0)
+ d.inode.watches.Notify("", linux.IN_ATTRIB, 0, vfs.InodeEvent)
parentDir.insertChildLocked(fs.newDentry(d.inode), name)
return nil
})
@@ -361,7 +361,7 @@ afterTrailingSymlink:
if err != nil {
return nil, err
}
- parentDir.inode.watches.Notify(name, linux.IN_CREATE, 0)
+ parentDir.inode.watches.Notify(name, linux.IN_CREATE, 0, vfs.PathEvent)
parentDir.inode.touchCMtime()
return fd, nil
}
@@ -613,7 +613,7 @@ func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error
return err
}
parentDir.removeChildLocked(child)
- parentDir.inode.watches.Notify(name, linux.IN_DELETE|linux.IN_ISDIR, 0)
+ parentDir.inode.watches.Notify(name, linux.IN_DELETE|linux.IN_ISDIR, 0, vfs.InodeEvent)
// Remove links for child, child/., and child/..
child.inode.decLinksLocked()
child.inode.decLinksLocked()
@@ -636,7 +636,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
}
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
- d.InotifyWithParent(ev, 0)
+ d.InotifyWithParent(ev, 0, vfs.InodeEvent)
}
return nil
}
@@ -784,7 +784,7 @@ func (fs *filesystem) SetxattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
return err
}
- d.InotifyWithParent(linux.IN_ATTRIB, 0)
+ d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
return nil
}
@@ -800,7 +800,7 @@ func (fs *filesystem) RemovexattrAt(ctx context.Context, rp *vfs.ResolvingPath,
return err
}
- d.InotifyWithParent(linux.IN_ATTRIB, 0)
+ d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
return nil
}
diff --git a/pkg/sentry/fsimpl/tmpfs/tmpfs.go b/pkg/sentry/fsimpl/tmpfs/tmpfs.go
index 1d83b6840..f0e098702 100644
--- a/pkg/sentry/fsimpl/tmpfs/tmpfs.go
+++ b/pkg/sentry/fsimpl/tmpfs/tmpfs.go
@@ -163,6 +163,11 @@ type dentry struct {
// filesystem.mu.
name string
+ // unlinked indicates whether this dentry has been unlinked from its parent.
+ // It is only set to true on an unlink operation, and never set from true to
+ // false. unlinked is protected by filesystem.mu.
+ unlinked bool
+
// dentryEntry (ugh) links dentries into their parent directory.childList.
dentryEntry
@@ -202,7 +207,7 @@ func (d *dentry) DecRef() {
}
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
-func (d *dentry) InotifyWithParent(events uint32, cookie uint32) {
+func (d *dentry) InotifyWithParent(events uint32, cookie uint32, et vfs.EventType) {
if d.inode.isDir() {
events |= linux.IN_ISDIR
}
@@ -211,9 +216,9 @@ func (d *dentry) InotifyWithParent(events uint32, cookie uint32) {
if d.parent != nil {
// Note that d.parent or d.name may be stale if there is a concurrent
// rename operation. Inotify does not provide consistency guarantees.
- d.parent.inode.watches.Notify(d.name, events, cookie)
+ d.parent.inode.watches.NotifyWithExclusions(d.name, events, cookie, et, d.unlinked)
}
- d.inode.watches.Notify("", events, cookie)
+ d.inode.watches.Notify("", events, cookie, et)
}
// Watches implements vfs.DentryImpl.Watches.
@@ -676,9 +681,8 @@ func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions)
return err
}
- // Generate inotify events.
if ev := vfs.InotifyEventFromStatMask(opts.Stat.Mask); ev != 0 {
- d.InotifyWithParent(ev, 0)
+ d.InotifyWithParent(ev, 0, vfs.InodeEvent)
}
return nil
}
@@ -701,7 +705,7 @@ func (fd *fileDescription) Setxattr(ctx context.Context, opts vfs.SetxattrOption
}
// Generate inotify events.
- d.InotifyWithParent(linux.IN_ATTRIB, 0)
+ d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
return nil
}
@@ -713,7 +717,7 @@ func (fd *fileDescription) Removexattr(ctx context.Context, name string) error {
}
// Generate inotify events.
- d.InotifyWithParent(linux.IN_ATTRIB, 0)
+ d.InotifyWithParent(linux.IN_ATTRIB, 0, vfs.InodeEvent)
return nil
}
diff --git a/pkg/sentry/kernel/fd_table.go b/pkg/sentry/kernel/fd_table.go
index ef73e1169..dbfcef0fa 100644
--- a/pkg/sentry/kernel/fd_table.go
+++ b/pkg/sentry/kernel/fd_table.go
@@ -158,7 +158,7 @@ func (f *FDTable) dropVFS2(file *vfs.FileDescription) {
if file.IsWritable() {
ev = linux.IN_CLOSE_WRITE
}
- file.Dentry().InotifyWithParent(ev, 0)
+ file.Dentry().InotifyWithParent(ev, 0, vfs.PathEvent)
// Drop the table reference.
file.DecRef()
diff --git a/pkg/sentry/syscalls/linux/vfs2/read_write.go b/pkg/sentry/syscalls/linux/vfs2/read_write.go
index 92b5631a3..7f9debd4a 100644
--- a/pkg/sentry/syscalls/linux/vfs2/read_write.go
+++ b/pkg/sentry/syscalls/linux/vfs2/read_write.go
@@ -94,7 +94,7 @@ func read(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, opt
n, err := file.Read(t, dst, opts)
if err != syserror.ErrWouldBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return n, err
}
@@ -102,7 +102,7 @@ func read(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, opt
allowBlock, deadline, hasDeadline := blockPolicy(t, file)
if !allowBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return n, err
}
@@ -135,7 +135,7 @@ func read(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, opt
file.EventUnregister(&w)
if total > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return total, err
}
@@ -258,7 +258,7 @@ func pread(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, of
n, err := file.PRead(t, dst, offset, opts)
if err != syserror.ErrWouldBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return n, err
}
@@ -266,7 +266,7 @@ func pread(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, of
allowBlock, deadline, hasDeadline := blockPolicy(t, file)
if !allowBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return n, err
}
@@ -299,7 +299,7 @@ func pread(t *kernel.Task, file *vfs.FileDescription, dst usermem.IOSequence, of
file.EventUnregister(&w)
if total > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return total, err
}
@@ -364,7 +364,7 @@ func write(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, op
n, err := file.Write(t, src, opts)
if err != syserror.ErrWouldBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0)
+ file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0, vfs.PathEvent)
}
return n, err
}
@@ -372,7 +372,7 @@ func write(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, op
allowBlock, deadline, hasDeadline := blockPolicy(t, file)
if !allowBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0)
+ file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0, vfs.PathEvent)
}
return n, err
}
@@ -405,7 +405,7 @@ func write(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, op
file.EventUnregister(&w)
if total > 0 {
- file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0)
+ file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0, vfs.PathEvent)
}
return total, err
}
@@ -528,7 +528,7 @@ func pwrite(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, o
n, err := file.PWrite(t, src, offset, opts)
if err != syserror.ErrWouldBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0)
+ file.Dentry().InotifyWithParent(linux.IN_MODIFY, 0, vfs.PathEvent)
}
return n, err
}
@@ -536,7 +536,7 @@ func pwrite(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, o
allowBlock, deadline, hasDeadline := blockPolicy(t, file)
if !allowBlock {
if n > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return n, err
}
@@ -569,7 +569,7 @@ func pwrite(t *kernel.Task, file *vfs.FileDescription, src usermem.IOSequence, o
file.EventUnregister(&w)
if total > 0 {
- file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0)
+ file.Dentry().InotifyWithParent(linux.IN_ACCESS, 0, vfs.PathEvent)
}
return total, err
}
diff --git a/pkg/sentry/vfs/anonfs.go b/pkg/sentry/vfs/anonfs.go
index 55a3d54cc..b7c6b60b8 100644
--- a/pkg/sentry/vfs/anonfs.go
+++ b/pkg/sentry/vfs/anonfs.go
@@ -301,7 +301,7 @@ func (d *anonDentry) DecRef() {
// InotifyWithParent implements DentryImpl.InotifyWithParent.
//
// TODO(gvisor.dev/issue/1479): Implement inotify.
-func (d *anonDentry) InotifyWithParent(events uint32, cookie uint32) {}
+func (d *anonDentry) InotifyWithParent(events uint32, cookie uint32, et EventType) {}
// Watches implements DentryImpl.Watches.
//
diff --git a/pkg/sentry/vfs/dentry.go b/pkg/sentry/vfs/dentry.go
index d61b9e09b..24af13eb1 100644
--- a/pkg/sentry/vfs/dentry.go
+++ b/pkg/sentry/vfs/dentry.go
@@ -113,7 +113,7 @@ type DentryImpl interface {
//
// Note that the events may not actually propagate up to the user, depending
// on the event masks.
- InotifyWithParent(events uint32, cookie uint32)
+ InotifyWithParent(events uint32, cookie uint32, et EventType)
// Watches returns the set of inotify watches for the file corresponding to
// the Dentry. Dentries that are hard links to the same underlying file
@@ -151,8 +151,8 @@ func (d *Dentry) isMounted() bool {
// InotifyWithParent notifies all watches on the inodes for this dentry and
// its parent of events.
-func (d *Dentry) InotifyWithParent(events uint32, cookie uint32) {
- d.impl.InotifyWithParent(events, cookie)
+func (d *Dentry) InotifyWithParent(events uint32, cookie uint32, et EventType) {
+ d.impl.InotifyWithParent(events, cookie, et)
}
// Watches returns the set of inotify watches associated with d.
diff --git a/pkg/sentry/vfs/inotify.go b/pkg/sentry/vfs/inotify.go
index 1d28ccb46..05a3051a4 100644
--- a/pkg/sentry/vfs/inotify.go
+++ b/pkg/sentry/vfs/inotify.go
@@ -33,6 +33,19 @@ import (
// must be a power 2 for rounding below.
const inotifyEventBaseSize = 16
+// EventType defines different kinds of inotfiy events.
+//
+// The way events are labelled appears somewhat arbitrary, but they must match
+// Linux so that IN_EXCL_UNLINK behaves as it does in Linux.
+type EventType uint8
+
+// PathEvent and InodeEvent correspond to FSNOTIFY_EVENT_PATH and
+// FSNOTIFY_EVENT_INODE in Linux.
+const (
+ PathEvent EventType = iota
+ InodeEvent EventType = iota
+)
+
// Inotify represents an inotify instance created by inotify_init(2) or
// inotify_init1(2). Inotify implements FileDescriptionImpl.
//
@@ -419,13 +432,22 @@ func (w *Watches) Remove(id uint64) {
}
// Notify queues a new event with all watches in this set.
-func (w *Watches) Notify(name string, events, cookie uint32) {
+func (w *Watches) Notify(name string, events, cookie uint32, et EventType) {
+ w.NotifyWithExclusions(name, events, cookie, et, false)
+}
+
+// NotifyWithExclusions queues a new event with watches in this set. Watches
+// with IN_EXCL_UNLINK are skipped if the event is coming from a child that
+// has been unlinked.
+func (w *Watches) NotifyWithExclusions(name string, events, cookie uint32, et EventType, unlinked bool) {
// N.B. We don't defer the unlocks because Notify is in the hot path of
// all IO operations, and the defer costs too much for small IO
// operations.
w.mu.RLock()
for _, watch := range w.ws {
- // TODO(gvisor.dev/issue/1479): Skip for IN_EXCL_UNLINK cases.
+ if unlinked && watch.ExcludeUnlinkedChildren() && et == PathEvent {
+ continue
+ }
watch.Notify(name, events, cookie)
}
w.mu.RUnlock()
@@ -434,7 +456,7 @@ func (w *Watches) Notify(name string, events, cookie uint32) {
// HandleDeletion is called when the watch target is destroyed to emit
// the appropriate events.
func (w *Watches) HandleDeletion() {
- w.Notify("", linux.IN_DELETE_SELF, 0)
+ w.Notify("", linux.IN_DELETE_SELF, 0, InodeEvent)
// TODO(gvisor.dev/issue/1479): This doesn't work because maps are not copied
// by value. Ideally, we wouldn't have this circular locking so we can just
@@ -655,8 +677,8 @@ func InotifyEventFromStatMask(mask uint32) uint32 {
// InotifyRemoveChild sends the appriopriate notifications to the watch sets of
// the child being removed and its parent.
func InotifyRemoveChild(self, parent *Watches, name string) {
- self.Notify("", linux.IN_ATTRIB, 0)
- parent.Notify(name, linux.IN_DELETE, 0)
+ self.Notify("", linux.IN_ATTRIB, 0, InodeEvent)
+ parent.Notify(name, linux.IN_DELETE, 0, InodeEvent)
// TODO(gvisor.dev/issue/1479): implement IN_EXCL_UNLINK.
}
@@ -668,8 +690,8 @@ func InotifyRename(ctx context.Context, renamed, oldParent, newParent *Watches,
dirEv = linux.IN_ISDIR
}
cookie := uniqueid.InotifyCookie(ctx)
- oldParent.Notify(oldName, dirEv|linux.IN_MOVED_FROM, cookie)
- newParent.Notify(newName, dirEv|linux.IN_MOVED_TO, cookie)
+ oldParent.Notify(oldName, dirEv|linux.IN_MOVED_FROM, cookie, InodeEvent)
+ newParent.Notify(newName, dirEv|linux.IN_MOVED_TO, cookie, InodeEvent)
// Somewhat surprisingly, self move events do not have a cookie.
- renamed.Notify("", linux.IN_MOVE_SELF, 0)
+ renamed.Notify("", linux.IN_MOVE_SELF, 0, InodeEvent)
}
diff --git a/pkg/sentry/vfs/vfs.go b/pkg/sentry/vfs/vfs.go
index be6f21dba..52643a7c5 100644
--- a/pkg/sentry/vfs/vfs.go
+++ b/pkg/sentry/vfs/vfs.go
@@ -422,7 +422,7 @@ func (vfs *VirtualFilesystem) OpenAt(ctx context.Context, creds *auth.Credential
}
}
- fd.Dentry().InotifyWithParent(linux.IN_OPEN, 0)
+ fd.Dentry().InotifyWithParent(linux.IN_OPEN, 0, PathEvent)
return fd, nil
}
if !rp.handleError(err) {