Skip to contentSkip to conversation

2.6-mm inotify update

PATCH In review Robert Love 1 participant 2 messages May 3, 2005

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 2.6-mm inotify update

Andrew,

Following patch is an update (not a replacement, I'll send that next if you prefer) to inotify in 2.6.12-rc3-mm2. Please apply.

This patch contains the following changes:

- Add a "Rationale" section to the inotify documentation and move the design FAQ there from the ChangeLog. We'll just stuff every conceivable design question in the documentation for all eternity.

- Mask out the user-space mask, allowing only legal bits to be set on a new watch. Return EINVAL if nothing is left.

- New default limits: 8192 watches, 128 devices, and 8192 events. This is an increase in events, which take up very little room, and a decrease in watches.

- Add the IN_ISDIR flag. For any event, the presence of this flag delineates whether the event occurred to a directory or a file. This allows us to remove the _FILE and _SUBDIR events.

- Misc. cleanup and optimizations

Please, apply.

Robert Love

 Documentation/filesystems/inotify.txt |   42 +++++++++++++
 fs/inotify.c                          |   23 ++++---
 fs/namei.c                            |    6 -
 include/linux/fsnotify.h              |  105 +++++++++++++++++++++-------------
 include/linux/inotify.h               |   12 +--
 5 files changed, 130 insertions(+), 58 deletions(-)
Documentation/filesystems/inotify.txt +42 -0
diff -urN linux-2.6.12-rc3-mm2/Documentation/filesystems/inotify.txt linux/Documentation/filesystems/inotify.txt
@@ -79,3 +79,45 @@
 off of each associated device and each associated inode.
 
 See fs/inotify.c for the locking and lifetime rules.
+
+
+(iii) Rationale
+
+Q: What is the design decision behind not tying the watch to the
+open fd of the watched object?
+
+A: Watches are associated with an open inotify device, not an
+open file.  This solves the primary problem with dnotify:
+keeping the file open pins the file and thus, worse, pins the
+mount.  Dnotify is therefore infeasible for use on a desktop
+system with removable media as the media cannot be unmounted.
+
+Q: What is the design decision behind using an-fd-per-device as
+opposed to an fd-per-watch?
+
+A: An fd-per-watch quickly consumes more file descriptors than
+are allowed, more fd's than are feasible to manage, and more
+fd's than are ideally select()-able.  Yes, root can bump the
+per-process fd limit and yes, users can use epoll, but requiring
+both is silly and an extraneous requirement.  A watch consumes
+less memory than an open file, separating the number spaces is
+thus sensible.  The current design is what user-space developers
+want: Users open the device, once, and add n watches, requiring
+but one fd and no twiddling with fd limits.
+Opening /dev/inotify two thousand times is silly.  If we can
+implement user-space's preferences cleanly--and we can, the idr
+layer makes stuff like this trivial--then we should.
+
+Q: Why a device node?
+
+A: The second biggest problem with dnotify is that the user
+interface sucks ass.  Signals are a terrible, terrible interface
+for file notification.  Or for anything, for that matter.  The
+idea solution, from all perspectives, is a file descriptor based
+one that allows basic file I/O and poll/select.  Obtaining the
+fd and managing the watches could of been done either via a
+device file or a family of new system calls.  We decided to
+implement a device file because adding three or four new system
+calls that mirrored open, close, and ioctl seemed silly.  A
+character device makes sense from user-space and was easy to
+implement inside of the kernel.
fs/inotify.c +14 -9
diff -urN linux-2.6.12-rc3-mm2/fs/inotify.c linux/fs/inotify.c
@@ -361,7 +361,7 @@
  * Callers must hold dev->sem.  This function can sleep.
  */
 static int inotify_dev_get_wd(struct inotify_device *dev,
-			     struct inotify_watch *watch)
+			      struct inotify_watch *watch)
 {
 	int ret;
 
@@ -734,7 +734,6 @@
 	INIT_LIST_HEAD(&dev->watches);
 	init_waitqueue_head(&dev->wq);
 	sema_init(&dev->sem, 1);
-
 	dev->event_count = 0;
 	dev->queue_size = 0;
 	dev->max_events = max_queued_events;
@@ -742,7 +741,7 @@
 	atomic_set(&dev->count, 0);
 
 	get_inotify_dev(dev);
-	atomic_inc(&current->user->inotify_devs);
+	atomic_inc(&user->inotify_devs);
 
 	file->private_data = dev;
 
@@ -800,8 +799,7 @@
 	return 0;
 }
 
-static int inotify_add_watch(struct inotify_device *dev,
-			     int fd, u32 mask)
+static int inotify_add_watch(struct inotify_device *dev, int fd, u32 mask)
 {
 	struct inotify_watch *watch, *old;
 	struct inode *inode;
@@ -816,6 +814,13 @@
 	down(&inode->inotify_sem);
 	down(&dev->sem);
 
+	/* don't let user-space set invalid bits: we don't want flags set */
+	mask &= IN_ALL_EVENTS;
+	if (!mask) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/*
 	 * Handle the case of re-adding a watch on an (inode,dev) pair that we
 	 * are already watching.  We just update the mask and return its wd.
@@ -870,7 +875,7 @@
 	down(&inode->inotify_sem);
 	down(&dev->sem);
 
-	/* make sure we did not race */
+	/* make sure that we did not race */
 	watch = idr_find(&dev->idr, wd);
 	if (likely(watch))
 		remove_watch(watch, dev);
@@ -947,9 +952,9 @@
 	if (unlikely(ret))
 		panic("inotify: misc_register returned %d\n", ret);
 
-	max_queued_events = 512;
-	max_user_devices = 64;
-	max_user_watches = 16384;
+	max_queued_events = 8192;
+	max_user_devices = 128;
+	max_user_watches = 8192;
 
 	class = inotify_device.class;
 	class_device_create_file(class, &class_device_attr_max_queued_events);
fs/namei.c +3 -3
diff -urN linux-2.6.12-rc3-mm2/fs/namei.c linux/fs/namei.c
@@ -2152,7 +2152,7 @@
 {
 	int error;
 	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
-	char *old_name;
+	const char *old_name;
 
 	if (old_dentry->d_inode == new_dentry->d_inode)
  		return 0;
@@ -2174,7 +2174,7 @@
 	DQUOT_INIT(old_dir);
 	DQUOT_INIT(new_dir);
 
-	old_name = fsnotify_oldname_init(old_dentry);
+	old_name = fsnotify_oldname_init(old_dentry->d_name.name);
 
 	if (is_dir)
 		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
@@ -2182,7 +2182,7 @@
 		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
 	if (!error) {
 		const char *new_name = old_dentry->d_name.name;
-		fsnotify_move(old_dir, new_dir, old_name, new_name);
+		fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir);
 	}
 	fsnotify_oldname_free(old_name);
 
include/linux/fsnotify.h +66 -39

204 lines not on this page · raw message

include/linux/inotify.h +5 -7
diff -urN linux-2.6.12-rc3-mm2/include/linux/inotify.h linux/include/linux/inotify.h
@@ -42,11 +42,9 @@
 #define IN_OPEN			0x00000020	/* File was opened */
 #define IN_MOVED_FROM		0x00000040	/* File was moved from X */
 #define IN_MOVED_TO		0x00000080	/* File was moved to Y */
-#define IN_DELETE_SUBDIR	0x00000100	/* Subdir was deleted */
-#define IN_DELETE_FILE		0x00000200	/* Subfile was deleted */
-#define IN_CREATE_SUBDIR	0x00000400	/* Subdir was created */
-#define IN_CREATE_FILE		0x00000800	/* Subfile was created */
-#define IN_DELETE_SELF		0x00001000	/* Self was deleted */
+#define IN_CREATE		0x00000100	/* Subfile was created */
+#define IN_DELETE		0x00000200	/* Subfile was deleted */
+#define IN_DELETE_SELF		0x00000400	/* Self was deleted */
 
 /* the following are legal events.  they are sent as needed to any watch */
 #define IN_UNMOUNT		0x00002000	/* Backing fs was unmounted */
@@ -58,6 +56,7 @@
 #define IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO) /* moves */
 
 /* special flags */
+#define IN_ISDIR		0x40000000	/* event occurred against dir */
 #define IN_ONESHOT		0x80000000	/* only send event once */
 
 /*
@@ -67,8 +66,7 @@
  */
 #define IN_ALL_EVENTS	(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
 			 IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \
-			 IN_MOVED_TO | IN_DELETE_SUBDIR | IN_DELETE_FILE | \
-			 IN_CREATE_SUBDIR | IN_CREATE_FILE | IN_DELETE_SELF)
+			 IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF)
 
 #define INOTIFY_IOCTL_MAGIC	'Q'
 #define INOTIFY_IOCTL_MAXNR	2
#1 lore raw

On Tue, 2005-05-03 at 14:48 -0400, Robert Love wrote:

Andrew,

Quoted 2 lines

Following patch is an update (not a replacement, I'll send that next if you prefer) to inotify in 2.6.12-rc3-mm2. Please apply.

Here is an complete rediff, if you prefer to replace the entire thing. Definitely makes diffing easier for me.

This revision contains the following changes over the previous:

- Add a "Rationale" section to the inotify documentation and move the design FAQ there from the ChangeLog. We'll just stuff every conceivable design question in the documentation for all eternity.

- Mask out the user-space mask, allowing only legal bits to be set on a new watch. Return EINVAL if nothing is left.

- New default limits: 8192 watches, 128 devices, and 8192 events. This is an increase in events, which take up very little room, and a decrease in watches.

- Add the IN_ISDIR flag. For any event, the presence of this flag delineates whether the event occurred to a directory or a file. This allows us to remove the _FILE and _SUBDIR events.

- Misc. cleanup and optimizations

Best,

Robert Love

inotify!

inotify is intended to correct the deficiencies of dnotify, particularly its inability to scale and its terrible user interface:

* dnotify requires the opening of one fd per each directory that you intend to watch. This quickly results in too many open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to directories. Sure, a change to a file in a directory affects the directory, but you are then forced to keep a cache of stat structures.
* dnotify's interface to user-space is awful. Signals?

inotify provides a more usable, simple, powerful solution to file change notification:

* inotify's interface is a device node, not SIGIO. You open a single fd to the device node, which is select()-able.
* inotify has an event that says "the filesystem that the item you were watching is on was unmounted."
* inotify can watch directories or files.

Inotify is currently used by Beagle (a desktop search infrastructure), Gamin (a FAM replacement), and other projects.

 Documentation/filesystems/inotify.txt |  123 ++++
 fs/Kconfig                            |   13 
 fs/Makefile                           |    1 
 fs/attr.c                             |   33 -
 fs/compat.c                           |   12 
 fs/file_table.c                       |    3 
 fs/inode.c                            |    6 
 fs/inotify.c                          |  978 ++++++++++++++++++++++++++++++++++
 fs/namei.c                            |   30 -
 fs/open.c                             |    4 
 fs/read_write.c                       |   15 
 fs/xattr.c                            |    5 
 include/linux/fs.h                    |    6 
 include/linux/fsnotify.h              |  257 ++++++++
 include/linux/inotify.h               |  124 ++++
 include/linux/sched.h                 |    4 
 kernel/user.c                         |    4 
 17 files changed, 1562 insertions(+), 56 deletions(-)
Documentation/filesystems/inotify.txt +123 -0
diff -urN linux-2.6.12-rc3/Documentation/filesystems/inotify.txt linux/Documentation/filesystems/inotify.txt
@@ -0,0 +1,123 @@
+				    inotify
+	     a powerful yet simple file change notification system
+
+
+
+Document started 15 Mar 2005 by Robert Love <[email protected]>
+
+(i) User Interface
+
+Inotify is controlled by a device node, /dev/inotify.  If you do not use udev,
+this device may need to be created manually.  First step, open it
+
+	int dev_fd = open ("/dev/inotify", O_RDONLY);
+
+Change events are managed by "watches".  A watch is an (object,mask) pair where
+the object is a file or directory and the mask is a bitmask of one or more
+inotify events that the application wishes to receive.  See <linux/inotify.h>
+for valid events.  A watch is referenced by a watch descriptor, or wd.
+
+Watches are added via a file descriptor.
+
+Watches on a directory will return events on any files inside of the directory.
+
+Adding a watch is simple,
+
+	/* 'wd' represents the watch on fd with mask */
+	struct inotify_request req = { fd, mask };
+	int wd = ioctl (dev_fd, INOTIFY_WATCH, &req);
+
+You can add a large number of files via something like
+
+	for each file to watch {
+		struct inotify_request req;
+		int file_fd;
+
+		file_fd = open (file, O_RDONLY);
+		if (fd < 0) {
+			perror ("open");
+			break;
+		}
+
+		req.fd = file_fd;
+		req.mask = mask;
+
+		wd = ioctl (dev_fd, INOTIFY_WATCH, &req);
+
+		close (fd);
+	}
+
+You can update an existing watch in the same manner, by passing in a new mask.
+
+An existing watch is removed via the INOTIFY_IGNORE ioctl, for example
+
+	ioctl (dev_fd, INOTIFY_IGNORE, wd);
+
+Events are provided in the form of an inotify_event structure that is read(2)
+from /dev/inotify.  The filename is of dynamic length and follows the struct.
+It is of size len.  The filename is padded with null bytes to ensure proper
+alignment.  This padding is reflected in len.
+
+You can slurp multiple events by passing a large buffer, for example
+
+	size_t len = read (fd, buf, BUF_LEN);
+
+Will return as many events as are available and fit in BUF_LEN.
+
+/dev/inotify is also select() and poll() able.
+
+You can find the size of the current event queue via the FIONREAD ioctl.
+
+All watches are destroyed and cleaned up on close.
+
+
+(ii) Internal Kernel Implementation
+
+Each open inotify device is associated with an inotify_device structure.
+
+Each watch is associated with an inotify_watch structure.  Watches are chained
+off of each associated device and each associated inode.
+
+See fs/inotify.c for the locking and lifetime rules.
+
+
+(iii) Rationale
+
+Q: What is the design decision behind not tying the watch to the
+open fd of the watched object?
+
+A: Watches are associated with an open inotify device, not an
+open file.  This solves the primary problem with dnotify:
+keeping the file open pins the file and thus, worse, pins the
+mount.  Dnotify is therefore infeasible for use on a desktop
+system with removable media as the media cannot be unmounted.
+
+Q: What is the design decision behind using an-fd-per-device as
+opposed to an fd-per-watch?
+
+A: An fd-per-watch quickly consumes more file descriptors than
+are allowed, more fd's than are feasible to manage, and more
+fd's than are ideally select()-able.  Yes, root can bump the
+per-process fd limit and yes, users can use epoll, but requiring
+both is silly and an extraneous requirement.  A watch consumes
+less memory than an open file, separating the number spaces is
+thus sensible.  The current design is what user-space developers
+want: Users open the device, once, and add n watches, requiring
+but one fd and no twiddling with fd limits.
+Opening /dev/inotify two thousand times is silly.  If we can
+implement user-space's preferences cleanly--and we can, the idr
+layer makes stuff like this trivial--then we should.
+
+Q: Why a device node?
+
+A: The second biggest problem with dnotify is that the user
+interface sucks ass.  Signals are a terrible, terrible interface
+for file notification.  Or for anything, for that matter.  The
+idea solution, from all perspectives, is a file descriptor based
+one that allows basic file I/O and poll/select.  Obtaining the
+fd and managing the watches could of been done either via a
+device file or a family of new system calls.  We decided to
+implement a device file because adding three or four new system
+calls that mirrored open, close, and ioctl seemed silly.  A
+character device makes sense from user-space and was easy to
+implement inside of the kernel.
fs/attr.c +4 -29
diff -urN linux-2.6.12-rc3/fs/attr.c linux/fs/attr.c
@@ -10,7 +10,7 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/smp_lock.h>
-#include <linux/dnotify.h>
+#include <linux/fsnotify.h>
 #include <linux/fcntl.h>
 #include <linux/quotaops.h>
 #include <linux/security.h>
@@ -107,31 +107,8 @@
 out:
 	return error;
 }
-
 EXPORT_SYMBOL(inode_setattr);
 
-int setattr_mask(unsigned int ia_valid)
-{
-	unsigned long dn_mask = 0;
-
-	if (ia_valid & ATTR_UID)
-		dn_mask |= DN_ATTRIB;
-	if (ia_valid & ATTR_GID)
-		dn_mask |= DN_ATTRIB;
-	if (ia_valid & ATTR_SIZE)
-		dn_mask |= DN_MODIFY;
-	/* both times implies a utime(s) call */
-	if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))
-		dn_mask |= DN_ATTRIB;
-	else if (ia_valid & ATTR_ATIME)
-		dn_mask |= DN_ACCESS;
-	else if (ia_valid & ATTR_MTIME)
-		dn_mask |= DN_MODIFY;
-	if (ia_valid & ATTR_MODE)
-		dn_mask |= DN_ATTRIB;
-	return dn_mask;
-}
-
 int notify_change(struct dentry * dentry, struct iattr * attr)
 {
 	struct inode *inode = dentry->d_inode;
@@ -197,11 +174,9 @@
 	if (ia_valid & ATTR_SIZE)
 		up_write(&dentry->d_inode->i_alloc_sem);
 
-	if (!error) {
-		unsigned long dn_mask = setattr_mask(ia_valid);
-		if (dn_mask)
-			dnotify_parent(dentry, dn_mask);
-	}
+	if (!error)
+		fsnotify_change(dentry, ia_valid);
+
 	return error;
 }
 
fs/compat.c +8 -4
diff -urN linux-2.6.12-rc3/fs/compat.c linux/fs/compat.c
@@ -37,7 +37,7 @@
 #include <linux/ctype.h>
 #include <linux/module.h>
 #include <linux/dirent.h>
-#include <linux/dnotify.h>
+#include <linux/fsnotify.h>
 #include <linux/highuid.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
@@ -1307,9 +1307,13 @@
 out:
 	if (iov != iovstack)
 		kfree(iov);
-	if ((ret + (type == READ)) > 0)
-		dnotify_parent(file->f_dentry,
-				(type == READ) ? DN_ACCESS : DN_MODIFY);
+	if ((ret + (type == READ)) > 0) {
+		struct dentry *dentry = file->f_dentry;
+		if (type == READ)
+			fsnotify_access(dentry);
+		else
+			fsnotify_modify(dentry);
+	}
 	return ret;
 }
 
fs/file_table.c +3 -0
diff -urN linux-2.6.12-rc3/fs/file_table.c linux/fs/file_table.c
@@ -16,6 +16,7 @@
 #include <linux/eventpoll.h>
 #include <linux/mount.h>
 #include <linux/cdev.h>
+#include <linux/fsnotify.h>
 
 /* sysctl tunables... */
 struct files_stat_struct files_stat = {
@@ -123,6 +124,8 @@
 	struct inode *inode = dentry->d_inode;
 
 	might_sleep();
+
+	fsnotify_close(file);
 	/*
 	 * The function eventpoll_release() should be the first called
 	 * in the file cleanup chain.
fs/inode.c +6 -0
diff -urN linux-2.6.12-rc3/fs/inode.c linux/fs/inode.c
@@ -21,6 +21,7 @@
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
+#include <linux/inotify.h>
 
 /*
  * This is needed for the following functions:
@@ -129,6 +130,10 @@
 #ifdef CONFIG_QUOTA
 		memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
 #endif
+#ifdef CONFIG_INOTIFY
+		INIT_LIST_HEAD(&inode->inotify_watches);
+		sema_init(&inode->inotify_sem, 1);
+#endif
 		inode->i_pipe = NULL;
 		inode->i_bdev = NULL;
 		inode->i_cdev = NULL;
@@ -355,6 +360,7 @@
 
 	down(&iprune_sem);
 	spin_lock(&inode_lock);
+	inotify_unmount_inodes(&sb->s_inodes);
 	busy = invalidate_list(&sb->s_inodes, &throw_away);
 	spin_unlock(&inode_lock);
 
fs/inotify.c +978 -0

978 lines not on this page · raw message

fs/Kconfig +13 -0
diff -urN linux-2.6.12-rc3/fs/Kconfig linux/fs/Kconfig
@@ -339,6 +339,19 @@
 	  If you don't know whether you need it, then you don't need it:
 	  answer N.
 
+config INOTIFY
+	bool "Inotify file change notification support"
+	default y
+	---help---
+	  Say Y here to enable inotify support and the /dev/inotify character
+	  device.  Inotify is a file change notification system and a
+	  replacement for dnotify.  Inotify fixes numerous shortcomings in
+	  dnotify and introduces several new features.  It allows monitoring
+	  of both files and directories via a single open fd.  Multiple file
+	  events are supported.
+
+	  If unsure, say Y.
+
 config QUOTA
 	bool "Quota support"
 	help
fs/Makefile +1 -0
diff -urN linux-2.6.12-rc3/fs/Makefile linux/fs/Makefile
@@ -11,6 +11,7 @@
 		attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \
 		seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \
 
+obj-$(CONFIG_INOTIFY)		+= inotify.o
 obj-$(CONFIG_EPOLL)		+= eventpoll.o
 obj-$(CONFIG_COMPAT)		+= compat.o
 
fs/namei.c +16 -14
diff -urN linux-2.6.12-rc3/fs/namei.c linux/fs/namei.c
@@ -21,7 +21,7 @@
 #include <linux/namei.h>
 #include <linux/quotaops.h>
 #include <linux/pagemap.h>
-#include <linux/dnotify.h>
+#include <linux/fsnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/personality.h>
 #include <linux/security.h>
@@ -1292,7 +1292,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->create(dir, dentry, mode, nd);
 	if (!error) {
-		inode_dir_notify(dir, DN_CREATE);
+		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_create(dir, dentry, mode);
 	}
 	return error;
@@ -1597,7 +1597,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->mknod(dir, dentry, mode, dev);
 	if (!error) {
-		inode_dir_notify(dir, DN_CREATE);
+		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_mknod(dir, dentry, mode, dev);
 	}
 	return error;
@@ -1670,7 +1670,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->mkdir(dir, dentry, mode);
 	if (!error) {
-		inode_dir_notify(dir, DN_CREATE);
+		fsnotify_mkdir(dir, dentry->d_name.name);
 		security_inode_post_mkdir(dir,dentry, mode);
 	}
 	return error;
@@ -1761,7 +1761,7 @@
 	}
 	up(&dentry->d_inode->i_sem);
 	if (!error) {
-		inode_dir_notify(dir, DN_DELETE);
+		fsnotify_rmdir(dentry, dentry->d_inode, dir);
 		d_delete(dentry);
 	}
 	dput(dentry);
@@ -1834,9 +1834,10 @@
 
 	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
 	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
+		fsnotify_unlink(dentry, dir);
 		d_delete(dentry);
-		inode_dir_notify(dir, DN_DELETE);
 	}
+
 	return error;
 }
 
@@ -1910,7 +1911,7 @@
 	DQUOT_INIT(dir);
 	error = dir->i_op->symlink(dir, dentry, oldname);
 	if (!error) {
-		inode_dir_notify(dir, DN_CREATE);
+		fsnotify_create(dir, dentry->d_name.name);
 		security_inode_post_symlink(dir, dentry, oldname);
 	}
 	return error;
@@ -1983,7 +1984,7 @@
 	error = dir->i_op->link(old_dentry, dir, new_dentry);
 	up(&old_dentry->d_inode->i_sem);
 	if (!error) {
-		inode_dir_notify(dir, DN_CREATE);
+		fsnotify_create(dir, new_dentry->d_name.name);
 		security_inode_post_link(old_dentry, dir, new_dentry);
 	}
 	return error;
@@ -2147,6 +2148,7 @@
 {
 	int error;
 	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+	const char *old_name;
 
 	if (old_dentry->d_inode == new_dentry->d_inode)
  		return 0;
@@ -2168,18 +2170,18 @@
 	DQUOT_INIT(old_dir);
 	DQUOT_INIT(new_dir);
 
+	old_name = fsnotify_oldname_init(old_dentry->d_name.name);
+
 	if (is_dir)
 		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
 	else
 		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
 	if (!error) {
-		if (old_dir == new_dir)
-			inode_dir_notify(old_dir, DN_RENAME);
-		else {
-			inode_dir_notify(old_dir, DN_DELETE);
-			inode_dir_notify(new_dir, DN_CREATE);
-		}
+		const char *new_name = old_dentry->d_name.name;
+		fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir);
 	}
+	fsnotify_oldname_free(old_name);
+
 	return error;
 }
 
fs/open.c +3 -1
diff -urN linux-2.6.12-rc3/fs/open.c linux/fs/open.c
@@ -10,7 +10,7 @@
 #include <linux/file.h>
 #include <linux/smp_lock.h>
 #include <linux/quotaops.h>
-#include <linux/dnotify.h>
+#include <linux/fsnotify.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/tty.h>
@@ -944,9 +944,11 @@
 		fd = get_unused_fd();
 		if (fd >= 0) {
 			struct file *f = filp_open(tmp, flags, mode);
+
 			error = PTR_ERR(f);
 			if (IS_ERR(f))
 				goto out_error;
+			fsnotify_open(f->f_dentry);
 			fd_install(fd, f);
 		}
 out:
fs/read_write.c +9 -6
diff -urN linux-2.6.12-rc3/fs/read_write.c linux/fs/read_write.c
@@ -10,7 +10,7 @@
 #include <linux/file.h>
 #include <linux/uio.h>
 #include <linux/smp_lock.h>
-#include <linux/dnotify.h>
+#include <linux/fsnotify.h>
 #include <linux/security.h>
 #include <linux/module.h>
 #include <linux/syscalls.h>
@@ -239,7 +239,7 @@
 			else
 				ret = do_sync_read(file, buf, count, pos);
 			if (ret > 0) {
-				dnotify_parent(file->f_dentry, DN_ACCESS);
+				fsnotify_access(file->f_dentry);
 				current->rchar += ret;
 			}
 			current->syscr++;
@@ -287,7 +287,7 @@
 			else
 				ret = do_sync_write(file, buf, count, pos);
 			if (ret > 0) {
-				dnotify_parent(file->f_dentry, DN_MODIFY);
+				fsnotify_modify(file->f_dentry);
 				current->wchar += ret;
 			}
 			current->syscw++;
@@ -523,9 +523,12 @@
 out:
 	if (iov != iovstack)
 		kfree(iov);
-	if ((ret + (type == READ)) > 0)
-		dnotify_parent(file->f_dentry,
-				(type == READ) ? DN_ACCESS : DN_MODIFY);
+	if ((ret + (type == READ)) > 0) {
+		if (type == READ)
+			fsnotify_access(file->f_dentry);
+		else
+			fsnotify_modify(file->f_dentry);
+	}
 	return ret;
 Efault:
 	ret = -EFAULT;
fs/xattr.c +4 -1
diff -urN linux-2.6.12-rc3/fs/xattr.c linux/fs/xattr.c
@@ -16,6 +16,7 @@
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/module.h>
+#include <linux/fsnotify.h>
 #include <asm/uaccess.h>
 
 /*
@@ -57,8 +58,10 @@
 		if (error)
 			goto out;
 		error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
-		if (!error)
+		if (!error) {
+			fsnotify_xattr(d);
 			security_inode_post_setxattr(d, kname, kvalue, size, flags);
+		}
 out:
 		up(&d->d_inode->i_sem);
 	}
include/linux/fs.h +5 -1
diff -urN linux-2.6.12-rc3/include/linux/fs.h linux/include/linux/fs.h
@@ -471,6 +471,11 @@
 	struct dnotify_struct	*i_dnotify; /* for directory notifications */
 #endif
 
+#ifdef CONFIG_INOTIFY
+	struct list_head	inotify_watches; /* watches on this inode */
+	struct semaphore	inotify_sem;	/* protects the watches list */
+#endif
+
 	unsigned long		i_state;
 	unsigned long		dirtied_when;	/* jiffies of first dirtying */
 
@@ -1365,7 +1370,6 @@
 extern int do_remount_sb(struct super_block *sb, int flags,
 			 void *data, int force);
 extern sector_t bmap(struct inode *, sector_t);
-extern int setattr_mask(unsigned int);
 extern int notify_change(struct dentry *, struct iattr *);
 extern int permission(struct inode *, int, struct nameidata *);
 extern int generic_permission(struct inode *, int,
include/linux/fsnotify.h +257 -0

257 lines not on this page · raw message

include/linux/inotify.h +124 -0

124 lines not on this page · raw message

include/linux/sched.h +4 -0
diff -urN linux-2.6.12-rc3/include/linux/sched.h linux/include/linux/sched.h
@@ -404,6 +404,10 @@
 	atomic_t processes;	/* How many processes does this user have? */
 	atomic_t files;		/* How many open files does this user have? */
 	atomic_t sigpending;	/* How many pending signals does this user have? */
+#ifdef CONFIG_INOTIFY
+	atomic_t inotify_watches; /* How many inotify watches does this user have? */
+	atomic_t inotify_devs;	/* How many inotify devs does this user have opened? */
+#endif
 	/* protected by mq_lock	*/
 	unsigned long mq_bytes;	/* How many bytes can be allocated to mqueue? */
 	unsigned long locked_shm; /* How many pages of mlocked shm ? */
kernel/user.c +4 -0
diff -urN linux-2.6.12-rc3/kernel/user.c linux/kernel/user.c
@@ -120,6 +120,10 @@
 		atomic_set(&new->processes, 0);
 		atomic_set(&new->files, 0);
 		atomic_set(&new->sigpending, 0);
+#ifdef CONFIG_INOTIFY
+		atomic_set(&new->inotify_watches, 0);
+		atomic_set(&new->inotify_devs, 0);
+#endif
 
 		new->mq_bytes = 0;
 		new->locked_shm = 0;