
From: Mike Waychison <Michael.Waychison@Sun.COM>

Hold on to the nameidata of the watched file until we have successfully 
added/updated the watch on the inode.

Signed-off-by: Mike Waychison <michael.waychison@sun.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/char/inotify.c |   48 ++++++++++++++++++++++-------------------
 1 files changed, 26 insertions(+), 22 deletions(-)

diff -puN drivers/char/inotify.c~inotify-fix_find_inode drivers/char/inotify.c
--- 25/drivers/char/inotify.c~inotify-fix_find_inode	2005-01-26 18:29:16.797237352 -0800
+++ 25-akpm/drivers/char/inotify.c	2005-01-26 18:29:16.802236592 -0800
@@ -181,33 +181,24 @@ static inline void put_inode_data(struct
 }
 
 /*
- * find_inode - resolve a user-given path to a specific inode and iget() it
+ * find_inode - resolve a user-given path to a specific inode and return a nd
  */
-static struct inode * find_inode(const char __user *dirname)
+static int find_inode(const char __user *dirname, struct nameidata *nd)
 {
-	struct inode *inode;
-	struct nameidata nd;
 	int error;
 
-	error = __user_walk(dirname, LOOKUP_FOLLOW, &nd);
+	error = __user_walk(dirname, LOOKUP_FOLLOW, nd);
 	if (error)
-		return ERR_PTR(error);
-
-	inode = nd.dentry->d_inode;
+		return error;
 
 	/* you can only watch an inode if you have read permissions on it */
-	error = permission(inode, MAY_READ, NULL);
+	error = permission(nd->dentry->d_inode, MAY_READ, NULL);
 	if (error) {
-		inode = ERR_PTR(error);
 		goto release_and_out;
 	}
 
-	spin_lock(&inode_lock);
-	__iget(inode);
-	spin_unlock(&inode_lock);
 release_and_out:
-	path_release(&nd);
-	return inode;
+	return error;
 }
 
 struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
@@ -911,11 +902,15 @@ static int inotify_add_watch(struct inot
 {
 	struct inode *inode;
 	struct inotify_watch *watch;
+	struct nameidata nd;
 	int ret;
 
-	inode = find_inode((const char __user*) request->dirname);
-	if (IS_ERR(inode))
-		return PTR_ERR(inode);
+	ret = find_inode((const char __user*) request->dirname, &nd);
+	if (ret)
+		return ret;
+
+	/* Held in place by references in nd */
+	inode = nd.dentry->d_inode;
 
 	spin_lock(&dev->lock);
 
@@ -930,7 +925,7 @@ static int inotify_add_watch(struct inot
 		owatch->mask = request->mask;
 		inode_update_watch_mask(inode);
 		spin_unlock(&dev->lock);
-		iput(inode);
+		path_release(&nd);
 
 		return owatch->wd;
 	}
@@ -939,7 +934,7 @@ static int inotify_add_watch(struct inot
 
 	watch = create_watch(dev, request->mask, inode);
 	if (!watch) {
-		iput(inode);
+		path_release(&nd);
 		return -ENOSPC;
 	}
 
@@ -949,7 +944,7 @@ static int inotify_add_watch(struct inot
 	if (inotify_dev_add_watch(dev, watch)) {
 		delete_watch(dev, watch);
 		spin_unlock(&dev->lock);
-		iput(inode);
+		path_release(&nd);
 		return -EINVAL;
 	}
 
@@ -958,12 +953,21 @@ static int inotify_add_watch(struct inot
 		list_del_init(&watch->d_list);
 		delete_watch(dev, watch);
 		spin_unlock(&dev->lock);
-		iput(inode);
+		path_release(&nd);
 		return ret;
 	}
 
 	spin_unlock(&dev->lock);
 
+	/*
+	 * demote the references in nd to a reference to inode held
+	 * by the watch.
+	 */
+	spin_lock(&inode_lock);
+	__iget(inode);
+	spin_unlock(&inode_lock);
+	path_release(&nd);
+
 	return watch->wd;
 }
 
_
