Commit 4c91e008 authored by Stephen Rothwell's avatar Stephen Rothwell Committed by Linus Torvalds

[PATCH] Directory Notification Fix

This patch fixes directory notification so that notifications get
cancelled when a process exits.  This make dnotify MUCH more stable and
usable :-)
parent 41c279a2
...@@ -38,60 +38,74 @@ static void redo_inode_mask(struct inode *inode) ...@@ -38,60 +38,74 @@ static void redo_inode_mask(struct inode *inode)
inode->i_dnotify_mask = new_mask; 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) 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 *odn;
struct dnotify_struct **prev; struct dnotify_struct **prev;
struct inode *inode; 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; return -EINVAL;
inode = filp->f_dentry->d_inode; inode = filp->f_dentry->d_inode;
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
return -ENOTDIR; return -ENOTDIR;
if (!turning_off) { dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); if (dn == NULL)
if (dn == NULL) return -ENOMEM;
return -ENOMEM;
}
write_lock(&dn_lock); write_lock(&dn_lock);
prev = &inode->i_dnotify; prev = &inode->i_dnotify;
for (odn = *prev; odn != NULL; prev = &odn->dn_next, odn = *prev) while ((odn = *prev) != NULL) {
if ((odn->dn_owner == current->files) && (odn->dn_filp == filp)) if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
break; odn->dn_fd = fd;
if (odn != NULL) { odn->dn_mask |= arg;
if (turning_off) { inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
*prev = odn->dn_next; kmem_cache_free(dn_cache, dn);
redo_inode_mask(inode); goto out;
dn = odn;
goto out_free;
} }
odn->dn_fd = fd; prev = &odn->dn_next;
odn->dn_mask |= arg;
inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
goto out_free;
} }
if (turning_off)
goto out;
filp->f_owner.pid = current->pid; filp->f_owner.pid = current->pid;
filp->f_owner.uid = current->uid; filp->f_owner.uid = current->uid;
filp->f_owner.euid = current->euid; filp->f_owner.euid = current->euid;
dn->dn_magic = DNOTIFY_MAGIC;
dn->dn_mask = arg; dn->dn_mask = arg;
dn->dn_fd = fd; dn->dn_fd = fd;
dn->dn_filp = filp; dn->dn_filp = filp;
dn->dn_owner = current->files; dn->dn_owner = id;
inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
dn->dn_next = inode->i_dnotify; dn->dn_next = inode->i_dnotify;
inode->i_dnotify = dn; inode->i_dnotify = dn;
out: out:
write_unlock(&dn_lock); write_unlock(&dn_lock);
return 0; return 0;
out_free:
kmem_cache_free(dn_cache, dn);
goto out;
} }
void __inode_dir_notify(struct inode *inode, unsigned long event) void __inode_dir_notify(struct inode *inode, unsigned long event)
...@@ -104,11 +118,6 @@ 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); write_lock(&dn_lock);
prev = &inode->i_dnotify; prev = &inode->i_dnotify;
while ((dn = *prev) != NULL) { 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) { if ((dn->dn_mask & event) == 0) {
prev = &dn->dn_next; prev = &dn->dn_next;
continue; continue;
......
...@@ -835,7 +835,7 @@ int filp_close(struct file *filp, fl_owner_t id) ...@@ -835,7 +835,7 @@ int filp_close(struct file *filp, fl_owner_t id)
retval = filp->f_op->flush(filp); retval = filp->f_op->flush(filp);
unlock_kernel(); unlock_kernel();
} }
fcntl_dirnotify(0, filp, 0); dnotify_flush(filp, id);
locks_remove_posix(filp, id); locks_remove_posix(filp, id);
fput(filp); fput(filp);
return retval; return retval;
......
/* /*
* Directory notification for Linux * Directory notification for Linux
* *
* Copyright 2000 (C) Stephen Rothwell * Copyright (C) 2000,2002 Stephen Rothwell
*/ */
#include <linux/fs.h> #include <linux/fs.h>
struct dnotify_struct { struct dnotify_struct {
struct dnotify_struct * dn_next; struct dnotify_struct * dn_next;
int dn_magic;
unsigned long dn_mask; /* Events to be notified unsigned long dn_mask; /* Events to be notified
see linux/fcntl.h */ see linux/fcntl.h */
int dn_fd; int dn_fd;
...@@ -16,9 +15,8 @@ struct dnotify_struct { ...@@ -16,9 +15,8 @@ struct dnotify_struct {
fl_owner_t dn_owner; fl_owner_t dn_owner;
}; };
#define DNOTIFY_MAGIC 0x444E4F54
extern void __inode_dir_notify(struct inode *, unsigned long); 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); extern int fcntl_dirnotify(int, struct file *, unsigned long);
static inline void inode_dir_notify(struct inode *inode, unsigned long event) static inline void inode_dir_notify(struct inode *inode, unsigned long event)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment