]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Directory Notification Fix
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 9 May 2002 05:03:28 +0000 (22:03 -0700)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Thu, 9 May 2002 05:03:28 +0000 (22:03 -0700)
This patch fixes directory notification so that notifications get
cancelled when a process exits.  This make dnotify MUCH more stable and
usable :-)

fs/dnotify.c
fs/open.c
include/linux/dnotify.h

index 3cf7f0656f289076a38f08dbe53032b2c31c4f30..5152699a849359eb4404e3ef7ba0744e8af12dc5 100644 (file)
@@ -38,60 +38,74 @@ static void redo_inode_mask(struct inode *inode)
        inode->i_dnotify_mask = new_mask;
 }
 
+void dnotify_flush(struct file *filp, fl_owner_t id)
+{
+       struct dnotify_struct *dn;
+       struct dnotify_struct **prev;
+       struct inode *inode;
+
+       inode = filp->f_dentry->d_inode;
+       if (!S_ISDIR(inode->i_mode))
+               return;
+       write_lock(&dn_lock);
+       prev = &inode->i_dnotify;
+       while ((dn = *prev) != NULL) {
+               if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
+                       *prev = dn->dn_next;
+                       redo_inode_mask(inode);
+                       kmem_cache_free(dn_cache, dn);
+                       break;
+               }
+               prev = &dn->dn_next;
+       }
+       write_unlock(&dn_lock);
+}
+
 int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
 {
-       struct dnotify_struct *dn = NULL;
+       struct dnotify_struct *dn;
        struct dnotify_struct *odn;
        struct dnotify_struct **prev;
        struct inode *inode;
-       int turning_off = (arg & ~DN_MULTISHOT) == 0;
+       fl_owner_t id = current->files;
 
-       if (!turning_off && !dir_notify_enable)
+       if ((arg & ~DN_MULTISHOT) == 0) {
+               dnotify_flush(filp, id);
+               return 0;
+       }
+       if (!dir_notify_enable)
                return -EINVAL;
        inode = filp->f_dentry->d_inode;
        if (!S_ISDIR(inode->i_mode))
                return -ENOTDIR;
-       if (!turning_off) {
-               dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
-               if (dn == NULL)
-                       return -ENOMEM;
-       }
+       dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
+       if (dn == NULL)
+               return -ENOMEM;
        write_lock(&dn_lock);
        prev = &inode->i_dnotify;
-       for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev)
-               if ((odn->dn_owner == current->files) && (odn->dn_filp == filp))
-                       break;
-       if (odn != NULL) {
-               if (turning_off) {
-                       *prev = odn->dn_next;
-                       redo_inode_mask(inode);
-                       dn = odn;
-                       goto out_free;
+       while ((odn = *prev) != NULL) {
+               if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
+                       odn->dn_fd = fd;
+                       odn->dn_mask |= arg;
+                       inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+                       kmem_cache_free(dn_cache, dn);
+                       goto out;
                }
-               odn->dn_fd = fd;
-               odn->dn_mask |= arg;
-               inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
-               goto out_free;
+               prev = &odn->dn_next;
        }
-       if (turning_off)
-               goto out;
        filp->f_owner.pid = current->pid;
        filp->f_owner.uid = current->uid;
        filp->f_owner.euid = current->euid;
-       dn->dn_magic = DNOTIFY_MAGIC;
        dn->dn_mask = arg;
        dn->dn_fd = fd;
        dn->dn_filp = filp;
-       dn->dn_owner = current->files;
+       dn->dn_owner = id;
        inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
        dn->dn_next = inode->i_dnotify;
        inode->i_dnotify = dn;
 out:
        write_unlock(&dn_lock);
        return 0;
-out_free:
-       kmem_cache_free(dn_cache, dn);
-       goto out;
 }
 
 void __inode_dir_notify(struct inode *inode, unsigned long event)
@@ -104,11 +118,6 @@ void __inode_dir_notify(struct inode *inode, unsigned long event)
        write_lock(&dn_lock);
        prev = &inode->i_dnotify;
        while ((dn = *prev) != NULL) {
-               if (dn->dn_magic != DNOTIFY_MAGIC) {
-                       printk(KERN_ERR "__inode_dir_notify: bad magic "
-                               "number in dnotify_struct!\n");
-                       goto out;
-               }
                if ((dn->dn_mask & event) == 0) {
                        prev = &dn->dn_next;
                        continue;
index f342cd5e6a28978029a4d72a97314777c3cf0948..e0231b191336932d17928ec586683d682268c556 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -835,7 +835,7 @@ int filp_close(struct file *filp, fl_owner_t id)
                retval = filp->f_op->flush(filp);
                unlock_kernel();
        }
-       fcntl_dirnotify(0, filp, 0);
+       dnotify_flush(filp, id);
        locks_remove_posix(filp, id);
        fput(filp);
        return retval;
index 23e8733ab4fb8b79a1f72d651dc47b84438b6809..2db6774c9330b2e81db81fa4b18836b935c25e83 100644 (file)
@@ -1,14 +1,13 @@
 /*
  * Directory notification for Linux
  *
- * Copyright 2000 (C) Stephen Rothwell
+ * Copyright (C) 2000,2002 Stephen Rothwell
  */
 
 #include <linux/fs.h>
 
 struct dnotify_struct {
        struct dnotify_struct * dn_next;
-       int                     dn_magic;
        unsigned long           dn_mask;        /* Events to be notified
                                                   see linux/fcntl.h */
        int                     dn_fd;
@@ -16,9 +15,8 @@ struct dnotify_struct {
        fl_owner_t              dn_owner;
 };
 
-#define DNOTIFY_MAGIC  0x444E4F54
-
 extern void __inode_dir_notify(struct inode *, unsigned long);
+extern void dnotify_flush(struct file *filp, fl_owner_t id);
 extern int fcntl_dirnotify(int, struct file *, unsigned long);
 
 static inline void inode_dir_notify(struct inode *inode, unsigned long event)