User: Password:
|
|
Subscribe / Log in / New account

Filesystem Journal Notifications

From:  Abhijit Paithankar <apaithan@akamai.com>
To:  linux-fsdevel@vger.kernel.org
Subject:  [RFC PATCH] Filesystem Journal Notifications
Date:  Fri, 12 Sep 2008 15:06:49 -0700
Message-ID:  <20080912220649.GB26999@apaithan-desktop.sanmateo.corp.akamai.com>
Cc:  Andreas Dilger <adilger@sun.com>
Archive-link:  Article

Hi,

On journaling file systems, applications need to know when the meta-data
changes to files are written to disks in a manner which is persistent to
crashes and reboots.

One way to do it is to fsync every few operations. However, fsync is
blocking and affects performance.

The other (more efficient) way is to have the filesystem notify the
application when a transaction/change is written to disk.

This patch implements such a notification mechanism based on inotify for
Ext3 and Ext4.

Every time an application creates, renames, unlinks, etc a file or
directory, a journal_cookie is sent via the inotify mechanism. Ofcourse,
the application has to first register for such notifications on the
files/directories of interest.

Once the transaction corresponding to that journal_cookie is committed
to the journal another notification is sent to the application
indicating that the change is now persistent.

This initial patch includes changes to the inotify layer, ext3-fs, jbd,
ext4-fs and jbd2. It is based on the latest Linus' git tree.


Signed-off-by: Abhijit Paithankar <apaithan@akamai.com>
---

 fs/ext3/namei.c               |   44 ++++++++++++++++
 fs/ext4/ext4_jbd2.h           |    4 +
 fs/ext4/namei.c               |   40 ++++++++++++++
 fs/inotify.c                  |   20 ++++---
 fs/inotify_user.c             |   79 ++++++++++++++++++++++++++---
 fs/jbd/commit.c               |    1
 fs/jbd/journal.c              |    7 ++
 fs/jbd2/commit.c              |    1
 fs/jbd2/journal.c             |    7 ++
 include/linux/ext3_fsnotify.h |   83 ++++++++++++++++++++++++++++++
 include/linux/ext3_jbd.h      |    5 +
 include/linux/ext4_fsnotify.h |   81 ++++++++++++++++++++++++++++++
 include/linux/fs.h            |    1
 include/linux/fsnotify.h      |  113 +++++++++++++++++++++++++++++++++---------
 include/linux/inotify.h       |   32 ++++++++++-
 include/linux/jbd.h           |    4 +
 include/linux/jbd2.h          |    4 +
 include/linux/journal-head.h  |    1
 18 files changed, 486 insertions(+), 41 deletions(-)

---

Index: linux-2.6/fs/ext3/namei.c
===================================================================
--- linux-2.6.orig/fs/ext3/namei.c	2008-09-11 15:48:16.000000000 -0700
+++ linux-2.6/fs/ext3/namei.c	2008-09-12 12:17:11.000000000 -0700
@@ -36,6 +36,7 @@
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
+#include <linux/ext3_fsnotify.h>
 
 #include "namei.h"
 #include "xattr.h"
@@ -1709,6 +1710,10 @@
 		ext3_set_aops(inode);
 		err = ext3_add_nondir(handle, dentry, inode);
 	}
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_create(dir, dentry, journal_cookie);
+	}
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -1744,6 +1749,10 @@
 #endif
 		err = ext3_add_nondir(handle, dentry, inode);
 	}
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_create(dir, dentry, journal_cookie);
+	}
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -1817,6 +1826,12 @@
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
 	d_instantiate(dentry, inode);
+
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_mkdir(dir, dentry, journal_cookie);
+	}
+
 out_stop:
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
@@ -2093,6 +2108,10 @@
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
 
+	if (!handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_rmdir(dentry, inode, journal_cookie);
+	}
 end_rmdir:
 	ext3_journal_stop(handle);
 	brelse (bh);
@@ -2147,6 +2166,11 @@
 	ext3_mark_inode_dirty(handle, inode);
 	retval = 0;
 
+	if (!handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_unlink(dentry, inode, journal_cookie);
+	}
+
 end_unlink:
 	ext3_journal_stop(handle);
 	brelse (bh);
@@ -2202,6 +2226,10 @@
 	}
 	EXT3_I(inode)->i_disksize = inode->i_size;
 	err = ext3_add_nondir(handle, dentry, inode);
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_create(dir, dentry, journal_cookie);
+	}
 out_stop:
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
@@ -2239,6 +2267,10 @@
 	atomic_inc(&inode->i_count);
 
 	err = ext3_add_nondir(handle, dentry, inode);
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+		ext3_fsnotify_link(dir, inode, dentry, journal_cookie);
+	}
 	ext3_journal_stop(handle);
 	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -2260,6 +2292,7 @@
 	struct buffer_head * old_bh, * new_bh, * dir_bh;
 	struct ext3_dir_entry_2 * old_de, * new_de;
 	int retval;
+	const char *old_name = NULL;
 
 	old_bh = new_bh = dir_bh = NULL;
 
@@ -2273,6 +2306,7 @@
 	if (IS_ERR(handle))
 		return PTR_ERR(handle);
 
+	old_name = ext3_fsnotify_oldname_init(old_dentry->d_name.name);
 	if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
 		handle->h_sync = 1;
 
@@ -2397,7 +2431,17 @@
 	}
 	retval = 0;
 
+	if (!handle->h_sync) {
+		int isdir = S_ISDIR(old_dentry->d_inode->i_mode);
+		const char *new_name = new_dentry->d_name.name;
+		journal_cookie_t journal_cookie = ext3_journal_get_cookie(handle);
+
+		ext3_fsnotify_move(old_dir, new_dir, old_name, new_name, isdir,
+			new_dentry->d_inode, old_dentry, journal_cookie);
+	}
+
 end_rename:
+	ext3_fsnotify_oldname_free(old_name);
 	brelse (dir_bh);
 	brelse (old_bh);
 	brelse (new_bh);
Index: linux-2.6/fs/inotify.c
===================================================================
--- linux-2.6.orig/fs/inotify.c	2008-09-11 15:48:21.000000000 -0700
+++ linux-2.6/fs/inotify.c	2008-09-11 15:50:08.000000000 -0700
@@ -231,7 +231,7 @@
 				 struct inotify_watch *watch)
 {
 	remove_watch_no_event(watch, ih);
-	ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL);
+	ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL, 0);
 }
 EXPORT_SYMBOL_GPL(inotify_remove_watch_locked);
 
@@ -275,9 +275,12 @@
  * @cookie: cookie for synchronization, or zero
  * @name: filename, if any
  * @n_inode: inode associated with name
+ * @journal_cookie: journal cookie associated with this operation if the
+ * underlying filesystem supports journaling
  */
 void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
-			       const char *name, struct inode *n_inode)
+			       const char *name, struct inode *n_inode,
+			       journal_cookie_t journal_cookie)
 {
 	struct inotify_watch *watch, *next;
 
@@ -293,7 +296,7 @@
 			if (watch_mask & IN_ONESHOT)
 				remove_watch_no_event(watch, ih);
 			ih->in_ops->handle_event(watch, watch->wd, mask, cookie,
-						 name, n_inode);
+						 name, n_inode, journal_cookie);
 			mutex_unlock(&ih->mutex);
 		}
 	}
@@ -309,7 +312,8 @@
  * @name: filename, if any
  */
 void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
-				       u32 cookie, const char *name)
+				       u32 cookie, const char *name,
+					journal_cookie_t journal_cookie)
 {
 	struct dentry *parent;
 	struct inode *inode;
@@ -325,7 +329,7 @@
 		dget(parent);
 		spin_unlock(&dentry->d_lock);
 		inotify_inode_queue_event(inode, mask, cookie, name,
-					  dentry->d_inode);
+					  dentry->d_inode, journal_cookie);
 		dput(parent);
 	} else
 		spin_unlock(&dentry->d_lock);
@@ -409,7 +413,7 @@
 			struct inotify_handle *ih= watch->ih;
 			mutex_lock(&ih->mutex);
 			ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0,
-						 NULL, NULL);
+						 NULL, NULL, 0);
 			inotify_remove_watch_locked(ih, watch);
 			mutex_unlock(&ih->mutex);
 		}
@@ -576,7 +580,7 @@
 		mask_add = 1;
 
 	/* don't allow invalid bits: we don't want flags set */
-	mask &= IN_ALL_EVENTS | IN_ONESHOT;
+	mask &= IN_ALL_EVENTS | IN_ONESHOT | IN_JOURNAL_ALL_EVENTS | IN_JOURNAL_COMMIT;
 	if (unlikely(!mask))
 		return -EINVAL;
 
@@ -623,7 +627,7 @@
 	int newly_watched;
 
 	/* don't allow invalid bits: we don't want flags set */
-	mask &= IN_ALL_EVENTS | IN_ONESHOT;
+	mask &= IN_ALL_EVENTS | IN_ONESHOT | IN_JOURNAL_ALL_EVENTS | IN_JOURNAL_COMMIT;
 	if (unlikely(!mask))
 		return -EINVAL;
 	watch->mask = mask;
Index: linux-2.6/fs/inotify_user.c
===================================================================
--- linux-2.6.orig/fs/inotify_user.c	2008-09-11 15:48:28.000000000 -0700
+++ linux-2.6/fs/inotify_user.c	2008-09-11 16:42:41.000000000 -0700
@@ -185,7 +185,7 @@
  * This function can sleep.
  */
 static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
-						  const char *name)
+						  const char *name, journal_cookie_t journal_cookie)
 {
 	struct inotify_kernel_event *kevent;
 
@@ -204,6 +204,7 @@
 
 	if (name) {
 		size_t len, rem, event_size = sizeof(struct inotify_event);
+		size_t journal_cookie_sz;
 
 		/*
 		 * We need to pad the filename so as to properly align an
@@ -213,6 +214,10 @@
 		 * simple and safe for all architectures.
 		 */
 		len = strlen(name) + 1;
+		if (journal_cookie) {
+			journal_cookie_sz = sizeof(journal_cookie_t);
+			len = len + journal_cookie_sz;
+		}
 		rem = event_size - len;
 		if (len > event_size) {
 			rem = event_size - (len % event_size);
@@ -225,13 +230,52 @@
 			kmem_cache_free(event_cachep, kevent);
 			return NULL;
 		}
-		memcpy(kevent->name, name, len);
+		if (journal_cookie) {
+			char *ch;
+
+			memcpy(kevent->name, name, strlen(name));
+			ch = kevent->name;
+			ch += strlen(name);
+			memset(ch++, 0, 1);
+			sprintf(ch, "%d", journal_cookie);
+		}
+		else {
+			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;
+		if (journal_cookie) {
+			size_t len, rem, event_size = sizeof(struct inotify_event);
+			size_t journal_cookie_sz = sizeof(journal_cookie_t);
+			char *ch;
+
+			len = 1 + journal_cookie_sz;
+			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;
+			}
+			memset(kevent->name, 0, 1);
+			ch = kevent->name;
+			ch += 1;
+			sprintf(ch, "%d", journal_cookie);
+			if (rem)
+				memset(kevent->name + len, 0, rem);
+			kevent->event.len = len + rem;
+		}
+		else {
+			kevent->event.len = 0;
+			kevent->name = NULL;
+		}
 	}
 
 	return kevent;
@@ -269,7 +313,7 @@
  */
 static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
 				    u32 cookie, const char *name,
-				    struct inode *ignored)
+				    struct inode *ignored, journal_cookie_t journal_cookie)
 {
 	struct inotify_user_watch *watch;
 	struct inotify_device *dev;
@@ -280,11 +324,23 @@
 
 	mutex_lock(&dev->ev_mutex);
 
+	if (mask & IN_JOURNAL_COMMIT) {
+		if (!(w->inode) || !(w->inode->i_sb) || !(w->inode->i_sb->s_priv_inode))
+			goto out;
+	}
 	/* we can safely put the watch as we don't reference it while
 	 * generating the event
 	 */
-	if (mask & IN_IGNORED || w->mask & IN_ONESHOT)
+	if (mask & IN_IGNORED || w->mask & IN_ONESHOT) {
+                if (w->mask & IN_JOURNAL_COMMIT) {
+                        struct super_block *sb = NULL;
+                        if (w->inode && w->inode->i_sb)
+                                sb = w->inode->i_sb;
+                        if (sb && sb->s_priv_inode)
+                                sb->s_priv_inode = NULL;
+                }
 		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);
@@ -304,9 +360,9 @@
 
 	/* 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);
+		kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL, 0);
 	else
-		kevent = kernel_event(wd, mask, cookie, name);
+		kevent = kernel_event(wd, mask, cookie, name, journal_cookie);
 
 	if (unlikely(!kevent))
 		goto out;
@@ -686,6 +742,13 @@
 	ret = inotify_find_update_watch(dev->ih, inode, mask);
 	if (ret == -ENOENT)
 		ret = create_watch(dev, inode, mask);
+	if (ret >= 0) {
+		if (mask & IN_JOURNAL_COMMIT) {
+                        struct super_block *sb = inode->i_sb;
+                        if (sb)
+                                sb->s_priv_inode = inode;
+		}
+	}
 	mutex_unlock(&dev->up_mutex);
 
 	path_put(&path);
Index: linux-2.6/fs/jbd/commit.c
===================================================================
--- linux-2.6.orig/fs/jbd/commit.c	2008-09-11 15:48:38.000000000 -0700
+++ linux-2.6/fs/jbd/commit.c	2008-09-12 11:31:15.000000000 -0700
@@ -928,6 +928,7 @@
 	}
 	spin_unlock(&journal->j_list_lock);
 
+	journal_notify_commit(journal, journal->j_commit_sequence);
 	jbd_debug(1, "JBD: commit %d complete, head %d\n",
 		  journal->j_commit_sequence, journal->j_tail_sequence);
 
Index: linux-2.6/fs/jbd/journal.c
===================================================================
--- linux-2.6.orig/fs/jbd/journal.c	2008-09-11 15:48:44.000000000 -0700
+++ linux-2.6/fs/jbd/journal.c	2008-09-11 15:50:08.000000000 -0700
@@ -80,6 +80,7 @@
 EXPORT_SYMBOL(journal_invalidatepage);
 EXPORT_SYMBOL(journal_try_to_free_buffers);
 EXPORT_SYMBOL(journal_force_commit);
+EXPORT_SYMBOL(journal_get_cookie);
 
 static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
 static void __journal_abort_soft (journal_t *journal, int errno);
@@ -1607,6 +1608,12 @@
 	return 1 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 }
 
+/* We don't need to do anything here other than return the txid. */
+journal_cookie_t journal_get_cookie(handle_t *handle)
+{
+	return handle->h_transaction->t_tid;
+}
+
 /*
  * Journal_head storage management
  */
Index: linux-2.6/include/linux/ext3_fsnotify.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/include/linux/ext3_fsnotify.h	2008-09-12 11:31:44.000000000 -0700
@@ -0,0 +1,83 @@
+/*
+ * linux/include/linux/ext3_fsnotify.h
+ *
+ * Copyright (C) 2008 Akamai Technologies
+ * Abhijit Paithankar <apaithan@akamai.com>
+ *
+ * fsnotify interface for Ext3 with journal cookies
+ */
+
+#ifndef EXT3_FSNOTIFY_H_
+#define EXT3_FSNOTIFY_H_
+
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/fsnotify.h>
+
+#define SB(x) x->h_transaction->t_journal->j_private
+
+static const char *ext3_fsnotify_oldname_init(const char *name)
+{
+	return fsnotify_oldname_init(name);
+}
+
+static void ext3_fsnotify_oldname_free(const char *old_name)
+{
+	fsnotify_oldname_free(old_name);
+}
+
+static void ext3_fsnotify_unlink(struct dentry *dentry, struct inode *inode,
+				journal_cookie_t journal_cookie)
+{
+	int isdir = S_ISDIR(dentry->d_inode->i_mode);
+	if (!inode->i_nlink)
+		fsnotify_journal_inoderemove(inode, journal_cookie);
+
+	fsnotify_journal_nameremove(dentry, isdir, journal_cookie);
+}
+
+static void ext3_fsnotify_move(struct inode *old_dir, struct inode *new_dir,
+				      const char *old_name, const char *new_name,
+				      int isdir, struct inode *target, struct dentry *moved,
+				      journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_move(old_dir, new_dir, old_name, new_name, isdir,
+			target, moved, journal_cookie);
+}
+
+static void ext3_fsnotify_create(struct inode *dir, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_create(dir, dentry, journal_cookie);
+}
+
+static void ext3_fsnotify_mkdir(struct inode *dir, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_mkdir(dir, dentry, journal_cookie);
+}
+
+static void ext3_fsnotify_rmdir(struct dentry *dentry, struct inode *inode,
+				journal_cookie_t journal_cookie)
+{
+	ext3_fsnotify_unlink(dentry, inode, journal_cookie);
+
+}
+
+static void ext3_fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_link(dir, inode, dentry, journal_cookie);
+}
+
+void journal_notify_commit(journal_t *journal, journal_cookie_t journal_cookie)
+{
+	struct super_block *sb = journal->j_private;
+
+	if (sb && sb->s_priv_inode)
+		fsnotify_journal_commit(sb->s_priv_inode, journal_cookie);
+}
+
+#endif /* EXT3_FSNOTIFY_H_ */
Index: linux-2.6/include/linux/ext3_jbd.h
===================================================================
--- linux-2.6.orig/include/linux/ext3_jbd.h	2008-09-11 15:48:56.000000000 -0700
+++ linux-2.6/include/linux/ext3_jbd.h	2008-09-11 15:50:08.000000000 -0700
@@ -223,4 +223,9 @@
 	return 0;
 }
 
+static inline journal_cookie_t ext3_journal_get_cookie(handle_t *handle)
+{
+	return journal_get_cookie(handle);
+}
+
 #endif	/* _LINUX_EXT3_JBD_H */
Index: linux-2.6/include/linux/fs.h
===================================================================
--- linux-2.6.orig/include/linux/fs.h	2008-09-11 15:49:00.000000000 -0700
+++ linux-2.6/include/linux/fs.h	2008-09-11 15:50:08.000000000 -0700
@@ -1105,6 +1105,7 @@
 
 	char s_id[32];				/* Informational name */
 
+	struct inode		*s_priv_inode;	/* used by EXT3 for journal notifications */
 	void 			*s_fs_info;	/* Filesystem private info */
 
 	/*
Index: linux-2.6/include/linux/fsnotify.h
===================================================================
--- linux-2.6.orig/include/linux/fsnotify.h	2008-09-11 15:49:04.000000000 -0700
+++ linux-2.6/include/linux/fsnotify.h	2008-09-12 10:27:09.000000000 -0700
@@ -34,6 +34,12 @@
 	inotify_d_move(entry);
 }
 
+static inline void fsnotify_journal_commit(struct inode *inode, journal_cookie_t journal_cookie)
+{
+	inotify_inode_queue_event(inode, IN_JOURNAL_COMMIT, 0,
+				  NULL, NULL, journal_cookie);
+}
+
 /*
  * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir
  */
@@ -54,21 +60,47 @@
 	if (isdir)
 		isdir = IN_ISDIR;
 	inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name,
-				  source);
+				  source, 0);
 	inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name,
-				  source);
+				  source, 0);
 
 	if (target) {
-		inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL);
+		inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL, 0);
 		inotify_inode_is_dead(target);
 	}
 
 	if (source) {
-		inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
+		inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL, 0);
 	}
 	audit_inode_child(new_name, moved, new_dir);
 }
 
+static inline void fsnotify_journal_move(struct inode *old_dir, struct inode *new_dir,
+					 const char *old_name, const char *new_name,
+					 int isdir, struct inode *target, struct dentry *moved,
+					 journal_cookie_t journal_cookie)
+{
+	struct inode *source = moved->d_inode;
+	u32 cookie = inotify_get_cookie();
+
+	if (isdir)
+		isdir = IN_ISDIR;
+	inotify_inode_queue_event(old_dir, IN_JOURNAL_MOVED_FROM|isdir,cookie,old_name,
+				  source, journal_cookie);
+	inotify_inode_queue_event(new_dir, IN_JOURNAL_MOVED_TO|isdir, cookie, new_name,
+				  source, journal_cookie);
+
+	if (target) {
+		inotify_inode_queue_event(target, IN_JOURNAL_DELETE_SELF, 0, NULL, NULL,
+					 journal_cookie);
+	}
+
+	if (source) {
+		inotify_inode_queue_event(source, IN_JOURNAL_MOVE_SELF, 0, NULL, NULL,
+					 journal_cookie);
+	}
+}
+
 /*
  * fsnotify_nameremove - a filename was removed from a directory
  */
@@ -77,7 +109,16 @@
 	if (isdir)
 		isdir = IN_ISDIR;
 	dnotify_parent(dentry, DN_DELETE);
-	inotify_dentry_parent_queue_event(dentry, IN_DELETE|isdir, 0, dentry->d_name.name);
+	inotify_dentry_parent_queue_event(dentry, IN_DELETE|isdir, 0, dentry->d_name.name, 0);
+}
+
+static inline void fsnotify_journal_nameremove(struct dentry *dentry, int isdir,
+						journal_cookie_t journal_cookie)
+{
+        if (isdir)
+                isdir = IN_ISDIR;
+        inotify_dentry_parent_queue_event(dentry, IN_JOURNAL_DELETE|isdir, 0, dentry->d_name.name,
+					 journal_cookie);
 }
 
 /*
@@ -85,16 +126,24 @@
  */
 static inline void fsnotify_inoderemove(struct inode *inode)
 {
-	inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL);
+	inotify_inode_queue_event(inode, IN_DELETE_SELF, 0,
+						NULL, NULL, 0);
 	inotify_inode_is_dead(inode);
 }
 
+static inline void fsnotify_journal_inoderemove(struct inode *inode,
+						journal_cookie_t journal_cookie)
+{
+	inotify_inode_queue_event(inode, IN_JOURNAL_DELETE_SELF, 0,
+				 NULL, NULL, journal_cookie);
+}
+
 /*
  * fsnotify_link_count - inode's link count changed
  */
 static inline void fsnotify_link_count(struct inode *inode)
 {
-	inotify_inode_queue_event(inode, IN_ATTRIB, 0, NULL, NULL);
+	inotify_inode_queue_event(inode, IN_ATTRIB, 0, NULL, NULL, 0);
 }
 
 /*
@@ -104,10 +153,17 @@
 {
 	inode_dir_notify(inode, DN_CREATE);
 	inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name,
-				  dentry->d_inode);
+				  dentry->d_inode, 0);
 	audit_inode_child(dentry->d_name.name, dentry, inode);
 }
 
+static inline void fsnotify_journal_create(struct inode *inode, struct dentry *dentry,
+						journal_cookie_t journal_cookie)
+{
+        inotify_inode_queue_event(inode, IN_JOURNAL_CREATE, 0, dentry->d_name.name,
+                                  dentry->d_inode, journal_cookie);
+}
+
 /*
  * fsnotify_link - new hardlink in 'inode' directory
  * Note: We have to pass also the linked inode ptr as some filesystems leave
@@ -117,11 +173,17 @@
 {
 	inode_dir_notify(dir, DN_CREATE);
 	inotify_inode_queue_event(dir, IN_CREATE, 0, new_dentry->d_name.name,
-				  inode);
+				  inode, 0);
 	fsnotify_link_count(inode);
 	audit_inode_child(new_dentry->d_name.name, new_dentry, dir);
 }
 
+static inline void fsnotify_journal_link(struct inode *dir, struct inode *inode,
+				struct dentry *new_dentry, journal_cookie_t journal_cookie)
+{
+	inotify_inode_queue_event(dir, IN_JOURNAL_CREATE, 0, new_dentry->d_name.name,
+				  inode, journal_cookie);
+}
 /*
  * fsnotify_mkdir - directory 'name' was created
  */
@@ -129,10 +191,17 @@
 {
 	inode_dir_notify(inode, DN_CREATE);
 	inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, 
-				  dentry->d_name.name, dentry->d_inode);
+				  dentry->d_name.name, dentry->d_inode, 0);
 	audit_inode_child(dentry->d_name.name, dentry, inode);
 }
 
+static inline void fsnotify_journal_mkdir(struct inode *inode, struct dentry *dentry,
+					 journal_cookie_t journal_cookie)
+{
+	inotify_inode_queue_event(inode, IN_JOURNAL_CREATE | IN_ISDIR, 0,
+                                  dentry->d_name.name, dentry->d_inode, journal_cookie);
+}
+
 /*
  * fsnotify_access - file was read
  */
@@ -145,8 +214,8 @@
 		mask |= IN_ISDIR;
 
 	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);
+	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name, 0);
+	inotify_inode_queue_event(inode, mask, 0, NULL, NULL, 0);
 }
 
 /*
@@ -161,8 +230,8 @@
 		mask |= IN_ISDIR;
 
 	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);
+	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name, 0);
+	inotify_inode_queue_event(inode, mask, 0, NULL, NULL, 0);
 }
 
 /*
@@ -176,8 +245,8 @@
 	if (S_ISDIR(inode->i_mode))
 		mask |= IN_ISDIR;
 
-	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name, 0);
+	inotify_inode_queue_event(inode, mask, 0, NULL, NULL, 0);
 }
 
 /*
@@ -194,8 +263,8 @@
 	if (S_ISDIR(inode->i_mode))
 		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, mask, 0, name, 0);
+	inotify_inode_queue_event(inode, mask, 0, NULL, NULL, 0);
 }
 
 /*
@@ -209,8 +278,8 @@
 	if (S_ISDIR(inode->i_mode))
 		mask |= IN_ISDIR;
 
-	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
-	inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
+	inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name, 0);
+	inotify_inode_queue_event(inode, mask, 0, NULL, NULL, 0);
 }
 
 /*
@@ -257,9 +326,9 @@
 	if (in_mask) {
 		if (S_ISDIR(inode->i_mode))
 			in_mask |= IN_ISDIR;
-		inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL);
+		inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL, 0);
 		inotify_dentry_parent_queue_event(dentry, in_mask, 0,
-						  dentry->d_name.name);
+						  dentry->d_name.name, 0);
 	}
 }
 
Index: linux-2.6/include/linux/inotify.h
===================================================================
--- linux-2.6.orig/include/linux/inotify.h	2008-09-11 15:49:08.000000000 -0700
+++ linux-2.6/include/linux/inotify.h	2008-09-12 12:37:16.000000000 -0700
@@ -44,6 +44,28 @@
 #define IN_Q_OVERFLOW		0x00004000	/* Event queued overflowed */
 #define IN_IGNORED		0x00008000	/* File was ignored */
 
+/*
+ * These are journal events. They are sent from a journaling
+ * file system or journal.
+ *
+ * IN_JOURNAL_COMMIT is special. Only one file per journaling filesystem super-block
+ * is allowed. Registering for this event with multiple files will overwrite previous
+ * registrations. The last registration for this will be taken as current and commit
+ * notifications will be delivered to only that file.
+ *
+ * The inode registered for this event should not be unlinked. This will disable
+ * any notifications for journal commits.
+ *
+ * If this event is not registered for, journal commit messages will not be delivered.
+ */
+#define IN_JOURNAL_COMMIT	0x00010000	/* Journal on this partition committed a transaction */
+#define IN_JOURNAL_MOVED_FROM	0x00020000	/* Journal transaction to move file from X started */
+#define IN_JOURNAL_MOVED_TO	0x00040000	/* Journal transaction to move file to Y started */
+#define IN_JOURNAL_MOVE_SELF	0x00080000	/* Journal transaction to move self started */
+#define IN_JOURNAL_DELETE	0x00100000	/* Journal transaction to delete subfile started */
+#define IN_JOURNAL_DELETE_SELF	0x00200000	/* Journal transaction to delete self started */
+#define IN_JOURNAL_CREATE	0x00400000	/* Journal transaction to create file started */
+
 /* helper events */
 #define IN_CLOSE		(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */
 #define IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO) /* moves */
@@ -69,10 +91,14 @@
 #define IN_CLOEXEC O_CLOEXEC
 #define IN_NONBLOCK O_NONBLOCK
 
+#define IN_JOURNAL_ALL_EVENTS	(IN_JOURNAL_MOVED_FROM | IN_JOURNAL_MOVED_TO | \
+				 IN_JOURNAL_MOVE_SELF | IN_JOURNAL_DELETE | \
+				 IN_JOURNAL_DELETE_SELF | IN_JOURNAL_CREATE)
 #ifdef __KERNEL__
 
 #include <linux/dcache.h>
 #include <linux/fs.h>
+#include <linux/journal-head.h>
 
 /*
  * struct inotify_watch - represents a watch request on a specific inode
@@ -97,7 +123,7 @@
 
 struct inotify_operations {
 	void (*handle_event)(struct inotify_watch *, u32, u32, u32,
-			     const char *, struct inode *);
+			     const char *, struct inode *, journal_cookie_t);
 	void (*destroy_watch)(struct inotify_watch *);
 };
 
@@ -108,9 +134,9 @@
 extern void inotify_d_instantiate(struct dentry *, struct inode *);
 extern void inotify_d_move(struct dentry *);
 extern void inotify_inode_queue_event(struct inode *, __u32, __u32,
-				      const char *, struct inode *);
+				      const char *, struct inode *, journal_cookie_t);
 extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,
-					      const char *);
+					      const char *, journal_cookie_t);
 extern void inotify_unmount_inodes(struct list_head *);
 extern void inotify_inode_is_dead(struct inode *);
 extern u32 inotify_get_cookie(void);
Index: linux-2.6/include/linux/jbd.h
===================================================================
--- linux-2.6.orig/include/linux/jbd.h	2008-09-11 15:49:12.000000000 -0700
+++ linux-2.6/include/linux/jbd.h	2008-09-12 12:34:58.000000000 -0700
@@ -114,6 +114,9 @@
  * This is an opaque datatype.
  **/
 typedef struct journal_s	journal_t;	/* Journal control structure */
+
+extern void journal_notify_commit(journal_t *journal, journal_cookie_t journal_cookie);
+
 #endif
 
 /*
@@ -919,6 +922,7 @@
 extern int	   journal_clear_err  (journal_t *);
 extern int	   journal_bmap(journal_t *, unsigned long, unsigned long *);
 extern int	   journal_force_commit(journal_t *);
+extern journal_cookie_t journal_get_cookie(handle_t *handle);
 
 /*
  * journal_head management
Index: linux-2.6/fs/ext4/ext4_jbd2.h
===================================================================
--- linux-2.6.orig/fs/ext4/ext4_jbd2.h	2008-09-12 11:53:15.000000000 -0700
+++ linux-2.6/fs/ext4/ext4_jbd2.h	2008-09-12 11:55:56.000000000 -0700
@@ -239,4 +239,8 @@
 	return 0;
 }
 
+static inline journal_cookie_t ext4_journal_get_cookie(handle_t *handle)
+{
+	return jbd2_journal_get_cookie(handle);
+}
 #endif	/* _EXT4_JBD2_H */
Index: linux-2.6/fs/ext4/namei.c
===================================================================
--- linux-2.6.orig/fs/ext4/namei.c	2008-09-12 11:29:43.000000000 -0700
+++ linux-2.6/fs/ext4/namei.c	2008-09-12 12:26:00.000000000 -0700
@@ -34,6 +34,7 @@
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
+#include <linux/ext4_fsnotify.h>
 #include "ext4.h"
 #include "ext4_jbd2.h"
 
@@ -1741,6 +1742,10 @@
 		ext4_set_aops(inode);
 		err = ext4_add_nondir(handle, dentry, inode);
 	}
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_create(dir, dentry, journal_cookie);
+	}
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -1776,6 +1781,10 @@
 #endif
 		err = ext4_add_nondir(handle, dentry, inode);
 	}
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_create(dir, dentry, journal_cookie);
+	}
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -1846,6 +1855,10 @@
 	ext4_update_dx_flag(dir);
 	ext4_mark_inode_dirty(handle, dir);
 	d_instantiate(dentry, inode);
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_mkdir(dir, dentry, journal_cookie);
+	}
 out_stop:
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
@@ -2122,6 +2135,10 @@
 	ext4_update_dx_flag(dir);
 	ext4_mark_inode_dirty(handle, dir);
 
+	if (!handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_rmdir(dentry, inode, journal_cookie);
+	}
 end_rmdir:
 	ext4_journal_stop(handle);
 	brelse (bh);
@@ -2176,6 +2193,10 @@
 	ext4_mark_inode_dirty(handle, inode);
 	retval = 0;
 
+	if (!handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_unlink(dentry, inode, journal_cookie);
+	}
 end_unlink:
 	ext4_journal_stop(handle);
 	brelse (bh);
@@ -2233,6 +2254,10 @@
 	}
 	EXT4_I(inode)->i_disksize = inode->i_size;
 	err = ext4_add_nondir(handle, dentry, inode);
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_create(dir, dentry, journal_cookie);
+	}
 out_stop:
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
@@ -2271,6 +2296,10 @@
 	atomic_inc(&inode->i_count);
 
 	err = ext4_add_nondir(handle, dentry, inode);
+	if (!err && !handle->h_sync) {
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+		ext4_fsnotify_link(dir, inode, dentry, journal_cookie);
+	}
 	ext4_journal_stop(handle);
 	if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries))
 		goto retry;
@@ -2292,6 +2321,7 @@
 	struct buffer_head * old_bh, * new_bh, * dir_bh;
 	struct ext4_dir_entry_2 * old_de, * new_de;
 	int retval;
+	const char *old_name = NULL;
 
 	old_bh = new_bh = dir_bh = NULL;
 
@@ -2305,6 +2335,7 @@
 	if (IS_ERR(handle))
 		return PTR_ERR(handle);
 
+	old_name = ext4_fsnotify_oldname_init(old_dentry->d_name.name);
 	if (IS_DIRSYNC(old_dir) || IS_DIRSYNC(new_dir))
 		handle->h_sync = 1;
 
@@ -2432,7 +2463,16 @@
 	}
 	retval = 0;
 
+	if (!handle->h_sync) {
+		int isdir = S_ISDIR(old_dentry->d_inode->i_mode);
+		const char *new_name = new_dentry->d_name.name;
+		journal_cookie_t journal_cookie = ext4_journal_get_cookie(handle);
+
+		ext4_fsnotify_move(old_dir, new_dir, old_name, new_name, isdir,
+				   new_dentry->d_inode, old_dentry, journal_cookie);
+	}
 end_rename:
+	ext4_fsnotify_oldname_free(old_name);
 	brelse (dir_bh);
 	brelse (old_bh);
 	brelse (new_bh);
Index: linux-2.6/fs/jbd2/commit.c
===================================================================
--- linux-2.6.orig/fs/jbd2/commit.c	2008-09-12 11:01:08.000000000 -0700
+++ linux-2.6/fs/jbd2/commit.c	2008-09-12 11:36:00.000000000 -0700
@@ -990,6 +990,7 @@
 	}
 	spin_unlock(&journal->j_list_lock);
 
+	jbd2_journal_notify_commit(journal, journal->j_commit_sequence);
 	jbd_debug(1, "JBD: commit %d complete, head %d\n",
 		  journal->j_commit_sequence, journal->j_tail_sequence);
 
Index: linux-2.6/fs/jbd2/journal.c
===================================================================
--- linux-2.6.orig/fs/jbd2/journal.c	2008-09-12 11:44:10.000000000 -0700
+++ linux-2.6/fs/jbd2/journal.c	2008-09-12 11:47:28.000000000 -0700
@@ -84,6 +84,7 @@
 EXPORT_SYMBOL(jbd2_journal_init_jbd_inode);
 EXPORT_SYMBOL(jbd2_journal_release_jbd_inode);
 EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate);
+EXPORT_SYMBOL(jbd2_journal_get_cookie);
 
 static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
 static void __journal_abort_soft (journal_t *journal, int errno);
@@ -1940,6 +1941,12 @@
 	return 1 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
 }
 
+/* We don't need to do anything here other than return the txid */
+journal_cookie_t jbd2_journal_get_cookie(handle_t *handle)
+{
+	return handle->h_transaction->t_tid;
+}
+
 /*
  * helper functions to deal with 32 or 64bit block numbers.
  */
Index: linux-2.6/include/linux/ext4_fsnotify.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/include/linux/ext4_fsnotify.h	2008-09-12 11:36:37.000000000 -0700
@@ -0,0 +1,81 @@
+/*
+ * linux/include/linux/ext4_fsnotify.h
+ *
+ * Copyright (C) 2008 Akamai Technologies
+ * Abhijit Paithankar <apaithan@akamai.com>
+ *
+ * fsnotify interface for Ext4 with journal cookies
+ */
+
+#ifndef EXT4_FSNOTIFY_H_
+#define EXT4_FSNOTIFY_H_
+
+#include <linux/fs.h>
+#include <linux/jbd2.h>
+#include <linux/fsnotify.h>
+
+#define SB(x) x->h_transaction->t_journal->j_private
+
+static const char *ext4_fsnotify_oldname_init(const char *name)
+{
+	return fsnotify_oldname_init(name);
+}
+
+static void ext4_fsnotify_oldname_free(const char *old_name)
+{
+	fsnotify_oldname_free(old_name);
+}
+
+static void ext4_fsnotify_unlink(struct dentry *dentry, struct inode *inode,
+				journal_cookie_t journal_cookie)
+{
+	int isdir = S_ISDIR(dentry->d_inode->i_mode);
+	if (!inode->i_nlink)
+		fsnotify_journal_inoderemove(inode, journal_cookie);
+
+	fsnotify_journal_nameremove(dentry, isdir, journal_cookie);
+}
+
+static void ext4_fsnotify_move(struct inode *old_dir, struct inode *new_dir,
+				      const char *old_name, const char *new_name,
+				      int isdir, struct inode *target, struct dentry *moved,
+				      journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_move(old_dir, new_dir, old_name, new_name, isdir,
+			target, moved, journal_cookie);
+}
+
+static void ext4_fsnotify_create(struct inode *dir, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_create(dir, dentry, journal_cookie);
+}
+
+static void ext4_fsnotify_mkdir(struct inode *dir, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_mkdir(dir, dentry, journal_cookie);
+}
+
+static void ext4_fsnotify_rmdir(struct dentry *dentry, struct inode *inode,
+				journal_cookie_t journal_cookie)
+{
+	ext4_fsnotify_unlink(dentry, inode, journal_cookie);
+
+}
+
+static void ext4_fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *dentry,
+				journal_cookie_t journal_cookie)
+{
+	fsnotify_journal_link(dir, inode, dentry, journal_cookie);
+}
+
+void jbd2_journal_notify_commit(journal_t *journal, journal_cookie_t journal_cookie)
+{
+	struct super_block *sb = journal->j_private;
+
+	if (sb && sb->s_priv_inode)
+		fsnotify_journal_commit(sb->s_priv_inode, journal_cookie);
+}
+
+#endif /* EXT4_FSNOTIFY_H_ */
Index: linux-2.6/include/linux/jbd2.h
===================================================================
--- linux-2.6.orig/include/linux/jbd2.h	2008-09-12 10:55:04.000000000 -0700
+++ linux-2.6/include/linux/jbd2.h	2008-09-12 12:35:14.000000000 -0700
@@ -115,6 +115,8 @@
  * This is an opaque datatype.
  **/
 typedef struct journal_s	journal_t;	/* Journal control structure */
+
+extern void jbd2_journal_notify_commit(journal_t *journal, journal_cookie_t journal_cookie);
 #endif
 
 /*
@@ -1076,6 +1078,8 @@
 extern void	   jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode);
 extern void	   jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_inode *jinode);
 
+extern journal_cookie_t jbd2_journal_get_cookie(handle_t *handle);
+
 /*
  * journal_head management
  */
Index: linux-2.6/include/linux/journal-head.h
===================================================================
--- linux-2.6.orig/include/linux/journal-head.h	2008-09-12 12:35:48.000000000 -0700
+++ linux-2.6/include/linux/journal-head.h	2008-09-12 12:36:06.000000000 -0700
@@ -13,6 +13,7 @@
 typedef unsigned int		tid_t;		/* Unique transaction ID */
 typedef struct transaction_s	transaction_t;	/* Compound transaction type */
 struct buffer_head;
+typedef tid_t journal_cookie_t; /* Opaque journal cookie */
 
 struct journal_head {
 	/*
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html



Copyright © 2008, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds