inotify: send IN_ATTRIB events when link count changes
authorJan Kara <jack@suse.cz>
Wed, 6 Feb 2008 09:37:13 +0000 (01:37 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Wed, 6 Feb 2008 18:41:05 +0000 (10:41 -0800)
Currently, no notification event has been sent when inode's link count
changed.  This is inconvenient for the application in some cases:

Suppose you have the following directory structure

    foo/test
    bar/

and you watch test.  If someone does "mv foo/test bar/", you get event
IN_MOVE_SELF and you know something has happened with the file "test".
However if someone does "ln foo/test bar/test" and "rm foo/test" you get no
inotify event for the file "test" (only directories "foo" and "bar" receive
events).

Furthermore it could be argued that link count belongs to file's metadata and
thus IN_ATTRIB should be sent when it changes.

The following patch implements sending of IN_ATTRIB inotify events when link
count of the inode changes, i.e., when a hardlink to the inode is created or
when it is removed.  This event is sent in addition to all the events sent so
far.  In particular, when a last link to a file is removed, IN_ATTRIB event is
sent in addition to IN_DELETE_SELF event.

Signed-off-by: Jan Kara <jack@suse.cz>
Acked-by: Morten Welinder <mwelinder@gmail.com>
Cc: Robert Love <rlove@google.com>
Cc: John McCutchan <ttb@tentacle.dhs.org>
Cc: Steven French <sfrench@us.ibm.com>
Cc: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/namei.c
include/linux/fsnotify.h

index 73e2e665817a100c9f05c33a12756f639d9c5ae7..241cff423653692ec067afa71ccf9c8ca4e86b05 100644 (file)
@@ -2188,6 +2188,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)) {
+               fsnotify_link_count(dentry->d_inode);
                d_delete(dentry);
        }
 
@@ -2360,7 +2361,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
        error = dir->i_op->link(old_dentry, dir, new_dentry);
        mutex_unlock(&old_dentry->d_inode->i_mutex);
        if (!error)
-               fsnotify_create(dir, new_dentry);
+               fsnotify_link(dir, old_dentry->d_inode, new_dentry);
        return error;
 }
 
index 2bd31fa623b6a7b12c3aa8ac9369cc698b0746d2..d4b7c4ac72e6dd70e9bbda61158b36847e83bb2a 100644 (file)
@@ -91,6 +91,14 @@ static inline void fsnotify_inoderemove(struct inode *inode)
        inotify_inode_is_dead(inode);
 }
 
+/*
+ * 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);
+}
+
 /*
  * fsnotify_create - 'name' was linked in
  */
@@ -102,6 +110,20 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
        audit_inode_child(dentry->d_name.name, dentry, inode);
 }
 
+/*
+ * fsnotify_link - new hardlink in 'inode' directory
+ * Note: We have to pass also the linked inode ptr as some filesystems leave
+ *   new_dentry->d_inode NULL and instantiate inode pointer later
+ */
+static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry)
+{
+       inode_dir_notify(dir, DN_CREATE);
+       inotify_inode_queue_event(dir, IN_CREATE, 0, new_dentry->d_name.name,
+                                 inode);
+       fsnotify_link_count(inode);
+       audit_inode_child(new_dentry->d_name.name, new_dentry, dir);
+}
+
 /*
  * fsnotify_mkdir - directory 'name' was created
  */