The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH =-v3 00/21] fanotify: novel file access notification and permission system
@ 2008-11-12 16:10 Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 01/21] filesystem notification: create fs/notify to contain all fs notification Eric Paris
                   ` (20 more replies)
  0 siblings, 21 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

the following is a file notification and access system intended to allow
a variety of userspace programs to get information about filesystem
events no matter where or how they happen on a system and use that in
conjunction with the actual on disk data related to that event to
provide additional services such as file change indexing or content
based antivirus scanning.  Minor changes are almost certainly possible
to make this notification and access interface usable for HSMs.  fscking
all notify is generally refered to as fanotify, or for the weak of heart
you can call it file access notify system.  The ideas behind this
code are based on talpa the GPL antivirus interface originally written
by Sophos and on the feedback from lkml and malware-list.  This is
however a complete rewrite from scratch.

The last patch set addressed every complaint/critisism from lists.  This
set adds a couple new features, some documentation, and a couple fixes to
the networking headers to support bi-arch machines.

**fanotify-executive-summary**

fanotify has a number of event types and only sends events for S_ISREG()
files.  These events incluse open, read, write, and permissions checking
for open and read.  The permissions checking events require that the
listener return some sort of allow/deny/more_time response as the
original process blocks until it gets an event (or times out)
listeners may register a group which will get notifications about
any combination of these events, it will be up to the listener to
determine what events they are interested in hearing or mediating access
decisions for.

groups are a construct in which userspace indicates what priority (only
really used for permission type events) and what type of events its
listeners want to hear.  A single group may have unlimited listeners but
each event will only go to ONE listener.  Two groups may register for
the same type of events and one listener in EACH group will get a copy
of the event.

The user interface for fanotify is all through sockopt calls.  Userspace
pulls events from the kernel using getsockopt and sends responses to the
kernel using setsockopt.  These are the only socket operations defined
for PF_FANOTIFY sockets.

---

Eric Paris (21):
      fanotify: add Documentation
      fanotify: allow fastpath entries to survive inode modification
      fanotify: evict misbehaving clients
      fanotify: all userspace to set timeouts
      fanotify: add option to clear all fastpaths
      fanotify: send file f_flags along with notifications
      fanotify: send tgid with notification messages
      fanotify: send pid with fanotify notification events
      fanotify: ability for userspace to delay responses
      fanotify: user interface for access decisions
      fanotify: give a special access permission check
      fanotify: blocking and access granting
      fanotify: add group priorities
      fanotify: add a userspace interface for fastpaths
      fanotify: fastpath to ignore certain in core inodes
      fanotify: add a userspace interface for fanotify notifications
      fanotify: make use of the new fsnotify_open_exec calls
      fsnotify: sys_execve and sys_uselib do not call into fsnotify
      fanotify: fscking all notify, system wide file access notification
      fsnotify: pass a file instead of an inode to open, read, and write
      filesystem notification: create fs/notify to contain all fs notification


 Documentation/filesystems/fanotify/fanotify.txt    |  214 ++++++
 .../filesystems/fanotify/fanotify_tester.c         |  255 +++++++
 fs/Kconfig                                         |   39 -
 fs/Makefile                                        |    5 
 fs/aio.c                                           |    7 
 fs/compat.c                                        |    5 
 fs/dnotify.c                                       |  194 -----
 fs/exec.c                                          |   15 
 fs/inode.c                                         |    6 
 fs/inotify.c                                       |  773 --------------------
 fs/inotify_user.c                                  |  778 --------------------
 fs/nfsd/vfs.c                                      |    4 
 fs/notify/Kconfig                                  |   52 +
 fs/notify/Makefile                                 |    6 
 fs/notify/access.c                                 |  222 ++++++
 fs/notify/dnotify.c                                |  194 +++++
 fs/notify/fanotify.c                               |  152 ++++
 fs/notify/fanotify.h                               |  112 +++
 fs/notify/fastpath.c                               |  264 +++++++
 fs/notify/group.c                                  |  163 ++++
 fs/notify/inotify.c                                |  773 ++++++++++++++++++++
 fs/notify/inotify_user.c                           |  778 ++++++++++++++++++++
 fs/notify/notification.c                           |  277 +++++++
 fs/open.c                                          |    7 
 fs/read_write.c                                    |   14 
 include/linux/Kbuild                               |    1 
 include/linux/fanotify.h                           |  197 +++++
 include/linux/fs.h                                 |    5 
 include/linux/fsnotify.h                           |   39 +
 include/linux/sched.h                              |    1 
 include/linux/socket.h                             |    5 
 mm/mmap.c                                          |    7 
 mm/mprotect.c                                      |    6 
 mm/nommu.c                                         |    7 
 net/Makefile                                       |    1 
 net/core/sock.c                                    |    6 
 net/fanotify/Makefile                              |    5 
 net/fanotify/af_fanotify.c                         |  295 ++++++++
 net/fanotify/af_fanotify.h                         |   20 +
 39 files changed, 4096 insertions(+), 1808 deletions(-)
 create mode 100644 Documentation/filesystems/fanotify/fanotify.txt
 create mode 100644 Documentation/filesystems/fanotify/fanotify_tester.c
 delete mode 100644 fs/dnotify.c
 delete mode 100644 fs/inotify.c
 delete mode 100644 fs/inotify_user.c
 create mode 100644 fs/notify/Kconfig
 create mode 100644 fs/notify/Makefile
 create mode 100644 fs/notify/access.c
 create mode 100644 fs/notify/dnotify.c
 create mode 100644 fs/notify/fanotify.c
 create mode 100644 fs/notify/fanotify.h
 create mode 100644 fs/notify/fastpath.c
 create mode 100644 fs/notify/group.c
 create mode 100644 fs/notify/inotify.c
 create mode 100644 fs/notify/inotify_user.c
 create mode 100644 fs/notify/notification.c
 create mode 100644 include/linux/fanotify.h
 create mode 100644 net/fanotify/Makefile
 create mode 100644 net/fanotify/af_fanotify.c
 create mode 100644 net/fanotify/af_fanotify.h

-- 
Signature

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH =-v3 01/21] filesystem notification: create fs/notify to contain all fs notification
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 02/21] fsnotify: pass a file instead of an inode to open, read, and write Eric Paris
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Adding yet another filesystem notification system it seemed like a good
idea to clean up fs/ but creating an fs/notify and putting everything
there.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/Kconfig               |   39 --
 fs/Makefile              |    5 
 fs/dnotify.c             |  194 -----------
 fs/inotify.c             |  773 ----------------------------------------------
 fs/inotify_user.c        |  778 ----------------------------------------------
 fs/notify/Kconfig        |   38 ++
 fs/notify/Makefile       |    4 
 fs/notify/dnotify.c      |  194 +++++++++++
 fs/notify/inotify.c      |  773 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/notify/inotify_user.c |  778 ++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1789 insertions(+), 1787 deletions(-)
 delete mode 100644 fs/dnotify.c
 delete mode 100644 fs/inotify.c
 delete mode 100644 fs/inotify_user.c
 create mode 100644 fs/notify/Kconfig
 create mode 100644 fs/notify/Makefile
 create mode 100644 fs/notify/dnotify.c
 create mode 100644 fs/notify/inotify.c
 create mode 100644 fs/notify/inotify_user.c

diff --git a/fs/Kconfig b/fs/Kconfig
index 522469a..ff0e819 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -270,44 +270,7 @@ config OCFS2_COMPAT_JBD
 
 endif # BLOCK
 
-config DNOTIFY
-	bool "Dnotify support"
-	default y
-	help
-	  Dnotify is a directory-based per-fd file change notification system
-	  that uses signals to communicate events to user-space.  There exist
-	  superior alternatives, but some applications may still rely on
-	  dnotify.
-
-	  If unsure, say Y.
-
-config INOTIFY
-	bool "Inotify file change notification support"
-	default y
-	---help---
-	  Say Y here to enable inotify support.  Inotify is a file change
-	  notification system and a replacement for dnotify.  Inotify fixes
-	  numerous shortcomings in dnotify and introduces several new features
-	  including multiple file events, one-shot support, and unmount
-	  notification.
-
-	  For more information, see <file:Documentation/filesystems/inotify.txt>
-
-	  If unsure, say Y.
-
-config INOTIFY_USER
-	bool "Inotify support for userspace"
-	depends on INOTIFY
-	default y
-	---help---
-	  Say Y here to enable inotify support for userspace, including the
-	  associated system calls.  Inotify allows monitoring of both files and
-	  directories via a single open fd.  Events are read from the file
-	  descriptor, which is also select()- and poll()-able.
-
-	  For more information, see <file:Documentation/filesystems/inotify.txt>
-
-	  If unsure, say Y.
+source "fs/notify/Kconfig"
 
 config QUOTA
 	bool "Quota support"
diff --git a/fs/Makefile b/fs/Makefile
index d9f8afe..e6f423d 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -20,8 +20,7 @@ obj-y +=	no-block.o
 endif
 
 obj-$(CONFIG_BLK_DEV_INTEGRITY) += bio-integrity.o
-obj-$(CONFIG_INOTIFY)		+= inotify.o
-obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
+obj-y				+= notify/
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
 obj-$(CONFIG_ANON_INODES)	+= anon_inodes.o
 obj-$(CONFIG_SIGNALFD)		+= signalfd.o
@@ -57,8 +56,6 @@ obj-$(CONFIG_QFMT_V1)		+= quota_v1.o
 obj-$(CONFIG_QFMT_V2)		+= quota_v2.o
 obj-$(CONFIG_QUOTACTL)		+= quota.o
 
-obj-$(CONFIG_DNOTIFY)		+= dnotify.o
-
 obj-$(CONFIG_PROC_FS)		+= proc/
 obj-y				+= partitions/
 obj-$(CONFIG_SYSFS)		+= sysfs/
diff --git a/fs/dnotify.c b/fs/dnotify.c
deleted file mode 100644
index 676073b..0000000
--- a/fs/dnotify.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Directory notifications for Linux.
- *
- * Copyright (C) 2000,2001,2002 Stephen Rothwell
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- */
-#include <linux/fs.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/dnotify.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/fdtable.h>
-
-int dir_notify_enable __read_mostly = 1;
-
-static struct kmem_cache *dn_cache __read_mostly;
-
-static void redo_inode_mask(struct inode *inode)
-{
-	unsigned long new_mask;
-	struct dnotify_struct *dn;
-
-	new_mask = 0;
-	for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
-		new_mask |= dn->dn_mask & ~DN_MULTISHOT;
-	inode->i_dnotify_mask = new_mask;
-}
-
-void dnotify_flush(struct file *filp, fl_owner_t id)
-{
-	struct dnotify_struct *dn;
-	struct dnotify_struct **prev;
-	struct inode *inode;
-
-	inode = filp->f_path.dentry->d_inode;
-	if (!S_ISDIR(inode->i_mode))
-		return;
-	spin_lock(&inode->i_lock);
-	prev = &inode->i_dnotify;
-	while ((dn = *prev) != NULL) {
-		if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
-			*prev = dn->dn_next;
-			redo_inode_mask(inode);
-			kmem_cache_free(dn_cache, dn);
-			break;
-		}
-		prev = &dn->dn_next;
-	}
-	spin_unlock(&inode->i_lock);
-}
-
-int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
-{
-	struct dnotify_struct *dn;
-	struct dnotify_struct *odn;
-	struct dnotify_struct **prev;
-	struct inode *inode;
-	fl_owner_t id = current->files;
-	struct file *f;
-	int error = 0;
-
-	if ((arg & ~DN_MULTISHOT) == 0) {
-		dnotify_flush(filp, id);
-		return 0;
-	}
-	if (!dir_notify_enable)
-		return -EINVAL;
-	inode = filp->f_path.dentry->d_inode;
-	if (!S_ISDIR(inode->i_mode))
-		return -ENOTDIR;
-	dn = kmem_cache_alloc(dn_cache, GFP_KERNEL);
-	if (dn == NULL)
-		return -ENOMEM;
-	spin_lock(&inode->i_lock);
-	prev = &inode->i_dnotify;
-	while ((odn = *prev) != NULL) {
-		if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
-			odn->dn_fd = fd;
-			odn->dn_mask |= arg;
-			inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
-			goto out_free;
-		}
-		prev = &odn->dn_next;
-	}
-
-	rcu_read_lock();
-	f = fcheck(fd);
-	rcu_read_unlock();
-	/* we'd lost the race with close(), sod off silently */
-	/* note that inode->i_lock prevents reordering problems
-	 * between accesses to descriptor table and ->i_dnotify */
-	if (f != filp)
-		goto out_free;
-
-	error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
-	if (error)
-		goto out_free;
-
-	dn->dn_mask = arg;
-	dn->dn_fd = fd;
-	dn->dn_filp = filp;
-	dn->dn_owner = id;
-	inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
-	dn->dn_next = inode->i_dnotify;
-	inode->i_dnotify = dn;
-	spin_unlock(&inode->i_lock);
-
-	if (filp->f_op && filp->f_op->dir_notify)
-		return filp->f_op->dir_notify(filp, arg);
-	return 0;
-
-out_free:
-	spin_unlock(&inode->i_lock);
-	kmem_cache_free(dn_cache, dn);
-	return error;
-}
-
-void __inode_dir_notify(struct inode *inode, unsigned long event)
-{
-	struct dnotify_struct *	dn;
-	struct dnotify_struct **prev;
-	struct fown_struct *	fown;
-	int			changed = 0;
-
-	spin_lock(&inode->i_lock);
-	prev = &inode->i_dnotify;
-	while ((dn = *prev) != NULL) {
-		if ((dn->dn_mask & event) == 0) {
-			prev = &dn->dn_next;
-			continue;
-		}
-		fown = &dn->dn_filp->f_owner;
-		send_sigio(fown, dn->dn_fd, POLL_MSG);
-		if (dn->dn_mask & DN_MULTISHOT)
-			prev = &dn->dn_next;
-		else {
-			*prev = dn->dn_next;
-			changed = 1;
-			kmem_cache_free(dn_cache, dn);
-		}
-	}
-	if (changed)
-		redo_inode_mask(inode);
-	spin_unlock(&inode->i_lock);
-}
-
-EXPORT_SYMBOL(__inode_dir_notify);
-
-/*
- * This is hopelessly wrong, but unfixable without API changes.  At
- * least it doesn't oops the kernel...
- *
- * To safely access ->d_parent we need to keep d_move away from it.  Use the
- * dentry's d_lock for this.
- */
-void dnotify_parent(struct dentry *dentry, unsigned long event)
-{
-	struct dentry *parent;
-
-	if (!dir_notify_enable)
-		return;
-
-	spin_lock(&dentry->d_lock);
-	parent = dentry->d_parent;
-	if (parent->d_inode->i_dnotify_mask & event) {
-		dget(parent);
-		spin_unlock(&dentry->d_lock);
-		__inode_dir_notify(parent->d_inode, event);
-		dput(parent);
-	} else {
-		spin_unlock(&dentry->d_lock);
-	}
-}
-EXPORT_SYMBOL_GPL(dnotify_parent);
-
-static int __init dnotify_init(void)
-{
-	dn_cache = kmem_cache_create("dnotify_cache",
-		sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL);
-	return 0;
-}
-
-module_init(dnotify_init)
diff --git a/fs/inotify.c b/fs/inotify.c
deleted file mode 100644
index 690e725..0000000
--- a/fs/inotify.c
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * fs/inotify.c - inode-based file event notifications
- *
- * Authors:
- *	John McCutchan	<ttb@tentacle.dhs.org>
- *	Robert Love	<rml@novell.com>
- *
- * Kernel API added by: Amy Griffis <amy.griffis@hp.com>
- *
- * Copyright (C) 2005 John McCutchan
- * Copyright 2006 Hewlett-Packard Development Company, L.P.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/idr.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/writeback.h>
-#include <linux/inotify.h>
-
-static atomic_t inotify_cookie;
-
-/*
- * Lock ordering:
- *
- * dentry->d_lock (used to keep d_move() away from dentry->d_parent)
- * iprune_mutex (synchronize shrink_icache_memory())
- * 	inode_lock (protects the super_block->s_inodes list)
- * 	inode->inotify_mutex (protects inode->inotify_watches and watches->i_list)
- * 		inotify_handle->mutex (protects inotify_handle and watches->h_list)
- *
- * The inode->inotify_mutex and inotify_handle->mutex and held during execution
- * of a caller's event handler.  Thus, the caller must not hold any locks
- * taken in their event handler while calling any of the published inotify
- * interfaces.
- */
-
-/*
- * Lifetimes of the three main data structures--inotify_handle, inode, and
- * inotify_watch--are managed by reference count.
- *
- * inotify_handle: Lifetime is from inotify_init() to inotify_destroy().
- * Additional references can bump the count via get_inotify_handle() and drop
- * the count via put_inotify_handle().
- *
- * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch()
- * to remove_watch_no_event().  Additional references can bump the count via
- * get_inotify_watch() and drop the count via put_inotify_watch().  The caller
- * is reponsible for the final put after receiving IN_IGNORED, or when using
- * IN_ONESHOT after receiving the first event.  Inotify does the final put if
- * inotify_destroy() is called.
- *
- * inode: Pinned so long as the inode is associated with a watch, from
- * inotify_add_watch() to the final put_inotify_watch().
- */
-
-/*
- * struct inotify_handle - represents an inotify instance
- *
- * This structure is protected by the mutex 'mutex'.
- */
-struct inotify_handle {
-	struct idr		idr;		/* idr mapping wd -> watch */
-	struct mutex		mutex;		/* protects this bad boy */
-	struct list_head	watches;	/* list of watches */
-	atomic_t		count;		/* reference count */
-	u32			last_wd;	/* the last wd allocated */
-	const struct inotify_operations *in_ops; /* inotify caller operations */
-};
-
-static inline void get_inotify_handle(struct inotify_handle *ih)
-{
-	atomic_inc(&ih->count);
-}
-
-static inline void put_inotify_handle(struct inotify_handle *ih)
-{
-	if (atomic_dec_and_test(&ih->count)) {
-		idr_destroy(&ih->idr);
-		kfree(ih);
-	}
-}
-
-/**
- * get_inotify_watch - grab a reference to an inotify_watch
- * @watch: watch to grab
- */
-void get_inotify_watch(struct inotify_watch *watch)
-{
-	atomic_inc(&watch->count);
-}
-EXPORT_SYMBOL_GPL(get_inotify_watch);
-
-/**
- * put_inotify_watch - decrements the ref count on a given watch.  cleans up
- * watch references if the count reaches zero.  inotify_watch is freed by
- * inotify callers via the destroy_watch() op.
- * @watch: watch to release
- */
-void put_inotify_watch(struct inotify_watch *watch)
-{
-	if (atomic_dec_and_test(&watch->count)) {
-		struct inotify_handle *ih = watch->ih;
-
-		iput(watch->inode);
-		ih->in_ops->destroy_watch(watch);
-		put_inotify_handle(ih);
-	}
-}
-EXPORT_SYMBOL_GPL(put_inotify_watch);
-
-/*
- * inotify_handle_get_wd - returns the next WD for use by the given handle
- *
- * Callers must hold ih->mutex.  This function can sleep.
- */
-static int inotify_handle_get_wd(struct inotify_handle *ih,
-				 struct inotify_watch *watch)
-{
-	int ret;
-
-	do {
-		if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL)))
-			return -ENOSPC;
-		ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd);
-	} while (ret == -EAGAIN);
-
-	if (likely(!ret))
-		ih->last_wd = watch->wd;
-
-	return ret;
-}
-
-/*
- * inotify_inode_watched - returns nonzero if there are watches on this inode
- * and zero otherwise.  We call this lockless, we do not care if we race.
- */
-static inline int inotify_inode_watched(struct inode *inode)
-{
-	return !list_empty(&inode->inotify_watches);
-}
-
-/*
- * Get child dentry flag into synch with parent inode.
- * Flag should always be clear for negative dentrys.
- */
-static void set_dentry_child_flags(struct inode *inode, int watched)
-{
-	struct dentry *alias;
-
-	spin_lock(&dcache_lock);
-	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
-		struct dentry *child;
-
-		list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
-			if (!child->d_inode)
-				continue;
-
-			spin_lock(&child->d_lock);
-			if (watched)
-				child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
-			else
-				child->d_flags &=~DCACHE_INOTIFY_PARENT_WATCHED;
-			spin_unlock(&child->d_lock);
-		}
-	}
-	spin_unlock(&dcache_lock);
-}
-
-/*
- * inotify_find_handle - find the watch associated with the given inode and
- * handle
- *
- * Callers must hold inode->inotify_mutex.
- */
-static struct inotify_watch *inode_find_handle(struct inode *inode,
-					       struct inotify_handle *ih)
-{
-	struct inotify_watch *watch;
-
-	list_for_each_entry(watch, &inode->inotify_watches, i_list) {
-		if (watch->ih == ih)
-			return watch;
-	}
-
-	return NULL;
-}
-
-/*
- * remove_watch_no_event - remove watch without the IN_IGNORED event.
- *
- * Callers must hold both inode->inotify_mutex and ih->mutex.
- */
-static void remove_watch_no_event(struct inotify_watch *watch,
-				  struct inotify_handle *ih)
-{
-	list_del(&watch->i_list);
-	list_del(&watch->h_list);
-
-	if (!inotify_inode_watched(watch->inode))
-		set_dentry_child_flags(watch->inode, 0);
-
-	idr_remove(&ih->idr, watch->wd);
-}
-
-/**
- * inotify_remove_watch_locked - Remove a watch from both the handle and the
- * inode.  Sends the IN_IGNORED event signifying that the inode is no longer
- * watched.  May be invoked from a caller's event handler.
- * @ih: inotify handle associated with watch
- * @watch: watch to remove
- *
- * Callers must hold both inode->inotify_mutex and ih->mutex.
- */
-void inotify_remove_watch_locked(struct inotify_handle *ih,
-				 struct inotify_watch *watch)
-{
-	remove_watch_no_event(watch, ih);
-	ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL);
-}
-EXPORT_SYMBOL_GPL(inotify_remove_watch_locked);
-
-/* Kernel API for producing events */
-
-/*
- * inotify_d_instantiate - instantiate dcache entry for inode
- */
-void inotify_d_instantiate(struct dentry *entry, struct inode *inode)
-{
-	struct dentry *parent;
-
-	if (!inode)
-		return;
-
-	spin_lock(&entry->d_lock);
-	parent = entry->d_parent;
-	if (parent->d_inode && inotify_inode_watched(parent->d_inode))
-		entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
-	spin_unlock(&entry->d_lock);
-}
-
-/*
- * inotify_d_move - dcache entry has been moved
- */
-void inotify_d_move(struct dentry *entry)
-{
-	struct dentry *parent;
-
-	parent = entry->d_parent;
-	if (inotify_inode_watched(parent->d_inode))
-		entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
-	else
-		entry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED;
-}
-
-/**
- * inotify_inode_queue_event - queue an event to all watches on this inode
- * @inode: inode event is originating from
- * @mask: event mask describing this event
- * @cookie: cookie for synchronization, or zero
- * @name: filename, if any
- * @n_inode: inode associated with name
- */
-void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
-			       const char *name, struct inode *n_inode)
-{
-	struct inotify_watch *watch, *next;
-
-	if (!inotify_inode_watched(inode))
-		return;
-
-	mutex_lock(&inode->inotify_mutex);
-	list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
-		u32 watch_mask = watch->mask;
-		if (watch_mask & mask) {
-			struct inotify_handle *ih= watch->ih;
-			mutex_lock(&ih->mutex);
-			if (watch_mask & IN_ONESHOT)
-				remove_watch_no_event(watch, ih);
-			ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
-						 name, n_inode);
-			mutex_unlock(&ih->mutex);
-		}
-	}
-	mutex_unlock(&inode->inotify_mutex);
-}
-EXPORT_SYMBOL_GPL(inotify_inode_queue_event);
-
-/**
- * inotify_dentry_parent_queue_event - queue an event to a dentry's parent
- * @dentry: the dentry in question, we queue against this dentry's parent
- * @mask: event mask describing this event
- * @cookie: cookie for synchronization, or zero
- * @name: filename, if any
- */
-void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
-				       u32 cookie, const char *name)
-{
-	struct dentry *parent;
-	struct inode *inode;
-
-	if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED))
-		return;
-
-	spin_lock(&dentry->d_lock);
-	parent = dentry->d_parent;
-	inode = parent->d_inode;
-
-	if (inotify_inode_watched(inode)) {
-		dget(parent);
-		spin_unlock(&dentry->d_lock);
-		inotify_inode_queue_event(inode, mask, cookie, name,
-					  dentry->d_inode);
-		dput(parent);
-	} else
-		spin_unlock(&dentry->d_lock);
-}
-EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event);
-
-/**
- * inotify_get_cookie - return a unique cookie for use in synchronizing events.
- */
-u32 inotify_get_cookie(void)
-{
-	return atomic_inc_return(&inotify_cookie);
-}
-EXPORT_SYMBOL_GPL(inotify_get_cookie);
-
-/**
- * inotify_unmount_inodes - an sb is unmounting.  handle any watched inodes.
- * @list: list of inodes being unmounted (sb->s_inodes)
- *
- * Called with inode_lock held, protecting the unmounting super block's list
- * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay.
- * We temporarily drop inode_lock, however, and CAN block.
- */
-void inotify_unmount_inodes(struct list_head *list)
-{
-	struct inode *inode, *next_i, *need_iput = NULL;
-
-	list_for_each_entry_safe(inode, next_i, list, i_sb_list) {
-		struct inotify_watch *watch, *next_w;
-		struct inode *need_iput_tmp;
-		struct list_head *watches;
-
-		/*
-		 * If i_count is zero, the inode cannot have any watches and
-		 * doing an __iget/iput with MS_ACTIVE clear would actually
-		 * evict all inodes with zero i_count from icache which is
-		 * unnecessarily violent and may in fact be illegal to do.
-		 */
-		if (!atomic_read(&inode->i_count))
-			continue;
-
-		/*
-		 * We cannot __iget() an inode in state I_CLEAR, I_FREEING, or
-		 * I_WILL_FREE which is fine because by that point the inode
-		 * cannot have any associated watches.
-		 */
-		if (inode->i_state & (I_CLEAR | I_FREEING | I_WILL_FREE))
-			continue;
-
-		need_iput_tmp = need_iput;
-		need_iput = NULL;
-		/* In case inotify_remove_watch_locked() drops a reference. */
-		if (inode != need_iput_tmp)
-			__iget(inode);
-		else
-			need_iput_tmp = NULL;
-		/* In case the dropping of a reference would nuke next_i. */
-		if ((&next_i->i_sb_list != list) &&
-				atomic_read(&next_i->i_count) &&
-				!(next_i->i_state & (I_CLEAR | I_FREEING |
-					I_WILL_FREE))) {
-			__iget(next_i);
-			need_iput = next_i;
-		}
-
-		/*
-		 * We can safely drop inode_lock here because we hold
-		 * references on both inode and next_i.  Also no new inodes
-		 * will be added since the umount has begun.  Finally,
-		 * iprune_mutex keeps shrink_icache_memory() away.
-		 */
-		spin_unlock(&inode_lock);
-
-		if (need_iput_tmp)
-			iput(need_iput_tmp);
-
-		/* for each watch, send IN_UNMOUNT and then remove it */
-		mutex_lock(&inode->inotify_mutex);
-		watches = &inode->inotify_watches;
-		list_for_each_entry_safe(watch, next_w, watches, i_list) {
-			struct inotify_handle *ih= watch->ih;
-			mutex_lock(&ih->mutex);
-			ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0,
-						 NULL, NULL);
-			inotify_remove_watch_locked(ih, watch);
-			mutex_unlock(&ih->mutex);
-		}
-		mutex_unlock(&inode->inotify_mutex);
-		iput(inode);		
-
-		spin_lock(&inode_lock);
-	}
-}
-EXPORT_SYMBOL_GPL(inotify_unmount_inodes);
-
-/**
- * inotify_inode_is_dead - an inode has been deleted, cleanup any watches
- * @inode: inode that is about to be removed
- */
-void inotify_inode_is_dead(struct inode *inode)
-{
-	struct inotify_watch *watch, *next;
-
-	mutex_lock(&inode->inotify_mutex);
-	list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
-		struct inotify_handle *ih = watch->ih;
-		mutex_lock(&ih->mutex);
-		inotify_remove_watch_locked(ih, watch);
-		mutex_unlock(&ih->mutex);
-	}
-	mutex_unlock(&inode->inotify_mutex);
-}
-EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
-
-/* Kernel Consumer API */
-
-/**
- * inotify_init - allocate and initialize an inotify instance
- * @ops: caller's inotify operations
- */
-struct inotify_handle *inotify_init(const struct inotify_operations *ops)
-{
-	struct inotify_handle *ih;
-
-	ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL);
-	if (unlikely(!ih))
-		return ERR_PTR(-ENOMEM);
-
-	idr_init(&ih->idr);
-	INIT_LIST_HEAD(&ih->watches);
-	mutex_init(&ih->mutex);
-	ih->last_wd = 0;
-	ih->in_ops = ops;
-	atomic_set(&ih->count, 0);
-	get_inotify_handle(ih);
-
-	return ih;
-}
-EXPORT_SYMBOL_GPL(inotify_init);
-
-/**
- * inotify_init_watch - initialize an inotify watch
- * @watch: watch to initialize
- */
-void inotify_init_watch(struct inotify_watch *watch)
-{
-	INIT_LIST_HEAD(&watch->h_list);
-	INIT_LIST_HEAD(&watch->i_list);
-	atomic_set(&watch->count, 0);
-	get_inotify_watch(watch); /* initial get */
-}
-EXPORT_SYMBOL_GPL(inotify_init_watch);
-
-/**
- * inotify_destroy - clean up and destroy an inotify instance
- * @ih: inotify handle
- */
-void inotify_destroy(struct inotify_handle *ih)
-{
-	/*
-	 * Destroy all of the watches for this handle. Unfortunately, not very
-	 * pretty.  We cannot do a simple iteration over the list, because we
-	 * do not know the inode until we iterate to the watch.  But we need to
-	 * hold inode->inotify_mutex before ih->mutex.  The following works.
-	 */
-	while (1) {
-		struct inotify_watch *watch;
-		struct list_head *watches;
-		struct inode *inode;
-
-		mutex_lock(&ih->mutex);
-		watches = &ih->watches;
-		if (list_empty(watches)) {
-			mutex_unlock(&ih->mutex);
-			break;
-		}
-		watch = list_first_entry(watches, struct inotify_watch, h_list);
-		get_inotify_watch(watch);
-		mutex_unlock(&ih->mutex);
-
-		inode = watch->inode;
-		mutex_lock(&inode->inotify_mutex);
-		mutex_lock(&ih->mutex);
-
-		/* make sure we didn't race with another list removal */
-		if (likely(idr_find(&ih->idr, watch->wd))) {
-			remove_watch_no_event(watch, ih);
-			put_inotify_watch(watch);
-		}
-
-		mutex_unlock(&ih->mutex);
-		mutex_unlock(&inode->inotify_mutex);
-		put_inotify_watch(watch);
-	}
-
-	/* free this handle: the put matching the get in inotify_init() */
-	put_inotify_handle(ih);
-}
-EXPORT_SYMBOL_GPL(inotify_destroy);
-
-/**
- * inotify_find_watch - find an existing watch for an (ih,inode) pair
- * @ih: inotify handle
- * @inode: inode to watch
- * @watchp: pointer to existing inotify_watch
- *
- * Caller must pin given inode (via nameidata).
- */
-s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
-		       struct inotify_watch **watchp)
-{
-	struct inotify_watch *old;
-	int ret = -ENOENT;
-
-	mutex_lock(&inode->inotify_mutex);
-	mutex_lock(&ih->mutex);
-
-	old = inode_find_handle(inode, ih);
-	if (unlikely(old)) {
-		get_inotify_watch(old); /* caller must put watch */
-		*watchp = old;
-		ret = old->wd;
-	}
-
-	mutex_unlock(&ih->mutex);
-	mutex_unlock(&inode->inotify_mutex);
-
-	return ret;
-}
-EXPORT_SYMBOL_GPL(inotify_find_watch);
-
-/**
- * inotify_find_update_watch - find and update the mask of an existing watch
- * @ih: inotify handle
- * @inode: inode's watch to update
- * @mask: mask of events to watch
- *
- * Caller must pin given inode (via nameidata).
- */
-s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode,
-			      u32 mask)
-{
-	struct inotify_watch *old;
-	int mask_add = 0;
-	int ret;
-
-	if (mask & IN_MASK_ADD)
-		mask_add = 1;
-
-	/* don't allow invalid bits: we don't want flags set */
-	mask &= IN_ALL_EVENTS | IN_ONESHOT;
-	if (unlikely(!mask))
-		return -EINVAL;
-
-	mutex_lock(&inode->inotify_mutex);
-	mutex_lock(&ih->mutex);
-
-	/*
-	 * Handle the case of re-adding a watch on an (inode,ih) pair that we
-	 * are already watching.  We just update the mask and return its wd.
-	 */
-	old = inode_find_handle(inode, ih);
-	if (unlikely(!old)) {
-		ret = -ENOENT;
-		goto out;
-	}
-
-	if (mask_add)
-		old->mask |= mask;
-	else
-		old->mask = mask;
-	ret = old->wd;
-out:
-	mutex_unlock(&ih->mutex);
-	mutex_unlock(&inode->inotify_mutex);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(inotify_find_update_watch);
-
-/**
- * inotify_add_watch - add a watch to an inotify instance
- * @ih: inotify handle
- * @watch: caller allocated watch structure
- * @inode: inode to watch
- * @mask: mask of events to watch
- *
- * Caller must pin given inode (via nameidata).
- * Caller must ensure it only calls inotify_add_watch() once per watch.
- * Calls inotify_handle_get_wd() so may sleep.
- */
-s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
-		      struct inode *inode, u32 mask)
-{
-	int ret = 0;
-	int newly_watched;
-
-	/* don't allow invalid bits: we don't want flags set */
-	mask &= IN_ALL_EVENTS | IN_ONESHOT;
-	if (unlikely(!mask))
-		return -EINVAL;
-	watch->mask = mask;
-
-	mutex_lock(&inode->inotify_mutex);
-	mutex_lock(&ih->mutex);
-
-	/* Initialize a new watch */
-	ret = inotify_handle_get_wd(ih, watch);
-	if (unlikely(ret))
-		goto out;
-	ret = watch->wd;
-
-	/* save a reference to handle and bump the count to make it official */
-	get_inotify_handle(ih);
-	watch->ih = ih;
-
-	/*
-	 * Save a reference to the inode and bump the ref count to make it
-	 * official.  We hold a reference to nameidata, which makes this safe.
-	 */
-	watch->inode = igrab(inode);
-
-	/* Add the watch to the handle's and the inode's list */
-	newly_watched = !inotify_inode_watched(inode);
-	list_add(&watch->h_list, &ih->watches);
-	list_add(&watch->i_list, &inode->inotify_watches);
-	/*
-	 * Set child flags _after_ adding the watch, so there is no race
-	 * windows where newly instantiated children could miss their parent's
-	 * watched flag.
-	 */
-	if (newly_watched)
-		set_dentry_child_flags(inode, 1);
-
-out:
-	mutex_unlock(&ih->mutex);
-	mutex_unlock(&inode->inotify_mutex);
-	return ret;
-}
-EXPORT_SYMBOL_GPL(inotify_add_watch);
-
-/**
- * inotify_clone_watch - put the watch next to existing one
- * @old: already installed watch
- * @new: new watch
- *
- * Caller must hold the inotify_mutex of inode we are dealing with;
- * it is expected to remove the old watch before unlocking the inode.
- */
-s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new)
-{
-	struct inotify_handle *ih = old->ih;
-	int ret = 0;
-
-	new->mask = old->mask;
-	new->ih = ih;
-
-	mutex_lock(&ih->mutex);
-
-	/* Initialize a new watch */
-	ret = inotify_handle_get_wd(ih, new);
-	if (unlikely(ret))
-		goto out;
-	ret = new->wd;
-
-	get_inotify_handle(ih);
-
-	new->inode = igrab(old->inode);
-
-	list_add(&new->h_list, &ih->watches);
-	list_add(&new->i_list, &old->inode->inotify_watches);
-out:
-	mutex_unlock(&ih->mutex);
-	return ret;
-}
-
-void inotify_evict_watch(struct inotify_watch *watch)
-{
-	get_inotify_watch(watch);
-	mutex_lock(&watch->ih->mutex);
-	inotify_remove_watch_locked(watch->ih, watch);
-	mutex_unlock(&watch->ih->mutex);
-}
-
-/**
- * inotify_rm_wd - remove a watch from an inotify instance
- * @ih: inotify handle
- * @wd: watch descriptor to remove
- *
- * Can sleep.
- */
-int inotify_rm_wd(struct inotify_handle *ih, u32 wd)
-{
-	struct inotify_watch *watch;
-	struct inode *inode;
-
-	mutex_lock(&ih->mutex);
-	watch = idr_find(&ih->idr, wd);
-	if (unlikely(!watch)) {
-		mutex_unlock(&ih->mutex);
-		return -EINVAL;
-	}
-	get_inotify_watch(watch);
-	inode = watch->inode;
-	mutex_unlock(&ih->mutex);
-
-	mutex_lock(&inode->inotify_mutex);
-	mutex_lock(&ih->mutex);
-
-	/* make sure that we did not race */
-	if (likely(idr_find(&ih->idr, wd) == watch))
-		inotify_remove_watch_locked(ih, watch);
-
-	mutex_unlock(&ih->mutex);
-	mutex_unlock(&inode->inotify_mutex);
-	put_inotify_watch(watch);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(inotify_rm_wd);
-
-/**
- * inotify_rm_watch - remove a watch from an inotify instance
- * @ih: inotify handle
- * @watch: watch to remove
- *
- * Can sleep.
- */
-int inotify_rm_watch(struct inotify_handle *ih,
-		     struct inotify_watch *watch)
-{
-	return inotify_rm_wd(ih, watch->wd);
-}
-EXPORT_SYMBOL_GPL(inotify_rm_watch);
-
-/*
- * inotify_setup - core initialization function
- */
-static int __init inotify_setup(void)
-{
-	atomic_set(&inotify_cookie, 0);
-
-	return 0;
-}
-
-module_init(inotify_setup);
diff --git a/fs/inotify_user.c b/fs/inotify_user.c
deleted file mode 100644
index d367e9b..0000000
--- a/fs/inotify_user.c
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * fs/inotify_user.c - inotify support for userspace
- *
- * Authors:
- *	John McCutchan	<ttb@tentacle.dhs.org>
- *	Robert Love	<rml@novell.com>
- *
- * Copyright (C) 2005 John McCutchan
- * Copyright 2006 Hewlett-Packard Development Company, L.P.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mount.h>
-#include <linux/namei.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/inotify.h>
-#include <linux/syscalls.h>
-#include <linux/magic.h>
-
-#include <asm/ioctls.h>
-
-static struct kmem_cache *watch_cachep __read_mostly;
-static struct kmem_cache *event_cachep __read_mostly;
-
-static struct vfsmount *inotify_mnt __read_mostly;
-
-/* these are configurable via /proc/sys/fs/inotify/ */
-static int inotify_max_user_instances __read_mostly;
-static int inotify_max_user_watches __read_mostly;
-static int inotify_max_queued_events __read_mostly;
-
-/*
- * Lock ordering:
- *
- * inotify_dev->up_mutex (ensures we don't re-add the same watch)
- * 	inode->inotify_mutex (protects inode's watch list)
- * 		inotify_handle->mutex (protects inotify_handle's watch list)
- * 			inotify_dev->ev_mutex (protects device's event queue)
- */
-
-/*
- * Lifetimes of the main data structures:
- *
- * inotify_device: Lifetime is managed by reference count, from
- * sys_inotify_init() until release.  Additional references can bump the count
- * via get_inotify_dev() and drop the count via put_inotify_dev().
- *
- * inotify_user_watch: Lifetime is from create_watch() to the receipt of an
- * IN_IGNORED event from inotify, or when using IN_ONESHOT, to receipt of the
- * first event, or to inotify_destroy().
- */
-
-/*
- * struct inotify_device - represents an inotify instance
- *
- * This structure is protected by the mutex 'mutex'.
- */
-struct inotify_device {
-	wait_queue_head_t 	wq;		/* wait queue for i/o */
-	struct mutex		ev_mutex;	/* protects event queue */
-	struct mutex		up_mutex;	/* synchronizes watch updates */
-	struct list_head 	events;		/* list of queued events */
-	atomic_t		count;		/* reference count */
-	struct user_struct	*user;		/* user who opened this dev */
-	struct inotify_handle	*ih;		/* inotify handle */
-	struct fasync_struct    *fa;            /* async notification */
-	unsigned int		queue_size;	/* size of the queue (bytes) */
-	unsigned int		event_count;	/* number of pending events */
-	unsigned int		max_events;	/* maximum number of events */
-};
-
-/*
- * struct inotify_kernel_event - An inotify event, originating from a watch and
- * queued for user-space.  A list of these is attached to each instance of the
- * device.  In read(), this list is walked and all events that can fit in the
- * buffer are returned.
- *
- * Protected by dev->ev_mutex of the device in which we are queued.
- */
-struct inotify_kernel_event {
-	struct inotify_event	event;	/* the user-space event */
-	struct list_head        list;	/* entry in inotify_device's list */
-	char			*name;	/* filename, if any */
-};
-
-/*
- * struct inotify_user_watch - our version of an inotify_watch, we add
- * a reference to the associated inotify_device.
- */
-struct inotify_user_watch {
-	struct inotify_device	*dev;	/* associated device */
-	struct inotify_watch	wdata;	/* inotify watch data */
-};
-
-#ifdef CONFIG_SYSCTL
-
-#include <linux/sysctl.h>
-
-static int zero;
-
-ctl_table inotify_table[] = {
-	{
-		.ctl_name	= INOTIFY_MAX_USER_INSTANCES,
-		.procname	= "max_user_instances",
-		.data		= &inotify_max_user_instances,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.strategy	= &sysctl_intvec,
-		.extra1		= &zero,
-	},
-	{
-		.ctl_name	= INOTIFY_MAX_USER_WATCHES,
-		.procname	= "max_user_watches",
-		.data		= &inotify_max_user_watches,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.strategy	= &sysctl_intvec,
-		.extra1		= &zero,
-	},
-	{
-		.ctl_name	= INOTIFY_MAX_QUEUED_EVENTS,
-		.procname	= "max_queued_events",
-		.data		= &inotify_max_queued_events,
-		.maxlen		= sizeof(int),
-		.mode		= 0644,
-		.proc_handler	= &proc_dointvec_minmax,
-		.strategy	= &sysctl_intvec,
-		.extra1		= &zero
-	},
-	{ .ctl_name = 0 }
-};
-#endif /* CONFIG_SYSCTL */
-
-static inline void get_inotify_dev(struct inotify_device *dev)
-{
-	atomic_inc(&dev->count);
-}
-
-static inline void put_inotify_dev(struct inotify_device *dev)
-{
-	if (atomic_dec_and_test(&dev->count)) {
-		atomic_dec(&dev->user->inotify_devs);
-		free_uid(dev->user);
-		kfree(dev);
-	}
-}
-
-/*
- * free_inotify_user_watch - cleans up the watch and its references
- */
-static void free_inotify_user_watch(struct inotify_watch *w)
-{
-	struct inotify_user_watch *watch;
-	struct inotify_device *dev;
-
-	watch = container_of(w, struct inotify_user_watch, wdata);
-	dev = watch->dev;
-
-	atomic_dec(&dev->user->inotify_watches);
-	put_inotify_dev(dev);
-	kmem_cache_free(watch_cachep, watch);
-}
-
-/*
- * kernel_event - create a new kernel event with the given parameters
- *
- * This function can sleep.
- */
-static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
-						  const char *name)
-{
-	struct inotify_kernel_event *kevent;
-
-	kevent = kmem_cache_alloc(event_cachep, GFP_NOFS);
-	if (unlikely(!kevent))
-		return NULL;
-
-	/* we hand this out to user-space, so zero it just in case */
-	memset(&kevent->event, 0, sizeof(struct inotify_event));
-
-	kevent->event.wd = wd;
-	kevent->event.mask = mask;
-	kevent->event.cookie = cookie;
-
-	INIT_LIST_HEAD(&kevent->list);
-
-	if (name) {
-		size_t len, rem, event_size = sizeof(struct inotify_event);
-
-		/*
-		 * We need to pad the filename so as to properly align an
-		 * array of inotify_event structures.  Because the structure is
-		 * small and the common case is a small filename, we just round
-		 * up to the next multiple of the structure's sizeof.  This is
-		 * simple and safe for all architectures.
-		 */
-		len = strlen(name) + 1;
-		rem = event_size - len;
-		if (len > event_size) {
-			rem = event_size - (len % event_size);
-			if (len % event_size == 0)
-				rem = 0;
-		}
-
-		kevent->name = kmalloc(len + rem, GFP_KERNEL);
-		if (unlikely(!kevent->name)) {
-			kmem_cache_free(event_cachep, kevent);
-			return NULL;
-		}
-		memcpy(kevent->name, name, len);
-		if (rem)
-			memset(kevent->name + len, 0, rem);
-		kevent->event.len = len + rem;
-	} else {
-		kevent->event.len = 0;
-		kevent->name = NULL;
-	}
-
-	return kevent;
-}
-
-/*
- * inotify_dev_get_event - return the next event in the given dev's queue
- *
- * Caller must hold dev->ev_mutex.
- */
-static inline struct inotify_kernel_event *
-inotify_dev_get_event(struct inotify_device *dev)
-{
-	return list_entry(dev->events.next, struct inotify_kernel_event, list);
-}
-
-/*
- * inotify_dev_get_last_event - return the last event in the given dev's queue
- *
- * Caller must hold dev->ev_mutex.
- */
-static inline struct inotify_kernel_event *
-inotify_dev_get_last_event(struct inotify_device *dev)
-{
-	if (list_empty(&dev->events))
-		return NULL;
-	return list_entry(dev->events.prev, struct inotify_kernel_event, list);
-}
-
-/*
- * inotify_dev_queue_event - event handler registered with core inotify, adds
- * a new event to the given device
- *
- * Can sleep (calls kernel_event()).
- */
-static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
-				    u32 cookie, const char *name,
-				    struct inode *ignored)
-{
-	struct inotify_user_watch *watch;
-	struct inotify_device *dev;
-	struct inotify_kernel_event *kevent, *last;
-
-	watch = container_of(w, struct inotify_user_watch, wdata);
-	dev = watch->dev;
-
-	mutex_lock(&dev->ev_mutex);
-
-	/* we can safely put the watch as we don't reference it while
-	 * generating the event
-	 */
-	if (mask & IN_IGNORED || w->mask & IN_ONESHOT)
-		put_inotify_watch(w); /* final put */
-
-	/* coalescing: drop this event if it is a dupe of the previous */
-	last = inotify_dev_get_last_event(dev);
-	if (last && last->event.mask == mask && last->event.wd == wd &&
-			last->event.cookie == cookie) {
-		const char *lastname = last->name;
-
-		if (!name && !lastname)
-			goto out;
-		if (name && lastname && !strcmp(lastname, name))
-			goto out;
-	}
-
-	/* the queue overflowed and we already sent the Q_OVERFLOW event */
-	if (unlikely(dev->event_count > dev->max_events))
-		goto out;
-
-	/* if the queue overflows, we need to notify user space */
-	if (unlikely(dev->event_count == dev->max_events))
-		kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
-	else
-		kevent = kernel_event(wd, mask, cookie, name);
-
-	if (unlikely(!kevent))
-		goto out;
-
-	/* queue the event and wake up anyone waiting */
-	dev->event_count++;
-	dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
-	list_add_tail(&kevent->list, &dev->events);
-	wake_up_interruptible(&dev->wq);
-	kill_fasync(&dev->fa, SIGIO, POLL_IN);
-
-out:
-	mutex_unlock(&dev->ev_mutex);
-}
-
-/*
- * remove_kevent - cleans up the given kevent
- *
- * Caller must hold dev->ev_mutex.
- */
-static void remove_kevent(struct inotify_device *dev,
-			  struct inotify_kernel_event *kevent)
-{
-	list_del(&kevent->list);
-
-	dev->event_count--;
-	dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
-}
-
-/*
- * free_kevent - frees the given kevent.
- */
-static void free_kevent(struct inotify_kernel_event *kevent)
-{
-	kfree(kevent->name);
-	kmem_cache_free(event_cachep, kevent);
-}
-
-/*
- * inotify_dev_event_dequeue - destroy an event on the given device
- *
- * Caller must hold dev->ev_mutex.
- */
-static void inotify_dev_event_dequeue(struct inotify_device *dev)
-{
-	if (!list_empty(&dev->events)) {
-		struct inotify_kernel_event *kevent;
-		kevent = inotify_dev_get_event(dev);
-		remove_kevent(dev, kevent);
-		free_kevent(kevent);
-	}
-}
-
-/*
- * find_inode - resolve a user-given path to a specific inode
- */
-static int find_inode(const char __user *dirname, struct path *path,
-		      unsigned flags)
-{
-	int error;
-
-	error = user_path_at(AT_FDCWD, dirname, flags, path);
-	if (error)
-		return error;
-	/* you can only watch an inode if you have read permissions on it */
-	error = inode_permission(path->dentry->d_inode, MAY_READ);
-	if (error)
-		path_put(path);
-	return error;
-}
-
-/*
- * create_watch - creates a watch on the given device.
- *
- * Callers must hold dev->up_mutex.
- */
-static int create_watch(struct inotify_device *dev, struct inode *inode,
-			u32 mask)
-{
-	struct inotify_user_watch *watch;
-	int ret;
-
-	if (atomic_read(&dev->user->inotify_watches) >=
-			inotify_max_user_watches)
-		return -ENOSPC;
-
-	watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
-	if (unlikely(!watch))
-		return -ENOMEM;
-
-	/* save a reference to device and bump the count to make it official */
-	get_inotify_dev(dev);
-	watch->dev = dev;
-
-	atomic_inc(&dev->user->inotify_watches);
-
-	inotify_init_watch(&watch->wdata);
-	ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
-	if (ret < 0)
-		free_inotify_user_watch(&watch->wdata);
-
-	return ret;
-}
-
-/* Device Interface */
-
-static unsigned int inotify_poll(struct file *file, poll_table *wait)
-{
-	struct inotify_device *dev = file->private_data;
-	int ret = 0;
-
-	poll_wait(file, &dev->wq, wait);
-	mutex_lock(&dev->ev_mutex);
-	if (!list_empty(&dev->events))
-		ret = POLLIN | POLLRDNORM;
-	mutex_unlock(&dev->ev_mutex);
-
-	return ret;
-}
-
-static ssize_t inotify_read(struct file *file, char __user *buf,
-			    size_t count, loff_t *pos)
-{
-	size_t event_size = sizeof (struct inotify_event);
-	struct inotify_device *dev;
-	char __user *start;
-	int ret;
-	DEFINE_WAIT(wait);
-
-	start = buf;
-	dev = file->private_data;
-
-	while (1) {
-
-		prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
-
-		mutex_lock(&dev->ev_mutex);
-		if (!list_empty(&dev->events)) {
-			ret = 0;
-			break;
-		}
-		mutex_unlock(&dev->ev_mutex);
-
-		if (file->f_flags & O_NONBLOCK) {
-			ret = -EAGAIN;
-			break;
-		}
-
-		if (signal_pending(current)) {
-			ret = -EINTR;
-			break;
-		}
-
-		schedule();
-	}
-
-	finish_wait(&dev->wq, &wait);
-	if (ret)
-		return ret;
-
-	while (1) {
-		struct inotify_kernel_event *kevent;
-
-		ret = buf - start;
-		if (list_empty(&dev->events))
-			break;
-
-		kevent = inotify_dev_get_event(dev);
-		if (event_size + kevent->event.len > count) {
-			if (ret == 0 && count > 0) {
-				/*
-				 * could not get a single event because we
-				 * didn't have enough buffer space.
-				 */
-				ret = -EINVAL;
-			}
-			break;
-		}
-		remove_kevent(dev, kevent);
-
-		/*
-		 * Must perform the copy_to_user outside the mutex in order
-		 * to avoid a lock order reversal with mmap_sem.
-		 */
-		mutex_unlock(&dev->ev_mutex);
-
-		if (copy_to_user(buf, &kevent->event, event_size)) {
-			ret = -EFAULT;
-			break;
-		}
-		buf += event_size;
-		count -= event_size;
-
-		if (kevent->name) {
-			if (copy_to_user(buf, kevent->name, kevent->event.len)){
-				ret = -EFAULT;
-				break;
-			}
-			buf += kevent->event.len;
-			count -= kevent->event.len;
-		}
-
-		free_kevent(kevent);
-
-		mutex_lock(&dev->ev_mutex);
-	}
-	mutex_unlock(&dev->ev_mutex);
-
-	return ret;
-}
-
-static int inotify_fasync(int fd, struct file *file, int on)
-{
-	struct inotify_device *dev = file->private_data;
-
-	return fasync_helper(fd, file, on, &dev->fa) >= 0 ? 0 : -EIO;
-}
-
-static int inotify_release(struct inode *ignored, struct file *file)
-{
-	struct inotify_device *dev = file->private_data;
-
-	inotify_destroy(dev->ih);
-
-	/* destroy all of the events on this device */
-	mutex_lock(&dev->ev_mutex);
-	while (!list_empty(&dev->events))
-		inotify_dev_event_dequeue(dev);
-	mutex_unlock(&dev->ev_mutex);
-
-	/* free this device: the put matching the get in inotify_init() */
-	put_inotify_dev(dev);
-
-	return 0;
-}
-
-static long inotify_ioctl(struct file *file, unsigned int cmd,
-			  unsigned long arg)
-{
-	struct inotify_device *dev;
-	void __user *p;
-	int ret = -ENOTTY;
-
-	dev = file->private_data;
-	p = (void __user *) arg;
-
-	switch (cmd) {
-	case FIONREAD:
-		ret = put_user(dev->queue_size, (int __user *) p);
-		break;
-	}
-
-	return ret;
-}
-
-static const struct file_operations inotify_fops = {
-	.poll           = inotify_poll,
-	.read           = inotify_read,
-	.fasync         = inotify_fasync,
-	.release        = inotify_release,
-	.unlocked_ioctl = inotify_ioctl,
-	.compat_ioctl	= inotify_ioctl,
-};
-
-static const struct inotify_operations inotify_user_ops = {
-	.handle_event	= inotify_dev_queue_event,
-	.destroy_watch	= free_inotify_user_watch,
-};
-
-asmlinkage long sys_inotify_init1(int flags)
-{
-	struct inotify_device *dev;
-	struct inotify_handle *ih;
-	struct user_struct *user;
-	struct file *filp;
-	int fd, ret;
-
-	/* Check the IN_* constants for consistency.  */
-	BUILD_BUG_ON(IN_CLOEXEC != O_CLOEXEC);
-	BUILD_BUG_ON(IN_NONBLOCK != O_NONBLOCK);
-
-	if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
-		return -EINVAL;
-
-	fd = get_unused_fd_flags(flags & O_CLOEXEC);
-	if (fd < 0)
-		return fd;
-
-	filp = get_empty_filp();
-	if (!filp) {
-		ret = -ENFILE;
-		goto out_put_fd;
-	}
-
-	user = get_uid(current->user);
-	if (unlikely(atomic_read(&user->inotify_devs) >=
-			inotify_max_user_instances)) {
-		ret = -EMFILE;
-		goto out_free_uid;
-	}
-
-	dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
-	if (unlikely(!dev)) {
-		ret = -ENOMEM;
-		goto out_free_uid;
-	}
-
-	ih = inotify_init(&inotify_user_ops);
-	if (IS_ERR(ih)) {
-		ret = PTR_ERR(ih);
-		goto out_free_dev;
-	}
-	dev->ih = ih;
-	dev->fa = NULL;
-
-	filp->f_op = &inotify_fops;
-	filp->f_path.mnt = mntget(inotify_mnt);
-	filp->f_path.dentry = dget(inotify_mnt->mnt_root);
-	filp->f_mapping = filp->f_path.dentry->d_inode->i_mapping;
-	filp->f_mode = FMODE_READ;
-	filp->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-	filp->private_data = dev;
-
-	INIT_LIST_HEAD(&dev->events);
-	init_waitqueue_head(&dev->wq);
-	mutex_init(&dev->ev_mutex);
-	mutex_init(&dev->up_mutex);
-	dev->event_count = 0;
-	dev->queue_size = 0;
-	dev->max_events = inotify_max_queued_events;
-	dev->user = user;
-	atomic_set(&dev->count, 0);
-
-	get_inotify_dev(dev);
-	atomic_inc(&user->inotify_devs);
-	fd_install(fd, filp);
-
-	return fd;
-out_free_dev:
-	kfree(dev);
-out_free_uid:
-	free_uid(user);
-	put_filp(filp);
-out_put_fd:
-	put_unused_fd(fd);
-	return ret;
-}
-
-asmlinkage long sys_inotify_init(void)
-{
-	return sys_inotify_init1(0);
-}
-
-asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
-{
-	struct inode *inode;
-	struct inotify_device *dev;
-	struct path path;
-	struct file *filp;
-	int ret, fput_needed;
-	unsigned flags = 0;
-
-	filp = fget_light(fd, &fput_needed);
-	if (unlikely(!filp))
-		return -EBADF;
-
-	/* verify that this is indeed an inotify instance */
-	if (unlikely(filp->f_op != &inotify_fops)) {
-		ret = -EINVAL;
-		goto fput_and_out;
-	}
-
-	if (!(mask & IN_DONT_FOLLOW))
-		flags |= LOOKUP_FOLLOW;
-	if (mask & IN_ONLYDIR)
-		flags |= LOOKUP_DIRECTORY;
-
-	ret = find_inode(pathname, &path, flags);
-	if (unlikely(ret))
-		goto fput_and_out;
-
-	/* inode held in place by reference to path; dev by fget on fd */
-	inode = path.dentry->d_inode;
-	dev = filp->private_data;
-
-	mutex_lock(&dev->up_mutex);
-	ret = inotify_find_update_watch(dev->ih, inode, mask);
-	if (ret == -ENOENT)
-		ret = create_watch(dev, inode, mask);
-	mutex_unlock(&dev->up_mutex);
-
-	path_put(&path);
-fput_and_out:
-	fput_light(filp, fput_needed);
-	return ret;
-}
-
-asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
-{
-	struct file *filp;
-	struct inotify_device *dev;
-	int ret, fput_needed;
-
-	filp = fget_light(fd, &fput_needed);
-	if (unlikely(!filp))
-		return -EBADF;
-
-	/* verify that this is indeed an inotify instance */
-	if (unlikely(filp->f_op != &inotify_fops)) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	dev = filp->private_data;
-
-	/* we free our watch data when we get IN_IGNORED */
-	ret = inotify_rm_wd(dev->ih, wd);
-
-out:
-	fput_light(filp, fput_needed);
-	return ret;
-}
-
-static int
-inotify_get_sb(struct file_system_type *fs_type, int flags,
-	       const char *dev_name, void *data, struct vfsmount *mnt)
-{
-	return get_sb_pseudo(fs_type, "inotify", NULL,
-			INOTIFYFS_SUPER_MAGIC, mnt);
-}
-
-static struct file_system_type inotify_fs_type = {
-    .name           = "inotifyfs",
-    .get_sb         = inotify_get_sb,
-    .kill_sb        = kill_anon_super,
-};
-
-/*
- * inotify_user_setup - Our initialization function.  Note that we cannnot return
- * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
- * must result in panic().
- */
-static int __init inotify_user_setup(void)
-{
-	int ret;
-
-	ret = register_filesystem(&inotify_fs_type);
-	if (unlikely(ret))
-		panic("inotify: register_filesystem returned %d!\n", ret);
-
-	inotify_mnt = kern_mount(&inotify_fs_type);
-	if (IS_ERR(inotify_mnt))
-		panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
-
-	inotify_max_queued_events = 16384;
-	inotify_max_user_instances = 128;
-	inotify_max_user_watches = 8192;
-
-	watch_cachep = kmem_cache_create("inotify_watch_cache",
-					 sizeof(struct inotify_user_watch),
-					 0, SLAB_PANIC, NULL);
-	event_cachep = kmem_cache_create("inotify_event_cache",
-					 sizeof(struct inotify_kernel_event),
-					 0, SLAB_PANIC, NULL);
-
-	return 0;
-}
-
-module_init(inotify_user_setup);
diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig
new file mode 100644
index 0000000..23415de
--- /dev/null
+++ b/fs/notify/Kconfig
@@ -0,0 +1,38 @@
+config DNOTIFY
+	bool "Dnotify support"
+	default y
+	help
+	  Dnotify is a directory-based per-fd file change notification system
+	  that uses signals to communicate events to user-space.  There exist
+	  superior alternatives, but some applications may still rely on
+	  dnotify.
+
+	  If unsure, say Y.
+
+config INOTIFY
+	bool "Inotify file change notification support"
+	default y
+	---help---
+	  Say Y here to enable inotify support.  Inotify is a file change
+	  notification system and a replacement for dnotify.  Inotify fixes
+	  numerous shortcomings in dnotify and introduces several new features
+	  including multiple file events, one-shot support, and unmount
+	  notification.
+
+	  For more information, see <file:Documentation/filesystems/inotify.txt>
+
+	  If unsure, say Y.
+
+config INOTIFY_USER
+	bool "Inotify support for userspace"
+	depends on INOTIFY
+	default y
+	---help---
+	  Say Y here to enable inotify support for userspace, including the
+	  associated system calls.  Inotify allows monitoring of both files and
+	  directories via a single open fd.  Events are read from the file
+	  descriptor, which is also select()- and poll()-able.
+
+	  For more information, see <file:Documentation/filesystems/inotify.txt>
+
+	  If unsure, say Y.
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
new file mode 100644
index 0000000..882ecf9
--- /dev/null
+++ b/fs/notify/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_INOTIFY)		+= inotify.o
+obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
+
+obj-$(CONFIG_DNOTIFY)		+= dnotify.o
diff --git a/fs/notify/dnotify.c b/fs/notify/dnotify.c
new file mode 100644
index 0000000..676073b
--- /dev/null
+++ b/fs/notify/dnotify.c
@@ -0,0 +1,194 @@
+/*
+ * Directory notifications for Linux.
+ *
+ * Copyright (C) 2000,2001,2002 Stephen Rothwell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/dnotify.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/fdtable.h>
+
+int dir_notify_enable __read_mostly = 1;
+
+static struct kmem_cache *dn_cache __read_mostly;
+
+static void redo_inode_mask(struct inode *inode)
+{
+	unsigned long new_mask;
+	struct dnotify_struct *dn;
+
+	new_mask = 0;
+	for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
+		new_mask |= dn->dn_mask & ~DN_MULTISHOT;
+	inode->i_dnotify_mask = new_mask;
+}
+
+void dnotify_flush(struct file *filp, fl_owner_t id)
+{
+	struct dnotify_struct *dn;
+	struct dnotify_struct **prev;
+	struct inode *inode;
+
+	inode = filp->f_path.dentry->d_inode;
+	if (!S_ISDIR(inode->i_mode))
+		return;
+	spin_lock(&inode->i_lock);
+	prev = &inode->i_dnotify;
+	while ((dn = *prev) != NULL) {
+		if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
+			*prev = dn->dn_next;
+			redo_inode_mask(inode);
+			kmem_cache_free(dn_cache, dn);
+			break;
+		}
+		prev = &dn->dn_next;
+	}
+	spin_unlock(&inode->i_lock);
+}
+
+int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
+{
+	struct dnotify_struct *dn;
+	struct dnotify_struct *odn;
+	struct dnotify_struct **prev;
+	struct inode *inode;
+	fl_owner_t id = current->files;
+	struct file *f;
+	int error = 0;
+
+	if ((arg & ~DN_MULTISHOT) == 0) {
+		dnotify_flush(filp, id);
+		return 0;
+	}
+	if (!dir_notify_enable)
+		return -EINVAL;
+	inode = filp->f_path.dentry->d_inode;
+	if (!S_ISDIR(inode->i_mode))
+		return -ENOTDIR;
+	dn = kmem_cache_alloc(dn_cache, GFP_KERNEL);
+	if (dn == NULL)
+		return -ENOMEM;
+	spin_lock(&inode->i_lock);
+	prev = &inode->i_dnotify;
+	while ((odn = *prev) != NULL) {
+		if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
+			odn->dn_fd = fd;
+			odn->dn_mask |= arg;
+			inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+			goto out_free;
+		}
+		prev = &odn->dn_next;
+	}
+
+	rcu_read_lock();
+	f = fcheck(fd);
+	rcu_read_unlock();
+	/* we'd lost the race with close(), sod off silently */
+	/* note that inode->i_lock prevents reordering problems
+	 * between accesses to descriptor table and ->i_dnotify */
+	if (f != filp)
+		goto out_free;
+
+	error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
+	if (error)
+		goto out_free;
+
+	dn->dn_mask = arg;
+	dn->dn_fd = fd;
+	dn->dn_filp = filp;
+	dn->dn_owner = id;
+	inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+	dn->dn_next = inode->i_dnotify;
+	inode->i_dnotify = dn;
+	spin_unlock(&inode->i_lock);
+
+	if (filp->f_op && filp->f_op->dir_notify)
+		return filp->f_op->dir_notify(filp, arg);
+	return 0;
+
+out_free:
+	spin_unlock(&inode->i_lock);
+	kmem_cache_free(dn_cache, dn);
+	return error;
+}
+
+void __inode_dir_notify(struct inode *inode, unsigned long event)
+{
+	struct dnotify_struct *	dn;
+	struct dnotify_struct **prev;
+	struct fown_struct *	fown;
+	int			changed = 0;
+
+	spin_lock(&inode->i_lock);
+	prev = &inode->i_dnotify;
+	while ((dn = *prev) != NULL) {
+		if ((dn->dn_mask & event) == 0) {
+			prev = &dn->dn_next;
+			continue;
+		}
+		fown = &dn->dn_filp->f_owner;
+		send_sigio(fown, dn->dn_fd, POLL_MSG);
+		if (dn->dn_mask & DN_MULTISHOT)
+			prev = &dn->dn_next;
+		else {
+			*prev = dn->dn_next;
+			changed = 1;
+			kmem_cache_free(dn_cache, dn);
+		}
+	}
+	if (changed)
+		redo_inode_mask(inode);
+	spin_unlock(&inode->i_lock);
+}
+
+EXPORT_SYMBOL(__inode_dir_notify);
+
+/*
+ * This is hopelessly wrong, but unfixable without API changes.  At
+ * least it doesn't oops the kernel...
+ *
+ * To safely access ->d_parent we need to keep d_move away from it.  Use the
+ * dentry's d_lock for this.
+ */
+void dnotify_parent(struct dentry *dentry, unsigned long event)
+{
+	struct dentry *parent;
+
+	if (!dir_notify_enable)
+		return;
+
+	spin_lock(&dentry->d_lock);
+	parent = dentry->d_parent;
+	if (parent->d_inode->i_dnotify_mask & event) {
+		dget(parent);
+		spin_unlock(&dentry->d_lock);
+		__inode_dir_notify(parent->d_inode, event);
+		dput(parent);
+	} else {
+		spin_unlock(&dentry->d_lock);
+	}
+}
+EXPORT_SYMBOL_GPL(dnotify_parent);
+
+static int __init dnotify_init(void)
+{
+	dn_cache = kmem_cache_create("dnotify_cache",
+		sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL);
+	return 0;
+}
+
+module_init(dnotify_init)
diff --git a/fs/notify/inotify.c b/fs/notify/inotify.c
new file mode 100644
index 0000000..690e725
--- /dev/null
+++ b/fs/notify/inotify.c
@@ -0,0 +1,773 @@
+/*
+ * fs/inotify.c - inode-based file event notifications
+ *
+ * Authors:
+ *	John McCutchan	<ttb@tentacle.dhs.org>
+ *	Robert Love	<rml@novell.com>
+ *
+ * Kernel API added by: Amy Griffis <amy.griffis@hp.com>
+ *
+ * Copyright (C) 2005 John McCutchan
+ * Copyright 2006 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/writeback.h>
+#include <linux/inotify.h>
+
+static atomic_t inotify_cookie;
+
+/*
+ * Lock ordering:
+ *
+ * dentry->d_lock (used to keep d_move() away from dentry->d_parent)
+ * iprune_mutex (synchronize shrink_icache_memory())
+ * 	inode_lock (protects the super_block->s_inodes list)
+ * 	inode->inotify_mutex (protects inode->inotify_watches and watches->i_list)
+ * 		inotify_handle->mutex (protects inotify_handle and watches->h_list)
+ *
+ * The inode->inotify_mutex and inotify_handle->mutex and held during execution
+ * of a caller's event handler.  Thus, the caller must not hold any locks
+ * taken in their event handler while calling any of the published inotify
+ * interfaces.
+ */
+
+/*
+ * Lifetimes of the three main data structures--inotify_handle, inode, and
+ * inotify_watch--are managed by reference count.
+ *
+ * inotify_handle: Lifetime is from inotify_init() to inotify_destroy().
+ * Additional references can bump the count via get_inotify_handle() and drop
+ * the count via put_inotify_handle().
+ *
+ * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch()
+ * to remove_watch_no_event().  Additional references can bump the count via
+ * get_inotify_watch() and drop the count via put_inotify_watch().  The caller
+ * is reponsible for the final put after receiving IN_IGNORED, or when using
+ * IN_ONESHOT after receiving the first event.  Inotify does the final put if
+ * inotify_destroy() is called.
+ *
+ * inode: Pinned so long as the inode is associated with a watch, from
+ * inotify_add_watch() to the final put_inotify_watch().
+ */
+
+/*
+ * struct inotify_handle - represents an inotify instance
+ *
+ * This structure is protected by the mutex 'mutex'.
+ */
+struct inotify_handle {
+	struct idr		idr;		/* idr mapping wd -> watch */
+	struct mutex		mutex;		/* protects this bad boy */
+	struct list_head	watches;	/* list of watches */
+	atomic_t		count;		/* reference count */
+	u32			last_wd;	/* the last wd allocated */
+	const struct inotify_operations *in_ops; /* inotify caller operations */
+};
+
+static inline void get_inotify_handle(struct inotify_handle *ih)
+{
+	atomic_inc(&ih->count);
+}
+
+static inline void put_inotify_handle(struct inotify_handle *ih)
+{
+	if (atomic_dec_and_test(&ih->count)) {
+		idr_destroy(&ih->idr);
+		kfree(ih);
+	}
+}
+
+/**
+ * get_inotify_watch - grab a reference to an inotify_watch
+ * @watch: watch to grab
+ */
+void get_inotify_watch(struct inotify_watch *watch)
+{
+	atomic_inc(&watch->count);
+}
+EXPORT_SYMBOL_GPL(get_inotify_watch);
+
+/**
+ * put_inotify_watch - decrements the ref count on a given watch.  cleans up
+ * watch references if the count reaches zero.  inotify_watch is freed by
+ * inotify callers via the destroy_watch() op.
+ * @watch: watch to release
+ */
+void put_inotify_watch(struct inotify_watch *watch)
+{
+	if (atomic_dec_and_test(&watch->count)) {
+		struct inotify_handle *ih = watch->ih;
+
+		iput(watch->inode);
+		ih->in_ops->destroy_watch(watch);
+		put_inotify_handle(ih);
+	}
+}
+EXPORT_SYMBOL_GPL(put_inotify_watch);
+
+/*
+ * inotify_handle_get_wd - returns the next WD for use by the given handle
+ *
+ * Callers must hold ih->mutex.  This function can sleep.
+ */
+static int inotify_handle_get_wd(struct inotify_handle *ih,
+				 struct inotify_watch *watch)
+{
+	int ret;
+
+	do {
+		if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL)))
+			return -ENOSPC;
+		ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd);
+	} while (ret == -EAGAIN);
+
+	if (likely(!ret))
+		ih->last_wd = watch->wd;
+
+	return ret;
+}
+
+/*
+ * inotify_inode_watched - returns nonzero if there are watches on this inode
+ * and zero otherwise.  We call this lockless, we do not care if we race.
+ */
+static inline int inotify_inode_watched(struct inode *inode)
+{
+	return !list_empty(&inode->inotify_watches);
+}
+
+/*
+ * Get child dentry flag into synch with parent inode.
+ * Flag should always be clear for negative dentrys.
+ */
+static void set_dentry_child_flags(struct inode *inode, int watched)
+{
+	struct dentry *alias;
+
+	spin_lock(&dcache_lock);
+	list_for_each_entry(alias, &inode->i_dentry, d_alias) {
+		struct dentry *child;
+
+		list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) {
+			if (!child->d_inode)
+				continue;
+
+			spin_lock(&child->d_lock);
+			if (watched)
+				child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
+			else
+				child->d_flags &=~DCACHE_INOTIFY_PARENT_WATCHED;
+			spin_unlock(&child->d_lock);
+		}
+	}
+	spin_unlock(&dcache_lock);
+}
+
+/*
+ * inotify_find_handle - find the watch associated with the given inode and
+ * handle
+ *
+ * Callers must hold inode->inotify_mutex.
+ */
+static struct inotify_watch *inode_find_handle(struct inode *inode,
+					       struct inotify_handle *ih)
+{
+	struct inotify_watch *watch;
+
+	list_for_each_entry(watch, &inode->inotify_watches, i_list) {
+		if (watch->ih == ih)
+			return watch;
+	}
+
+	return NULL;
+}
+
+/*
+ * remove_watch_no_event - remove watch without the IN_IGNORED event.
+ *
+ * Callers must hold both inode->inotify_mutex and ih->mutex.
+ */
+static void remove_watch_no_event(struct inotify_watch *watch,
+				  struct inotify_handle *ih)
+{
+	list_del(&watch->i_list);
+	list_del(&watch->h_list);
+
+	if (!inotify_inode_watched(watch->inode))
+		set_dentry_child_flags(watch->inode, 0);
+
+	idr_remove(&ih->idr, watch->wd);
+}
+
+/**
+ * inotify_remove_watch_locked - Remove a watch from both the handle and the
+ * inode.  Sends the IN_IGNORED event signifying that the inode is no longer
+ * watched.  May be invoked from a caller's event handler.
+ * @ih: inotify handle associated with watch
+ * @watch: watch to remove
+ *
+ * Callers must hold both inode->inotify_mutex and ih->mutex.
+ */
+void inotify_remove_watch_locked(struct inotify_handle *ih,
+				 struct inotify_watch *watch)
+{
+	remove_watch_no_event(watch, ih);
+	ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(inotify_remove_watch_locked);
+
+/* Kernel API for producing events */
+
+/*
+ * inotify_d_instantiate - instantiate dcache entry for inode
+ */
+void inotify_d_instantiate(struct dentry *entry, struct inode *inode)
+{
+	struct dentry *parent;
+
+	if (!inode)
+		return;
+
+	spin_lock(&entry->d_lock);
+	parent = entry->d_parent;
+	if (parent->d_inode && inotify_inode_watched(parent->d_inode))
+		entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
+	spin_unlock(&entry->d_lock);
+}
+
+/*
+ * inotify_d_move - dcache entry has been moved
+ */
+void inotify_d_move(struct dentry *entry)
+{
+	struct dentry *parent;
+
+	parent = entry->d_parent;
+	if (inotify_inode_watched(parent->d_inode))
+		entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
+	else
+		entry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED;
+}
+
+/**
+ * inotify_inode_queue_event - queue an event to all watches on this inode
+ * @inode: inode event is originating from
+ * @mask: event mask describing this event
+ * @cookie: cookie for synchronization, or zero
+ * @name: filename, if any
+ * @n_inode: inode associated with name
+ */
+void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
+			       const char *name, struct inode *n_inode)
+{
+	struct inotify_watch *watch, *next;
+
+	if (!inotify_inode_watched(inode))
+		return;
+
+	mutex_lock(&inode->inotify_mutex);
+	list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
+		u32 watch_mask = watch->mask;
+		if (watch_mask & mask) {
+			struct inotify_handle *ih= watch->ih;
+			mutex_lock(&ih->mutex);
+			if (watch_mask & IN_ONESHOT)
+				remove_watch_no_event(watch, ih);
+			ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
+						 name, n_inode);
+			mutex_unlock(&ih->mutex);
+		}
+	}
+	mutex_unlock(&inode->inotify_mutex);
+}
+EXPORT_SYMBOL_GPL(inotify_inode_queue_event);
+
+/**
+ * inotify_dentry_parent_queue_event - queue an event to a dentry's parent
+ * @dentry: the dentry in question, we queue against this dentry's parent
+ * @mask: event mask describing this event
+ * @cookie: cookie for synchronization, or zero
+ * @name: filename, if any
+ */
+void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
+				       u32 cookie, const char *name)
+{
+	struct dentry *parent;
+	struct inode *inode;
+
+	if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED))
+		return;
+
+	spin_lock(&dentry->d_lock);
+	parent = dentry->d_parent;
+	inode = parent->d_inode;
+
+	if (inotify_inode_watched(inode)) {
+		dget(parent);
+		spin_unlock(&dentry->d_lock);
+		inotify_inode_queue_event(inode, mask, cookie, name,
+					  dentry->d_inode);
+		dput(parent);
+	} else
+		spin_unlock(&dentry->d_lock);
+}
+EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event);
+
+/**
+ * inotify_get_cookie - return a unique cookie for use in synchronizing events.
+ */
+u32 inotify_get_cookie(void)
+{
+	return atomic_inc_return(&inotify_cookie);
+}
+EXPORT_SYMBOL_GPL(inotify_get_cookie);
+
+/**
+ * inotify_unmount_inodes - an sb is unmounting.  handle any watched inodes.
+ * @list: list of inodes being unmounted (sb->s_inodes)
+ *
+ * Called with inode_lock held, protecting the unmounting super block's list
+ * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay.
+ * We temporarily drop inode_lock, however, and CAN block.
+ */
+void inotify_unmount_inodes(struct list_head *list)
+{
+	struct inode *inode, *next_i, *need_iput = NULL;
+
+	list_for_each_entry_safe(inode, next_i, list, i_sb_list) {
+		struct inotify_watch *watch, *next_w;
+		struct inode *need_iput_tmp;
+		struct list_head *watches;
+
+		/*
+		 * If i_count is zero, the inode cannot have any watches and
+		 * doing an __iget/iput with MS_ACTIVE clear would actually
+		 * evict all inodes with zero i_count from icache which is
+		 * unnecessarily violent and may in fact be illegal to do.
+		 */
+		if (!atomic_read(&inode->i_count))
+			continue;
+
+		/*
+		 * We cannot __iget() an inode in state I_CLEAR, I_FREEING, or
+		 * I_WILL_FREE which is fine because by that point the inode
+		 * cannot have any associated watches.
+		 */
+		if (inode->i_state & (I_CLEAR | I_FREEING | I_WILL_FREE))
+			continue;
+
+		need_iput_tmp = need_iput;
+		need_iput = NULL;
+		/* In case inotify_remove_watch_locked() drops a reference. */
+		if (inode != need_iput_tmp)
+			__iget(inode);
+		else
+			need_iput_tmp = NULL;
+		/* In case the dropping of a reference would nuke next_i. */
+		if ((&next_i->i_sb_list != list) &&
+				atomic_read(&next_i->i_count) &&
+				!(next_i->i_state & (I_CLEAR | I_FREEING |
+					I_WILL_FREE))) {
+			__iget(next_i);
+			need_iput = next_i;
+		}
+
+		/*
+		 * We can safely drop inode_lock here because we hold
+		 * references on both inode and next_i.  Also no new inodes
+		 * will be added since the umount has begun.  Finally,
+		 * iprune_mutex keeps shrink_icache_memory() away.
+		 */
+		spin_unlock(&inode_lock);
+
+		if (need_iput_tmp)
+			iput(need_iput_tmp);
+
+		/* for each watch, send IN_UNMOUNT and then remove it */
+		mutex_lock(&inode->inotify_mutex);
+		watches = &inode->inotify_watches;
+		list_for_each_entry_safe(watch, next_w, watches, i_list) {
+			struct inotify_handle *ih= watch->ih;
+			mutex_lock(&ih->mutex);
+			ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0,
+						 NULL, NULL);
+			inotify_remove_watch_locked(ih, watch);
+			mutex_unlock(&ih->mutex);
+		}
+		mutex_unlock(&inode->inotify_mutex);
+		iput(inode);		
+
+		spin_lock(&inode_lock);
+	}
+}
+EXPORT_SYMBOL_GPL(inotify_unmount_inodes);
+
+/**
+ * inotify_inode_is_dead - an inode has been deleted, cleanup any watches
+ * @inode: inode that is about to be removed
+ */
+void inotify_inode_is_dead(struct inode *inode)
+{
+	struct inotify_watch *watch, *next;
+
+	mutex_lock(&inode->inotify_mutex);
+	list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
+		struct inotify_handle *ih = watch->ih;
+		mutex_lock(&ih->mutex);
+		inotify_remove_watch_locked(ih, watch);
+		mutex_unlock(&ih->mutex);
+	}
+	mutex_unlock(&inode->inotify_mutex);
+}
+EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
+
+/* Kernel Consumer API */
+
+/**
+ * inotify_init - allocate and initialize an inotify instance
+ * @ops: caller's inotify operations
+ */
+struct inotify_handle *inotify_init(const struct inotify_operations *ops)
+{
+	struct inotify_handle *ih;
+
+	ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL);
+	if (unlikely(!ih))
+		return ERR_PTR(-ENOMEM);
+
+	idr_init(&ih->idr);
+	INIT_LIST_HEAD(&ih->watches);
+	mutex_init(&ih->mutex);
+	ih->last_wd = 0;
+	ih->in_ops = ops;
+	atomic_set(&ih->count, 0);
+	get_inotify_handle(ih);
+
+	return ih;
+}
+EXPORT_SYMBOL_GPL(inotify_init);
+
+/**
+ * inotify_init_watch - initialize an inotify watch
+ * @watch: watch to initialize
+ */
+void inotify_init_watch(struct inotify_watch *watch)
+{
+	INIT_LIST_HEAD(&watch->h_list);
+	INIT_LIST_HEAD(&watch->i_list);
+	atomic_set(&watch->count, 0);
+	get_inotify_watch(watch); /* initial get */
+}
+EXPORT_SYMBOL_GPL(inotify_init_watch);
+
+/**
+ * inotify_destroy - clean up and destroy an inotify instance
+ * @ih: inotify handle
+ */
+void inotify_destroy(struct inotify_handle *ih)
+{
+	/*
+	 * Destroy all of the watches for this handle. Unfortunately, not very
+	 * pretty.  We cannot do a simple iteration over the list, because we
+	 * do not know the inode until we iterate to the watch.  But we need to
+	 * hold inode->inotify_mutex before ih->mutex.  The following works.
+	 */
+	while (1) {
+		struct inotify_watch *watch;
+		struct list_head *watches;
+		struct inode *inode;
+
+		mutex_lock(&ih->mutex);
+		watches = &ih->watches;
+		if (list_empty(watches)) {
+			mutex_unlock(&ih->mutex);
+			break;
+		}
+		watch = list_first_entry(watches, struct inotify_watch, h_list);
+		get_inotify_watch(watch);
+		mutex_unlock(&ih->mutex);
+
+		inode = watch->inode;
+		mutex_lock(&inode->inotify_mutex);
+		mutex_lock(&ih->mutex);
+
+		/* make sure we didn't race with another list removal */
+		if (likely(idr_find(&ih->idr, watch->wd))) {
+			remove_watch_no_event(watch, ih);
+			put_inotify_watch(watch);
+		}
+
+		mutex_unlock(&ih->mutex);
+		mutex_unlock(&inode->inotify_mutex);
+		put_inotify_watch(watch);
+	}
+
+	/* free this handle: the put matching the get in inotify_init() */
+	put_inotify_handle(ih);
+}
+EXPORT_SYMBOL_GPL(inotify_destroy);
+
+/**
+ * inotify_find_watch - find an existing watch for an (ih,inode) pair
+ * @ih: inotify handle
+ * @inode: inode to watch
+ * @watchp: pointer to existing inotify_watch
+ *
+ * Caller must pin given inode (via nameidata).
+ */
+s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode,
+		       struct inotify_watch **watchp)
+{
+	struct inotify_watch *old;
+	int ret = -ENOENT;
+
+	mutex_lock(&inode->inotify_mutex);
+	mutex_lock(&ih->mutex);
+
+	old = inode_find_handle(inode, ih);
+	if (unlikely(old)) {
+		get_inotify_watch(old); /* caller must put watch */
+		*watchp = old;
+		ret = old->wd;
+	}
+
+	mutex_unlock(&ih->mutex);
+	mutex_unlock(&inode->inotify_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(inotify_find_watch);
+
+/**
+ * inotify_find_update_watch - find and update the mask of an existing watch
+ * @ih: inotify handle
+ * @inode: inode's watch to update
+ * @mask: mask of events to watch
+ *
+ * Caller must pin given inode (via nameidata).
+ */
+s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode,
+			      u32 mask)
+{
+	struct inotify_watch *old;
+	int mask_add = 0;
+	int ret;
+
+	if (mask & IN_MASK_ADD)
+		mask_add = 1;
+
+	/* don't allow invalid bits: we don't want flags set */
+	mask &= IN_ALL_EVENTS | IN_ONESHOT;
+	if (unlikely(!mask))
+		return -EINVAL;
+
+	mutex_lock(&inode->inotify_mutex);
+	mutex_lock(&ih->mutex);
+
+	/*
+	 * Handle the case of re-adding a watch on an (inode,ih) pair that we
+	 * are already watching.  We just update the mask and return its wd.
+	 */
+	old = inode_find_handle(inode, ih);
+	if (unlikely(!old)) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	if (mask_add)
+		old->mask |= mask;
+	else
+		old->mask = mask;
+	ret = old->wd;
+out:
+	mutex_unlock(&ih->mutex);
+	mutex_unlock(&inode->inotify_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(inotify_find_update_watch);
+
+/**
+ * inotify_add_watch - add a watch to an inotify instance
+ * @ih: inotify handle
+ * @watch: caller allocated watch structure
+ * @inode: inode to watch
+ * @mask: mask of events to watch
+ *
+ * Caller must pin given inode (via nameidata).
+ * Caller must ensure it only calls inotify_add_watch() once per watch.
+ * Calls inotify_handle_get_wd() so may sleep.
+ */
+s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch,
+		      struct inode *inode, u32 mask)
+{
+	int ret = 0;
+	int newly_watched;
+
+	/* don't allow invalid bits: we don't want flags set */
+	mask &= IN_ALL_EVENTS | IN_ONESHOT;
+	if (unlikely(!mask))
+		return -EINVAL;
+	watch->mask = mask;
+
+	mutex_lock(&inode->inotify_mutex);
+	mutex_lock(&ih->mutex);
+
+	/* Initialize a new watch */
+	ret = inotify_handle_get_wd(ih, watch);
+	if (unlikely(ret))
+		goto out;
+	ret = watch->wd;
+
+	/* save a reference to handle and bump the count to make it official */
+	get_inotify_handle(ih);
+	watch->ih = ih;
+
+	/*
+	 * Save a reference to the inode and bump the ref count to make it
+	 * official.  We hold a reference to nameidata, which makes this safe.
+	 */
+	watch->inode = igrab(inode);
+
+	/* Add the watch to the handle's and the inode's list */
+	newly_watched = !inotify_inode_watched(inode);
+	list_add(&watch->h_list, &ih->watches);
+	list_add(&watch->i_list, &inode->inotify_watches);
+	/*
+	 * Set child flags _after_ adding the watch, so there is no race
+	 * windows where newly instantiated children could miss their parent's
+	 * watched flag.
+	 */
+	if (newly_watched)
+		set_dentry_child_flags(inode, 1);
+
+out:
+	mutex_unlock(&ih->mutex);
+	mutex_unlock(&inode->inotify_mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(inotify_add_watch);
+
+/**
+ * inotify_clone_watch - put the watch next to existing one
+ * @old: already installed watch
+ * @new: new watch
+ *
+ * Caller must hold the inotify_mutex of inode we are dealing with;
+ * it is expected to remove the old watch before unlocking the inode.
+ */
+s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new)
+{
+	struct inotify_handle *ih = old->ih;
+	int ret = 0;
+
+	new->mask = old->mask;
+	new->ih = ih;
+
+	mutex_lock(&ih->mutex);
+
+	/* Initialize a new watch */
+	ret = inotify_handle_get_wd(ih, new);
+	if (unlikely(ret))
+		goto out;
+	ret = new->wd;
+
+	get_inotify_handle(ih);
+
+	new->inode = igrab(old->inode);
+
+	list_add(&new->h_list, &ih->watches);
+	list_add(&new->i_list, &old->inode->inotify_watches);
+out:
+	mutex_unlock(&ih->mutex);
+	return ret;
+}
+
+void inotify_evict_watch(struct inotify_watch *watch)
+{
+	get_inotify_watch(watch);
+	mutex_lock(&watch->ih->mutex);
+	inotify_remove_watch_locked(watch->ih, watch);
+	mutex_unlock(&watch->ih->mutex);
+}
+
+/**
+ * inotify_rm_wd - remove a watch from an inotify instance
+ * @ih: inotify handle
+ * @wd: watch descriptor to remove
+ *
+ * Can sleep.
+ */
+int inotify_rm_wd(struct inotify_handle *ih, u32 wd)
+{
+	struct inotify_watch *watch;
+	struct inode *inode;
+
+	mutex_lock(&ih->mutex);
+	watch = idr_find(&ih->idr, wd);
+	if (unlikely(!watch)) {
+		mutex_unlock(&ih->mutex);
+		return -EINVAL;
+	}
+	get_inotify_watch(watch);
+	inode = watch->inode;
+	mutex_unlock(&ih->mutex);
+
+	mutex_lock(&inode->inotify_mutex);
+	mutex_lock(&ih->mutex);
+
+	/* make sure that we did not race */
+	if (likely(idr_find(&ih->idr, wd) == watch))
+		inotify_remove_watch_locked(ih, watch);
+
+	mutex_unlock(&ih->mutex);
+	mutex_unlock(&inode->inotify_mutex);
+	put_inotify_watch(watch);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(inotify_rm_wd);
+
+/**
+ * inotify_rm_watch - remove a watch from an inotify instance
+ * @ih: inotify handle
+ * @watch: watch to remove
+ *
+ * Can sleep.
+ */
+int inotify_rm_watch(struct inotify_handle *ih,
+		     struct inotify_watch *watch)
+{
+	return inotify_rm_wd(ih, watch->wd);
+}
+EXPORT_SYMBOL_GPL(inotify_rm_watch);
+
+/*
+ * inotify_setup - core initialization function
+ */
+static int __init inotify_setup(void)
+{
+	atomic_set(&inotify_cookie, 0);
+
+	return 0;
+}
+
+module_init(inotify_setup);
diff --git a/fs/notify/inotify_user.c b/fs/notify/inotify_user.c
new file mode 100644
index 0000000..d367e9b
--- /dev/null
+++ b/fs/notify/inotify_user.c
@@ -0,0 +1,778 @@
+/*
+ * fs/inotify_user.c - inotify support for userspace
+ *
+ * Authors:
+ *	John McCutchan	<ttb@tentacle.dhs.org>
+ *	Robert Love	<rml@novell.com>
+ *
+ * Copyright (C) 2005 John McCutchan
+ * Copyright 2006 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/inotify.h>
+#include <linux/syscalls.h>
+#include <linux/magic.h>
+
+#include <asm/ioctls.h>
+
+static struct kmem_cache *watch_cachep __read_mostly;
+static struct kmem_cache *event_cachep __read_mostly;
+
+static struct vfsmount *inotify_mnt __read_mostly;
+
+/* these are configurable via /proc/sys/fs/inotify/ */
+static int inotify_max_user_instances __read_mostly;
+static int inotify_max_user_watches __read_mostly;
+static int inotify_max_queued_events __read_mostly;
+
+/*
+ * Lock ordering:
+ *
+ * inotify_dev->up_mutex (ensures we don't re-add the same watch)
+ * 	inode->inotify_mutex (protects inode's watch list)
+ * 		inotify_handle->mutex (protects inotify_handle's watch list)
+ * 			inotify_dev->ev_mutex (protects device's event queue)
+ */
+
+/*
+ * Lifetimes of the main data structures:
+ *
+ * inotify_device: Lifetime is managed by reference count, from
+ * sys_inotify_init() until release.  Additional references can bump the count
+ * via get_inotify_dev() and drop the count via put_inotify_dev().
+ *
+ * inotify_user_watch: Lifetime is from create_watch() to the receipt of an
+ * IN_IGNORED event from inotify, or when using IN_ONESHOT, to receipt of the
+ * first event, or to inotify_destroy().
+ */
+
+/*
+ * struct inotify_device - represents an inotify instance
+ *
+ * This structure is protected by the mutex 'mutex'.
+ */
+struct inotify_device {
+	wait_queue_head_t 	wq;		/* wait queue for i/o */
+	struct mutex		ev_mutex;	/* protects event queue */
+	struct mutex		up_mutex;	/* synchronizes watch updates */
+	struct list_head 	events;		/* list of queued events */
+	atomic_t		count;		/* reference count */
+	struct user_struct	*user;		/* user who opened this dev */
+	struct inotify_handle	*ih;		/* inotify handle */
+	struct fasync_struct    *fa;            /* async notification */
+	unsigned int		queue_size;	/* size of the queue (bytes) */
+	unsigned int		event_count;	/* number of pending events */
+	unsigned int		max_events;	/* maximum number of events */
+};
+
+/*
+ * struct inotify_kernel_event - An inotify event, originating from a watch and
+ * queued for user-space.  A list of these is attached to each instance of the
+ * device.  In read(), this list is walked and all events that can fit in the
+ * buffer are returned.
+ *
+ * Protected by dev->ev_mutex of the device in which we are queued.
+ */
+struct inotify_kernel_event {
+	struct inotify_event	event;	/* the user-space event */
+	struct list_head        list;	/* entry in inotify_device's list */
+	char			*name;	/* filename, if any */
+};
+
+/*
+ * struct inotify_user_watch - our version of an inotify_watch, we add
+ * a reference to the associated inotify_device.
+ */
+struct inotify_user_watch {
+	struct inotify_device	*dev;	/* associated device */
+	struct inotify_watch	wdata;	/* inotify watch data */
+};
+
+#ifdef CONFIG_SYSCTL
+
+#include <linux/sysctl.h>
+
+static int zero;
+
+ctl_table inotify_table[] = {
+	{
+		.ctl_name	= INOTIFY_MAX_USER_INSTANCES,
+		.procname	= "max_user_instances",
+		.data		= &inotify_max_user_instances,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero,
+	},
+	{
+		.ctl_name	= INOTIFY_MAX_USER_WATCHES,
+		.procname	= "max_user_watches",
+		.data		= &inotify_max_user_watches,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero,
+	},
+	{
+		.ctl_name	= INOTIFY_MAX_QUEUED_EVENTS,
+		.procname	= "max_queued_events",
+		.data		= &inotify_max_queued_events,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &zero
+	},
+	{ .ctl_name = 0 }
+};
+#endif /* CONFIG_SYSCTL */
+
+static inline void get_inotify_dev(struct inotify_device *dev)
+{
+	atomic_inc(&dev->count);
+}
+
+static inline void put_inotify_dev(struct inotify_device *dev)
+{
+	if (atomic_dec_and_test(&dev->count)) {
+		atomic_dec(&dev->user->inotify_devs);
+		free_uid(dev->user);
+		kfree(dev);
+	}
+}
+
+/*
+ * free_inotify_user_watch - cleans up the watch and its references
+ */
+static void free_inotify_user_watch(struct inotify_watch *w)
+{
+	struct inotify_user_watch *watch;
+	struct inotify_device *dev;
+
+	watch = container_of(w, struct inotify_user_watch, wdata);
+	dev = watch->dev;
+
+	atomic_dec(&dev->user->inotify_watches);
+	put_inotify_dev(dev);
+	kmem_cache_free(watch_cachep, watch);
+}
+
+/*
+ * kernel_event - create a new kernel event with the given parameters
+ *
+ * This function can sleep.
+ */
+static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
+						  const char *name)
+{
+	struct inotify_kernel_event *kevent;
+
+	kevent = kmem_cache_alloc(event_cachep, GFP_NOFS);
+	if (unlikely(!kevent))
+		return NULL;
+
+	/* we hand this out to user-space, so zero it just in case */
+	memset(&kevent->event, 0, sizeof(struct inotify_event));
+
+	kevent->event.wd = wd;
+	kevent->event.mask = mask;
+	kevent->event.cookie = cookie;
+
+	INIT_LIST_HEAD(&kevent->list);
+
+	if (name) {
+		size_t len, rem, event_size = sizeof(struct inotify_event);
+
+		/*
+		 * We need to pad the filename so as to properly align an
+		 * array of inotify_event structures.  Because the structure is
+		 * small and the common case is a small filename, we just round
+		 * up to the next multiple of the structure's sizeof.  This is
+		 * simple and safe for all architectures.
+		 */
+		len = strlen(name) + 1;
+		rem = event_size - len;
+		if (len > event_size) {
+			rem = event_size - (len % event_size);
+			if (len % event_size == 0)
+				rem = 0;
+		}
+
+		kevent->name = kmalloc(len + rem, GFP_KERNEL);
+		if (unlikely(!kevent->name)) {
+			kmem_cache_free(event_cachep, kevent);
+			return NULL;
+		}
+		memcpy(kevent->name, name, len);
+		if (rem)
+			memset(kevent->name + len, 0, rem);
+		kevent->event.len = len + rem;
+	} else {
+		kevent->event.len = 0;
+		kevent->name = NULL;
+	}
+
+	return kevent;
+}
+
+/*
+ * inotify_dev_get_event - return the next event in the given dev's queue
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static inline struct inotify_kernel_event *
+inotify_dev_get_event(struct inotify_device *dev)
+{
+	return list_entry(dev->events.next, struct inotify_kernel_event, list);
+}
+
+/*
+ * inotify_dev_get_last_event - return the last event in the given dev's queue
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static inline struct inotify_kernel_event *
+inotify_dev_get_last_event(struct inotify_device *dev)
+{
+	if (list_empty(&dev->events))
+		return NULL;
+	return list_entry(dev->events.prev, struct inotify_kernel_event, list);
+}
+
+/*
+ * inotify_dev_queue_event - event handler registered with core inotify, adds
+ * a new event to the given device
+ *
+ * Can sleep (calls kernel_event()).
+ */
+static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
+				    u32 cookie, const char *name,
+				    struct inode *ignored)
+{
+	struct inotify_user_watch *watch;
+	struct inotify_device *dev;
+	struct inotify_kernel_event *kevent, *last;
+
+	watch = container_of(w, struct inotify_user_watch, wdata);
+	dev = watch->dev;
+
+	mutex_lock(&dev->ev_mutex);
+
+	/* we can safely put the watch as we don't reference it while
+	 * generating the event
+	 */
+	if (mask & IN_IGNORED || w->mask & IN_ONESHOT)
+		put_inotify_watch(w); /* final put */
+
+	/* coalescing: drop this event if it is a dupe of the previous */
+	last = inotify_dev_get_last_event(dev);
+	if (last && last->event.mask == mask && last->event.wd == wd &&
+			last->event.cookie == cookie) {
+		const char *lastname = last->name;
+
+		if (!name && !lastname)
+			goto out;
+		if (name && lastname && !strcmp(lastname, name))
+			goto out;
+	}
+
+	/* the queue overflowed and we already sent the Q_OVERFLOW event */
+	if (unlikely(dev->event_count > dev->max_events))
+		goto out;
+
+	/* if the queue overflows, we need to notify user space */
+	if (unlikely(dev->event_count == dev->max_events))
+		kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
+	else
+		kevent = kernel_event(wd, mask, cookie, name);
+
+	if (unlikely(!kevent))
+		goto out;
+
+	/* queue the event and wake up anyone waiting */
+	dev->event_count++;
+	dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
+	list_add_tail(&kevent->list, &dev->events);
+	wake_up_interruptible(&dev->wq);
+	kill_fasync(&dev->fa, SIGIO, POLL_IN);
+
+out:
+	mutex_unlock(&dev->ev_mutex);
+}
+
+/*
+ * remove_kevent - cleans up the given kevent
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static void remove_kevent(struct inotify_device *dev,
+			  struct inotify_kernel_event *kevent)
+{
+	list_del(&kevent->list);
+
+	dev->event_count--;
+	dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
+}
+
+/*
+ * free_kevent - frees the given kevent.
+ */
+static void free_kevent(struct inotify_kernel_event *kevent)
+{
+	kfree(kevent->name);
+	kmem_cache_free(event_cachep, kevent);
+}
+
+/*
+ * inotify_dev_event_dequeue - destroy an event on the given device
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static void inotify_dev_event_dequeue(struct inotify_device *dev)
+{
+	if (!list_empty(&dev->events)) {
+		struct inotify_kernel_event *kevent;
+		kevent = inotify_dev_get_event(dev);
+		remove_kevent(dev, kevent);
+		free_kevent(kevent);
+	}
+}
+
+/*
+ * find_inode - resolve a user-given path to a specific inode
+ */
+static int find_inode(const char __user *dirname, struct path *path,
+		      unsigned flags)
+{
+	int error;
+
+	error = user_path_at(AT_FDCWD, dirname, flags, path);
+	if (error)
+		return error;
+	/* you can only watch an inode if you have read permissions on it */
+	error = inode_permission(path->dentry->d_inode, MAY_READ);
+	if (error)
+		path_put(path);
+	return error;
+}
+
+/*
+ * create_watch - creates a watch on the given device.
+ *
+ * Callers must hold dev->up_mutex.
+ */
+static int create_watch(struct inotify_device *dev, struct inode *inode,
+			u32 mask)
+{
+	struct inotify_user_watch *watch;
+	int ret;
+
+	if (atomic_read(&dev->user->inotify_watches) >=
+			inotify_max_user_watches)
+		return -ENOSPC;
+
+	watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL);
+	if (unlikely(!watch))
+		return -ENOMEM;
+
+	/* save a reference to device and bump the count to make it official */
+	get_inotify_dev(dev);
+	watch->dev = dev;
+
+	atomic_inc(&dev->user->inotify_watches);
+
+	inotify_init_watch(&watch->wdata);
+	ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
+	if (ret < 0)
+		free_inotify_user_watch(&watch->wdata);
+
+	return ret;
+}
+
+/* Device Interface */
+
+static unsigned int inotify_poll(struct file *file, poll_table *wait)
+{
+	struct inotify_device *dev = file->private_data;
+	int ret = 0;
+
+	poll_wait(file, &dev->wq, wait);
+	mutex_lock(&dev->ev_mutex);
+	if (!list_empty(&dev->events))
+		ret = POLLIN | POLLRDNORM;
+	mutex_unlock(&dev->ev_mutex);
+
+	return ret;
+}
+
+static ssize_t inotify_read(struct file *file, char __user *buf,
+			    size_t count, loff_t *pos)
+{
+	size_t event_size = sizeof (struct inotify_event);
+	struct inotify_device *dev;
+	char __user *start;
+	int ret;
+	DEFINE_WAIT(wait);
+
+	start = buf;
+	dev = file->private_data;
+
+	while (1) {
+
+		prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
+
+		mutex_lock(&dev->ev_mutex);
+		if (!list_empty(&dev->events)) {
+			ret = 0;
+			break;
+		}
+		mutex_unlock(&dev->ev_mutex);
+
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+
+		schedule();
+	}
+
+	finish_wait(&dev->wq, &wait);
+	if (ret)
+		return ret;
+
+	while (1) {
+		struct inotify_kernel_event *kevent;
+
+		ret = buf - start;
+		if (list_empty(&dev->events))
+			break;
+
+		kevent = inotify_dev_get_event(dev);
+		if (event_size + kevent->event.len > count) {
+			if (ret == 0 && count > 0) {
+				/*
+				 * could not get a single event because we
+				 * didn't have enough buffer space.
+				 */
+				ret = -EINVAL;
+			}
+			break;
+		}
+		remove_kevent(dev, kevent);
+
+		/*
+		 * Must perform the copy_to_user outside the mutex in order
+		 * to avoid a lock order reversal with mmap_sem.
+		 */
+		mutex_unlock(&dev->ev_mutex);
+
+		if (copy_to_user(buf, &kevent->event, event_size)) {
+			ret = -EFAULT;
+			break;
+		}
+		buf += event_size;
+		count -= event_size;
+
+		if (kevent->name) {
+			if (copy_to_user(buf, kevent->name, kevent->event.len)){
+				ret = -EFAULT;
+				break;
+			}
+			buf += kevent->event.len;
+			count -= kevent->event.len;
+		}
+
+		free_kevent(kevent);
+
+		mutex_lock(&dev->ev_mutex);
+	}
+	mutex_unlock(&dev->ev_mutex);
+
+	return ret;
+}
+
+static int inotify_fasync(int fd, struct file *file, int on)
+{
+	struct inotify_device *dev = file->private_data;
+
+	return fasync_helper(fd, file, on, &dev->fa) >= 0 ? 0 : -EIO;
+}
+
+static int inotify_release(struct inode *ignored, struct file *file)
+{
+	struct inotify_device *dev = file->private_data;
+
+	inotify_destroy(dev->ih);
+
+	/* destroy all of the events on this device */
+	mutex_lock(&dev->ev_mutex);
+	while (!list_empty(&dev->events))
+		inotify_dev_event_dequeue(dev);
+	mutex_unlock(&dev->ev_mutex);
+
+	/* free this device: the put matching the get in inotify_init() */
+	put_inotify_dev(dev);
+
+	return 0;
+}
+
+static long inotify_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct inotify_device *dev;
+	void __user *p;
+	int ret = -ENOTTY;
+
+	dev = file->private_data;
+	p = (void __user *) arg;
+
+	switch (cmd) {
+	case FIONREAD:
+		ret = put_user(dev->queue_size, (int __user *) p);
+		break;
+	}
+
+	return ret;
+}
+
+static const struct file_operations inotify_fops = {
+	.poll           = inotify_poll,
+	.read           = inotify_read,
+	.fasync         = inotify_fasync,
+	.release        = inotify_release,
+	.unlocked_ioctl = inotify_ioctl,
+	.compat_ioctl	= inotify_ioctl,
+};
+
+static const struct inotify_operations inotify_user_ops = {
+	.handle_event	= inotify_dev_queue_event,
+	.destroy_watch	= free_inotify_user_watch,
+};
+
+asmlinkage long sys_inotify_init1(int flags)
+{
+	struct inotify_device *dev;
+	struct inotify_handle *ih;
+	struct user_struct *user;
+	struct file *filp;
+	int fd, ret;
+
+	/* Check the IN_* constants for consistency.  */
+	BUILD_BUG_ON(IN_CLOEXEC != O_CLOEXEC);
+	BUILD_BUG_ON(IN_NONBLOCK != O_NONBLOCK);
+
+	if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
+		return -EINVAL;
+
+	fd = get_unused_fd_flags(flags & O_CLOEXEC);
+	if (fd < 0)
+		return fd;
+
+	filp = get_empty_filp();
+	if (!filp) {
+		ret = -ENFILE;
+		goto out_put_fd;
+	}
+
+	user = get_uid(current->user);
+	if (unlikely(atomic_read(&user->inotify_devs) >=
+			inotify_max_user_instances)) {
+		ret = -EMFILE;
+		goto out_free_uid;
+	}
+
+	dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
+	if (unlikely(!dev)) {
+		ret = -ENOMEM;
+		goto out_free_uid;
+	}
+
+	ih = inotify_init(&inotify_user_ops);
+	if (IS_ERR(ih)) {
+		ret = PTR_ERR(ih);
+		goto out_free_dev;
+	}
+	dev->ih = ih;
+	dev->fa = NULL;
+
+	filp->f_op = &inotify_fops;
+	filp->f_path.mnt = mntget(inotify_mnt);
+	filp->f_path.dentry = dget(inotify_mnt->mnt_root);
+	filp->f_mapping = filp->f_path.dentry->d_inode->i_mapping;
+	filp->f_mode = FMODE_READ;
+	filp->f_flags = O_RDONLY | (flags & O_NONBLOCK);
+	filp->private_data = dev;
+
+	INIT_LIST_HEAD(&dev->events);
+	init_waitqueue_head(&dev->wq);
+	mutex_init(&dev->ev_mutex);
+	mutex_init(&dev->up_mutex);
+	dev->event_count = 0;
+	dev->queue_size = 0;
+	dev->max_events = inotify_max_queued_events;
+	dev->user = user;
+	atomic_set(&dev->count, 0);
+
+	get_inotify_dev(dev);
+	atomic_inc(&user->inotify_devs);
+	fd_install(fd, filp);
+
+	return fd;
+out_free_dev:
+	kfree(dev);
+out_free_uid:
+	free_uid(user);
+	put_filp(filp);
+out_put_fd:
+	put_unused_fd(fd);
+	return ret;
+}
+
+asmlinkage long sys_inotify_init(void)
+{
+	return sys_inotify_init1(0);
+}
+
+asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
+{
+	struct inode *inode;
+	struct inotify_device *dev;
+	struct path path;
+	struct file *filp;
+	int ret, fput_needed;
+	unsigned flags = 0;
+
+	filp = fget_light(fd, &fput_needed);
+	if (unlikely(!filp))
+		return -EBADF;
+
+	/* verify that this is indeed an inotify instance */
+	if (unlikely(filp->f_op != &inotify_fops)) {
+		ret = -EINVAL;
+		goto fput_and_out;
+	}
+
+	if (!(mask & IN_DONT_FOLLOW))
+		flags |= LOOKUP_FOLLOW;
+	if (mask & IN_ONLYDIR)
+		flags |= LOOKUP_DIRECTORY;
+
+	ret = find_inode(pathname, &path, flags);
+	if (unlikely(ret))
+		goto fput_and_out;
+
+	/* inode held in place by reference to path; dev by fget on fd */
+	inode = path.dentry->d_inode;
+	dev = filp->private_data;
+
+	mutex_lock(&dev->up_mutex);
+	ret = inotify_find_update_watch(dev->ih, inode, mask);
+	if (ret == -ENOENT)
+		ret = create_watch(dev, inode, mask);
+	mutex_unlock(&dev->up_mutex);
+
+	path_put(&path);
+fput_and_out:
+	fput_light(filp, fput_needed);
+	return ret;
+}
+
+asmlinkage long sys_inotify_rm_watch(int fd, u32 wd)
+{
+	struct file *filp;
+	struct inotify_device *dev;
+	int ret, fput_needed;
+
+	filp = fget_light(fd, &fput_needed);
+	if (unlikely(!filp))
+		return -EBADF;
+
+	/* verify that this is indeed an inotify instance */
+	if (unlikely(filp->f_op != &inotify_fops)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dev = filp->private_data;
+
+	/* we free our watch data when we get IN_IGNORED */
+	ret = inotify_rm_wd(dev->ih, wd);
+
+out:
+	fput_light(filp, fput_needed);
+	return ret;
+}
+
+static int
+inotify_get_sb(struct file_system_type *fs_type, int flags,
+	       const char *dev_name, void *data, struct vfsmount *mnt)
+{
+	return get_sb_pseudo(fs_type, "inotify", NULL,
+			INOTIFYFS_SUPER_MAGIC, mnt);
+}
+
+static struct file_system_type inotify_fs_type = {
+    .name           = "inotifyfs",
+    .get_sb         = inotify_get_sb,
+    .kill_sb        = kill_anon_super,
+};
+
+/*
+ * inotify_user_setup - Our initialization function.  Note that we cannnot return
+ * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
+ * must result in panic().
+ */
+static int __init inotify_user_setup(void)
+{
+	int ret;
+
+	ret = register_filesystem(&inotify_fs_type);
+	if (unlikely(ret))
+		panic("inotify: register_filesystem returned %d!\n", ret);
+
+	inotify_mnt = kern_mount(&inotify_fs_type);
+	if (IS_ERR(inotify_mnt))
+		panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt));
+
+	inotify_max_queued_events = 16384;
+	inotify_max_user_instances = 128;
+	inotify_max_user_watches = 8192;
+
+	watch_cachep = kmem_cache_create("inotify_watch_cache",
+					 sizeof(struct inotify_user_watch),
+					 0, SLAB_PANIC, NULL);
+	event_cachep = kmem_cache_create("inotify_event_cache",
+					 sizeof(struct inotify_kernel_event),
+					 0, SLAB_PANIC, NULL);
+
+	return 0;
+}
+
+module_init(inotify_user_setup);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 02/21] fsnotify: pass a file instead of an inode to open, read, and write
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 01/21] filesystem notification: create fs/notify to contain all fs notification Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 03/21] fanotify: fscking all notify, system wide file access notification Eric Paris
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

fanotify, the upcoming notification system actually needs a f_path so it can
do opens in the context of listeners, and it needs a file so it can get f_flags
from the original process.  Close already was passing a file.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/compat.c              |    5 ++---
 fs/nfsd/vfs.c            |    4 ++--
 fs/open.c                |    2 +-
 fs/read_write.c          |    8 ++++----
 include/linux/fsnotify.h |    9 ++++++---
 5 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/fs/compat.c b/fs/compat.c
index e5f49f5..4a7788f 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1158,11 +1158,10 @@ out:
 	if (iov != iovstack)
 		kfree(iov);
 	if ((ret + (type == READ)) > 0) {
-		struct dentry *dentry = file->f_path.dentry;
 		if (type == READ)
-			fsnotify_access(dentry);
+			fsnotify_access(file);
 		else
-			fsnotify_modify(dentry);
+			fsnotify_modify(file);
 	}
 	return ret;
 }
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 4433c8f..f4bc1e6 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -940,7 +940,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
 		nfsdstats.io_read += host_err;
 		*count = host_err;
 		err = 0;
-		fsnotify_access(file->f_path.dentry);
+		fsnotify_access(file);
 	} else 
 		err = nfserrno(host_err);
 out:
@@ -1007,7 +1007,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
 	set_fs(oldfs);
 	if (host_err >= 0) {
 		nfsdstats.io_write += cnt;
-		fsnotify_modify(file->f_path.dentry);
+		fsnotify_modify(file);
 	}
 
 	/* clear setuid/setgid flag after write */
diff --git a/fs/open.c b/fs/open.c
index 83cdb9d..9d69dd9 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1020,7 +1020,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
 				put_unused_fd(fd);
 				fd = PTR_ERR(f);
 			} else {
-				fsnotify_open(f->f_path.dentry);
+				fsnotify_open(f);
 				fd_install(fd, f);
 			}
 		}
diff --git a/fs/read_write.c b/fs/read_write.c
index 969a6d9..7eb2949 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -280,7 +280,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
 		else
 			ret = do_sync_read(file, buf, count, pos);
 		if (ret > 0) {
-			fsnotify_access(file->f_path.dentry);
+			fsnotify_access(file);
 			add_rchar(current, ret);
 		}
 		inc_syscr(current);
@@ -335,7 +335,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
 		else
 			ret = do_sync_write(file, buf, count, pos);
 		if (ret > 0) {
-			fsnotify_modify(file->f_path.dentry);
+			fsnotify_modify(file);
 			add_wchar(current, ret);
 		}
 		inc_syscw(current);
@@ -626,9 +626,9 @@ out:
 		kfree(iov);
 	if ((ret + (type == READ)) > 0) {
 		if (type == READ)
-			fsnotify_access(file->f_path.dentry);
+			fsnotify_access(file);
 		else
-			fsnotify_modify(file->f_path.dentry);
+			fsnotify_modify(file);
 	}
 	return ret;
 }
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 00fbd5b..dec1afb 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -136,8 +136,9 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
 /*
  * fsnotify_access - file was read
  */
-static inline void fsnotify_access(struct dentry *dentry)
+static inline void fsnotify_access(struct file *file)
 {
+	struct dentry *dentry = file->f_path.dentry;
 	struct inode *inode = dentry->d_inode;
 	u32 mask = IN_ACCESS;
 
@@ -152,8 +153,9 @@ static inline void fsnotify_access(struct dentry *dentry)
 /*
  * fsnotify_modify - file was modified
  */
-static inline void fsnotify_modify(struct dentry *dentry)
+static inline void fsnotify_modify(struct file *file)
 {
+	struct dentry *dentry = file->f_path.dentry;
 	struct inode *inode = dentry->d_inode;
 	u32 mask = IN_MODIFY;
 
@@ -168,8 +170,9 @@ static inline void fsnotify_modify(struct dentry *dentry)
 /*
  * fsnotify_open - file was opened
  */
-static inline void fsnotify_open(struct dentry *dentry)
+static inline void fsnotify_open(struct file *file)
 {
+	struct dentry *dentry = file->f_path.dentry;
 	struct inode *inode = dentry->d_inode;
 	u32 mask = IN_OPEN;
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 03/21] fanotify: fscking all notify, system wide file access notification
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 01/21] filesystem notification: create fs/notify to contain all fs notification Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 02/21] fsnotify: pass a file instead of an inode to open, read, and write Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify Eric Paris
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

A new system wide file access notification system.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/Kconfig        |   14 ++
 fs/notify/Makefile       |    2 
 fs/notify/fanotify.c     |   99 +++++++++++++++++
 fs/notify/fanotify.h     |   63 +++++++++++
 fs/notify/group.c        |  110 +++++++++++++++++++
 fs/notify/notification.c |  266 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/Kbuild     |    1 
 include/linux/fanotify.h |   95 ++++++++++++++++
 include/linux/fsnotify.h |   14 ++
 include/linux/sched.h    |    1 
 10 files changed, 661 insertions(+), 4 deletions(-)
 create mode 100644 fs/notify/fanotify.c
 create mode 100644 fs/notify/fanotify.h
 create mode 100644 fs/notify/group.c
 create mode 100644 fs/notify/notification.c
 create mode 100644 include/linux/fanotify.h

diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig
index 23415de..97cc832 100644
--- a/fs/notify/Kconfig
+++ b/fs/notify/Kconfig
@@ -36,3 +36,17 @@ config INOTIFY_USER
 	  For more information, see <file:Documentation/filesystems/inotify.txt>
 
 	  If unsure, say Y.
+
+config FANOTIFY
+        bool "Filesystem wide access notification"
+        select SECURITY
+        default y
+        ---help---
+           Say Y here to enable fanotify suport.  fanotify is a system wide
+           file access notification interface.  Events are read from from a
+           single open fd and in doing so a fd is created in the reading process
+           which points to the same data as the one on which the event occured.
+
+           For more information, see <file:Documentation/filesystems/fanotify.txt>
+
+           If unsure, say Y.
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 882ecf9..c467c97 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_INOTIFY)		+= inotify.o
 obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
+
+obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o group.o
diff --git a/fs/notify/fanotify.c b/fs/notify/fanotify.c
new file mode 100644
index 0000000..e2a338e
--- /dev/null
+++ b/fs/notify/fanotify.c
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/uaccess.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+struct dentry *fanotify_fs_root;
+
+void fanotify(struct file *file, unsigned int mask)
+{
+	struct fanotify_group *group;
+	struct fanotify_event *event = NULL;
+	struct task_struct *tsk = current;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	int idx;
+
+	if (likely(list_empty(&fanotify_groups)))
+		return;
+
+	if (tsk->flags & PF_NOFACCESS)
+		return;
+
+	if (!S_ISREG(inode->i_mode))
+		return;
+
+	/*
+	 * SRCU!!  the groups list is very very much read only and the path is
+	 * very hot (assuming something is using fanotify)  Not blocking while
+	 * walking this list is ugly.  We could preallocate an event and an
+	 * event holder for every group that event might need to be put on, but
+	 * all that possibly wasted allocation is nuts.  For all we know there
+	 * are already fastpath entries, groups don't need this event, or all
+	 * sorts of reasons to believe not every kernel action is going to get
+	 * sent to userspace.  Hopefully this won't get shit on too much,
+	 * because going to a mutex here is really going to needlessly serialize
+	 * read/write/open/close across the whole system....
+	 */
+	idx = srcu_read_lock(&fanotify_grp_srcu_struct);
+	list_for_each_entry_rcu(group, &fanotify_groups, group_list) {
+		if (mask & group->mask) {
+			if (!event) {
+				event = create_event(file, mask);
+				/* shit, we OOM'd and now we can't tell, lets hope something else blows up */
+				if (!event)
+					break;
+			}
+			fanotify_add_event_to_notif(group, event);
+		}
+	}
+	srcu_read_unlock(&fanotify_grp_srcu_struct, idx);
+	/*
+	 * create_event() take a reference so the event can't be cleaned up while
+	 * we are still trying to add it to lists
+	 */
+	if (event)
+		fanotify_put_event(event);
+}
+EXPORT_SYMBOL_GPL(fanotify);
+
+static __init int fanotify_init(void)
+{
+	int ret;
+
+	ret = fanotify_notification_init();
+	if (ret)
+		return ret;
+
+	return init_srcu_struct(&fanotify_grp_srcu_struct);
+}
+__initcall(fanotify_init);
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
new file mode 100644
index 0000000..2cbe0ba
--- /dev/null
+++ b/fs/notify/fanotify.h
@@ -0,0 +1,63 @@
+#ifndef _LINUX_FANOTIFY_PRIVATE_H
+#define _LINUX_FANOTIFY_PRIVATE_H
+
+#include <linux/fanotify.h>
+
+
+#include <asm/atomic.h>
+#include <linux/dcache.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/*
+ * A single event can be queued in multiple group->notification_lists.
+ *
+ * each group->notification_list will point to an event_holer which in turns points
+ * to the actual event that needs to be sent to userspace.
+ *
+ * Seemed cheaper to create a refcnt'd event and a small holder for every group
+ * than create a different event for every group
+ * 
+ */
+struct fanotify_event_holder {
+	struct fanotify_event *event;
+	struct list_head event_list;
+};
+
+/*
+ * all of the information about the original object we want to now send to
+ * a scanner.  If you want to carry more info from the accessing task to the
+ * listener this structure is where you need to be adding fields.
+ */
+struct fanotify_event {
+	/*
+	 * If we create an event we are also going to need to create a holder
+	 * to link to a group.  So embed one holder in the event.  Means only
+	 * one allocation for the common case where we only have one group
+	 */
+	struct fanotify_event_holder holder;
+	spinlock_t holder_spinlock; /* protection for the associated event_holder */
+	struct path path;	/* path from the original access */
+	unsigned int mask;	/* the type of access */
+	atomic_t refcnt;	/* how many groups still are using/need to send this event */
+};
+
+extern struct srcu_struct fanotify_grp_srcu_struct;
+extern struct list_head fanotify_groups;
+
+extern int fanotify_check_notif_queue(struct fanotify_group *group);
+extern void fanotify_get_event(struct fanotify_event *event);
+extern void fanotify_put_event(struct fanotify_event *event);
+extern int fanotify_add_event_to_notif(struct fanotify_group *group, struct fanotify_event *event);
+extern struct fanotify_event *remove_event_from_group_notification(struct fanotify_group *group);
+extern void fanotify_notification_clear_group(struct fanotify_group *group);
+extern struct fanotify_event *create_event(struct file *file, unsigned int mask);
+extern struct fanotify_event_holder *alloc_event_holder(void);
+extern void fanotify_destroy_event_holder(struct fanotify_event_holder *holder);
+extern __init int fanotify_notification_init(void);
+extern __init int fanotify_notification_uninit(void);
+#endif	/* _LINUX_FANOTIFY_PRIVATE_H */
diff --git a/fs/notify/group.c b/fs/notify/group.c
new file mode 100644
index 0000000..fc3d736
--- /dev/null
+++ b/fs/notify/group.c
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/uaccess.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+DEFINE_MUTEX(fanotify_grp_mutex);
+struct srcu_struct fanotify_grp_srcu_struct;
+LIST_HEAD(fanotify_groups);
+
+void fanotify_get_group(struct fanotify_group *group)
+{
+	atomic_inc(&group->refcnt);
+}
+
+struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask)
+{
+	struct fanotify_group *group_iter;
+	struct fanotify_group *group = NULL;
+
+	mutex_lock(&fanotify_grp_mutex);
+	list_for_each_entry_rcu(group_iter, &fanotify_groups, group_list) {
+		if (group_iter->group_num == group_num) {
+			if (group_iter->mask == mask) {
+				fanotify_get_group(group_iter);
+				group = group_iter;
+			} else
+				group = ERR_PTR(-EEXIST);
+			goto out;
+		}
+	}
+
+	group = kmalloc(sizeof(struct fanotify_group), GFP_KERNEL);
+	if (!group) {
+		group = ERR_PTR(-ENOMEM);
+		goto out;
+	}
+
+	atomic_set(&group->refcnt, 1);
+
+	group->group_num = group_num;
+	group->mask = mask;
+
+	mutex_init(&group->notification_mutex);
+	INIT_LIST_HEAD(&group->notification_list);
+	init_waitqueue_head(&group->notification_waitq);
+
+	/* add it */
+	list_add_rcu(&group->group_list, &fanotify_groups);
+
+out:
+	mutex_unlock(&fanotify_grp_mutex);
+	return group;
+}
+
+void fanotify_kill_group(struct fanotify_group *group)
+{
+	/* clear the notification queue of all events */
+	fanotify_notification_clear_group(group);
+
+	kfree(group);
+}
+
+void fanotify_put_group(struct fanotify_group *group)
+{
+	mutex_lock(&fanotify_grp_mutex);
+	if (atomic_dec_and_test(&group->refcnt)) {
+
+		list_del_rcu(&group->group_list);
+		mutex_unlock(&fanotify_grp_mutex);
+
+		synchronize_srcu(&fanotify_grp_srcu_struct);
+
+		fanotify_kill_group(group);
+
+		return;
+	}
+	mutex_unlock(&fanotify_grp_mutex);
+
+	return;
+}
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
new file mode 100644
index 0000000..3dd69ef
--- /dev/null
+++ b/fs/notify/notification.c
@@ -0,0 +1,266 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/atomic.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mount.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+static struct kmem_cache *event_kmem_cache;
+static struct kmem_cache *event_holder_kmem_cache;
+
+int fanotify_check_notif_queue(struct fanotify_group *group)
+{
+	mutex_lock(&group->notification_mutex);
+	if (!list_empty(&group->notification_list))
+		return 1;
+	mutex_unlock(&group->notification_mutex);
+	return 0;
+}
+
+void fanotify_get_event(struct fanotify_event *event)
+{
+	atomic_inc(&event->refcnt);
+}
+
+void fanotify_put_event(struct fanotify_event *event)
+{
+	if (!event)
+		return;
+
+	if (atomic_dec_and_test(&event->refcnt)) {
+		path_put(&event->path);
+		event->path.dentry = NULL;
+		event->path.mnt = NULL;
+
+		event->mask = 0;
+		kmem_cache_free(event_kmem_cache, event);
+	}
+}
+
+struct fanotify_event_holder *alloc_event_holder(void)
+{
+	return kmem_cache_alloc(event_holder_kmem_cache, GFP_KERNEL);
+}
+
+void fanotify_destroy_event_holder(struct fanotify_event_holder *holder)
+{
+	kmem_cache_free(event_holder_kmem_cache, holder);
+}
+
+int fanotify_add_event_to_notif(struct fanotify_group *group, struct fanotify_event *event)
+{
+	struct fanotify_event_holder *holder;
+
+	/*
+	 * holder locking!!!
+	 *
+	 * only this task is going to be adding events to lists.  fanotify
+	 * listeners may be removing events from groups and freeing holders
+	 * but only we will be adding.  So locking is pretty easy.  We don't
+	 * need to lock when we check the list.  We don't really care what
+	 * order we see things from the freeing tasks.  If we see the holder
+	 * list is empty we know after we take the spinlock that holder will
+	 * be safe to use.
+	 *
+	 * When only looking at the notification list this is all useless since
+	 * everything meaningful is already being protected by the
+	 * notification_mutex but since access lists also use event_holders we
+	 * need this.
+	 */
+	if (list_empty(&event->holder.event_list))
+		holder = (struct fanotify_event_holder *)event;
+	else
+		holder = alloc_event_holder();
+	if (!holder)
+		return -ENOMEM;
+
+	fanotify_get_event(event);
+
+
+	mutex_lock(&group->notification_mutex);
+	spin_lock(&event->holder_spinlock);
+	holder->event = event;
+	list_add_tail(&holder->event_list, &group->notification_list);
+	spin_unlock(&event->holder_spinlock);
+	mutex_unlock(&group->notification_mutex);
+
+
+	wake_up(&group->notification_waitq);
+
+	return 0;
+}
+
+/*
+ * must be called with group->notification_mutex held and must know event is present.
+ * it is the responsibility of the caller to call put_event() on the returned
+ * structure
+ */
+struct fanotify_event *remove_event_from_group_notification(struct fanotify_group *group)
+{
+	struct fanotify_event *event;
+	struct fanotify_event_holder *holder;
+
+	holder = list_first_entry(&group->notification_list, struct fanotify_event_holder, event_list);
+
+	event = holder->event;
+
+	spin_lock(&event->holder_spinlock);
+	holder->event = NULL;
+	/*
+	 * be sure you are done with holder, since after this point it
+	 * could be reused in another group
+	 */
+	list_del_init(&holder->event_list);
+	spin_unlock(&event->holder_spinlock);
+
+	/* event == holder means we are referenced through the in event holder */
+	if (event != (struct fanotify_event *)holder)
+		fanotify_destroy_event_holder(holder);
+
+	return event;
+}
+
+void fanotify_notification_clear_group(struct fanotify_group *group)
+{
+	struct fanotify_event *event;
+
+	while (fanotify_check_notif_queue(group)) {
+		event = remove_event_from_group_notification(group);
+		fanotify_put_event(event);
+		/* fanotify_check_notif_queue() took this lock */
+		mutex_unlock(&group->notification_mutex);
+	}
+}
+
+struct fanotify_event *create_event(struct file *file, unsigned int mask)
+{
+	struct fanotify_event *event;
+
+	event = kmem_cache_alloc(event_kmem_cache, GFP_KERNEL);
+	if (!event)
+		return NULL;
+
+	event->holder.event = NULL;
+	INIT_LIST_HEAD(&event->holder.event_list);
+	atomic_set(&event->refcnt, 1);
+
+	spin_lock_init(&event->holder_spinlock);
+
+	event->path.dentry = file->f_path.dentry;
+	event->path.mnt = file->f_path.mnt;
+	path_get(&event->path);
+
+	event->mask = mask;
+
+	WARN_ON(!event->path.dentry);
+	WARN_ON(!event->path.mnt);
+
+	return event;
+}
+
+int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock)
+{
+	int rc;
+	int client_fd;
+	struct dentry *dentry;
+	struct vfsmount *mnt;
+	struct file *new_file;
+
+	struct fanotify_event *event;
+
+	memset(data, 0, sizeof(struct fanotify_event_metadata));
+
+	/* get a notification off the queue blocking if we can/should */
+	if (fanotify_check_notif_queue(group)) {
+		event = remove_event_from_group_notification(group);
+		mutex_unlock(&group->notification_mutex);
+	} else {
+		if (nonblock)
+			return -EAGAIN;
+		rc = wait_event_interruptible(group->notification_waitq, fanotify_check_notif_queue(group));
+		if (!rc) {
+			event = remove_event_from_group_notification(group);
+			mutex_unlock(&group->notification_mutex);
+		} else {
+			return rc;
+		}
+	}
+
+	BUG_ON(!event);
+
+	client_fd = get_unused_fd();
+	if (client_fd < 0)
+		return client_fd;
+
+	/*
+	 * we need a new file handle for the userspace program so it can read even if it was
+	 * originally opened O_WRONLY.
+	 */
+	dentry = dget(event->path.dentry);
+	mnt = mntget(event->path.mnt);
+	new_file = dentry_open(dentry, mnt, O_RDONLY);
+	if (IS_ERR(new_file)) {
+		/*
+		 * we still send an event even if we can't open the file.  this
+		 * can happen for say tasks are gone and we try to open their
+		 * /proc entries or we try to open a WRONLY file like in sysfs
+		 * we just send the errno to userspace since there isn't much
+		 * else we can do.
+		 */
+		put_unused_fd(client_fd);
+		client_fd = PTR_ERR(new_file);
+	} else {
+		fd_install(client_fd, new_file);
+	}
+
+	data->fd = client_fd;
+	data->mask = event->mask;
+
+	fanotify_put_event(event);
+
+	return 0;
+}
+
+__init int fanotify_notification_uninit(void)
+{
+	kmem_cache_destroy(event_kmem_cache);
+	event_kmem_cache = NULL;
+	kmem_cache_destroy(event_holder_kmem_cache);
+	event_holder_kmem_cache = NULL;
+
+	return 0;
+}
+
+__init int fanotify_notification_init(void)
+{
+	event_kmem_cache = kmem_cache_create("fanotify_event", sizeof(struct fanotify_event), 0, SLAB_PANIC, NULL);
+	event_holder_kmem_cache = kmem_cache_create("fanotify_event_holder", sizeof(struct fanotify_event_holder), 0, SLAB_PANIC, NULL);
+
+	return 0;
+}
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index e531783..9f6d7d9 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -246,6 +246,7 @@ unifdef-y += inet_diag.h
 unifdef-y += in.h
 unifdef-y += in6.h
 unifdef-y += inotify.h
+unifdef-y += fanotify.h
 unifdef-y += input.h
 unifdef-y += ip.h
 unifdef-y += ipc.h
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
new file mode 100644
index 0000000..7f1179e
--- /dev/null
+++ b/include/linux/fanotify.h
@@ -0,0 +1,95 @@
+/*
+ * Filesystem access notification for Linux
+ *
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ * Basis of file stolen from inotify.h
+ */
+
+#ifndef _LINUX_FANOTIFY_H
+#define _LINUX_FANOTIFY_H
+
+/* the following events that user-space can register for */
+#define FAN_ACCESS		0x00000001	/* File was accessed */
+#define FAN_MODIFY		0x00000002	/* File was modified */
+#define FAN_CLOSE_NOWRITE	0x00000004	/* Unwrittable file closed */
+#define FAN_CLOSE_WRITE		0x00000008	/* Writtable file closed */
+#define FAN_OPEN		0x00000010	/* File was opened */
+
+/* FIXME currently Q's have no limit.... */
+#define FAN_Q_OVERFLOW		0x80000000	/* Event queued overflowed */
+
+/* helper events */
+#define FAN_CLOSE		(FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
+
+/*
+ * All of the events - we build the list by hand so that we can add flags in
+ * the future and not break backward compatibility.  Apps will get only the
+ * events that they originally wanted.  Be sure to add new events here!
+ */
+#define FAN_ALL_EVENTS	(FAN_ACCESS |\
+			 FAN_MODIFY |\
+			 FAN_CLOSE |\
+			 FAN_OPEN)
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#include <sys/types.h>
+#endif
+
+struct fanotify_event_metadata {
+	int32_t fd;
+	uint32_t mask;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+#include <asm/atomic.h>
+
+struct fanotify_group {
+	struct list_head group_list;	/* list of all groups on the system */
+	unsigned int group_num;		/* the 'name' of the event */
+	unsigned int mask;			/* mask of events this group cares about */
+	atomic_t refcnt;		/* num of processes with a special file open */
+
+	/* needed to send notification to userspace */
+	struct mutex notification_mutex;/* protect the notification_list */
+	struct list_head notification_list;	/* list of event_holder this group needs to send to userspace */
+	wait_queue_head_t notification_waitq;	/* read() on the notification file blocks on this waitq */
+};
+
+#ifdef CONFIG_FANOTIFY
+
+extern void fanotify(struct file *file, unsigned int mask);
+
+extern void fanotify_get_group(struct fanotify_group *group);
+extern struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask);
+extern void fanotify_put_group(struct fanotify_group *group);
+#else
+
+static inline void fanotify(struct file *file, unsigned int mask)
+{}
+
+static inline void fanotify_get_group(struct fanotify_group *group)
+{}
+
+static inline struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask)
+{
+	return NULL;
+}
+
+static inline extern void fanotify_put_group(struct fanotify_group *group)
+{}
+
+#endif	/* CONFIG_FANOTIFY */
+
+#endif	/* __KERNEL __ */
+
+#endif	/* _LINUX_FANOTIFY_H */
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index dec1afb..3a1b0dc 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -13,6 +13,7 @@
 
 #include <linux/dnotify.h>
 #include <linux/inotify.h>
+#include <linux/fanotify.h>
 #include <linux/audit.h>
 
 /*
@@ -148,6 +149,7 @@ static inline void fsnotify_access(struct file *file)
 	dnotify_parent(dentry, DN_ACCESS);
 	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
 	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	fanotify(file, FAN_ACCESS);
 }
 
 /*
@@ -165,6 +167,7 @@ static inline void fsnotify_modify(struct file *file)
 	dnotify_parent(dentry, DN_MODIFY);
 	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
 	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	fanotify(file, FAN_MODIFY);
 }
 
 /*
@@ -181,6 +184,7 @@ static inline void fsnotify_open(struct file *file)
 
 	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
 	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	fanotify(file, FAN_OPEN);
 }
 
 /*
@@ -192,13 +196,15 @@ static inline void fsnotify_close(struct file *file)
 	struct inode *inode = dentry->d_inode;
 	const char *name = dentry->d_name.name;
 	fmode_t mode = file->f_mode;
-	u32 mask = (mode & FMODE_WRITE) ? IN_CLOSE_WRITE : IN_CLOSE_NOWRITE;
+	u32 in_mask = (mode & FMODE_WRITE) ? IN_CLOSE_WRITE : IN_CLOSE_NOWRITE;
+	u32 fan_mask = (mode & FMODE_WRITE) ? FAN_CLOSE_WRITE : FAN_CLOSE_NOWRITE;
 
 	if (S_ISDIR(inode->i_mode))
-		mask |= IN_ISDIR;
+		in_mask |= IN_ISDIR;
 
-	inotify_dentry_parent_queue_event(dentry, mask, 0, name);
-	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	inotify_dentry_parent_queue_event(dentry, in_mask, 0, name);
+	inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL);
+	fanotify(file, fan_mask);
 }
 
 /*
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 295b7c7..7e889f1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1538,6 +1538,7 @@ extern cputime_t task_gtime(struct task_struct *p);
 #define PF_EXITING	0x00000004	/* getting shut down */
 #define PF_EXITPIDONE	0x00000008	/* pi exit done on shut down */
 #define PF_VCPU		0x00000010	/* I'm a virtual CPU */
+#define PF_NOFACCESS	0x00000020	/* exclude from all faccesses */
 #define PF_FORKNOEXEC	0x00000040	/* forked but didn't exec */
 #define PF_SUPERPRIV	0x00000100	/* used super-user privileges */
 #define PF_DUMPCORE	0x00000200	/* dumped core */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (2 preceding siblings ...)
  2008-11-12 16:10 ` [PATCH =-v3 03/21] fanotify: fscking all notify, system wide file access notification Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:49   ` Christoph Hellwig
  2008-11-12 16:10 ` [PATCH =-v3 05/21] fanotify: make use of the new fsnotify_open_exec calls Eric Paris
                   ` (16 subsequent siblings)
  20 siblings, 1 reply; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

sys_execve and sys_uselib do not call into fsnotify so inotify, dnotify,
and importantly to me fanotify do not see opens on things which are going
to be exectued.  Create a generic fsnotify hook for these paths.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/exec.c                |    5 +++++
 include/linux/fsnotify.h |    7 +++++++
 2 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 4e834f1..8f56995 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -51,6 +51,7 @@
 #include <linux/audit.h>
 #include <linux/tracehook.h>
 #include <linux/kmod.h>
+#include <linux/fsnotify.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -135,6 +136,8 @@ asmlinkage long sys_uselib(const char __user * library)
 	if (IS_ERR(file))
 		goto out;
 
+	fsnotify_open_exec(file);
+
 	error = -ENOEXEC;
 	if(file->f_op) {
 		struct linux_binfmt * fmt;
@@ -687,6 +690,8 @@ struct file *open_exec(const char *name)
 	if (IS_ERR(file))
 		return file;
 
+	fsnotify_open_exec(file);
+
 	err = deny_write_access(file);
 	if (err) {
 		fput(file);
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 3a1b0dc..bf53881 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -171,6 +171,13 @@ static inline void fsnotify_modify(struct file *file)
 }
 
 /*
+ * fsnotify_open_exec - file was opened by execve of uselib
+ */
+static inline void fsnotify_open_exec(struct file *file)
+{
+}
+
+/*
  * fsnotify_open - file was opened
  */
 static inline void fsnotify_open(struct file *file)


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 05/21] fanotify: make use of the new fsnotify_open_exec calls
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (3 preceding siblings ...)
  2008-11-12 16:10 ` [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:10 ` [PATCH =-v3 06/21] fanotify: add a userspace interface for fanotify notifications Eric Paris
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

This function sends fanotify events for opens which we know are being used
for exec.  These are basically just systecalls to sys_execve and sys_uselib

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 include/linux/fanotify.h |    4 +++-
 include/linux/fsnotify.h |    3 ++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 7f1179e..c991bd9 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -14,13 +14,15 @@
 #define FAN_MODIFY		0x00000002	/* File was modified */
 #define FAN_CLOSE_NOWRITE	0x00000004	/* Unwrittable file closed */
 #define FAN_CLOSE_WRITE		0x00000008	/* Writtable file closed */
-#define FAN_OPEN		0x00000010	/* File was opened */
+#define FAN_OPEN_NOEXEC		0x00000010	/* File was opened */
+#define FAN_OPEN_EXEC		0x00000020	/* File was opened with the intention of being exec'ed */
 
 /* FIXME currently Q's have no limit.... */
 #define FAN_Q_OVERFLOW		0x80000000	/* Event queued overflowed */
 
 /* helper events */
 #define FAN_CLOSE		(FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
+#define FAN_OPEN		(FAN_OPEN_NOEXEC | FAN_OPEN_EXEC) /* open */
 
 /*
  * All of the events - we build the list by hand so that we can add flags in
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index bf53881..894f573 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -175,6 +175,7 @@ static inline void fsnotify_modify(struct file *file)
  */
 static inline void fsnotify_open_exec(struct file *file)
 {
+	fanotify(file, FAN_OPEN_EXEC);
 }
 
 /*
@@ -191,7 +192,7 @@ static inline void fsnotify_open(struct file *file)
 
 	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
 	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
-	fanotify(file, FAN_OPEN);
+	fanotify(file, FAN_OPEN_NOEXEC);
 }
 
 /*


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 06/21] fanotify: add a userspace interface for fanotify notifications
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (4 preceding siblings ...)
  2008-11-12 16:10 ` [PATCH =-v3 05/21] fanotify: make use of the new fsnotify_open_exec calls Eric Paris
@ 2008-11-12 16:10 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes Eric Paris
                   ` (14 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:10 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

notifications from fanotify need a userspace interface.  We are going to
try to do it with sockets.  Scary....

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 include/linux/fanotify.h   |   29 ++++-
 include/linux/socket.h     |    5 +
 net/Makefile               |    1 
 net/core/sock.c            |    6 +
 net/fanotify/Makefile      |    5 +
 net/fanotify/af_fanotify.c |  240 ++++++++++++++++++++++++++++++++++++++++++++
 net/fanotify/af_fanotify.h |   20 ++++
 7 files changed, 294 insertions(+), 12 deletions(-)
 create mode 100644 net/fanotify/Makefile
 create mode 100644 net/fanotify/af_fanotify.c
 create mode 100644 net/fanotify/af_fanotify.h

diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index c991bd9..76d88fd 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -34,17 +34,23 @@
 			 FAN_CLOSE |\
 			 FAN_OPEN)
 
-#ifdef __KERNEL__
 #include <linux/types.h>
-#else
-#include <stdint.h>
-#include <sys/types.h>
-#endif
 
+struct fanotify_addr {
+        __u32 group_num;
+        __u32 mask;
+        __u32 timeout;
+        __u32 unused[9];
+}  __attribute__((packed));
+
+/* struct used for FANOTIFY_GET_EVENT */
 struct fanotify_event_metadata {
-	int32_t fd;
-	uint32_t mask;
-};
+	__s32 fd;
+	__u32 mask;
+}  __attribute__((packed));
+
+/* fanotify getsockopt optvals */
+#define FANOTIFY_GET_EVENT	1
 
 #ifdef __KERNEL__
 
@@ -74,6 +80,8 @@ extern void fanotify(struct file *file, unsigned int mask);
 extern void fanotify_get_group(struct fanotify_group *group);
 extern struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask);
 extern void fanotify_put_group(struct fanotify_group *group);
+
+extern int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock);
 #else
 
 static inline void fanotify(struct file *file, unsigned int mask)
@@ -90,6 +98,11 @@ static inline struct fanotify_group *fanotify_find_group(unsigned int group_num,
 static inline extern void fanotify_put_group(struct fanotify_group *group)
 {}
 
+static inline int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock)
+{
+	memset(data, 0, sizeof(struct fanotify_event_metadata));
+	return 0;
+}
 #endif	/* CONFIG_FANOTIFY */
 
 #endif	/* __KERNEL __ */
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 20fc4bb..4fe7b83 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -191,7 +191,8 @@ struct ucred {
 #define AF_RXRPC	33	/* RxRPC sockets 		*/
 #define AF_ISDN		34	/* mISDN sockets 		*/
 #define AF_PHONET	35	/* Phonet sockets		*/
-#define AF_MAX		36	/* For now.. */
+#define AF_FANOTIFY	36	/* fscking all access sockets	*/
+#define AF_MAX		37	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -229,6 +230,7 @@ struct ucred {
 #define PF_RXRPC	AF_RXRPC
 #define PF_ISDN		AF_ISDN
 #define PF_PHONET	AF_PHONET
+#define PF_FANOTIFY	AF_FANOTIFY
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
@@ -298,6 +300,7 @@ struct ucred {
 #define SOL_PPPOL2TP	273
 #define SOL_BLUETOOTH	274
 #define SOL_PNPIPE	275
+#define SOL_FANOTIFY	276
 
 /* IPX options */
 #define IPX_TYPE	1
diff --git a/net/Makefile b/net/Makefile
index 27d1f10..5b98ba4 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_DECNET)		+= decnet/
 obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_PHONET)		+= phonet/
+obj-$(CONFIG_FANOTIFY)		+= fanotify/
 ifneq ($(CONFIG_VLAN_8021Q),)
 obj-y				+= 8021q/
 endif
diff --git a/net/core/sock.c b/net/core/sock.c
index 5e2a313..c7b0cee 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -155,7 +155,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
   "sk_lock-27"       , "sk_lock-28"          , "sk_lock-AF_CAN"      ,
   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
-  "sk_lock-AF_MAX"
+  "sk_lock-AF_FANOTIFY", "sk_lock-AF_MAX"
 };
 static const char *af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
@@ -170,7 +170,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
   "slock-27"       , "slock-28"          , "slock-AF_CAN"      ,
   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
-  "slock-AF_MAX"
+  "slock=AF_FANOTIFY", "slock-AF_MAX"
 };
 static const char *af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
@@ -185,7 +185,7 @@ static const char *af_family_clock_key_strings[AF_MAX+1] = {
   "clock-27"       , "clock-28"          , "clock-AF_CAN"      ,
   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
-  "clock-AF_MAX"
+  "clock-AF_FANOTIFY", "clock-AF_MAX"
 };
 #endif
 
diff --git a/net/fanotify/Makefile b/net/fanotify/Makefile
new file mode 100644
index 0000000..348cf5e
--- /dev/null
+++ b/net/fanotify/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the fanotify AF.
+#
+
+obj-$(CONFIG_FANOTIFY) += af_fanotify.o
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
new file mode 100644
index 0000000..7474cf1
--- /dev/null
+++ b/net/fanotify/af_fanotify.c
@@ -0,0 +1,240 @@
+#include <linux/errno.h>
+#include <linux/fanotify.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/types.h>
+
+#include <net/net_namespace.h>
+#include <net/sock.h>
+
+#include "af_fanotify.h"
+
+static const struct proto_ops fanotify_proto_ops;
+
+static struct proto fanotify_proto = {
+	.name     = "FANOTIFY",
+	.owner    = THIS_MODULE,
+	.obj_size = sizeof(struct fanotify_sock),
+};
+
+static int fan_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	struct fanotify_sock *fan_sock;
+
+	/* FIXME maybe a new LSM hook? */
+	if (!capable(CAP_NET_RAW))
+		return -EPERM;
+
+	if (protocol != 0)
+		return -ESOCKTNOSUPPORT;
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->state = SS_UNCONNECTED;
+
+	sk = sk_alloc(net, PF_FANOTIFY, GFP_KERNEL, &fanotify_proto);
+	if (sk == NULL)
+		return -ENOBUFS;
+
+	sock->ops = &fanotify_proto_ops;
+
+	sock_init_data(sock, sk);
+
+	sk->sk_family = PF_FANOTIFY;
+	//sk->sk_destruct = packet_sock_destruct;
+	sk_refcnt_debug_inc(sk);
+
+	fan_sock = fan_sk(sk);
+	fan_sock->group = NULL;
+
+	return 0;
+}
+
+static int fan_release(struct socket *sock)
+{
+	struct sock *sk;
+	struct fanotify_sock *fan_sock;
+
+	struct files_struct *files;
+	struct socket *testsock;
+	struct fdtable *fdt;
+	long j = -1;
+	unsigned long set, i;
+	int found = 0, err;
+
+	sk = sock->sk;
+	if (!sk)
+		return 0;
+
+	fan_sock = fan_sk(sk);
+
+	if (sock->state == SS_CONNECTED) {
+		sock->state = SS_UNCONNECTED;
+		fanotify_put_group(fan_sock->group);
+	}
+
+	fan_sock->group = NULL;
+
+	sock_orphan(sk);
+	sock->sk = NULL;
+
+	/* Purge queues */
+
+	sk_refcnt_debug_release(sk);
+
+	sock_put(sk);
+
+	/*
+	 * we do all of this shit to figure out if this process is closing the last
+	 * fanotify socket.  If this is the last one being closed we clear the
+	 * exclusion flag.  If this is not the last close we should remain excluded.
+	 *
+	 * the basic idea behind running all of the open fd's was stolen from
+	 * fs/exec.c:flush_old_files.
+	 */
+	files = current->files;
+	if (files)
+		spin_lock(&files->file_lock);
+	for (; !found && files ;) {
+		j++;
+		i = j * __NFDBITS;
+		fdt = files_fdtable(files);
+		if (i >= fdt->max_fds)
+			break;
+		set = fdt->open_fds->fds_bits[j];
+		if (!set)
+			continue;
+		spin_unlock(&files->file_lock);
+		for ( ; set && !found ; i++, set >>= 1) {
+			if (set & 1) {
+				testsock = sockfd_lookup(i, &err);
+				if (!testsock)
+					continue;
+				if ((testsock != sock) && (testsock->ops == &fanotify_proto_ops))
+					found = 1;
+				sockfd_put(testsock);
+			}
+		}
+		spin_lock(&files->file_lock);
+	}
+	if (files)
+		spin_unlock(&files->file_lock);
+
+	if (!found)
+		current->flags &= ~PF_NOFACCESS;
+
+	return 0;
+}
+
+static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct fanotify_addr *fan_addr = (struct fanotify_addr *)addr;
+	struct fanotify_sock *fan_sock;
+
+	if (addr_len != sizeof(struct fanotify_addr))
+		return -EINVAL;
+
+	if (sock->state != SS_UNCONNECTED)
+		return -EINVAL;
+
+	fan_sock = fan_sk(sock->sk);
+	fan_sock->group = fanotify_find_group(fan_addr->group_num, fan_addr->mask);
+
+	if (IS_ERR(fan_sock->group))
+		return PTR_ERR(fan_sock->group);
+
+	sock->state = SS_CONNECTED;
+	current->flags |= PF_NOFACCESS;
+
+	return 0;
+}
+
+static int fan_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+{
+	struct fanotify_sock *fan_sock;
+	struct fanotify_group *group;
+	union {
+		struct fanotify_event_metadata event_metadata;
+	} data;
+	int len, ret = 0;
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	if (sock->state != SS_CONNECTED)
+		return -EBADF;
+
+	if (level != SOL_FANOTIFY)
+		return -ENOPROTOOPT;
+
+	fan_sock = fan_sk(sock->sk);
+	group = fan_sock->group;
+
+	switch (optname) {
+	case FANOTIFY_GET_EVENT:
+		if (len < sizeof(struct fanotify_event_metadata))
+			return -ENOMEM;
+		else
+			len = sizeof(struct fanotify_event_metadata);
+		ret = fanotify_create_event_fd(group, &data.event_metadata, !!(sock->file->f_flags & O_NONBLOCK));
+		if (ret)
+			break;
+
+		ret = copy_to_user(optval, &data.event_metadata, sizeof(struct fanotify_event_metadata));
+		break;
+	default:
+		return -ENOPROTOOPT;
+	};
+
+	if (!ret)
+		ret = put_user(len, optlen);
+
+	return ret;
+}
+
+static const struct net_proto_family fanotify_family_ops = {
+	.family		=	PF_FANOTIFY,
+	.create		=	fan_sock_create,
+	.owner		=	THIS_MODULE,
+};
+
+static const struct proto_ops fanotify_proto_ops = {
+	.family =	PF_FANOTIFY,
+	.owner =	THIS_MODULE,
+	.release =	fan_release,
+	.bind =		fan_bind,
+	.connect =	sock_no_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	sock_no_accept,
+	.getname =	sock_no_getname,
+	.poll =		sock_no_poll,
+	.ioctl =	sock_no_ioctl,
+	.listen =	sock_no_listen,
+	.shutdown =	sock_no_shutdown,
+	.setsockopt =	sock_no_setsockopt,
+	.getsockopt =	fan_getsockopt,
+	.sendmsg =	sock_no_sendmsg,
+	.recvmsg =	sock_no_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage,
+};
+
+static int __init fanotify_socket_register(void)
+{
+	int rc = proto_register(&fanotify_proto, 0);
+
+	if (rc != 0)
+		goto out;
+
+	sock_register(&fanotify_family_ops);
+out:
+	return rc;
+}
+__initcall(fanotify_socket_register);
diff --git a/net/fanotify/af_fanotify.h b/net/fanotify/af_fanotify.h
new file mode 100644
index 0000000..a987d7b
--- /dev/null
+++ b/net/fanotify/af_fanotify.h
@@ -0,0 +1,20 @@
+#ifndef _LINUX_AF_FANOTIFY_H
+#define _LINUX_AF_FANOTIFY_H
+
+#ifdef __KERNEL__
+
+#include <linux/fanotify.h>
+#include <net/sock.h>
+
+struct fanotify_sock {
+	struct sock		sock;
+	struct fanotify_group	*group;
+};
+
+static inline struct fanotify_sock *fan_sk(struct sock *sock)
+{
+	return (struct fanotify_sock *)sock;
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_AF_NET_H */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (5 preceding siblings ...)
  2008-11-12 16:10 ` [PATCH =-v3 06/21] fanotify: add a userspace interface for fanotify notifications Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:50   ` Christoph Hellwig
  2008-11-12 22:38   ` Peter Zijlstra
  2008-11-12 16:11 ` [PATCH =-v3 08/21] fanotify: add a userspace interface for fastpaths Eric Paris
                   ` (13 subsequent siblings)
  20 siblings, 2 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Allows a process to ignore certain events for the given in core inodes.
This does not preclude the need to keep a userspace or xattr based
cache if a fanotifier listener wants something with a different lifetime
than in core inode.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/inode.c               |    6 +
 fs/notify/Makefile       |    2 
 fs/notify/fanotify.c     |   19 ++++
 fs/notify/fanotify.h     |   34 +++++++
 fs/notify/fastpath.c     |  230 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/notify/group.c        |    3 +
 include/linux/fanotify.h |   17 +++
 include/linux/fs.h       |    5 +
 include/linux/fsnotify.h |    8 ++
 9 files changed, 323 insertions(+), 1 deletions(-)
 create mode 100644 fs/notify/fastpath.c

diff --git a/fs/inode.c b/fs/inode.c
index 0487ddb..cc802ae 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -21,6 +21,7 @@
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
 #include <linux/inotify.h>
+#include <linux/fsnotify.h>
 #include <linux/mount.h>
 
 /*
@@ -183,6 +184,10 @@ static struct inode *alloc_inode(struct super_block *sb)
 		}
 		inode->i_private = NULL;
 		inode->i_mapping = mapping;
+#ifdef CONFIG_FANOTIFY
+		INIT_LIST_HEAD(&inode->fastpath_entries);
+		rwlock_init(&inode->fastpath_rwlock);
+#endif
 	}
 	return inode;
 }
@@ -191,6 +196,7 @@ void destroy_inode(struct inode *inode)
 {
 	BUG_ON(inode_has_buffers(inode));
 	security_inode_free(inode);
+	fsnotify_inode_delete(inode);
 	if (inode->i_sb->s_op->destroy_inode)
 		inode->i_sb->s_op->destroy_inode(inode);
 	else
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index c467c97..90cb910 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -3,4 +3,4 @@ obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
 
-obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o group.o
+obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o group.o fastpath.o
diff --git a/fs/notify/fanotify.c b/fs/notify/fanotify.c
index e2a338e..aa12e91 100644
--- a/fs/notify/fanotify.c
+++ b/fs/notify/fanotify.c
@@ -35,6 +35,15 @@
 
 struct dentry *fanotify_fs_root;
 
+void fanotify_inode_delete(struct inode *inode)
+{
+	if (likely(list_empty(&fanotify_groups)))
+		return;
+
+	fanotify_fastpath_clear(inode);
+}
+EXPORT_SYMBOL_GPL(fanotify_inode_delete);
+
 void fanotify(struct file *file, unsigned int mask)
 {
 	struct fanotify_group *group;
@@ -52,6 +61,8 @@ void fanotify(struct file *file, unsigned int mask)
 	if (!S_ISREG(inode->i_mode))
 		return;
 
+	if (mask & FAN_MODIFY)
+		fanotify_fastpath_clear(inode);
 	/*
 	 * SRCU!!  the groups list is very very much read only and the path is
 	 * very hot (assuming something is using fanotify)  Not blocking while
@@ -67,6 +78,8 @@ void fanotify(struct file *file, unsigned int mask)
 	idx = srcu_read_lock(&fanotify_grp_srcu_struct);
 	list_for_each_entry_rcu(group, &fanotify_groups, group_list) {
 		if (mask & group->mask) {
+			if (fanotify_is_fastpath(file, mask, group))
+				continue;
 			if (!event) {
 				event = create_event(file, mask);
 				/* shit, we OOM'd and now we can't tell, lets hope something else blows up */
@@ -94,6 +107,12 @@ static __init int fanotify_init(void)
 	if (ret)
 		return ret;
 
+	ret = fanotify_fastpath_init();
+	if (ret) {
+		fanotify_notification_uninit();
+		return ret;
+	}
+
 	return init_srcu_struct(&fanotify_grp_srcu_struct);
 }
 __initcall(fanotify_init);
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 2cbe0ba..bcc3f4b 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -46,6 +46,31 @@ struct fanotify_event {
 	atomic_t refcnt;	/* how many groups still are using/need to send this event */
 };
 
+/*
+ * a fastpath is simply an entry attached to an in core inode which allows an
+ * fanotify listener to indicate they are no longer interested in events of
+ * a type matching mask.
+ *
+ * these are flushed when an inode is evicted from core or when the inode is
+ * modified (as seen by fsnotify_access)
+ */
+struct fanotify_fastpath_entry {
+	struct fanotify_group *group;	/* group this fastpath entry is for */
+	unsigned int mask;		/* mask this fastpath entry is for */
+	struct inode *inode;		/* inode this entry is associated with */
+	/*
+	 * killcnt starts at 0 and is incremented when this entry will be killed
+	 * since multiple processes my try to kill it at the same time.
+	 * One might try to kill it from walking the i_list when an inode is being
+	 * kicked out of cache and one might try to kill it when an fanotify group
+	 * is being unregistered.  The last one to finish cleaning everything up
+	 * and decrement it to 0 will do the actual killing
+	 */
+	atomic_t killcnt;
+	struct list_head i_list;	/* list of fastpath_entries by inode->fastpath_entries */
+	struct list_head g_list;	/* list of fastpath_entries by group->fastpath_entries */
+};
+
 extern struct srcu_struct fanotify_grp_srcu_struct;
 extern struct list_head fanotify_groups;
 
@@ -60,4 +85,13 @@ extern struct fanotify_event_holder *alloc_event_holder(void);
 extern void fanotify_destroy_event_holder(struct fanotify_event_holder *holder);
 extern __init int fanotify_notification_init(void);
 extern __init int fanotify_notification_uninit(void);
+
+extern void fanotify_fastpath_get(struct fanotify_fastpath_entry *entry);
+extern void fanotify_fastpath_put(struct fanotify_fastpath_entry *entry);
+extern int fanotify_is_fastpath(struct file *file, unsigned int mask, struct fanotify_group *group);
+extern void fanotify_fastpath_clear_group(struct fanotify_group *group);
+extern void fanotify_fastpath_clear(struct inode *inode);
+extern __init int fanotify_fastpath_init(void);
+extern __init int fanotify_fastpath_uninit(void);
+
 #endif	/* _LINUX_FANOTIFY_PRIVATE_H */
diff --git a/fs/notify/fastpath.c b/fs/notify/fastpath.c
new file mode 100644
index 0000000..c4114af
--- /dev/null
+++ b/fs/notify/fastpath.c
@@ -0,0 +1,230 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/namei.h>
+#include <linux/poll.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/uaccess.h>
+#include <linux/writeback.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+static struct kmem_cache *fastpath_kmem_cache;
+
+static void fanotify_fastpath_kill(struct fanotify_fastpath_entry *entry)
+{
+	entry->group = NULL;
+	entry->inode = NULL;
+	entry->mask = 0;
+	entry->i_list.next = NULL;
+	entry->g_list.next = NULL;
+	kmem_cache_free(fastpath_kmem_cache, entry);
+}
+
+static struct fanotify_fastpath_entry *fanotify_fastpath_alloc(void)
+{
+	struct fanotify_fastpath_entry *entry;
+
+	entry = kmem_cache_alloc(fastpath_kmem_cache, GFP_KERNEL);
+
+	return entry;
+}
+
+void fanotify_fastpath_get(struct fanotify_fastpath_entry *entry)
+{
+	atomic_inc(&entry->killcnt);
+}
+
+void fanotify_fastpath_put(struct fanotify_fastpath_entry *entry)
+{
+	if (atomic_dec_and_test(&entry->killcnt))
+		fanotify_fastpath_kill(entry);
+}
+
+int fanotify_is_fastpath(struct file *file, unsigned int mask, struct fanotify_group *group)
+{
+	struct inode *inode = file->f_path.dentry->d_inode;
+	struct fanotify_fastpath_entry *entry;
+	int found = 0;
+
+	read_lock(&inode->fastpath_rwlock);
+	list_for_each_entry(entry, &inode->fastpath_entries, i_list) {
+		/* is the entry for this group and is the entry->mask a superset of this event mask? */
+		if ((entry->group == group) && ((entry->mask & mask) == mask)) {
+			found = 1;
+			break;
+		}
+	}
+	read_unlock(&inode->fastpath_rwlock);
+
+	return found;
+}
+
+void fanotify_fastpath_clear_group(struct fanotify_group *group)
+{
+	struct fanotify_fastpath_entry *entry;
+	struct inode *inode;
+
+	mutex_lock(&group->fastpath_mutex);
+	while (!list_empty(&group->fastpath_entries)) {
+		entry = list_first_entry(&group->fastpath_entries, struct fanotify_fastpath_entry, g_list);
+
+		/* make sure the entry survives until it is off both lists */
+		fanotify_fastpath_get(entry);
+
+		/* remove from g_list */
+		list_del_init(&entry->g_list);
+		mutex_unlock(&group->fastpath_mutex);
+
+		inode = entry->inode;
+
+		/* remove from i_list */
+		write_lock(&inode->fastpath_rwlock);
+		if (!list_empty(&entry->i_list))
+			list_del_init(&entry->i_list);
+		write_unlock(&inode->fastpath_rwlock);
+
+		/* off both lists, may free now */
+		fanotify_fastpath_put(entry);
+
+		mutex_lock(&group->fastpath_mutex);
+	}
+	mutex_unlock(&group->fastpath_mutex);
+}
+
+void fanotify_fastpath_clear(struct inode *inode)
+{
+	struct fanotify_fastpath_entry *entry;
+	struct fanotify_group *group;
+
+	write_lock(&inode->fastpath_rwlock);
+	while (!list_empty(&inode->fastpath_entries)) {
+		entry = list_first_entry(&inode->fastpath_entries, struct fanotify_fastpath_entry, i_list);
+
+		/* make sure the entry survives until off both lists */
+		fanotify_fastpath_get(entry);
+		list_del_init(&entry->i_list);
+
+		group = entry->group;
+
+		write_unlock(&inode->fastpath_rwlock);
+
+		mutex_lock(&group->fastpath_mutex);
+		if (!list_empty(&entry->g_list))
+			list_del_init(&entry->g_list);
+		mutex_unlock(&group->fastpath_mutex);
+
+		/* off both lists, may kill now */
+		fanotify_fastpath_put(entry);
+
+		write_lock(&inode->fastpath_rwlock);
+	}
+	write_unlock(&inode->fastpath_rwlock);
+}
+
+/*
+ * add the fastpath entry to the in core inode referenced by fd
+ */
+int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned int mask)
+{
+	struct file *file;
+	int fput_needed;
+	int ret = 0;
+	/* we initialize entry to shut up the compiler in case we just to out... */
+	struct fanotify_fastpath_entry *entry = NULL, *lentry;
+	struct inode *inode;
+	bool used = true;
+
+	file = fget_light(fd, &fput_needed);
+	if (!file)
+		return -EBADF;
+
+	inode = file->f_path.dentry->d_inode;
+
+	if (!S_ISREG(inode->i_mode)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* pre allocate an entry so we can hold the writelock */
+	entry = fanotify_fastpath_alloc();
+	if (!entry) {
+		ret = -ENOMEM;
+		/* be sure that used==TRUE here so it doens't try to free */
+		goto out;
+	}
+
+	/*
+	 * this is the only place we hold both the group and the inode lock
+	 * we could take them in either order, but we take the mutex first
+	 * so we don't sleep holding the rwlock
+	 */
+	mutex_lock(&group->fastpath_mutex);
+	write_lock(&inode->fastpath_rwlock);
+	list_for_each_entry(lentry, &inode->fastpath_entries, i_list) {
+		if (lentry->group == group) {
+			lentry->mask |= mask;
+			used = false;
+			goto out_unlock;
+		}
+	}
+
+	atomic_set(&entry->killcnt, 0);
+	entry->group = group;
+	entry->mask = mask;
+	entry->inode = inode;
+
+	list_add(&entry->i_list, &inode->fastpath_entries);
+	list_add(&entry->g_list, &group->fastpath_entries);
+
+out_unlock:
+	write_unlock(&inode->fastpath_rwlock);
+	mutex_unlock(&group->fastpath_mutex);
+out:
+	fput_light(file, fput_needed);
+	if (!used)
+		fanotify_fastpath_kill(entry);
+	return ret;
+}
+
+
+__init int fanotify_fastpath_uninit(void)
+{
+	kmem_cache_destroy(fastpath_kmem_cache);
+	fastpath_kmem_cache = NULL;
+
+	return 0;
+}
+
+__init int fanotify_fastpath_init(void)
+{
+	fastpath_kmem_cache = kmem_cache_create("fanotify_fastpath_entry", sizeof(struct fanotify_fastpath_entry), 0, SLAB_PANIC, NULL);
+
+	return 0;
+}
diff --git a/fs/notify/group.c b/fs/notify/group.c
index fc3d736..ff9b5ef 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -87,6 +87,9 @@ void fanotify_kill_group(struct fanotify_group *group)
 	/* clear the notification queue of all events */
 	fanotify_notification_clear_group(group);
 
+	/* clear all fastpath entries for this group */
+	fanotify_fastpath_clear_group(group);
+
 	kfree(group);
 }
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 76d88fd..e0e63f2 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -71,17 +71,25 @@ struct fanotify_group {
 	struct mutex notification_mutex;/* protect the notification_list */
 	struct list_head notification_list;	/* list of event_holder this group needs to send to userspace */
 	wait_queue_head_t notification_waitq;	/* read() on the notification file blocks on this waitq */
+
+	/* stores all fastapth entries assoc with this group so they can be cleaned on unregister */
+	struct mutex fastpath_mutex;	/* protect fastpath_entries list */
+	struct list_head fastpath_entries; /* all fastpath entries for this group */
 };
 
 #ifdef CONFIG_FANOTIFY
 
 extern void fanotify(struct file *file, unsigned int mask);
+extern void fanotify_inode_delete(struct inode *inode);
 
 extern void fanotify_get_group(struct fanotify_group *group);
 extern struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask);
 extern void fanotify_put_group(struct fanotify_group *group);
 
+/* things called from the socket */
 extern int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock);
+extern int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned int mask);
+
 #else
 
 static inline void fanotify(struct file *file, unsigned int mask)
@@ -103,6 +111,15 @@ static inline int fanotify_create_event_fd(struct fanotify_group *group, struct
 	memset(data, 0, sizeof(struct fanotify_event_metadata));
 	return 0;
 }
+
+static inline int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned int mask)
+{
+	return 0;
+}
+
+static inline void fanotify_inode_delete(struct inode *inode)
+{}
+
 #endif	/* CONFIG_FANOTIFY */
 
 #endif	/* __KERNEL __ */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0dcdd94..0c35206 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -674,6 +674,11 @@ struct inode {
 	struct mutex		inotify_mutex;	/* protects the watches list */
 #endif
 
+#ifdef CONFIG_FANOTIFY
+	struct list_head	fastpath_entries; /* fanotify fastpath entries protected by group */
+	rwlock_t		fastpath_rwlock; /* protect the fastpath entries list */
+#endif
+
 	unsigned long		i_state;
 	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 894f573..522a2d7 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -82,6 +82,14 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
 }
 
 /*
+ * fsnotify_inode_delete - and inode is being evicted from cache, clean up is needed
+ */
+static inline void fsnotify_inode_delete(struct inode *inode)
+{
+	fanotify_inode_delete(inode);
+}
+
+/*
  * fsnotify_inoderemove - an inode is going away
  */
 static inline void fsnotify_inoderemove(struct inode *inode)


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 08/21] fanotify: add a userspace interface for fastpaths
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (6 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 09/21] fanotify: add group priorities Eric Paris
                   ` (12 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

turns out the fastpath code is useless without an interface.  This one
works.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/group.c          |    3 +++
 include/linux/fanotify.h   |    9 +++++++++
 net/fanotify/af_fanotify.c |   36 +++++++++++++++++++++++++++++++++++-
 3 files changed, 47 insertions(+), 1 deletions(-)

diff --git a/fs/notify/group.c b/fs/notify/group.c
index ff9b5ef..21037cc 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -74,6 +74,9 @@ struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int
 	INIT_LIST_HEAD(&group->notification_list);
 	init_waitqueue_head(&group->notification_waitq);
 
+	mutex_init(&group->fastpath_mutex);
+	INIT_LIST_HEAD(&group->fastpath_entries);
+
 	/* add it */
 	list_add_rcu(&group->group_list, &fanotify_groups);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index e0e63f2..0646840 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -52,6 +52,15 @@ struct fanotify_event_metadata {
 /* fanotify getsockopt optvals */
 #define FANOTIFY_GET_EVENT	1
 
+/* struct used for FANOTIFY_SET_FASTPATH */
+struct fanotify_so_fastpath {
+	__s32 fd;
+	__u32 mask;
+}  __attribute__((packed));
+
+/* fanotify setsockopt optvals */
+#define FANOTIFY_SET_FASTPATH	1
+
 #ifdef __KERNEL__
 
 #include <linux/fs.h>
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
index 7474cf1..6f2357c 100644
--- a/net/fanotify/af_fanotify.c
+++ b/net/fanotify/af_fanotify.c
@@ -156,6 +156,40 @@ static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 	return 0;
 }
 
+static int fan_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
+{
+	struct fanotify_sock *fan_sock;
+	struct fanotify_group *group;
+	union {
+		struct fanotify_so_fastpath fastpath;
+	} data;
+	int ret = 0;
+
+	if (sock->state != SS_CONNECTED)
+		return -EBADF;
+
+	if (level != SOL_FANOTIFY)
+		return -ENOPROTOOPT;
+
+	fan_sock = fan_sk(sock->sk);
+	group = fan_sock->group;
+
+	switch(optname) {
+	case FANOTIFY_SET_FASTPATH:
+		if (optlen < sizeof(struct fanotify_so_fastpath))
+			return -ENOMEM;
+		ret = copy_from_user(&data.fastpath, optval, sizeof(struct fanotify_so_fastpath));
+		if (ret)
+			return ret;
+		ret = fanotify_fastpath_add(group, data.fastpath.fd, data.fastpath.mask);
+		break;
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	return ret;
+}
+
 static int fan_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
 {
 	struct fanotify_sock *fan_sock;
@@ -218,7 +252,7 @@ static const struct proto_ops fanotify_proto_ops = {
 	.ioctl =	sock_no_ioctl,
 	.listen =	sock_no_listen,
 	.shutdown =	sock_no_shutdown,
-	.setsockopt =	sock_no_setsockopt,
+	.setsockopt =	fan_setsockopt,
 	.getsockopt =	fan_getsockopt,
 	.sendmsg =	sock_no_sendmsg,
 	.recvmsg =	sock_no_recvmsg,


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 09/21] fanotify: add group priorities
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (7 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 08/21] fanotify: add a userspace interface for fastpaths Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 10/21] fanotify: blocking and access granting Eric Paris
                   ` (11 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

In preperation for blocking fanotify calls group priorities must be added.
async events will be sent as quickly as possible without waiting for a
response.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/group.c          |   30 ++++++++++++++++++++++++------
 include/linux/fanotify.h   |    5 ++++-
 net/fanotify/af_fanotify.c |    2 +-
 3 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/fs/notify/group.c b/fs/notify/group.c
index 21037cc..37b8630 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -42,15 +42,16 @@ void fanotify_get_group(struct fanotify_group *group)
 	atomic_inc(&group->refcnt);
 }
 
-struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask)
+struct fanotify_group *fanotify_find_group(unsigned int priority, unsigned int group_num, unsigned int mask)
 {
 	struct fanotify_group *group_iter;
 	struct fanotify_group *group = NULL;
 
 	mutex_lock(&fanotify_grp_mutex);
 	list_for_each_entry_rcu(group_iter, &fanotify_groups, group_list) {
-		if (group_iter->group_num == group_num) {
-			if (group_iter->mask == mask) {
+		if (group_iter->priority == priority) {
+			if ((group_iter->mask == mask) &&
+			    (group_iter->group_num == group_num)) {
 				fanotify_get_group(group_iter);
 				group = group_iter;
 			} else
@@ -69,6 +70,7 @@ struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int
 
 	group->group_num = group_num;
 	group->mask = mask;
+	group->priority = priority;
 
 	mutex_init(&group->notification_mutex);
 	INIT_LIST_HEAD(&group->notification_list);
@@ -77,9 +79,26 @@ struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int
 	mutex_init(&group->fastpath_mutex);
 	INIT_LIST_HEAD(&group->fastpath_entries);
 
-	/* add it */
-	list_add_rcu(&group->group_list, &fanotify_groups);
+	/* Do we need to be the first entry? */
+	if (list_empty(&fanotify_groups)) {
+		list_add_rcu(&group->group_list, &fanotify_groups);
+		goto out;
+	}
+
+	list_for_each_entry(group_iter, &fanotify_groups, group_list) {
+		/* insert in front of this one? */
+		if (priority < group_iter->priority) {
+			/* I used list_add_tail() to insert in front of group_iter...  */
+			list_add_tail_rcu(&group->group_list, &group_iter->group_list);
+			break;
+		}
 
+		/* are we at the end?  if so insert at end */
+		if (list_is_last(&group_iter->group_list, &fanotify_groups)) {
+			list_add_tail_rcu(&group->group_list, &fanotify_groups);
+			break;
+		}
+	}
 out:
 	mutex_unlock(&fanotify_grp_mutex);
 	return group;
@@ -100,7 +119,6 @@ void fanotify_put_group(struct fanotify_group *group)
 {
 	mutex_lock(&fanotify_grp_mutex);
 	if (atomic_dec_and_test(&group->refcnt)) {
-
 		list_del_rcu(&group->group_list);
 		mutex_unlock(&fanotify_grp_mutex);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 0646840..ae16b17 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -37,6 +37,7 @@
 #include <linux/types.h>
 
 struct fanotify_addr {
+	__u32 priority;
         __u32 group_num;
         __u32 mask;
         __u32 timeout;
@@ -84,6 +85,8 @@ struct fanotify_group {
 	/* stores all fastapth entries assoc with this group so they can be cleaned on unregister */
 	struct mutex fastpath_mutex;	/* protect fastpath_entries list */
 	struct list_head fastpath_entries; /* all fastpath entries for this group */
+
+	unsigned int priority;		/* order this group should receive msgs.  low first */
 };
 
 #ifdef CONFIG_FANOTIFY
@@ -92,7 +95,7 @@ extern void fanotify(struct file *file, unsigned int mask);
 extern void fanotify_inode_delete(struct inode *inode);
 
 extern void fanotify_get_group(struct fanotify_group *group);
-extern struct fanotify_group *fanotify_find_group(unsigned int group_num, unsigned int mask);
+extern struct fanotify_group *fanotify_find_group(unsigned int priority, unsigned int group_num, unsigned int mask);
 extern void fanotify_put_group(struct fanotify_group *group);
 
 /* things called from the socket */
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
index 6f2357c..3b7283e 100644
--- a/net/fanotify/af_fanotify.c
+++ b/net/fanotify/af_fanotify.c
@@ -145,7 +145,7 @@ static int fan_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
 		return -EINVAL;
 
 	fan_sock = fan_sk(sock->sk);
-	fan_sock->group = fanotify_find_group(fan_addr->group_num, fan_addr->mask);
+	fan_sock->group = fanotify_find_group(fan_addr->priority, fan_addr->group_num, fan_addr->mask);
 
 	if (IS_ERR(fan_sock->group))
 		return PTR_ERR(fan_sock->group);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 10/21] fanotify: blocking and access granting
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (8 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 09/21] fanotify: add group priorities Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 11/21] fanotify: give a special access permission check Eric Paris
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

This patch introduces blocking an access granting for fanotify.  Events are
sent to userspace and the original process is blocked (assuming not
O_NONBLOCK) for at most the amount of time determined by the group.  Total
maximum blocking time is the sum of all groups possible blocking time.

O_NONBLOCK processes trying to read and a group registered to require
blocking/access control on read MUST have a fastpath entry in order to ever
make progress.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/aio.c                 |    7 ++
 fs/notify/Makefile       |    2 -
 fs/notify/access.c       |  164 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/notify/fanotify.c     |   46 +++++++++++--
 fs/notify/fanotify.h     |   17 ++++-
 fs/notify/group.c        |    8 ++
 fs/notify/notification.c |    2 +
 fs/open.c                |    5 +
 fs/read_write.c          |    6 ++
 include/linux/fanotify.h |   35 +++++++++-
 10 files changed, 278 insertions(+), 14 deletions(-)
 create mode 100644 fs/notify/access.c

diff --git a/fs/aio.c b/fs/aio.c
index f658441..008de79 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -31,6 +31,7 @@
 #include <linux/workqueue.h>
 #include <linux/security.h>
 #include <linux/eventfd.h>
+#include <linux/fanotify.h>
 
 #include <asm/kmap_types.h>
 #include <asm/uaccess.h>
@@ -1454,6 +1455,9 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
+		ret = fanotify(file, FAN_ACCESS_PERM);
+		if (unlikely(ret))
+			break;
 		ret = aio_setup_single_vector(kiocb);
 		if (ret)
 			break;
@@ -1486,6 +1490,9 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
+		ret = fanotify(file, FAN_ACCESS_PERM);
+		if (unlikely(ret))
+			break;
 		ret = aio_setup_vectored_rw(READ, kiocb);
 		if (ret)
 			break;
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 90cb910..0af890a 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -3,4 +3,4 @@ obj-$(CONFIG_INOTIFY_USER)	+= inotify_user.o
 
 obj-$(CONFIG_DNOTIFY)		+= dnotify.o
 
-obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o group.o fastpath.o
+obj-$(CONFIG_FANOTIFY)		+= fanotify.o notification.o group.o fastpath.o access.o
diff --git a/fs/notify/access.c b/fs/notify/access.c
new file mode 100644
index 0000000..6a2606c
--- /dev/null
+++ b/fs/notify/access.c
@@ -0,0 +1,164 @@
+/*
+ *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/atomic.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/fanotify.h>
+#include "fanotify.h"
+
+int fanotify_add_event_to_access(struct fanotify_group *group, struct fanotify_event *event)
+{
+	struct fanotify_event_holder *holder;
+
+	if (list_empty(&event->holder.event_list))
+		holder = (struct fanotify_event_holder *)event;
+	else
+		holder = alloc_event_holder();
+	if (!holder)
+		return -ENOMEM;
+
+	fanotify_get_event(event);
+
+	mutex_lock(&group->access_mutex);
+	spin_lock(&event->holder_spinlock);
+	holder->event = event;
+	list_add_tail(&holder->event_list, &group->access_list);
+	spin_unlock(&event->holder_spinlock);
+	mutex_unlock(&group->access_mutex);
+
+	return 0;
+}
+
+int fanotify_get_response_from_access(struct fanotify_group *group, struct fanotify_event *event)
+{
+	int ret;
+	ret = wait_event_interruptible_timeout(group->access_waitq,
+						event->response,
+						msecs_to_jiffies(5000));
+	/* Timeout or signal? */
+	if (ret <= 0) {
+		struct fanotify_event_holder *holder;
+
+		/* pull the event off the access_list */
+		mutex_lock(&group->access_mutex);
+		list_for_each_entry(holder, &group->access_list, event_list) {
+			if (holder->event != event)
+				continue;
+
+			spin_lock(&event->holder_spinlock);
+			holder->event = NULL;
+			/* as soon as we do this the event.holder might get reused */
+			list_del_init(&holder->event_list);
+			spin_unlock(&event->holder_spinlock);
+
+			if (event != (struct fanotify_event *)holder)
+				fanotify_destroy_event_holder(holder);
+			fanotify_put_event(event);
+			break;
+		}
+		mutex_unlock(&group->access_mutex);
+
+		/*
+		 * if we took a signal, return ERESTARTSYS
+		 * if we timed out userspace is broken so return ALLOW
+		 */
+		return ret;
+	}
+
+	/* userspace responded, convert to something usable */
+	switch (event->response) {
+	case FAN_ALLOW:
+		return 0;
+	case FAN_DENY:
+	default:
+		return -EPERM;
+	}
+}
+
+int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response)
+{
+	struct fanotify_event *event = NULL;
+	struct fanotify_event_holder *holder;
+
+	/*
+	 * make sure the response is valid, if invalid we do nothing and either
+	 * userspace can send a valid responce or we will clean it up after the
+	 * timeout
+	 */
+	if (response & ~(FAN_ALLOW | FAN_DENY))
+		return -EINVAL;
+
+	mutex_lock(&group->access_mutex);
+	list_for_each_entry(holder, &group->access_list, event_list) {
+		if (holder->event->cookie != cookie)
+			continue;
+
+		event = holder->event;
+		spin_lock(&event->holder_spinlock);
+		holder->event = NULL;
+		/* as soon as we do this the event.holder might be reused */
+		list_del_init(&holder->event_list);
+		spin_unlock(&event->holder_spinlock);
+
+		if (event != (struct fanotify_event *)holder)
+			fanotify_destroy_event_holder(holder);
+		break;
+	}
+	mutex_unlock(&group->access_mutex);
+
+	if (!event)
+		return -ENOENT;
+
+	event->response = response;
+	wake_up(&group->access_waitq);
+	fanotify_put_event(event);
+
+	return 0;
+}
+
+void fanotify_access_clear_group(struct fanotify_group *group)
+{
+	struct fanotify_event *event = NULL;
+	struct fanotify_event_holder *holder;
+
+	mutex_lock(&group->access_mutex);
+	list_for_each_entry(holder, &group->access_list, event_list) {
+		event = holder->event;
+		spin_lock(&event->holder_spinlock);
+		holder->event = NULL;
+		/* as soon as we do this the event.holder might be reused */
+		list_del_init(&holder->event_list);
+		spin_unlock(&event->holder_spinlock);
+
+		if (event != (struct fanotify_event *)holder)
+			fanotify_destroy_event_holder(holder);
+
+		event->response = FAN_ALLOW;
+		wake_up(&group->access_waitq);
+		fanotify_put_event(event);
+	}
+	mutex_unlock(&group->access_mutex);
+}
diff --git a/fs/notify/fanotify.c b/fs/notify/fanotify.c
index aa12e91..d995c14 100644
--- a/fs/notify/fanotify.c
+++ b/fs/notify/fanotify.c
@@ -44,22 +44,22 @@ void fanotify_inode_delete(struct inode *inode)
 }
 EXPORT_SYMBOL_GPL(fanotify_inode_delete);
 
-void fanotify(struct file *file, unsigned int mask)
+int fanotify(struct file *file, unsigned int mask)
 {
 	struct fanotify_group *group;
 	struct fanotify_event *event = NULL;
 	struct task_struct *tsk = current;
 	struct inode *inode = file->f_path.dentry->d_inode;
-	int idx;
+	int idx, ret = 0;
 
 	if (likely(list_empty(&fanotify_groups)))
-		return;
+		return 0;
 
 	if (tsk->flags & PF_NOFACCESS)
-		return;
+		return 0;
 
 	if (!S_ISREG(inode->i_mode))
-		return;
+		return 0;
 
 	if (mask & FAN_MODIFY)
 		fanotify_fastpath_clear(inode);
@@ -86,7 +86,39 @@ void fanotify(struct file *file, unsigned int mask)
 				if (!event)
 					break;
 			}
-			fanotify_add_event_to_notif(group, event);
+			if (mask & FAN_ALL_EVENTS_PERM) {
+				/*
+				 * if you register for READ_ACCESS you MUST be setting
+				 * fastpath events or the client will NEVER make progress.
+				 * open is a blocking event so we don't require fastpath
+				 * entries and will wait for a decision.
+				 *
+				 * someday we could do an add_timer here, schedule a workqueue,
+				 * and in there we could check to see if this file got a fastpath
+				 * groups which don't obey the protocol could be evicted or an
+				 * O_NONBLOCK exclusion added.  There are some locking and
+				 * security implications to doing it that way, so for now we
+				 * are just going to assume userspace is adding fastpaths for
+				 * O_NONBLOCK files.....
+				 */
+				if ((mask & FAN_ACCESS_PERM) && (file->f_flags & O_NONBLOCK)) {
+					fanotify_add_event_to_notif(group, event);
+					ret = -EWOULDBLOCK;
+					break;
+				}
+				event->cookie = atomic_long_inc_return(&group->cookie);
+				event->response = 0;
+				/* put on access_list first so userspace can't be so fast responding it isn't on the list yet */
+				fanotify_add_event_to_access(group, event);
+
+				fanotify_add_event_to_notif(group, event);
+
+				ret = fanotify_get_response_from_access(group, event);
+				if (ret)
+					break;
+			} else {
+				fanotify_add_event_to_notif(group, event);
+			}
 		}
 	}
 	srcu_read_unlock(&fanotify_grp_srcu_struct, idx);
@@ -96,6 +128,8 @@ void fanotify(struct file *file, unsigned int mask)
 	 */
 	if (event)
 		fanotify_put_event(event);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(fanotify);
 
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index bcc3f4b..6423af1 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -14,12 +14,14 @@
 #include <linux/wait.h>
 
 /*
- * A single event can be queued in multiple group->notification_lists.
+ * A single event can be queued in multiple group->notification_lists and to at
+ * most one group->access_list at the same time.
  *
- * each group->notification_list will point to an event_holer which in turns points
- * to the actual event that needs to be sent to userspace.
+ * each group->notification_list or group->access_list will point to an
+ * event_holer which in turns points to the actual event that needs to be sent
+ * to userspace.
  *
- * Seemed cheaper to create a refcnt'd event and a small holder for every group
+ * Seemed cheaper to create a refcnt'd event and a small holder for every list
  * than create a different event for every group
  * 
  */
@@ -44,6 +46,9 @@ struct fanotify_event {
 	struct path path;	/* path from the original access */
 	unsigned int mask;	/* the type of access */
 	atomic_t refcnt;	/* how many groups still are using/need to send this event */
+	/* if waiting for a userspace access answer this is the cookie they will send back */
+	unsigned long cookie;
+	unsigned int response;	/* userspace answer to question */
 };
 
 /*
@@ -94,4 +99,8 @@ extern void fanotify_fastpath_clear(struct inode *inode);
 extern __init int fanotify_fastpath_init(void);
 extern __init int fanotify_fastpath_uninit(void);
 
+extern int fanotify_add_event_to_access(struct fanotify_group *group, struct fanotify_event *event);
+extern int fanotify_get_response_from_access(struct fanotify_group *group, struct fanotify_event *event);
+extern void fanotify_access_clear_group(struct fanotify_group *group);
+
 #endif	/* _LINUX_FANOTIFY_PRIVATE_H */
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 37b8630..68f895e 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -79,6 +79,11 @@ struct fanotify_group *fanotify_find_group(unsigned int priority, unsigned int g
 	mutex_init(&group->fastpath_mutex);
 	INIT_LIST_HEAD(&group->fastpath_entries);
 
+	atomic_set(&group->cookie, 0);
+	mutex_init(&group->access_mutex);
+	INIT_LIST_HEAD(&group->access_list);
+	init_waitqueue_head(&group->access_waitq);
+
 	/* Do we need to be the first entry? */
 	if (list_empty(&fanotify_groups)) {
 		list_add_rcu(&group->group_list, &fanotify_groups);
@@ -112,6 +117,9 @@ void fanotify_kill_group(struct fanotify_group *group)
 	/* clear all fastpath entries for this group */
 	fanotify_fastpath_clear_group(group);
 
+	/* set an allow for all outstanding access decisions */
+	fanotify_access_clear_group(group);
+
 	kfree(group);
 }
 
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index 3dd69ef..89bb02c 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -176,6 +176,7 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 	event->path.mnt = file->f_path.mnt;
 	path_get(&event->path);
 
+	event->cookie = 0;
 	event->mask = mask;
 
 	WARN_ON(!event->path.dentry);
@@ -241,6 +242,7 @@ int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event
 
 	data->fd = client_fd;
 	data->mask = event->mask;
+	data->cookie = event->cookie;
 
 	fanotify_put_event(event);
 
diff --git a/fs/open.c b/fs/open.c
index 9d69dd9..9a77834 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -29,6 +29,7 @@
 #include <linux/rcupdate.h>
 #include <linux/audit.h>
 #include <linux/falloc.h>
+#include <linux/fanotify.h>
 
 int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
@@ -820,6 +821,10 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
 	if (error)
 		goto cleanup_all;
 
+	error = fanotify(f, FAN_OPEN_PERM);
+	if (error)
+		goto cleanup_all;
+
 	if (!open && f->f_op)
 		open = f->f_op->open;
 	if (open) {
diff --git a/fs/read_write.c b/fs/read_write.c
index 7eb2949..6f8cf69 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -16,6 +16,7 @@
 #include <linux/syscalls.h>
 #include <linux/pagemap.h>
 #include <linux/splice.h>
+#include <linux/fanotify.h>
 #include "read_write.h"
 
 #include <asm/uaccess.h>
@@ -223,6 +224,11 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
 				read_write == READ ? MAY_READ : MAY_WRITE);
 	if (retval)
 		return retval;
+	if (read_write == READ) {
+		retval = fanotify(file, FAN_ACCESS_PERM);
+		if (retval)
+			return retval;
+	}
 	return count > MAX_RW_COUNT ? MAX_RW_COUNT : count;
 }
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index ae16b17..83c3d5e 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -17,6 +17,10 @@
 #define FAN_OPEN_NOEXEC		0x00000010	/* File was opened */
 #define FAN_OPEN_EXEC		0x00000020	/* File was opened with the intention of being exec'ed */
 
+/* userspace may also request blocking for permission checks for open and read */
+#define FAN_ACCESS_PERM		0x00000100
+#define FAN_OPEN_PERM		0x00000200
+
 /* FIXME currently Q's have no limit.... */
 #define FAN_Q_OVERFLOW		0x80000000	/* Event queued overflowed */
 
@@ -33,6 +37,16 @@
 			 FAN_MODIFY |\
 			 FAN_CLOSE |\
 			 FAN_OPEN)
+/*
+ * Like the above list of events only these are the event types in which the
+ * kernel will wait for answers.
+ */
+#define FAN_ALL_EVENTS_PERM    (FAN_OPEN_PERM |\
+				FAN_ACCESS_PERM)
+
+/* answers will need to be either allow or deny */
+#define FAN_ALLOW		0x00000001
+#define FAN_DENY		0x00000002
 
 #include <linux/types.h>
 
@@ -48,6 +62,7 @@ struct fanotify_addr {
 struct fanotify_event_metadata {
 	__s32 fd;
 	__u32 mask;
+	__u64 cookie;
 }  __attribute__((packed));
 
 /* fanotify getsockopt optvals */
@@ -86,12 +101,18 @@ struct fanotify_group {
 	struct mutex fastpath_mutex;	/* protect fastpath_entries list */
 	struct list_head fastpath_entries; /* all fastpath entries for this group */
 
+	/* needed to track outstanding requests we expect to hear from userspace */
+	atomic_long_t cookie;		/* next cookie send to userspace for a decision */
+	struct mutex access_mutex;	/* protect access_list list */
+	struct list_head access_list;	/* event_holder we need an answer from userspace */
+	wait_queue_head_t access_waitq;	/* we wait here for userspace access decisions */
+
 	unsigned int priority;		/* order this group should receive msgs.  low first */
 };
 
 #ifdef CONFIG_FANOTIFY
 
-extern void fanotify(struct file *file, unsigned int mask);
+extern int fanotify(struct file *file, unsigned int mask);
 extern void fanotify_inode_delete(struct inode *inode);
 
 extern void fanotify_get_group(struct fanotify_group *group);
@@ -101,11 +122,14 @@ extern void fanotify_put_group(struct fanotify_group *group);
 /* things called from the socket */
 extern int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock);
 extern int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned int mask);
+extern int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response);
 
 #else
 
-static inline void fanotify(struct file *file, unsigned int mask)
-{}
+static inline int fanotify(struct file *file, unsigned int mask)
+{
+	return 0;
+}
 
 static inline void fanotify_get_group(struct fanotify_group *group)
 {}
@@ -132,6 +156,11 @@ static inline int fanotify_fastpath_add(struct fanotify_group *group, int fd, un
 static inline void fanotify_inode_delete(struct inode *inode)
 {}
 
+static inline int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response)
+{
+	return 0;
+}
+
 #endif	/* CONFIG_FANOTIFY */
 
 #endif	/* __KERNEL __ */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 11/21] fanotify: give a special access permission check
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (9 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 10/21] fanotify: blocking and access granting Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:53   ` Christoph Hellwig
  2008-11-12 16:11 ` [PATCH =-v3 12/21] fanotify: user interface for access decisions Eric Paris
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

add a special FANOTIFY_ACCESS_EXEC_PERM check.  This will be applied any
place a process tries to access a file with execute permissions.  Mainly
sys_execve, sys_uselib, and mmap'ing a file for exec.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/aio.c                 |    4 ++--
 fs/exec.c                |   10 ++++++++++
 fs/read_write.c          |    2 +-
 include/linux/fanotify.h |    6 ++++--
 mm/mmap.c                |    7 +++++++
 mm/mprotect.c            |    6 ++++++
 mm/nommu.c               |    7 +++++++
 7 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/fs/aio.c b/fs/aio.c
index 008de79..6395403 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1455,7 +1455,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
-		ret = fanotify(file, FAN_ACCESS_PERM);
+		ret = fanotify(file, FAN_ACCESS_NOEXEC_PERM);
 		if (unlikely(ret))
 			break;
 		ret = aio_setup_single_vector(kiocb);
@@ -1490,7 +1490,7 @@ static ssize_t aio_setup_iocb(struct kiocb *kiocb)
 		ret = security_file_permission(file, MAY_READ);
 		if (unlikely(ret))
 			break;
-		ret = fanotify(file, FAN_ACCESS_PERM);
+		ret = fanotify(file, FAN_ACCESS_NOEXEC_PERM);
 		if (unlikely(ret))
 			break;
 		ret = aio_setup_vectored_rw(READ, kiocb);
diff --git a/fs/exec.c b/fs/exec.c
index 8f56995..3d88fa3 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -137,6 +137,11 @@ asmlinkage long sys_uselib(const char __user * library)
 		goto out;
 
 	fsnotify_open_exec(file);
+	error = fanotify(file, FAN_ACCESS_EXEC_PERM);
+	if (error) {
+		fput(file);
+		goto out;
+	}
 
 	error = -ENOEXEC;
 	if(file->f_op) {
@@ -691,6 +696,11 @@ struct file *open_exec(const char *name)
 		return file;
 
 	fsnotify_open_exec(file);
+	err = fanotify(file, FAN_ACCESS_EXEC_PERM);
+	if (err) {
+		fput(file);
+		goto out;
+	}
 
 	err = deny_write_access(file);
 	if (err) {
diff --git a/fs/read_write.c b/fs/read_write.c
index 6f8cf69..5100cd2 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -225,7 +225,7 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
 	if (retval)
 		return retval;
 	if (read_write == READ) {
-		retval = fanotify(file, FAN_ACCESS_PERM);
+		retval = fanotify(file, FAN_ACCESS_NOEXEC_PERM);
 		if (retval)
 			return retval;
 	}
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 83c3d5e..efc62e9 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -18,8 +18,9 @@
 #define FAN_OPEN_EXEC		0x00000020	/* File was opened with the intention of being exec'ed */
 
 /* userspace may also request blocking for permission checks for open and read */
-#define FAN_ACCESS_PERM		0x00000100
-#define FAN_OPEN_PERM		0x00000200
+#define FAN_ACCESS_NOEXEC_PERM	0x00000100
+#define FAN_ACCESS_EXEC_PERM	0x00000200
+#define FAN_OPEN_PERM		0x00000400
 
 /* FIXME currently Q's have no limit.... */
 #define FAN_Q_OVERFLOW		0x80000000	/* Event queued overflowed */
@@ -27,6 +28,7 @@
 /* helper events */
 #define FAN_CLOSE		(FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
 #define FAN_OPEN		(FAN_OPEN_NOEXEC | FAN_OPEN_EXEC) /* open */
+#define FAN_ACCESS_PERM		(FAN_ACCESS_NOEXEC_PERM | FAN_ACCESS_EXEC_PERM) /* access perm */
 
 /*
  * All of the events - we build the list by hand so that we can add flags in
diff --git a/mm/mmap.c b/mm/mmap.c
index de14ac2..8013252 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/fanotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
 #include <linux/hugetlb.h>
@@ -1027,6 +1028,12 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
 		default:
 			return -EINVAL;
 		}
+
+		if (prot & PROT_EXEC) {
+			error = fanotify(file, FAN_ACCESS_EXEC_PERM);
+			if (error)
+				return error;
+		}
 	} else {
 		switch (flags & MAP_TYPE) {
 		case MAP_SHARED:
diff --git a/mm/mprotect.c b/mm/mprotect.c
index fded06f..af1da2d 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -16,6 +16,7 @@
 #include <linux/fs.h>
 #include <linux/highmem.h>
 #include <linux/security.h>
+#include <linux/fanotify.h>
 #include <linux/mempolicy.h>
 #include <linux/personality.h>
 #include <linux/syscalls.h>
@@ -294,6 +295,11 @@ sys_mprotect(unsigned long start, size_t len, unsigned long prot)
 		if (error)
 			goto out;
 
+		if (vma->vm_file && (prot & PROT_EXEC))
+			error = fanotify(vma->vm_file, FAN_ACCESS_EXEC_PERM);
+		if (error)
+			goto out;
+
 		tmp = vma->vm_end;
 		if (tmp > end)
 			tmp = end;
diff --git a/mm/nommu.c b/mm/nommu.c
index 7695dc8..d376916 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -18,6 +18,7 @@
 #include <linux/mman.h>
 #include <linux/swap.h>
 #include <linux/file.h>
+#include <linux/fanotify.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
@@ -731,6 +732,12 @@ static int validate_mmap_request(struct file *file,
 			/* backing file is not executable, try to copy */
 			capabilities &= ~BDI_CAP_MAP_DIRECT;
 		}
+
+		if (prot & PROT_EXEC) {
+			ret = fanotify(file, FAN_ACCESS_EXEC_PERM);
+			if (ret)
+				return ret;
+		}
 	}
 	else {
 		/* anonymous mappings are always memory backed and can be


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 12/21] fanotify: user interface for access decisions
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (10 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 11/21] fanotify: give a special access permission check Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 13/21] fanotify: ability for userspace to delay responses Eric Paris
                   ` (8 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

turns out that access decisions are useless if there is no way to do
anything about them.  So we add an interface.  Alan will hate it, but at
least it works...

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 include/linux/fanotify.h   |    7 +++++++
 net/fanotify/af_fanotify.c |    9 +++++++++
 2 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index efc62e9..7bc15ec 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -76,8 +76,15 @@ struct fanotify_so_fastpath {
 	__u32 mask;
 }  __attribute__((packed));
 
+/* struct used for FANOTIFY_SEND_RESPONSE */
+struct fanotify_so_access {
+	__u64 cookie;
+	__u32 response;
+}  __attribute__((packed));
+
 /* fanotify setsockopt optvals */
 #define FANOTIFY_SET_FASTPATH	1
+#define FANOTIFY_SEND_RESPONSE	2
 
 #ifdef __KERNEL__
 
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
index 3b7283e..eef7e2a 100644
--- a/net/fanotify/af_fanotify.c
+++ b/net/fanotify/af_fanotify.c
@@ -162,6 +162,7 @@ static int fan_setsockopt(struct socket *sock, int level, int optname, char __us
 	struct fanotify_group *group;
 	union {
 		struct fanotify_so_fastpath fastpath;
+		struct fanotify_so_access access;
 	} data;
 	int ret = 0;
 
@@ -183,6 +184,14 @@ static int fan_setsockopt(struct socket *sock, int level, int optname, char __us
 			return ret;
 		ret = fanotify_fastpath_add(group, data.fastpath.fd, data.fastpath.mask);
 		break;
+	case FANOTIFY_SEND_RESPONSE:
+		if (optlen < sizeof(struct fanotify_so_access))
+			return -ENOMEM;
+		ret = copy_from_user(&data.access, optval, sizeof(struct fanotify_so_access));
+		if (ret)
+			return ret;
+		ret = fanotify_process_access_response(group, data.access.cookie, data.access.response);
+		break;
 	default:
 		return -ENOPROTOOPT;
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 13/21] fanotify: ability for userspace to delay responses
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (11 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 12/21] fanotify: user interface for access decisions Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 14/21] fanotify: send pid with fanotify notification events Eric Paris
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Userspace may know that it is behind, may know it is going to take a while
to get a good response, or have any number of other reasons to block an
access request.  FAN_RESTTIMER will reset the timeout for an fanotify
access decision response.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/access.c       |   31 +++++++++++++++++++++++++++----
 fs/notify/fanotify.h     |    1 +
 fs/notify/notification.c |    2 ++
 include/linux/fanotify.h |    1 +
 4 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/fs/notify/access.c b/fs/notify/access.c
index 6a2606c..5b3b32e 100644
--- a/fs/notify/access.c
+++ b/fs/notify/access.c
@@ -55,6 +55,8 @@ int fanotify_add_event_to_access(struct fanotify_group *group, struct fanotify_e
 int fanotify_get_response_from_access(struct fanotify_group *group, struct fanotify_event *event)
 {
 	int ret;
+
+retry:
 	ret = wait_event_interruptible_timeout(group->access_waitq,
 						event->response,
 						msecs_to_jiffies(5000));
@@ -89,13 +91,25 @@ int fanotify_get_response_from_access(struct fanotify_group *group, struct fanot
 	}
 
 	/* userspace responded, convert to something usable */
+	spin_lock(&event->response_lock);
 	switch (event->response) {
 	case FAN_ALLOW:
-		return 0;
+		ret = 0;
+		break;
 	case FAN_DENY:
+		ret = -EPERM;
+		break;
+	case FAN_RESETTIMER:
+		event->response = 0;
+		spin_unlock(&event->response_lock);
+		goto retry;
 	default:
-		return -EPERM;
+		ret = -EPERM;
+		break;
 	}
+	spin_unlock(&event->response_lock);
+
+	return ret;
 }
 
 int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response)
@@ -108,7 +122,7 @@ int fanotify_process_access_response(struct fanotify_group *group, unsigned long
 	 * userspace can send a valid responce or we will clean it up after the
 	 * timeout
 	 */
-	if (response & ~(FAN_ALLOW | FAN_DENY))
+	if (response & ~(FAN_ALLOW | FAN_DENY | FAN_RESETTIMER))
 		return -EINVAL;
 
 	mutex_lock(&group->access_mutex);
@@ -117,6 +131,11 @@ int fanotify_process_access_response(struct fanotify_group *group, unsigned long
 			continue;
 
 		event = holder->event;
+
+		/* FAN_RESETTIMER should be left on the list for later....  */
+		if (response == FAN_RESETTIMER)
+			break;
+
 		spin_lock(&event->holder_spinlock);
 		holder->event = NULL;
 		/* as soon as we do this the event.holder might be reused */
@@ -132,9 +151,13 @@ int fanotify_process_access_response(struct fanotify_group *group, unsigned long
 	if (!event)
 		return -ENOENT;
 
+	spin_lock(&event->response_lock);
 	event->response = response;
+	spin_unlock(&event->response_lock);
 	wake_up(&group->access_waitq);
-	fanotify_put_event(event);
+
+	if (response != FAN_RESETTIMER)
+		fanotify_put_event(event);
 
 	return 0;
 }
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 6423af1..4f42a1f 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -48,6 +48,7 @@ struct fanotify_event {
 	atomic_t refcnt;	/* how many groups still are using/need to send this event */
 	/* if waiting for a userspace access answer this is the cookie they will send back */
 	unsigned long cookie;
+	spinlock_t response_lock; /* protects response */
 	unsigned int response;	/* userspace answer to question */
 };
 
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index 89bb02c..dd0d2db 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -27,6 +27,7 @@
 #include <linux/path.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
 
 #include <linux/fanotify.h>
 #include "fanotify.h"
@@ -169,6 +170,7 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 	event->holder.event = NULL;
 	INIT_LIST_HEAD(&event->holder.event_list);
 	atomic_set(&event->refcnt, 1);
+	spin_lock_init(&event->response_lock);
 
 	spin_lock_init(&event->holder_spinlock);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 7bc15ec..b01309e 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -49,6 +49,7 @@
 /* answers will need to be either allow or deny */
 #define FAN_ALLOW		0x00000001
 #define FAN_DENY		0x00000002
+#define FAN_RESETTIMER		0x00000004
 
 #include <linux/types.h>
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 14/21] fanotify: send pid with fanotify notification events
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (12 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 13/21] fanotify: ability for userspace to delay responses Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 15/21] fanotify: send tgid with notification messages Eric Paris
                   ` (6 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Often things like AV scanners may want to allow access to 'bad' files
based on the process making the access help by sending the pid of the
originally acting process.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/fanotify.h     |    1 +
 fs/notify/notification.c |    3 +++
 include/linux/fanotify.h |    1 +
 3 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 4f42a1f..55b0bab 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -48,6 +48,7 @@ struct fanotify_event {
 	atomic_t refcnt;	/* how many groups still are using/need to send this event */
 	/* if waiting for a userspace access answer this is the cookie they will send back */
 	unsigned long cookie;
+	pid_t pid;		/* pid of the original process */
 	spinlock_t response_lock; /* protects response */
 	unsigned int response;	/* userspace answer to question */
 };
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index dd0d2db..da2045a 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -181,6 +181,8 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 	event->cookie = 0;
 	event->mask = mask;
 
+	event->pid = current->pid;
+
 	WARN_ON(!event->path.dentry);
 	WARN_ON(!event->path.mnt);
 
@@ -245,6 +247,7 @@ int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event
 	data->fd = client_fd;
 	data->mask = event->mask;
 	data->cookie = event->cookie;
+	data->pid = event->pid;
 
 	fanotify_put_event(event);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index b01309e..a5cded3 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -66,6 +66,7 @@ struct fanotify_event_metadata {
 	__s32 fd;
 	__u32 mask;
 	__u64 cookie;
+	pid_t pid;
 }  __attribute__((packed));
 
 /* fanotify getsockopt optvals */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 15/21] fanotify: send tgid with notification messages
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (13 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 14/21] fanotify: send pid with fanotify notification events Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 16/21] fanotify: send file f_flags along with notifications Eric Paris
                   ` (5 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Send tgid of original process to fanotify listeners

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/fanotify.h     |    1 +
 fs/notify/notification.c |    2 ++
 include/linux/fanotify.h |    1 +
 3 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 55b0bab..e6d7898 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -49,6 +49,7 @@ struct fanotify_event {
 	/* if waiting for a userspace access answer this is the cookie they will send back */
 	unsigned long cookie;
 	pid_t pid;		/* pid of the original process */
+	pid_t tgid;		/* tgid of the original process */
 	spinlock_t response_lock; /* protects response */
 	unsigned int response;	/* userspace answer to question */
 };
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index da2045a..acd13b0 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -182,6 +182,7 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 	event->mask = mask;
 
 	event->pid = current->pid;
+	event->tgid = current->tgid;
 
 	WARN_ON(!event->path.dentry);
 	WARN_ON(!event->path.mnt);
@@ -248,6 +249,7 @@ int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event
 	data->mask = event->mask;
 	data->cookie = event->cookie;
 	data->pid = event->pid;
+	data->tgid = event->tgid;
 
 	fanotify_put_event(event);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index a5cded3..6cbcfc3 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -67,6 +67,7 @@ struct fanotify_event_metadata {
 	__u32 mask;
 	__u64 cookie;
 	pid_t pid;
+	pid_t tgid;
 }  __attribute__((packed));
 
 /* fanotify getsockopt optvals */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 16/21] fanotify: send file f_flags along with notifications
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (14 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 15/21] fanotify: send tgid with notification messages Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:11 ` [PATCH =-v3 17/21] fanotify: add option to clear all fastpaths Eric Paris
                   ` (4 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

fanotify listeners may be interested in what flags the file associated with
a particular event were set.  This sends f_flags along with the event
metadata.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/fanotify.h     |    1 +
 fs/notify/notification.c |    2 ++
 include/linux/fanotify.h |    1 +
 3 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index e6d7898..2721da2 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -50,6 +50,7 @@ struct fanotify_event {
 	unsigned long cookie;
 	pid_t pid;		/* pid of the original process */
 	pid_t tgid;		/* tgid of the original process */
+	unsigned int f_flags;	/* f_flags from the original file */
 	spinlock_t response_lock; /* protects response */
 	unsigned int response;	/* userspace answer to question */
 };
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index acd13b0..4d1d046 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -183,6 +183,7 @@ struct fanotify_event *create_event(struct file *file, unsigned int mask)
 
 	event->pid = current->pid;
 	event->tgid = current->tgid;
+	event->f_flags = file->f_flags;
 
 	WARN_ON(!event->path.dentry);
 	WARN_ON(!event->path.mnt);
@@ -250,6 +251,7 @@ int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event
 	data->cookie = event->cookie;
 	data->pid = event->pid;
 	data->tgid = event->tgid;
+	data->f_flags = event->f_flags;
 
 	fanotify_put_event(event);
 
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 6cbcfc3..14ad6f9 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -68,6 +68,7 @@ struct fanotify_event_metadata {
 	__u64 cookie;
 	pid_t pid;
 	pid_t tgid;
+	__u32 f_flags;
 }  __attribute__((packed));
 
 /* fanotify getsockopt optvals */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 17/21] fanotify: add option to clear all fastpaths
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (15 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 16/21] fanotify: send file f_flags along with notifications Eric Paris
@ 2008-11-12 16:11 ` Eric Paris
  2008-11-12 16:12 ` [PATCH =-v3 18/21] fanotify: all userspace to set timeouts Eric Paris
                   ` (3 subsequent siblings)
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:11 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

fanotify users because of a change in decision policy may need to clear all
fastpath entries.  This simply adds a new setsockopt call which will do
exactly that.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 include/linux/fanotify.h   |    1 +
 net/fanotify/af_fanotify.c |    3 +++
 2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 14ad6f9..b7ab01f 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -89,6 +89,7 @@ struct fanotify_so_access {
 /* fanotify setsockopt optvals */
 #define FANOTIFY_SET_FASTPATH	1
 #define FANOTIFY_SEND_RESPONSE	2
+#define FANOTIFY_CLEAR_ALL_FASTPATH	3
 
 #ifdef __KERNEL__
 
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
index eef7e2a..7c98a22 100644
--- a/net/fanotify/af_fanotify.c
+++ b/net/fanotify/af_fanotify.c
@@ -192,6 +192,9 @@ static int fan_setsockopt(struct socket *sock, int level, int optname, char __us
 			return ret;
 		ret = fanotify_process_access_response(group, data.access.cookie, data.access.response);
 		break;
+	case FANOTIFY_CLEAR_ALL_FASTPATH:
+		fanotify_fastpath_clear_group(group);
+		break;
 	default:
 		return -ENOPROTOOPT;
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 18/21] fanotify: all userspace to set timeouts
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (16 preceding siblings ...)
  2008-11-12 16:11 ` [PATCH =-v3 17/21] fanotify: add option to clear all fastpaths Eric Paris
@ 2008-11-12 16:12 ` Eric Paris
  2008-11-12 16:56   ` Christoph Hellwig
  2008-11-12 16:12 ` [PATCH =-v3 19/21] fanotify: evict misbehaving clients Eric Paris
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:12 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

fanotify when used to make access decisions has a default 5 second timeout.
This patch will allow userspace listeners to change their timeout on a
group wide basis.  Userspace listeners will still be able to reset the
timeout for individual events.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/access.c         |    2 +-
 fs/notify/fanotify.h       |    1 -
 fs/notify/group.c          |    2 ++
 include/linux/fanotify.h   |    8 ++++++++
 net/fanotify/af_fanotify.c |    9 +++++++++
 5 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/fs/notify/access.c b/fs/notify/access.c
index 5b3b32e..0c04a60 100644
--- a/fs/notify/access.c
+++ b/fs/notify/access.c
@@ -59,7 +59,7 @@ int fanotify_get_response_from_access(struct fanotify_group *group, struct fanot
 retry:
 	ret = wait_event_interruptible_timeout(group->access_waitq,
 						event->response,
-						msecs_to_jiffies(5000));
+						msecs_to_jiffies(group->timeout));
 	/* Timeout or signal? */
 	if (ret <= 0) {
 		struct fanotify_event_holder *holder;
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index 2721da2..a370ff9 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -98,7 +98,6 @@ extern __init int fanotify_notification_uninit(void);
 extern void fanotify_fastpath_get(struct fanotify_fastpath_entry *entry);
 extern void fanotify_fastpath_put(struct fanotify_fastpath_entry *entry);
 extern int fanotify_is_fastpath(struct file *file, unsigned int mask, struct fanotify_group *group);
-extern void fanotify_fastpath_clear_group(struct fanotify_group *group);
 extern void fanotify_fastpath_clear(struct inode *inode);
 extern __init int fanotify_fastpath_init(void);
 extern __init int fanotify_fastpath_uninit(void);
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 68f895e..650f31e 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -84,6 +84,8 @@ struct fanotify_group *fanotify_find_group(unsigned int priority, unsigned int g
 	INIT_LIST_HEAD(&group->access_list);
 	init_waitqueue_head(&group->access_waitq);
 
+	group->timeout = 5000;
+
 	/* Do we need to be the first entry? */
 	if (list_empty(&fanotify_groups)) {
 		list_add_rcu(&group->group_list, &fanotify_groups);
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index b7ab01f..c6a2a3c 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -90,6 +90,7 @@ struct fanotify_so_access {
 #define FANOTIFY_SET_FASTPATH	1
 #define FANOTIFY_SEND_RESPONSE	2
 #define FANOTIFY_CLEAR_ALL_FASTPATH	3
+#define FANOTIFY_SET_TIMEOUT		4
 
 #ifdef __KERNEL__
 
@@ -121,6 +122,8 @@ struct fanotify_group {
 	struct list_head access_list;	/* event_holder we need an answer from userspace */
 	wait_queue_head_t access_waitq;	/* we wait here for userspace access decisions */
 
+	unsigned int timeout;		/* timeout to wait for access requests in msec */
+
 	unsigned int priority;		/* order this group should receive msgs.  low first */
 };
 
@@ -137,6 +140,7 @@ extern void fanotify_put_group(struct fanotify_group *group);
 extern int fanotify_create_event_fd(struct fanotify_group *group, struct fanotify_event_metadata *data, int nonblock);
 extern int fanotify_fastpath_add(struct fanotify_group *group, int fd, unsigned int mask);
 extern int fanotify_process_access_response(struct fanotify_group *group, unsigned long cookie, unsigned int response);
+extern void fanotify_fastpath_clear_group(struct fanotify_group *group);
 
 #else
 
@@ -175,6 +179,10 @@ static inline int fanotify_process_access_response(struct fanotify_group *group,
 	return 0;
 }
 
+static inline  void fanotify_fastpath_clear_group(struct fanotify_group *group)
+{}
+
+
 #endif	/* CONFIG_FANOTIFY */
 
 #endif	/* __KERNEL __ */
diff --git a/net/fanotify/af_fanotify.c b/net/fanotify/af_fanotify.c
index 7c98a22..f56984e 100644
--- a/net/fanotify/af_fanotify.c
+++ b/net/fanotify/af_fanotify.c
@@ -163,6 +163,7 @@ static int fan_setsockopt(struct socket *sock, int level, int optname, char __us
 	union {
 		struct fanotify_so_fastpath fastpath;
 		struct fanotify_so_access access;
+		uint32_t timeout;
 	} data;
 	int ret = 0;
 
@@ -195,6 +196,14 @@ static int fan_setsockopt(struct socket *sock, int level, int optname, char __us
 	case FANOTIFY_CLEAR_ALL_FASTPATH:
 		fanotify_fastpath_clear_group(group);
 		break;
+	case FANOTIFY_SET_TIMEOUT:
+		if (optlen < sizeof(uint32_t))
+			return -ENOMEM;
+		ret = copy_from_user(&data.timeout, optval, sizeof(uint32_t));
+		if (ret)
+			return ret;
+		group->timeout = data.timeout;
+		break;
 	default:
 		return -ENOPROTOOPT;
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 19/21] fanotify: evict misbehaving clients
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (17 preceding siblings ...)
  2008-11-12 16:12 ` [PATCH =-v3 18/21] fanotify: all userspace to set timeouts Eric Paris
@ 2008-11-12 16:12 ` Eric Paris
  2008-11-12 16:12 ` [PATCH =-v3 20/21] fanotify: allow fastpath entries to survive inode modification Eric Paris
  2008-11-12 16:12 ` [PATCH =-v3 21/21] fanotify: add Documentation Eric Paris
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:12 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

fanotify clients that fail to respond to access requests withing their
timeout or which registered for read permissions checking but fail to then
set fastpaths on O_NONBLOCK files should be evicted.  I don't want
misbehaving clients to be able to bring down the system.

This patch tracks the number of times we hit one of these catagories.  If
we have 10 more misses than hits we will evict the group from the global
fanotify group list.

Signed-off-by: Eric Paris <eparis@redhat.com>

register for read permission checks and do not then
set fastpaths on
---

 fs/notify/access.c       |   35 +++++++++++++++++++++++++++++++++++
 fs/notify/fanotify.h     |    1 +
 fs/notify/group.c        |   21 ++++++++++++++++++++-
 include/linux/fanotify.h |    6 +++++-
 4 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/fs/notify/access.c b/fs/notify/access.c
index 0c04a60..73b40e1 100644
--- a/fs/notify/access.c
+++ b/fs/notify/access.c
@@ -29,6 +29,35 @@
 #include <linux/fanotify.h>
 #include "fanotify.h"
 
+static inline void userspace_success(struct fanotify_group *group)
+{
+	if (likely(group->misbehave_count == 0))
+		return;
+
+	spin_lock(&group->misbehave_lock);
+	if (group->misbehave_count > 0)
+		group->misbehave_count--;
+	spin_unlock(&group->misbehave_lock);
+}
+
+static inline void userspace_error(struct fanotify_group *group)
+{
+	int evict = 0;
+	spin_lock(&group->misbehave_lock);
+	group->misbehave_count++;
+	if (group->misbehave_count > 10) {
+		group->misbehave_count = 10;
+		evict = 1;
+		/* don't allow it to wrap */
+		if (unlikely(group->misbehave_count >= INT_MAX))
+			group->misbehave_count = 10;
+	}
+	spin_unlock(&group->misbehave_lock);
+
+	if (evict)
+		fanotify_evict_group(group);
+}
+
 int fanotify_add_event_to_access(struct fanotify_group *group, struct fanotify_event *event)
 {
 	struct fanotify_event_holder *holder;
@@ -83,6 +112,10 @@ retry:
 		}
 		mutex_unlock(&group->access_mutex);
 
+		/* we timed out, userspace screwed up */
+		if (!ret) {
+			userspace_error(group);
+		}
 		/*
 		 * if we took a signal, return ERESTARTSYS
 		 * if we timed out userspace is broken so return ALLOW
@@ -90,6 +123,8 @@ retry:
 		return ret;
 	}
 
+	userspace_success(group);
+
 	/* userspace responded, convert to something usable */
 	spin_lock(&event->response_lock);
 	switch (event->response) {
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index a370ff9..b31e19a 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -82,6 +82,7 @@ struct fanotify_fastpath_entry {
 
 extern struct srcu_struct fanotify_grp_srcu_struct;
 extern struct list_head fanotify_groups;
+extern void fanotify_evict_group(struct fanotify_group *group);
 
 extern int fanotify_check_notif_queue(struct fanotify_group *group);
 extern void fanotify_get_event(struct fanotify_event *event);
diff --git a/fs/notify/group.c b/fs/notify/group.c
index 650f31e..377195c 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -86,6 +86,10 @@ struct fanotify_group *fanotify_find_group(unsigned int priority, unsigned int g
 
 	group->timeout = 5000;
 
+	group->evicted = 0;
+	spin_lock_init(&group->misbehave_lock);
+	group->misbehave_count = 0;
+
 	/* Do we need to be the first entry? */
 	if (list_empty(&fanotify_groups)) {
 		list_add_rcu(&group->group_list, &fanotify_groups);
@@ -125,11 +129,26 @@ void fanotify_kill_group(struct fanotify_group *group)
 	kfree(group);
 }
 
+/*
+ * called when a group is misbehaving and needs to be kicked out.  group will
+ * not get really cleared up until clients exit, but it won't be able to hurt
+ * the system any more.
+ */
+void fanotify_evict_group(struct fanotify_group *group)
+{
+	mutex_lock(&fanotify_grp_mutex);
+	if (!group->evicted)
+		list_del_rcu(&group->group_list);
+	group->evicted = 1;
+	mutex_unlock(&fanotify_grp_mutex);
+}
+
 void fanotify_put_group(struct fanotify_group *group)
 {
 	mutex_lock(&fanotify_grp_mutex);
 	if (atomic_dec_and_test(&group->refcnt)) {
-		list_del_rcu(&group->group_list);
+		if (!group->evicted)
+			list_del_rcu(&group->group_list);
 		mutex_unlock(&fanotify_grp_mutex);
 
 		synchronize_srcu(&fanotify_grp_srcu_struct);
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index c6a2a3c..7a7bef6 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -104,7 +104,8 @@ struct fanotify_so_access {
 struct fanotify_group {
 	struct list_head group_list;	/* list of all groups on the system */
 	unsigned int group_num;		/* the 'name' of the event */
-	unsigned int mask;			/* mask of events this group cares about */
+	unsigned int mask;		/* mask of events this group cares about */
+	unsigned int evicted;		/* 1 if this group has been evicted for misbehaving */
 	atomic_t refcnt;		/* num of processes with a special file open */
 
 	/* needed to send notification to userspace */
@@ -124,6 +125,9 @@ struct fanotify_group {
 
 	unsigned int timeout;		/* timeout to wait for access requests in msec */
 
+	unsigned int misbehave_count;	/* net misbehaviours.  increment on failure, dec on success */
+	spinlock_t misbehave_lock;	/* protect misbehave_count */
+
 	unsigned int priority;		/* order this group should receive msgs.  low first */
 };
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 20/21] fanotify: allow fastpath entries to survive inode modification
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (18 preceding siblings ...)
  2008-11-12 16:12 ` [PATCH =-v3 19/21] fanotify: evict misbehaving clients Eric Paris
@ 2008-11-12 16:12 ` Eric Paris
  2008-11-12 16:12 ` [PATCH =-v3 21/21] fanotify: add Documentation Eric Paris
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:12 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

A listener may wish to choose to ignore inodes even if they have been
modified.  Setting a fastpath with FAN_SURVIVE_WRITE in the mask
will cause the fastpath to not be removed on inode modification.  The
only way to clear this fastpath will be to either clear all fastpaths for
the group, have the inode evicted from core, or to destroy the group and
create a new one.  This is useful for things like a DB file where the
scanner will not able or interested in hearing about events even if the
file is modified.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 fs/notify/fanotify.c     |    4 ++--
 fs/notify/fanotify.h     |    4 +++-
 fs/notify/fastpath.c     |   36 +++++++++++++++++++++++++++++++++++-
 include/linux/fanotify.h |    3 +++
 4 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/fs/notify/fanotify.c b/fs/notify/fanotify.c
index d995c14..aeb2225 100644
--- a/fs/notify/fanotify.c
+++ b/fs/notify/fanotify.c
@@ -40,7 +40,7 @@ void fanotify_inode_delete(struct inode *inode)
 	if (likely(list_empty(&fanotify_groups)))
 		return;
 
-	fanotify_fastpath_clear(inode);
+	fanotify_fastpath_clear(inode, FASTPATH_CLEAR_WRITE);
 }
 EXPORT_SYMBOL_GPL(fanotify_inode_delete);
 
@@ -62,7 +62,7 @@ int fanotify(struct file *file, unsigned int mask)
 		return 0;
 
 	if (mask & FAN_MODIFY)
-		fanotify_fastpath_clear(inode);
+		fanotify_fastpath_clear(inode, FASTPATH_SAVE_WRITE);
 	/*
 	 * SRCU!!  the groups list is very very much read only and the path is
 	 * very hot (assuming something is using fanotify)  Not blocking while
diff --git a/fs/notify/fanotify.h b/fs/notify/fanotify.h
index b31e19a..bda16d3 100644
--- a/fs/notify/fanotify.h
+++ b/fs/notify/fanotify.h
@@ -96,10 +96,12 @@ extern void fanotify_destroy_event_holder(struct fanotify_event_holder *holder);
 extern __init int fanotify_notification_init(void);
 extern __init int fanotify_notification_uninit(void);
 
+#define FASTPATH_CLEAR_WRITE	0
+#define FASTPATH_SAVE_WRITE	1
 extern void fanotify_fastpath_get(struct fanotify_fastpath_entry *entry);
 extern void fanotify_fastpath_put(struct fanotify_fastpath_entry *entry);
 extern int fanotify_is_fastpath(struct file *file, unsigned int mask, struct fanotify_group *group);
-extern void fanotify_fastpath_clear(struct inode *inode);
+extern void fanotify_fastpath_clear(struct inode *inode, int save_write);
 extern __init int fanotify_fastpath_init(void);
 extern __init int fanotify_fastpath_uninit(void);
 
diff --git a/fs/notify/fastpath.c b/fs/notify/fastpath.c
index c4114af..840754d 100644
--- a/fs/notify/fastpath.c
+++ b/fs/notify/fastpath.c
@@ -118,15 +118,49 @@ void fanotify_fastpath_clear_group(struct fanotify_group *group)
 	mutex_unlock(&group->fastpath_mutex);
 }
 
-void fanotify_fastpath_clear(struct inode *inode)
+void fanotify_fastpath_clear(struct inode *inode, int save_write)
 {
 	struct fanotify_fastpath_entry *entry;
 	struct fanotify_group *group;
+	struct fanotify_fastpath_entry *first_moved = NULL;
 
 	write_lock(&inode->fastpath_rwlock);
 	while (!list_empty(&inode->fastpath_entries)) {
 		entry = list_first_entry(&inode->fastpath_entries, struct fanotify_fastpath_entry, i_list);
 
+		/*
+		 * Basic idea is that we move entries that need to survive
+		 * to the end of the list and stop deleting when we hit the
+		 * first entry we moved to the back.
+		 *
+		 * Be careful though because first_moved could be freed as
+		 * soon as we drop the fastpath_rwlock either because its
+		 * group is being freed or because there was a group wide
+		 * fastpath flush.  So no dereferencing first_moved!
+		 */
+		if ((entry->mask & FAN_SURVIVE_WRITE) && (save_write == FASTPATH_SAVE_WRITE)) {
+			/*
+			 * if this is the first fastpath for this inode that is
+			 * surviving we need to remember it so we know when we
+			 * walked the entire list of fastpaths
+			 */
+			if (!first_moved)
+				first_moved = entry;
+			/*
+			 * If we saved this entry previously and found it on
+			 * the list a second time that means we walked the
+			 * entire list.  So we are finished
+			 */
+			else if (first_moved == entry)
+				break;
+
+			/* We need to kick this inode to the end. */
+			list_move_tail(&entry->i_list, &inode->fastpath_entries);
+
+			/* done with this fastpath, leave the rwlock held */
+			continue;
+		}
+
 		/* make sure the entry survives until off both lists */
 		fanotify_fastpath_get(entry);
 		list_del_init(&entry->i_list);
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 7a7bef6..fdd7a79 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -22,6 +22,9 @@
 #define FAN_ACCESS_EXEC_PERM	0x00000200
 #define FAN_OPEN_PERM		0x00000400
 
+/* any fastpath with this set will not be cleared on inode modification */
+#define FAN_SURVIVE_WRITE	0x00001000
+
 /* FIXME currently Q's have no limit.... */
 #define FAN_Q_OVERFLOW		0x80000000	/* Event queued overflowed */
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH =-v3 21/21] fanotify: add Documentation
  2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
                   ` (19 preceding siblings ...)
  2008-11-12 16:12 ` [PATCH =-v3 20/21] fanotify: allow fastpath entries to survive inode modification Eric Paris
@ 2008-11-12 16:12 ` Eric Paris
  20 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 16:12 UTC (permalink / raw)
  To: linux-kernel, malware-list; +Cc: viro, alan, arjan, greg, tytso, akpm

Add an example/test program which exercises all of fanotify's abilities and
include all of my writeup about fanotify in Documentation.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 Documentation/filesystems/fanotify/fanotify.txt    |  214 +++++++++++++++++
 .../filesystems/fanotify/fanotify_tester.c         |  255 ++++++++++++++++++++
 2 files changed, 469 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/filesystems/fanotify/fanotify.txt
 create mode 100644 Documentation/filesystems/fanotify/fanotify_tester.c

diff --git a/Documentation/filesystems/fanotify/fanotify.txt b/Documentation/filesystems/fanotify/fanotify.txt
new file mode 100644
index 0000000..b07d401
--- /dev/null
+++ b/Documentation/filesystems/fanotify/fanotify.txt
@@ -0,0 +1,214 @@
+fanotify is a new fscking all notify subsystem.  Much as inotify
+provides notification of filesystem activity for some registered subset
+of inodes fanotify provides notification of filesystem activity for ALL
+of the system's S_ISREG() files.  fanotify has a smaller number of
+notifications than inotify.
+
+User interface work can be found in net/fanotify.  Events are pulled from
+the kernel using getsockopt() and responses are sent to the kernel using
+setsockopt.  An example program which implements most if not all of the
+fanotify features can be found in the documentation directory. 
+
+The main implementation is in fs/notify/.  My layout is like so
+
+fanotify.c     --- all functions called from the main kernel
+group.c        --- groups are my implementation of my multiple listeners
+                   you can register different groups to get different
+                   event types.
+notification.c --- implementation surrounding the sending of events to a
+                   userspace listener.
+fastpath.c     --- implementation surrounding the addition of inode
+                   fastpath entries for performance.
+access.c       --- implementation surrounding the processing of
+                   responses from userspace when the events require
+                   a response.
+
+GROUPS AND EVENTS:
+A "group" registers with fanotify and in doing so indicates what that
+group should be called, what types of events it wishes to receive and
+in what order it should receive events relative to other groups.  An
+"event" is simply a notification about some filesystem action.  The list
+of all fanotify events are read, write, open_for_exec, open,
+close_was_not_writable, close_was_writable, open_need_access_decision,
+read_for exec_need_access_decision, read_need access_decisions.  Any number
+of groups may be created for any subset of event types.  One group may
+register to get reads and writes while another maybe register for opens
+and read_need_access_desision.
+
+Any number of userspace listeners may be active in a single group.  Each
+group will get ONE copy of a given filesystem event.  If there are 10
+listeners in a single group and one fanotify event is generated only ONE
+of those listeners will get the event.  If more than one group registers
+for the same type of event one listener in EACH GROUP will get a copy of
+that event.
+
+It all starts when a listener registers a group.  Registering a group
+is as done by binding group information to a fanotify socket.  Simple
+operation is something like.
+
+addr.name = 123
+addr.priority = 456
+addr.mask = 0x002
+
+sock = socket(PF_FANOTIFY);
+fd = bind(sock, addr);
+
+123 is just the name of the group, used so it is unlikely two listeners
+will accidentally use the same priority and mask and stumble into each
+others events.  456 is the priority (only interesting for blocking/access
+events, will describe later) and 0x002 is FAN_WRITE.  If one wanted read
+and write you would use 0x03 = (FAN_ACCESS | FAN_WRITE).  If this is the
+first time a process has bound to this address a struct fanotify_group
+is allocated and initialized (see fanotify_find_group()).  The group is
+added to a kernel global list called groups.
+
+The listener should now call getsockopt(fd, FANOTIFY_GET_EVENT,...).
+Since at this point there are no events this will block waiting for an
+event.
+
+Now lets say the original process calls open().  Open is going to happen
+exactly as before until it gets to the fsnotify code (this is where both
+inotify and dnotify hook into the kernel.)  From fsnotify we will call
+into the function fanotify() with the mask FAN_OPEN.  We will then walk
+to global groups list (which is ordered by priority, low first) looking
+for any groups which want to receive notification about FAN_OPEN and
+let's say we will find the group '123' that was registered above. An
+fanotify_event is allocated and any data we want the listener process to
+get about the original process is added to the fanotify_event.  The
+event contains a struct path with the dentry and vfsmount from the open
+done by the original process.
+
+Now we call add_event_to_group_notification() to add the event to the
+group->notification_list.  This function has a little bit of magic.
+Since an event may be needed in multiple groups notification_list I
+created a helper structure, a struct fanotify_event_holder.  Each entry
+in the group->event_list points to a unique event_holder which in turn
+points to the ref counted event in question.
+
+(assuming 2 groups)
+group1->notification_list ==> fanotify_event_holder1 ==> single_fanotify_event
+group2->notification_list ==> fanotify_event_holder2 ==> single_fanotify_event
+
+The magic is that since we will always need at least 1 holder I embedded
+one fanotify_event_holder inside an fanotity_event.  This means that
+when removing an event from the group->notification_list we may need to
+free the fanotify_event_holder (if it was allocated seperately) or we
+may need to just clear it (if it was the embedded holder.)
+
+After the event is added to the group->notification_list we wake up the
+listener processes.  The original process never blocked and at this
+point and is returning to userspace with the completed open.
+
+Simultaneously the listener process will now remove the event from the
+group->notification_list, see remove_event_from_group_notification().
+Create a new file, fd, and install such in the listener process, see
+fanotify_notification_read().  We will put_event (since this group
+is finished with it) and will return the getsockopt() call to userspace.
+
+The listener process will get a structure filled back from teh
+getsockopt call which will include an fd and some metadata about that
+fd.  This includes things like the original files f_flags, the original
+processes pid and things like that.
+
+The listener process must call close() when it is finished with this
+new fd.  But lets assume the listener, for whatever reason, decides it
+doesn't want to hear any more of this type of message for this inode.
+That means the listener process needs to "create a fastpath" entry.  To
+do this the listener process will need to call setsockopt(fd,
+FANOTIY_SET_FASTPATH, ...) the struct with the setsockopt will include
+things like what kind of events we don't want to hear about and what
+file this is associated with.  All fastpaths will be cleared on the next
+fsnotify write event (happens the same place in the code as mtime
+update)
+
+Inside the kernel (fanotify_fastpath_add()) what happens is that we will
+create a new fanotify_fastpath_entry for the group and mask in question
+and attach it to the inode.  The next time a process opens the inode in
+question we will search the global groups list for a group that matches
+the mask and we will look at the inode to see if there is a fastpath
+entry for this group and mask.  If there is an entry no event will be
+added to the group->event_list.
+
+The need for fastpaths (or calling what it really is, an in kernel
+cache) has been questioned.  I decided to include a little unscientific data
+here.  On a 32 way machine a make -j 32 took about 9 minutes 12 seconds.
+With that same machine running having one group and 32 listeners receiving
+every fanotify event that the kernel could send to userspace while the 
+listeners were responding to accesses as fast as they could (just a very tight get
+event, allow loop) it took 95 minutes 12 seconds.  Same process with in kernel
+fastpaths/cache results took 19 minutes 5 seconds.  More reasonable event
+requirements and a single listener took 10 minutes 35 sec.  So about a 15%
+perf hit to do any kind of permission checking to userspace.  Anyway, the need
+for fastpaths is quite clear.
+
+Assuming the event was for FAN_OPEN_PERM much of the above is the
+same.  Biggest difference is that the place from the kernel we will call
+into the fanotify code is different (fsnotify is not in a good place to
+provide security hooks).  If a group is found that wants this event the
+event is added to the group->access_list AND to the group->event_list.
+The original process is then blocked for a (now fixed 5 second) timeout
+waiting for the event to get a non-zero event->response on the
+group->access_waitq.
+
+The listener process will get a notification exactly as above from the
+notification file but this time will need to write an answer to the
+access file.  The answer is again a simple string indicating the cookie
+and the response (allow/deny).  If a response is received from userspace
+the event is removed from the group->access_list and the original
+process is woken up to continue, either by looking for the next group or
+by returning -EPERM.  Userspace may also return FAN_RESETTIMER which
+will reset the 5 second timeout.  A badly behaving userspace may hang an
+open indeffinetly.
+
+If the original process times out waiting for the listener process to
+give a response we currently just allow the security access.
+
+An interesting part of the code is fastpath cleanup handling.  Any time
+fanotify gets a FAN_MODIFY event we clear the fastpath entries for the
+associated inode.  This means our notification and access decisions are
+NOT race free but the races are small.  This is not perfect security.  A
+'problomatic' sequence of events would be like such
+
+process1 calls read on file
+listener scans file and finds it safe
+process2 writes to file
+listener creates a fastpath entry for file
+
+Maybe I should add an explicit clear fastpath request so userspace can
+close that race when it gets the write notification on file (if it so
+chooses.)  Remember I'm not trying to protect against a rogue process
+intelligently and actively attacking the system.  I'm trying to stop a
+correctly functioning yum from reading a bad rpm that it downloaded.
+I'm trying to stop an NFS server from accepting bad files and then
+apache serving them out on the net.
+
+There is the also a race between when a process calls mmap() (at that
+point we get an event and scan) and when the process actually faults in
+a page which may have been changed by a write from another process.  Its
+not perfect, but it's damn sure better than what we have now.
+
+Object lifetime:
+
+fanotify_group - exists from registration to unregistration.
+Unregistration racing with some the associated special file notification
+open was a bitch to figure out but eventually I based it on the groups
+existence in the global groups list.
+
+fanotify_event - created inside the main fanotify loop which runs the
+events list.  An event lasts until both the main loop ends AND the event
+is no longer needed by all groups for which it was queued.  A refcnt on
+the event is taken every time it is added to a group notification/access
+list and is dropped when the group has removed the event from the list
+and is finished with its contents.
+
+fanotify_event_holder - allocated when an event is added to a group
+notification/access list.  destroyed when an event is removed from a
+notification/access list.  There is the special case of the embedded
+holder inside the fanotify_event.  The embedded holder is assumed to be
+available for use if holder->event_list is empty.
+
+fanotify_fastpath_entry - created when a process writes to the fastpath
+special file and added to the inode list.  This entry is destroy in 3
+possible places.  If an inode has a modify event we flush them all.  If
+an inode is eviced from core we flush them all.  If a group is
+unregistered we flush them all for that group.
diff --git a/Documentation/filesystems/fanotify/fanotify_tester.c b/Documentation/filesystems/fanotify/fanotify_tester.c
new file mode 100644
index 0000000..ba5e830
--- /dev/null
+++ b/Documentation/filesystems/fanotify/fanotify_tester.c
@@ -0,0 +1,255 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+#include "fanotify.h"
+
+typedef void (*sighandler_t)(int);
+
+#ifndef SOL_FANOTIFY
+#define SOL_FANOTIFY 276
+#endif
+
+#ifndef AF_FANOTIFY
+#define AF_FANOTIFY 36
+#endif
+
+#ifndef PF_FANOTIFY
+#define PF_FANOTIFY AF_FANOTIFY
+#endif
+
+#define SEND_FASTPATH		0x01
+#define SEND_ACCESS_ALLOW	0x02
+#define SEND_ACCESS_DENY	0x04
+#define SEND_DELAY		0x08
+#define SEND_SURVIVE_WRITE	0x10
+
+#define SEND_ACCESS		(SEND_ACCESS_ALLOW | SEND_ACCESS_DENY)
+
+/* file descriptor we use for fanotify */
+int fan_fd;
+int get_evicted = 0;
+
+static int __send_fastpath(int fd, unsigned int mask)
+{
+	struct fanotify_so_fastpath fastpath;
+
+	fastpath.fd = fd;
+	fastpath.mask = mask;
+
+	return setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_SET_FASTPATH, &fastpath, sizeof(struct fanotify_so_fastpath));
+}
+
+static int send_fastpath(struct fanotify_event_metadata *data)
+{
+	return __send_fastpath(data->fd, data->mask);
+}
+
+static int send_survive_write(struct fanotify_event_metadata *data)
+{
+	return __send_fastpath(data->fd, FAN_SURVIVE_WRITE);
+}
+
+static int send_access(struct fanotify_event_metadata *data, int allow)
+{
+	struct fanotify_so_access access;
+
+	access.cookie = data->cookie;
+	if (allow)
+		access.response = FAN_ALLOW;
+	else
+		access.response = FAN_DENY;
+
+	return setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_SEND_RESPONSE, &access, sizeof(struct fanotify_so_access));
+}
+
+static int send_delay(struct fanotify_event_metadata *data)
+{
+	struct fanotify_so_access access;
+
+	access.cookie = data->cookie;
+	access.response = FAN_RESETTIMER;
+
+	return setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_SEND_RESPONSE, &access, sizeof(struct fanotify_so_access));
+}
+
+/* timeout is in msec */
+static int send_set_timeout(int timeout)
+{
+	return setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_SET_TIMEOUT, &timeout, sizeof(int));
+}
+
+static void clear_all_fastpath(int signum __attribute__((unused)))
+{
+	setsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_CLEAR_ALL_FASTPATH, NULL, 0);
+}
+
+static int get_response_opts(struct fanotify_event_metadata *data, char *path)
+{
+	unsigned int flags = 0;
+
+	if (data->fd < 0)
+		return flags;
+
+	if (data->mask & FAN_ALL_EVENTS_PERM)
+		flags = SEND_ACCESS_ALLOW;
+
+	if (!strcmp("/root/denyme", path)) {
+		flags &= ~SEND_ACCESS_ALLOW;
+		flags |= SEND_ACCESS_DENY;
+	} else if (!strcmp("/root/ignoreme", path)) {
+		flags = 0;
+	} else if (!strcmp("/root/delayme", path)) {
+		if (data->mask & FAN_ALL_EVENTS_PERM)
+			flags |= SEND_DELAY;
+	} else if (!strcmp("/root/surviveme", path)) {
+		flags |= (SEND_SURVIVE_WRITE | SEND_FASTPATH);
+	} else if (!strcmp("/root/evictme", path)) {
+		get_evicted = 1;
+	} else {
+		flags |= SEND_FASTPATH;
+
+		if (data->mask & FAN_ALL_EVENTS_PERM)
+			flags |= SEND_ACCESS_ALLOW;
+	}
+
+	if (get_evicted)
+		flags = 0;
+
+	return flags;
+}
+
+static int bind_fan_fd(char *argv[])
+{
+	struct fanotify_addr addr;
+	unsigned int priority;
+	unsigned int group_num;
+	unsigned int mask;
+
+	group_num = atoi(argv[1]);
+	priority = atoi(argv[2]);
+	mask = strtol(argv[3], (char **) NULL, 16);
+
+	memset(&addr, 0, sizeof(struct fanotify_addr));
+	addr.group_num = group_num;
+	addr.priority = priority;
+	addr.mask = mask;
+
+	return bind(fan_fd, (struct sockaddr *)&addr, sizeof(struct fanotify_addr));
+}
+
+int main(int argc, char *argv[])
+{
+	int ret;
+	unsigned long cnt;
+	socklen_t len;
+	sighandler_t sig;
+	struct fanotify_event_metadata metadata;
+	char readbuf[4096];
+	char path[4096];
+	unsigned int response_opts;
+
+	if (argc != 4) {
+		fprintf(stderr, "usage: %s group_num priority mask\n", argv[0]);
+		return 1;
+	}
+
+	sig = signal(SIGUSR1, clear_all_fastpath);
+	if (sig == SIG_ERR) {
+		fprintf(stderr, "unable to set the signal handler\n");
+		return 1;
+	}
+
+	fan_fd = socket(PF_FANOTIFY, SOCK_RAW, 0);
+	if (fan_fd == -1) {
+		perror("Creating socket");
+		return 1;
+	}
+
+	ret = bind_fan_fd(argv);
+	if (ret) {
+		perror("Binding");
+		goto out;
+	}
+
+	ret = send_set_timeout(1000);
+	if (ret) {
+		perror("Setting timeout");
+		goto out;
+	}
+
+	cnt = 0;
+
+	while (1) {
+		len = sizeof(struct fanotify_event_metadata);
+		/* get an event */
+		ret = getsockopt(fan_fd, SOL_FANOTIFY, FANOTIFY_GET_EVENT, &metadata, &len);
+		if (ret) {
+			perror("getsockopt");
+			goto out;
+		}
+
+		/* get the path */
+		if (metadata.fd >= 0) {
+			snprintf(readbuf, sizeof(readbuf), "/proc/self/fd/%d", metadata.fd);
+			ret = readlink(readbuf, path, sizeof(path)-1);
+			if (ret > 0)
+				path[ret] = '\0';
+			else
+				strcpy(path, "<error>");
+		} else {
+			strcpy(path, strerror(-metadata.fd));
+		}
+
+		/* using the event and path figure out how to respond */
+		response_opts = get_response_opts(&metadata, path);
+
+		/* should we send a fastpath? */
+		if (response_opts & SEND_FASTPATH) {
+			ret = send_fastpath(&metadata);
+			if (ret)
+				perror("Sending fastpath");
+		}
+
+		/* should out fastpath entry survive writes? */
+		if (response_opts & SEND_SURVIVE_WRITE) {
+			ret = send_survive_write(&metadata);
+			if (ret)
+				perror("Sending survive write");
+		}
+
+		/* should we delay this operation? */
+		if (response_opts & SEND_DELAY) {
+			int i;
+			for (i = 0; i < 14; i++) {
+				ret = send_delay(&metadata);
+				if (ret)
+					perror("Sending delay");
+				/* timeout is 1,000,000 so sleep for 750,000 */
+				usleep(750000);
+			}
+		}
+		/* do we need to send an access response? */
+		if (response_opts & SEND_ACCESS) {
+			ret = send_access(&metadata, !!(response_opts & SEND_ACCESS_ALLOW));
+			if (ret)
+				perror("Sending access");
+		}
+
+		/* dump something on the console so we know what events we took */
+		fprintf(stdout, "#%lu: mask:%x cookie:%llu fd:%d pid:%d tgid:%d f_flags:%x path:%s\n",
+			cnt++, metadata.mask, metadata.cookie, metadata.fd, metadata.pid, metadata.tgid,
+			metadata.f_flags, path);
+
+		/* if we got an open fd we need to close it */
+		if (metadata.fd >= 0)
+			close(metadata.fd);
+	};
+
+out:
+	close(fan_fd);
+	return ret;
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify
  2008-11-12 16:10 ` [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify Eric Paris
@ 2008-11-12 16:49   ` Christoph Hellwig
  2008-11-12 21:15     ` Eric Paris
  0 siblings, 1 reply; 34+ messages in thread
From: Christoph Hellwig @ 2008-11-12 16:49 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, Nov 12, 2008 at 11:10:47AM -0500, Eric Paris wrote:
> sys_execve and sys_uselib do not call into fsnotify so inotify, dnotify,
> and importantly to me fanotify do not see opens on things which are going
> to be exectued.  Create a generic fsnotify hook for these paths.

This should be submitted as a bugfix on it's own to the current
codebase.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:11 ` [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes Eric Paris
@ 2008-11-12 16:50   ` Christoph Hellwig
  2008-11-12 16:56     ` Alan Cox
  2008-11-12 22:38   ` Peter Zijlstra
  1 sibling, 1 reply; 34+ messages in thread
From: Christoph Hellwig @ 2008-11-12 16:50 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, Nov 12, 2008 at 11:11:03AM -0500, Eric Paris wrote:
> Allows a process to ignore certain events for the given in core inodes.
> This does not preclude the need to keep a userspace or xattr based
> cache if a fanotifier listener wants something with a different lifetime
> than in core inode.

Please just do this in userspace instead of bloating the inode.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 11/21] fanotify: give a special access permission check
  2008-11-12 16:11 ` [PATCH =-v3 11/21] fanotify: give a special access permission check Eric Paris
@ 2008-11-12 16:53   ` Christoph Hellwig
  2008-11-12 21:23     ` Eric Paris
  0 siblings, 1 reply; 34+ messages in thread
From: Christoph Hellwig @ 2008-11-12 16:53 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, Nov 12, 2008 at 11:11:24AM -0500, Eric Paris wrote:
>  	fsnotify_open_exec(file);
> +	error = fanotify(file, FAN_ACCESS_EXEC_PERM);
> +	if (error) {
> +		fput(file);
> +		goto out;
> +	}

Adding your own fanotify calls next to the fsnotify calls completely
defeats th purpose of these.  Please make sure we have one set of hooks.
And given that we now have three notification schemes I think it's
getting time that you also unifify the actuall backends (buffering, etc)
instead of faning out at the fsnotify layer and just leave dnotify and
inotify as tiny userspace interface layers.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 18/21] fanotify: all userspace to set timeouts
  2008-11-12 16:12 ` [PATCH =-v3 18/21] fanotify: all userspace to set timeouts Eric Paris
@ 2008-11-12 16:56   ` Christoph Hellwig
  2008-11-12 21:14     ` Eric Paris
  0 siblings, 1 reply; 34+ messages in thread
From: Christoph Hellwig @ 2008-11-12 16:56 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, Nov 12, 2008 at 11:12:01AM -0500, Eric Paris wrote:
> fanotify when used to make access decisions has a default 5 second timeout.
> This patch will allow userspace listeners to change their timeout on a
> group wide basis.  Userspace listeners will still be able to reset the
> timeout for individual events.

Really, I think you start to over-engineer.  I think this whole stuff
would have a much higher chance if you could come up with a staged set
of patchsets, ala:

 (1) unify the current *notify schemes
 (2) add a filesystem wide notify scheme
 (3) add blocking filesystem notifiers

and then maybe a fourth with all the misc little things that I doubt
will have much of a chance.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:50   ` Christoph Hellwig
@ 2008-11-12 16:56     ` Alan Cox
  2008-11-12 16:58       ` Christoph Hellwig
  0 siblings, 1 reply; 34+ messages in thread
From: Alan Cox @ 2008-11-12 16:56 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Eric Paris, linux-kernel, malware-list, viro, arjan, greg, tytso,
	akpm

On Wed, 12 Nov 2008 11:50:55 -0500
Christoph Hellwig <hch@infradead.org> wrote:

> On Wed, Nov 12, 2008 at 11:11:03AM -0500, Eric Paris wrote:
> > Allows a process to ignore certain events for the given in core inodes.
> > This does not preclude the need to keep a userspace or xattr based
> > cache if a fanotifier listener wants something with a different lifetime
> > than in core inode.
> 
> Please just do this in userspace instead of bloating the inode.

Sounds a good way of ruining the performance.

Alan

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:56     ` Alan Cox
@ 2008-11-12 16:58       ` Christoph Hellwig
  2008-11-12 20:52         ` Eric Paris
  0 siblings, 1 reply; 34+ messages in thread
From: Christoph Hellwig @ 2008-11-12 16:58 UTC (permalink / raw)
  To: Alan Cox
  Cc: Christoph Hellwig, Eric Paris, linux-kernel, malware-list, viro,
	arjan, greg, tytso, akpm

On Wed, Nov 12, 2008 at 04:56:38PM +0000, Alan Cox wrote:
> Sounds a good way of ruining the performance.

I don't think you can ruine performance of a system with AV systems
running even more.  While bloating the inode does cause enormous
problems in file serving or other extremly metadata intensive workloads.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:58       ` Christoph Hellwig
@ 2008-11-12 20:52         ` Eric Paris
  2009-12-08 15:22           ` John Ogness
  0 siblings, 1 reply; 34+ messages in thread
From: Eric Paris @ 2008-11-12 20:52 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Alan Cox, linux-kernel, malware-list, viro, arjan, greg, tytso,
	akpm

On Wed, 2008-11-12 at 11:58 -0500, Christoph Hellwig wrote:
> On Wed, Nov 12, 2008 at 04:56:38PM +0000, Alan Cox wrote:
> > Sounds a good way of ruining the performance.
> 
> I don't think you can ruine performance of a system with AV systems
> running even more.  While bloating the inode does cause enormous
> problems in file serving or other extremly metadata intensive workloads.

Kernel Build on a 32 way machine:
Stock kernel: 9 minutes 12 seconds
fanotify no in kernel fastpath: 95 minutes 12 seconds
Only events AV wants with in kernel fastpath: 10 minutes 35 seconds

Now I could probably redo the in kernel cache as some sort of per group
inode hash table of entries which wouldn't have to bloat the inode but
it would be much more expensive in terms of performance.  A lock and an
list if you want to use fanotify is as small as it can be made an is
exactly the same method taken by inotify.

************

I probably should have put this up in the patch description rather than
burried down in the Documentation directory but here it is:

The need for fastpaths (or calling what it really is, an in kernel
cache) has been questioned.  I decided to include a little unscientific data
here.  On a 32 way machine a make -j 32 took about 9 minutes 12 seconds.
With that same machine running having one group and 32 listeners receiving
every fanotify event that the kernel could send to userspace while the 
listeners were responding to accesses as fast as they could (just a very tight get
event, allow loop) it took 95 minutes 12 seconds.  Same process with in kernel
fastpaths/cache results took 19 minutes 5 seconds.  More reasonable event
requirements and a single listener took 10 minutes 35 sec.  So about a 15%
perf hit to do any kind of permission checking to userspace.  Anyway, the need
for fastpaths is quite clear.





^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 18/21] fanotify: all userspace to set timeouts
  2008-11-12 16:56   ` Christoph Hellwig
@ 2008-11-12 21:14     ` Eric Paris
  0 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 21:14 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, 2008-11-12 at 11:56 -0500, Christoph Hellwig wrote:
> On Wed, Nov 12, 2008 at 11:12:01AM -0500, Eric Paris wrote:
> > fanotify when used to make access decisions has a default 5 second timeout.
> > This patch will allow userspace listeners to change their timeout on a
> > group wide basis.  Userspace listeners will still be able to reset the
> > timeout for individual events.
> 
> Really, I think you start to over-engineer.  I think this whole stuff
> would have a much higher chance if you could come up with a staged set
> of patchsets, ala:
> 
>  (1) unify the current *notify schemes
>  (2) add a filesystem wide notify scheme
>  (3) add blocking filesystem notifiers
> 
> and then maybe a fourth with all the misc little things that I doubt
> will have much of a chance.

This patch set is mostly lined up from most useful to least and every
patch comiles and runs on its own.  The request for per listener
timeouts is based on the array of potential users.  Mainly it was put in
to allow HSMs to set a high timeout while they ran off and pulled a tape
out of the robot.  I already let them reset the timeout if they need
more time, so I'm find with dropping it if it is so hard to handle.

I'll take a look at #1 but #2 and #3 is exactly what I did.  I added
fanotify.  I added an interface for it.  And then I flushed out #2 to
the point it was useful on it's own (and off list I've gotten e-mail
indicating we have non-AV/malware people interested in using this part
of fanotify )

#3 is filled out from patches 10-13.  14+ are all misc but aside from
this particular patch I can't see any other way to achieve the intended
goals.

I'd love to hear any ideas on how to unitfy the three.  Heck I'd be glad
to hear any idea how to unify even inotify and dnotify since all three
have such wildly different lifetimes and implementations..

I'll poke at this but for now I see my patches as (mostly) clean,
useful, and complete.

-Eric


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify
  2008-11-12 16:49   ` Christoph Hellwig
@ 2008-11-12 21:15     ` Eric Paris
  0 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 21:15 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, 2008-11-12 at 11:49 -0500, Christoph Hellwig wrote:
> On Wed, Nov 12, 2008 at 11:10:47AM -0500, Eric Paris wrote:
> > sys_execve and sys_uselib do not call into fsnotify so inotify, dnotify,
> > and importantly to me fanotify do not see opens on things which are going
> > to be exectued.  Create a generic fsnotify hook for these paths.
> 
> This should be submitted as a bugfix on it's own to the current
> codebase.

I'll look into this.



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 11/21] fanotify: give a special access permission check
  2008-11-12 16:53   ` Christoph Hellwig
@ 2008-11-12 21:23     ` Eric Paris
  0 siblings, 0 replies; 34+ messages in thread
From: Eric Paris @ 2008-11-12 21:23 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

On Wed, 2008-11-12 at 11:53 -0500, Christoph Hellwig wrote:
> On Wed, Nov 12, 2008 at 11:11:24AM -0500, Eric Paris wrote:
> >  	fsnotify_open_exec(file);
> > +	error = fanotify(file, FAN_ACCESS_EXEC_PERM);
> > +	if (error) {
> > +		fput(file);
> > +		goto out;
> > +	}
> 
> Adding your own fanotify calls next to the fsnotify calls completely
> defeats th purpose of these.  Please make sure we have one set of hooks.

Absolutely in this case.  Would you prefer I funneled ALL fanotify
blocking calls through fsnotify?  I did back in patch #10 add fanotify
calls next to some security calls (it also might be possible for me to
pull the fsnotify calls of these paths up rather than introduce new
fsnotify hooks, I'm not yet certain.)

> And given that we now have three notification schemes I think it's
> getting time that you also unifify the actuall backends (buffering, etc)
> instead of faning out at the fsnotify layer and just leave dnotify and
> inotify as tiny userspace interface layers.

doing something like this would obviously require in kernel fastpaths
(as i'm sure you are aware.)   I guess then I get to use the space in
inode of both dnotify and inotify.  Like I said I'll try to see what I
can do down this path.

No matter what I think this is going to have to be merged first as
fanotify is quite broad in comparison to either d or i notify.

-Eric


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 16:11 ` [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes Eric Paris
  2008-11-12 16:50   ` Christoph Hellwig
@ 2008-11-12 22:38   ` Peter Zijlstra
  1 sibling, 0 replies; 34+ messages in thread
From: Peter Zijlstra @ 2008-11-12 22:38 UTC (permalink / raw)
  To: Eric Paris
  Cc: linux-kernel, malware-list, viro, alan, arjan, greg, tytso, akpm

 On Wed, 2008-11-12 at 11:11 -0500, Eric Paris wrote:
> +#ifdef CONFIG_FANOTIFY
> +       struct list_head        fastpath_entries; /* fanotify fastpath entries protected by group */
> +       rwlock_t                fastpath_rwlock; /* protect the fastpath entries list */
> +#endif

Are you really sure those rwlocks actually gain you performance?

rwlocks are really bad, the critical section must be short because its
non-preemptable, but both the lock and unlock are atomic ops that can
(and usually will) bounce cachelines. So you often get into the
situation where your performance is limited by the cacheline bouncing.

There's also a starvation case in there, where the cacheline is so hot
on one package/node that another package/node doesn't get it.

Regular spinlocks are usually faster in those cases (and on those archs
that have ticket locks avoid the starvation case too).

And for those cases where the read side is long enough to not be
dominated by the cacheline bouncing, RCU is usually the best way out.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes
  2008-11-12 20:52         ` Eric Paris
@ 2009-12-08 15:22           ` John Ogness
  0 siblings, 0 replies; 34+ messages in thread
From: John Ogness @ 2009-12-08 15:22 UTC (permalink / raw)
  To: Eric Paris
  Cc: Christoph Hellwig, Alan Cox, linux-kernel, malware-list, viro,
	arjan, greg, tytso, akpm

On 2008-11-12, Eric Paris <eparis@redhat.com> wrote:
> Kernel Build on a 32 way machine:
> Stock kernel: 9 minutes 12 seconds
> fanotify no in kernel fastpath: 95 minutes 12 seconds
> Only events AV wants with in kernel fastpath: 10 minutes 35 seconds

Can you provide the number of calls into the listeners for each type of
fanotify event? It would provide a better picture of the scope of the
issue.

I find your non-fastpath numbers quite alarming.

The DazukoFS stackable filesystem can also be used for userspace
listeners to acknowledge/handle filesystem events. (I only mention
DazukoFS here so that there is some kind of metric comparison that can
be used.) I find a 15% overhead in communication to be too
expensive. And a 934.7% performance hit is... well...

The DazukoFS stackable filesystem only provides file open events, but
its numbers may still be of interest here:

kernel build on tmpfs:          72m36.884s
kernel build on tmpfs+DazukoFS: 75m30.569s

That is an overhead of 4%. Your results show that fanotify (with
fastpaths!) has an overhead of 15%.

My test was done using Linux 2.6.32 and DazukoFS 3.1.2. The Linux source
and build were located on tmpfs to minimize hard drive hits. The machine
was a 600MHz PowerPC, which is why the build times were so long.

During the test, the DazukoFS listeners recorded a total of 991,907 file
open events.

IMHO event filtering should happen in userspace. My experience is that
the context switches can be quite cheap. Perhaps if we know which
fanotify events (and how many) occur within your test, we can begin to
understand why your results are what they are.

John Ogness

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2009-12-08 15:25 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-11-12 16:10 [PATCH =-v3 00/21] fanotify: novel file access notification and permission system Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 01/21] filesystem notification: create fs/notify to contain all fs notification Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 02/21] fsnotify: pass a file instead of an inode to open, read, and write Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 03/21] fanotify: fscking all notify, system wide file access notification Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 04/21] fsnotify: sys_execve and sys_uselib do not call into fsnotify Eric Paris
2008-11-12 16:49   ` Christoph Hellwig
2008-11-12 21:15     ` Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 05/21] fanotify: make use of the new fsnotify_open_exec calls Eric Paris
2008-11-12 16:10 ` [PATCH =-v3 06/21] fanotify: add a userspace interface for fanotify notifications Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 07/21] fanotify: fastpath to ignore certain in core inodes Eric Paris
2008-11-12 16:50   ` Christoph Hellwig
2008-11-12 16:56     ` Alan Cox
2008-11-12 16:58       ` Christoph Hellwig
2008-11-12 20:52         ` Eric Paris
2009-12-08 15:22           ` John Ogness
2008-11-12 22:38   ` Peter Zijlstra
2008-11-12 16:11 ` [PATCH =-v3 08/21] fanotify: add a userspace interface for fastpaths Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 09/21] fanotify: add group priorities Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 10/21] fanotify: blocking and access granting Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 11/21] fanotify: give a special access permission check Eric Paris
2008-11-12 16:53   ` Christoph Hellwig
2008-11-12 21:23     ` Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 12/21] fanotify: user interface for access decisions Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 13/21] fanotify: ability for userspace to delay responses Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 14/21] fanotify: send pid with fanotify notification events Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 15/21] fanotify: send tgid with notification messages Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 16/21] fanotify: send file f_flags along with notifications Eric Paris
2008-11-12 16:11 ` [PATCH =-v3 17/21] fanotify: add option to clear all fastpaths Eric Paris
2008-11-12 16:12 ` [PATCH =-v3 18/21] fanotify: all userspace to set timeouts Eric Paris
2008-11-12 16:56   ` Christoph Hellwig
2008-11-12 21:14     ` Eric Paris
2008-11-12 16:12 ` [PATCH =-v3 19/21] fanotify: evict misbehaving clients Eric Paris
2008-11-12 16:12 ` [PATCH =-v3 20/21] fanotify: allow fastpath entries to survive inode modification Eric Paris
2008-11-12 16:12 ` [PATCH =-v3 21/21] fanotify: add Documentation Eric Paris

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox