| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
#include <linux/fs.h> |
|---|
| 17 |
#include <linux/module.h> |
|---|
| 18 |
#include <linux/sched.h> |
|---|
| 19 |
#include <linux/dnotify.h> |
|---|
| 20 |
#include <linux/init.h> |
|---|
| 21 |
#include <linux/spinlock.h> |
|---|
| 22 |
#include <linux/slab.h> |
|---|
| 23 |
|
|---|
| 24 |
int dir_notify_enable = 1; |
|---|
| 25 |
|
|---|
| 26 |
static rwlock_t dn_lock = RW_LOCK_UNLOCKED; |
|---|
| 27 |
static kmem_cache_t *dn_cache; |
|---|
| 28 |
|
|---|
| 29 |
static void redo_inode_mask(struct inode *inode) |
|---|
| 30 |
{ |
|---|
| 31 |
unsigned long new_mask; |
|---|
| 32 |
struct dnotify_struct *dn; |
|---|
| 33 |
|
|---|
| 34 |
new_mask = 0; |
|---|
| 35 |
for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next) |
|---|
| 36 |
new_mask |= dn->dn_mask & ~DN_MULTISHOT; |
|---|
| 37 |
inode->i_dnotify_mask = new_mask; |
|---|
| 38 |
} |
|---|
| 39 |
|
|---|
| 40 |
void dnotify_flush(struct file *filp, fl_owner_t id) |
|---|
| 41 |
{ |
|---|
| 42 |
struct dnotify_struct *dn; |
|---|
| 43 |
struct dnotify_struct **prev; |
|---|
| 44 |
struct inode *inode; |
|---|
| 45 |
|
|---|
| 46 |
inode = filp->f_dentry->d_inode; |
|---|
| 47 |
if (!S_ISDIR(inode->i_mode)) |
|---|
| 48 |
return; |
|---|
| 49 |
write_lock(&dn_lock); |
|---|
| 50 |
prev = &inode->i_dnotify; |
|---|
| 51 |
while ((dn = *prev) != NULL) { |
|---|
| 52 |
if ((dn->dn_owner == id) && (dn->dn_filp == filp)) { |
|---|
| 53 |
*prev = dn->dn_next; |
|---|
| 54 |
redo_inode_mask(inode); |
|---|
| 55 |
kmem_cache_free(dn_cache, dn); |
|---|
| 56 |
break; |
|---|
| 57 |
} |
|---|
| 58 |
prev = &dn->dn_next; |
|---|
| 59 |
} |
|---|
| 60 |
write_unlock(&dn_lock); |
|---|
| 61 |
} |
|---|
| 62 |
|
|---|
| 63 |
int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) |
|---|
| 64 |
{ |
|---|
| 65 |
struct dnotify_struct *dn; |
|---|
| 66 |
struct dnotify_struct *odn; |
|---|
| 67 |
struct dnotify_struct **prev; |
|---|
| 68 |
struct inode *inode; |
|---|
| 69 |
fl_owner_t id = current->files; |
|---|
| 70 |
int error = 0; |
|---|
| 71 |
|
|---|
| 72 |
if ((arg & ~DN_MULTISHOT) == 0) { |
|---|
| 73 |
dnotify_flush(filp, id); |
|---|
| 74 |
return 0; |
|---|
| 75 |
} |
|---|
| 76 |
if (!dir_notify_enable) |
|---|
| 77 |
return -EINVAL; |
|---|
| 78 |
inode = filp->f_dentry->d_inode; |
|---|
| 79 |
if (!S_ISDIR(inode->i_mode)) |
|---|
| 80 |
return -ENOTDIR; |
|---|
| 81 |
dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL); |
|---|
| 82 |
if (dn == NULL) |
|---|
| 83 |
return -ENOMEM; |
|---|
| 84 |
write_lock(&dn_lock); |
|---|
| 85 |
prev = &inode->i_dnotify; |
|---|
| 86 |
while ((odn = *prev) != NULL) { |
|---|
| 87 |
if ((odn->dn_owner == id) && (odn->dn_filp == filp)) { |
|---|
| 88 |
odn->dn_fd = fd; |
|---|
| 89 |
odn->dn_mask |= arg; |
|---|
| 90 |
inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; |
|---|
| 91 |
goto out_free; |
|---|
| 92 |
} |
|---|
| 93 |
prev = &odn->dn_next; |
|---|
| 94 |
} |
|---|
| 95 |
|
|---|
| 96 |
error = f_setown(filp, current->pid, 0); |
|---|
| 97 |
if (error) |
|---|
| 98 |
goto out_free; |
|---|
| 99 |
|
|---|
| 100 |
dn->dn_mask = arg; |
|---|
| 101 |
dn->dn_fd = fd; |
|---|
| 102 |
dn->dn_filp = filp; |
|---|
| 103 |
dn->dn_owner = id; |
|---|
| 104 |
inode->i_dnotify_mask |= arg & ~DN_MULTISHOT; |
|---|
| 105 |
dn->dn_next = inode->i_dnotify; |
|---|
| 106 |
inode->i_dnotify = dn; |
|---|
| 107 |
out: |
|---|
| 108 |
write_unlock(&dn_lock); |
|---|
| 109 |
return error; |
|---|
| 110 |
out_free: |
|---|
| 111 |
kmem_cache_free(dn_cache, dn); |
|---|
| 112 |
goto out; |
|---|
| 113 |
} |
|---|
| 114 |
|
|---|
| 115 |
void __inode_dir_notify(struct inode *inode, unsigned long event) |
|---|
| 116 |
{ |
|---|
| 117 |
struct dnotify_struct * dn; |
|---|
| 118 |
struct dnotify_struct **prev; |
|---|
| 119 |
struct fown_struct * fown; |
|---|
| 120 |
int changed = 0; |
|---|
| 121 |
|
|---|
| 122 |
write_lock(&dn_lock); |
|---|
| 123 |
prev = &inode->i_dnotify; |
|---|
| 124 |
while ((dn = *prev) != NULL) { |
|---|
| 125 |
if ((dn->dn_mask & event) == 0) { |
|---|
| 126 |
prev = &dn->dn_next; |
|---|
| 127 |
continue; |
|---|
| 128 |
} |
|---|
| 129 |
fown = &dn->dn_filp->f_owner; |
|---|
| 130 |
send_sigio(fown, dn->dn_fd, POLL_MSG); |
|---|
| 131 |
if (dn->dn_mask & DN_MULTISHOT) |
|---|
| 132 |
prev = &dn->dn_next; |
|---|
| 133 |
else { |
|---|
| 134 |
*prev = dn->dn_next; |
|---|
| 135 |
changed = 1; |
|---|
| 136 |
kmem_cache_free(dn_cache, dn); |
|---|
| 137 |
} |
|---|
| 138 |
} |
|---|
| 139 |
if (changed) |
|---|
| 140 |
redo_inode_mask(inode); |
|---|
| 141 |
write_unlock(&dn_lock); |
|---|
| 142 |
} |
|---|
| 143 |
|
|---|
| 144 |
EXPORT_SYMBOL(__inode_dir_notify); |
|---|
| 145 |
|
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
|
|---|
| 149 |
|
|---|
| 150 |
|
|---|
| 151 |
|
|---|
| 152 |
|
|---|
| 153 |
void dnotify_parent(struct dentry *dentry, unsigned long event) |
|---|
| 154 |
{ |
|---|
| 155 |
struct dentry *parent; |
|---|
| 156 |
|
|---|
| 157 |
if (!dir_notify_enable) |
|---|
| 158 |
return; |
|---|
| 159 |
|
|---|
| 160 |
spin_lock(&dentry->d_lock); |
|---|
| 161 |
parent = dentry->d_parent; |
|---|
| 162 |
if (parent->d_inode->i_dnotify_mask & event) { |
|---|
| 163 |
dget(parent); |
|---|
| 164 |
spin_unlock(&dentry->d_lock); |
|---|
| 165 |
__inode_dir_notify(parent->d_inode, event); |
|---|
| 166 |
dput(parent); |
|---|
| 167 |
} else { |
|---|
| 168 |
spin_unlock(&dentry->d_lock); |
|---|
| 169 |
} |
|---|
| 170 |
} |
|---|
| 171 |
EXPORT_SYMBOL_GPL(dnotify_parent); |
|---|
| 172 |
|
|---|
| 173 |
static int __init dnotify_init(void) |
|---|
| 174 |
{ |
|---|
| 175 |
dn_cache = kmem_cache_create("dnotify_cache", |
|---|
| 176 |
sizeof(struct dnotify_struct), 0, SLAB_PANIC, NULL, NULL); |
|---|
| 177 |
return 0; |
|---|
| 178 |
} |
|---|
| 179 |
|
|---|
| 180 |
module_init(dnotify_init) |
|---|