From: Mike Waychison 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 Signed-off-by: Andrew Morton --- 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; } _