From 7544953685859875b5ac0260b6b1856066c092d6 Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Mon, 1 Aug 2005 11:00:45 -0400 Subject: [PATCH] inotify: fix file deletion by rename detection When a file is moved over an existing file that you are watching, inotify won't send you a DELETE_SELF event and it won't unref the inode until the inotify instance is closed by the application. Signed-off-by: John McCutchan Signed-off-by: Robert Love Signed-off-by: Linus Torvalds --- fs/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 02a824cd3c5..4a27eb79811 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2218,7 +2218,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, 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, is_dir); + fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir, new_dentry->d_inode); } fsnotify_oldname_free(old_name); -- cgit v1.2.3 From e234f35c54a30d040313e40833dcf623d14629b4 Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Thu, 4 Aug 2005 13:07:08 -0700 Subject: [PATCH] inotify delete race fix The included patch fixes a problem where a inotify client would receive a delete event before the file was actually deleted. The bug affects both dnotify & inotify. Signed-off-by: John McCutchan Signed-off-by: Robert Love Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/namei.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 4a27eb79811..83559dce428 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1801,8 +1801,8 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) } up(&dentry->d_inode->i_sem); if (!error) { - fsnotify_rmdir(dentry, dentry->d_inode, dir); d_delete(dentry); + fsnotify_rmdir(dentry, dentry->d_inode, dir); } dput(dentry); @@ -1874,8 +1874,14 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) /* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { +#if defined(CONFIG_INOTIFY) || defined(CONFIG_DNOTIFY) + dget(dentry); + d_delete(dentry); fsnotify_unlink(dentry, dir); + dput(dentry); +#else d_delete(dentry); +#endif } return error; -- cgit v1.2.3 From 0c3dba1534569734ba353afdf3f11def497ff2ac Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Thu, 4 Aug 2005 21:12:54 -0400 Subject: [PATCH] Clean up inotify delete race fix This avoids the whole #ifdef mess by just getting a copy of dentry->d_inode before d_delete is called - that makes the codepaths the same for the INOTIFY/DNOTIFY cases as for the regular no-notify case. I've been running this under a Gnome session for the last 10 minutes. Inotify is being used extensively. Signed-off-by: John McCutchan Signed-off-by: Linus Torvalds --- fs/namei.c | 9 ++------- include/linux/fsnotify.h | 4 +--- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/namei.c b/fs/namei.c index 83559dce428..32accb6a672 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1874,14 +1874,9 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) /* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { -#if defined(CONFIG_INOTIFY) || defined(CONFIG_DNOTIFY) - dget(dentry); + struct inode *inode = dentry->d_inode; d_delete(dentry); - fsnotify_unlink(dentry, dir); - dput(dentry); -#else - d_delete(dentry); -#endif + fsnotify_unlink(dentry, inode, dir); } return error; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index e96a4306ab3..1cb4935348d 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -46,10 +46,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, /* * fsnotify_unlink - file was unlinked */ -static inline void fsnotify_unlink(struct dentry *dentry, struct inode *dir) +static inline void fsnotify_unlink(struct dentry *dentry, struct inode *inode, struct inode *dir) { - struct inode *inode = dentry->d_inode; - inode_dir_notify(dir, DN_DELETE); inotify_inode_queue_event(dir, IN_DELETE, 0, dentry->d_name.name); inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL); -- cgit v1.2.3 From 7a91bf7f5c22c8407a9991cbd9ce5bb87caa6b4a Mon Sep 17 00:00:00 2001 From: John McCutchan Date: Mon, 8 Aug 2005 13:52:16 -0400 Subject: [PATCH] fsnotify_name/inoderemove The patch below unhooks fsnotify from vfs_unlink & vfs_rmdir. It introduces two new fsnotify calls, that are hooked in at the dcache level. This not only more closely matches how the VFS layer works, it also avoids the problem with locking and inode lifetimes. The two functions are - fsnotify_nameremove -- called when a directory entry is going away. It notifies the PARENT of the deletion. This is called from d_delete(). - inoderemove -- called when the files inode itself is going away. It notifies the inode that is being deleted. This is called from dentry_iput(). Signed-off-by: John McCutchan Signed-off-by: Linus Torvalds --- fs/dcache.c | 7 +++++++ fs/namei.c | 3 --- include/linux/fsnotify.h | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) (limited to 'fs/namei.c') diff --git a/fs/dcache.c b/fs/dcache.c index 3aa8a7e980d..a15a2e1f552 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,7 @@ static inline void dentry_iput(struct dentry * dentry) list_del_init(&dentry->d_alias); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); + fsnotify_inoderemove(inode); if (dentry->d_op && dentry->d_op->d_iput) dentry->d_op->d_iput(dentry, inode); else @@ -1165,13 +1167,16 @@ out: void d_delete(struct dentry * dentry) { + int isdir = 0; /* * Are we the only user? */ spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); + isdir = S_ISDIR(dentry->d_inode->i_mode); if (atomic_read(&dentry->d_count) == 1) { dentry_iput(dentry); + fsnotify_nameremove(dentry, isdir); return; } @@ -1180,6 +1185,8 @@ void d_delete(struct dentry * dentry) spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); + + fsnotify_nameremove(dentry, isdir); } static void __d_rehash(struct dentry * entry, struct hlist_head *list) diff --git a/fs/namei.c b/fs/namei.c index 32accb6a672..57046d98a74 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1802,7 +1802,6 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) up(&dentry->d_inode->i_sem); if (!error) { d_delete(dentry); - fsnotify_rmdir(dentry, dentry->d_inode, dir); } dput(dentry); @@ -1874,9 +1873,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry) /* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { - struct inode *inode = dentry->d_inode; d_delete(dentry); - fsnotify_unlink(dentry, inode, dir); } return error; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 1cb4935348d..9db31d251c2 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -67,6 +67,26 @@ static inline void fsnotify_rmdir(struct dentry *dentry, struct inode *inode, inotify_inode_is_dead(inode); } +/* + * fsnotify_nameremove - a filename was removed from a directory + */ +static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) +{ + if (isdir) + isdir = IN_ISDIR; + dnotify_parent(dentry, DN_DELETE); + inotify_dentry_parent_queue_event(dentry, IN_DELETE|isdir, 0, dentry->d_name.name); +} + +/* + * fsnotify_inoderemove - an inode is going away + */ +static inline void fsnotify_inoderemove(struct inode *inode) +{ + inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL); + inotify_inode_is_dead(inode); +} + /* * fsnotify_create - 'name' was linked in */ -- cgit v1.2.3