]>
Commit | Line | Data |
---|---|---|
3e062538 JR |
1 | inotify! |
2 | ||
3 | inotify is intended to correct the deficiencies of dnotify, particularly | |
4 | its inability to scale and its terrible user interface: | |
5 | ||
6 | * dnotify requires the opening of one fd per each directory | |
7 | that you intend to watch. This quickly results in too many | |
8 | open files and pins removable media, preventing unmount. | |
9 | * dnotify is directory-based. You only learn about changes to | |
10 | directories. Sure, a change to a file in a directory affects | |
11 | the directory, but you are then forced to keep a cache of | |
12 | stat structures. | |
13 | * dnotify's interface to user-space is awful. Signals? | |
14 | ||
15 | inotify provides a more usable, simple, powerful solution to file change | |
16 | notification: | |
17 | ||
18 | * inotify's interface is a device node, not SIGIO. You open a | |
19 | single fd to the device node, which is select()-able. | |
20 | * inotify has an event that says "the filesystem that the item | |
21 | you were watching is on was unmounted." | |
22 | * inotify can watch directories or files. | |
23 | ||
24 | Inotify is currently used by Beagle (a desktop search infrastructure) | |
25 | and Gamin (a FAM replacement). | |
26 | ||
27 | Signed-off-by: Robert Love <rml@novell.com> | |
28 | ||
29 | Documentation/filesystems/inotify.txt | 81 ++ | |
30 | fs/Kconfig | 13 | |
31 | fs/Makefile | 1 | |
32 | fs/attr.c | 33 - | |
33 | fs/compat.c | 12 | |
34 | fs/file_table.c | 3 | |
35 | fs/inode.c | 6 | |
36 | fs/inotify.c | 961 ++++++++++++++++++++++++++++++++++ | |
37 | fs/namei.c | 30 - | |
38 | fs/open.c | 6 | |
39 | fs/read_write.c | 15 | |
40 | include/linux/fs.h | 6 | |
41 | include/linux/fsnotify.h | 228 ++++++++ | |
42 | include/linux/inotify.h | 111 +++ | |
43 | include/linux/sched.h | 4 | |
44 | kernel/user.c | 4 | |
45 | 16 files changed, 1458 insertions(+), 56 deletions(-) | |
46 | ||
47 | diff -urN linux-2.6.12-rc2-mm1/Documentation/filesystems/inotify.txt linux/Documentation/filesystems/inotify.txt | |
48 | --- linux-2.6.12-rc2-mm1/Documentation/filesystems/inotify.txt 1969-12-31 19:00:00.000000000 -0500 | |
49 | +++ linux/Documentation/filesystems/inotify.txt 2005-04-05 12:41:51.000000000 -0400 | |
50 | @@ -0,0 +1,81 @@ | |
51 | + inotify | |
52 | + a powerful yet simple file change notification system | |
53 | + | |
54 | + | |
55 | + | |
56 | +Document started 15 Mar 2005 by Robert Love <rml@novell.com> | |
57 | + | |
58 | +(i) User Interface | |
59 | + | |
60 | +Inotify is controlled by a device node, /dev/inotify. If you do not use udev, | |
61 | +this device may need to be created manually. First step, open it | |
62 | + | |
63 | + int dev_fd = open ("/dev/inotify", O_RDONLY); | |
64 | + | |
65 | +Change events are managed by "watches". A watch is an (object,mask) pair where | |
66 | +the object is a file or directory and the mask is a bitmask of one or more | |
67 | +inotify events that the application wishes to receive. See <linux/inotify.h> | |
68 | +for valid events. A watch is referenced by a watch descriptor, or wd. | |
69 | + | |
70 | +Watches are added via a file descriptor. | |
71 | + | |
72 | +Watches on a directory will return events on any files inside of the directory. | |
73 | + | |
74 | +Adding a watch is simple, | |
75 | + | |
76 | + /* 'wd' represents the watch on fd with mask */ | |
77 | + struct inotify_request req = { fd, mask }; | |
78 | + int wd = ioctl (dev_fd, INOTIFY_WATCH, &req); | |
79 | + | |
80 | +You can add a large number of files via something like | |
81 | + | |
82 | + for each file to watch { | |
83 | + struct inotify_request req; | |
84 | + int file_fd; | |
85 | + | |
86 | + file_fd = open (file, O_RDONLY); | |
87 | + if (fd < 0) { | |
88 | + perror ("open"); | |
89 | + break; | |
90 | + } | |
91 | + | |
92 | + req.fd = file_fd; | |
93 | + req.mask = mask; | |
94 | + | |
95 | + wd = ioctl (dev_fd, INOTIFY_WATCH, &req); | |
96 | + | |
97 | + close (fd); | |
98 | + } | |
99 | + | |
100 | +You can update an existing watch in the same manner, by passing in a new mask. | |
101 | + | |
102 | +An existing watch is removed via the INOTIFY_IGNORE ioctl, for example | |
103 | + | |
104 | + ioctl (dev_fd, INOTIFY_IGNORE, wd); | |
105 | + | |
106 | +Events are provided in the form of an inotify_event structure that is read(2) | |
107 | +from /dev/inotify. The filename is of dynamic length and follows the struct. | |
108 | +It is of size len. The filename is padded with null bytes to ensure proper | |
109 | +alignment. This padding is reflected in len. | |
110 | + | |
111 | +You can slurp multiple events by passing a large buffer, for example | |
112 | + | |
113 | + size_t len = read (fd, buf, BUF_LEN); | |
114 | + | |
115 | +Will return as many events as are available and fit in BUF_LEN. | |
116 | + | |
117 | +/dev/inotify is also select() and poll() able. | |
118 | + | |
119 | +You can find the size of the current event queue via the FIONREAD ioctl. | |
120 | + | |
121 | +All watches are destroyed and cleaned up on close. | |
122 | + | |
123 | + | |
124 | +(ii) Internal Kernel Implementation | |
125 | + | |
126 | +Each open inotify device is associated with an inotify_device structure. | |
127 | + | |
128 | +Each watch is associated with an inotify_watch structure. Watches are chained | |
129 | +off of each associated device and each associated inode. | |
130 | + | |
131 | +See fs/inotify.c for the locking and lifetime rules. | |
132 | diff -urN linux-2.6.12-rc2-mm1/fs/attr.c linux/fs/attr.c | |
133 | --- linux-2.6.12-rc2-mm1/fs/attr.c 2005-04-05 12:40:03.000000000 -0400 | |
134 | +++ linux/fs/attr.c 2005-04-05 12:41:51.000000000 -0400 | |
135 | @@ -10,7 +10,7 @@ | |
136 | #include <linux/mm.h> | |
137 | #include <linux/string.h> | |
138 | #include <linux/smp_lock.h> | |
139 | -#include <linux/dnotify.h> | |
140 | +#include <linux/fsnotify.h> | |
141 | #include <linux/fcntl.h> | |
142 | #include <linux/quotaops.h> | |
143 | #include <linux/security.h> | |
144 | @@ -107,31 +107,8 @@ | |
145 | out: | |
146 | return error; | |
147 | } | |
148 | - | |
149 | EXPORT_SYMBOL(inode_setattr); | |
150 | ||
151 | -int setattr_mask(unsigned int ia_valid) | |
152 | -{ | |
153 | - unsigned long dn_mask = 0; | |
154 | - | |
155 | - if (ia_valid & ATTR_UID) | |
156 | - dn_mask |= DN_ATTRIB; | |
157 | - if (ia_valid & ATTR_GID) | |
158 | - dn_mask |= DN_ATTRIB; | |
159 | - if (ia_valid & ATTR_SIZE) | |
160 | - dn_mask |= DN_MODIFY; | |
161 | - /* both times implies a utime(s) call */ | |
162 | - if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME)) | |
163 | - dn_mask |= DN_ATTRIB; | |
164 | - else if (ia_valid & ATTR_ATIME) | |
165 | - dn_mask |= DN_ACCESS; | |
166 | - else if (ia_valid & ATTR_MTIME) | |
167 | - dn_mask |= DN_MODIFY; | |
168 | - if (ia_valid & ATTR_MODE) | |
169 | - dn_mask |= DN_ATTRIB; | |
170 | - return dn_mask; | |
171 | -} | |
172 | - | |
173 | int notify_change(struct dentry * dentry, struct iattr * attr) | |
174 | { | |
175 | struct inode *inode = dentry->d_inode; | |
176 | @@ -197,11 +174,9 @@ | |
177 | if (ia_valid & ATTR_SIZE) | |
178 | up_write(&dentry->d_inode->i_alloc_sem); | |
179 | ||
180 | - if (!error) { | |
181 | - unsigned long dn_mask = setattr_mask(ia_valid); | |
182 | - if (dn_mask) | |
183 | - dnotify_parent(dentry, dn_mask); | |
184 | - } | |
185 | + if (!error) | |
186 | + fsnotify_change(dentry, ia_valid); | |
187 | + | |
188 | return error; | |
189 | } | |
190 | ||
191 | diff -urN linux-2.6.12-rc2-mm1/fs/compat.c linux/fs/compat.c | |
192 | --- linux-2.6.12-rc2-mm1/fs/compat.c 2005-04-05 12:40:03.000000000 -0400 | |
193 | +++ linux/fs/compat.c 2005-04-05 12:41:51.000000000 -0400 | |
194 | @@ -36,7 +36,7 @@ | |
195 | #include <linux/ctype.h> | |
196 | #include <linux/module.h> | |
197 | #include <linux/dirent.h> | |
198 | -#include <linux/dnotify.h> | |
199 | +#include <linux/fsnotify.h> | |
200 | #include <linux/highuid.h> | |
201 | #include <linux/sunrpc/svc.h> | |
202 | #include <linux/nfsd/nfsd.h> | |
203 | @@ -1233,9 +1233,13 @@ | |
204 | out: | |
205 | if (iov != iovstack) | |
206 | kfree(iov); | |
207 | - if ((ret + (type == READ)) > 0) | |
208 | - dnotify_parent(file->f_dentry, | |
209 | - (type == READ) ? DN_ACCESS : DN_MODIFY); | |
210 | + if ((ret + (type == READ)) > 0) { | |
211 | + struct dentry *dentry = file->f_dentry; | |
212 | + if (type == READ) | |
213 | + fsnotify_access(dentry); | |
214 | + else | |
215 | + fsnotify_modify(dentry); | |
216 | + } | |
217 | return ret; | |
218 | } | |
219 | ||
220 | diff -urN linux-2.6.12-rc2-mm1/fs/file_table.c linux/fs/file_table.c | |
221 | --- linux-2.6.12-rc2-mm1/fs/file_table.c 2005-04-05 12:40:03.000000000 -0400 | |
222 | +++ linux/fs/file_table.c 2005-04-05 12:41:51.000000000 -0400 | |
223 | @@ -16,6 +16,7 @@ | |
224 | #include <linux/eventpoll.h> | |
225 | #include <linux/mount.h> | |
226 | #include <linux/cdev.h> | |
227 | +#include <linux/fsnotify.h> | |
228 | ||
229 | /* sysctl tunables... */ | |
230 | struct files_stat_struct files_stat = { | |
231 | @@ -123,6 +124,8 @@ | |
232 | struct inode *inode = dentry->d_inode; | |
233 | ||
234 | might_sleep(); | |
235 | + | |
236 | + fsnotify_close(file); | |
237 | /* | |
238 | * The function eventpoll_release() should be the first called | |
239 | * in the file cleanup chain. | |
240 | diff -urN linux-2.6.12-rc2-mm1/fs/inode.c linux/fs/inode.c | |
241 | --- linux-2.6.12-rc2-mm1/fs/inode.c 2005-04-05 12:40:03.000000000 -0400 | |
242 | +++ linux/fs/inode.c 2005-04-05 12:41:51.000000000 -0400 | |
243 | @@ -21,6 +21,7 @@ | |
244 | #include <linux/pagemap.h> | |
245 | #include <linux/cdev.h> | |
246 | #include <linux/bootmem.h> | |
247 | +#include <linux/inotify.h> | |
248 | ||
249 | /* | |
250 | * This is needed for the following functions: | |
251 | @@ -131,6 +132,10 @@ | |
252 | #ifdef CONFIG_QUOTA | |
253 | memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); | |
254 | #endif | |
255 | +#ifdef CONFIG_INOTIFY | |
256 | + INIT_LIST_HEAD(&inode->inotify_watches); | |
257 | + sema_init(&inode->inotify_sem, 1); | |
258 | +#endif | |
259 | inode->i_pipe = NULL; | |
260 | inode->i_bdev = NULL; | |
261 | inode->i_cdev = NULL; | |
262 | @@ -357,6 +362,7 @@ | |
263 | ||
264 | down(&iprune_sem); | |
265 | spin_lock(&inode_lock); | |
266 | + inotify_unmount_inodes(&sb->s_inodes); | |
267 | busy = invalidate_list(&sb->s_inodes, &throw_away); | |
268 | spin_unlock(&inode_lock); | |
269 | ||
270 | diff -urN linux-2.6.12-rc2-mm1/fs/inotify.c linux/fs/inotify.c | |
271 | --- linux-2.6.12-rc2-mm1/fs/inotify.c 1969-12-31 19:00:00.000000000 -0500 | |
272 | +++ linux/fs/inotify.c 2005-04-05 12:41:51.000000000 -0400 | |
273 | @@ -0,0 +1,961 @@ | |
274 | +/* | |
275 | + * fs/inotify.c - inode-based file event notifications | |
276 | + * | |
277 | + * Authors: | |
278 | + * John McCutchan <ttb@tentacle.dhs.org> | |
279 | + * Robert Love <rml@novell.com> | |
280 | + * | |
281 | + * Copyright (C) 2005 John McCutchan | |
282 | + * | |
283 | + * This program is free software; you can redistribute it and/or modify it | |
284 | + * under the terms of the GNU General Public License as published by the | |
285 | + * Free Software Foundation; either version 2, or (at your option) any | |
286 | + * later version. | |
287 | + * | |
288 | + * This program is distributed in the hope that it will be useful, but | |
289 | + * WITHOUT ANY WARRANTY; without even the implied warranty of | |
290 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
291 | + * General Public License for more details. | |
292 | + */ | |
293 | + | |
294 | +#include <linux/module.h> | |
295 | +#include <linux/kernel.h> | |
296 | +#include <linux/sched.h> | |
297 | +#include <linux/spinlock.h> | |
298 | +#include <linux/idr.h> | |
299 | +#include <linux/slab.h> | |
300 | +#include <linux/fs.h> | |
301 | +#include <linux/file.h> | |
302 | +#include <linux/namei.h> | |
303 | +#include <linux/poll.h> | |
304 | +#include <linux/device.h> | |
305 | +#include <linux/miscdevice.h> | |
306 | +#include <linux/init.h> | |
307 | +#include <linux/list.h> | |
308 | +#include <linux/writeback.h> | |
309 | +#include <linux/inotify.h> | |
310 | + | |
311 | +#include <asm/ioctls.h> | |
312 | + | |
313 | +static atomic_t inotify_cookie; | |
314 | + | |
315 | +static kmem_cache_t *watch_cachep; | |
316 | +static kmem_cache_t *event_cachep; | |
317 | + | |
318 | +static int max_user_devices; | |
319 | +static int max_user_watches; | |
320 | +static unsigned int max_queued_events; | |
321 | + | |
322 | +/* | |
323 | + * Lock ordering: | |
324 | + * | |
325 | + * dentry->d_lock (used to keep d_move() away from dentry->d_parent) | |
326 | + * iprune_sem (synchronize versus shrink_icache_memory()) | |
327 | + * inode_lock (protects the super_block->s_inodes list) | |
328 | + * inode->inotify_sem (protects inode->inotify_watches and watches->i_list) | |
329 | + * inotify_dev->sem (protects inotify_device and watches->d_list) | |
330 | + */ | |
331 | + | |
332 | +/* | |
333 | + * Lifetimes of the three main data structures--inotify_device, inode, and | |
334 | + * inotify_watch--are managed by reference count. | |
335 | + * | |
336 | + * inotify_device: Lifetime is from open until release. Additional references | |
337 | + * can bump the count via get_inotify_dev() and drop the count via | |
338 | + * put_inotify_dev(). | |
339 | + * | |
340 | + * inotify_watch: Lifetime is from create_watch() to destory_watch(). | |
341 | + * Additional references can bump the count via get_inotify_watch() and drop | |
342 | + * the count via put_inotify_watch(). | |
343 | + * | |
344 | + * inode: Pinned so long as the inode is associated with a watch, from | |
345 | + * create_watch() to put_inotify_watch(). | |
346 | + */ | |
347 | + | |
348 | +/* | |
349 | + * struct inotify_device - represents an open instance of an inotify device | |
350 | + * | |
351 | + * This structure is protected by the semaphore 'sem'. | |
352 | + */ | |
353 | +struct inotify_device { | |
354 | + wait_queue_head_t wq; /* wait queue for i/o */ | |
355 | + struct idr idr; /* idr mapping wd -> watch */ | |
356 | + struct semaphore sem; /* protects this bad boy */ | |
357 | + struct list_head events; /* list of queued events */ | |
358 | + struct list_head watches; /* list of watches */ | |
359 | + atomic_t count; /* reference count */ | |
360 | + struct user_struct *user; /* user who opened this dev */ | |
361 | + unsigned int queue_size; /* size of the queue (bytes) */ | |
362 | + unsigned int event_count; /* number of pending events */ | |
363 | + unsigned int max_events; /* maximum number of events */ | |
364 | +}; | |
365 | + | |
366 | +/* | |
367 | + * struct inotify_kernel_event - An intofiy event, originating from a watch and | |
368 | + * queued for user-space. A list of these is attached to each instance of the | |
369 | + * device. In read(), this list is walked and all events that can fit in the | |
370 | + * buffer are returned. | |
371 | + * | |
372 | + * Protected by dev->sem of the device in which we are queued. | |
373 | + */ | |
374 | +struct inotify_kernel_event { | |
375 | + struct inotify_event event; /* the user-space event */ | |
376 | + struct list_head list; /* entry in inotify_device's list */ | |
377 | + char *name; /* filename, if any */ | |
378 | +}; | |
379 | + | |
380 | +/* | |
381 | + * struct inotify_watch - represents a watch request on a specific inode | |
382 | + * | |
383 | + * d_list is protected by dev->sem of the associated watch->dev. | |
384 | + * i_list and mask are protected by inode->inotify_sem of the associated inode. | |
385 | + * dev, inode, and wd are never written to once the watch is created. | |
386 | + */ | |
387 | +struct inotify_watch { | |
388 | + struct list_head d_list; /* entry in inotify_device's list */ | |
389 | + struct list_head i_list; /* entry in inode's list */ | |
390 | + atomic_t count; /* reference count */ | |
391 | + struct inotify_device *dev; /* associated device */ | |
392 | + struct inode *inode; /* associated inode */ | |
393 | + s32 wd; /* watch descriptor */ | |
394 | + u32 mask; /* event mask for this watch */ | |
395 | +}; | |
396 | + | |
397 | +static ssize_t show_max_queued_events(struct class_device *class, char *buf) | |
398 | +{ | |
399 | + return sprintf(buf, "%d\n", max_queued_events); | |
400 | +} | |
401 | + | |
402 | +static ssize_t store_max_queued_events(struct class_device *class, | |
403 | + const char *buf, size_t count) | |
404 | +{ | |
405 | + unsigned int max; | |
406 | + | |
407 | + if (sscanf(buf, "%u", &max) > 0 && max > 0) { | |
408 | + max_queued_events = max; | |
409 | + return strlen(buf); | |
410 | + } | |
411 | + return -EINVAL; | |
412 | +} | |
413 | + | |
414 | +static ssize_t show_max_user_devices(struct class_device *class, char *buf) | |
415 | +{ | |
416 | + return sprintf(buf, "%d\n", max_user_devices); | |
417 | +} | |
418 | + | |
419 | +static ssize_t store_max_user_devices(struct class_device *class, | |
420 | + const char *buf, size_t count) | |
421 | +{ | |
422 | + int max; | |
423 | + | |
424 | + if (sscanf(buf, "%d", &max) > 0 && max > 0) { | |
425 | + max_user_devices = max; | |
426 | + return strlen(buf); | |
427 | + } | |
428 | + return -EINVAL; | |
429 | +} | |
430 | + | |
431 | +static ssize_t show_max_user_watches(struct class_device *class, char *buf) | |
432 | +{ | |
433 | + return sprintf(buf, "%d\n", max_user_watches); | |
434 | +} | |
435 | + | |
436 | +static ssize_t store_max_user_watches(struct class_device *class, | |
437 | + const char *buf, size_t count) | |
438 | +{ | |
439 | + int max; | |
440 | + | |
441 | + if (sscanf(buf, "%d", &max) > 0 && max > 0) { | |
442 | + max_user_watches = max; | |
443 | + return strlen(buf); | |
444 | + } | |
445 | + return -EINVAL; | |
446 | +} | |
447 | + | |
448 | +static CLASS_DEVICE_ATTR(max_queued_events, S_IRUGO | S_IWUSR, | |
449 | + show_max_queued_events, store_max_queued_events); | |
450 | +static CLASS_DEVICE_ATTR(max_user_devices, S_IRUGO | S_IWUSR, | |
451 | + show_max_user_devices, store_max_user_devices); | |
452 | +static CLASS_DEVICE_ATTR(max_user_watches, S_IRUGO | S_IWUSR, | |
453 | + show_max_user_watches, store_max_user_watches); | |
454 | + | |
455 | +static inline void get_inotify_dev(struct inotify_device *dev) | |
456 | +{ | |
457 | + atomic_inc(&dev->count); | |
458 | +} | |
459 | + | |
460 | +static inline void put_inotify_dev(struct inotify_device *dev) | |
461 | +{ | |
462 | + if (atomic_dec_and_test(&dev->count)) { | |
463 | + atomic_dec(&dev->user->inotify_devs); | |
464 | + free_uid(dev->user); | |
465 | + kfree(dev); | |
466 | + } | |
467 | +} | |
468 | + | |
469 | +static inline void get_inotify_watch(struct inotify_watch *watch) | |
470 | +{ | |
471 | + atomic_inc(&watch->count); | |
472 | +} | |
473 | + | |
474 | +/* | |
475 | + * put_inotify_watch - decrements the ref count on a given watch. cleans up | |
476 | + * the watch and its references if the count reaches zero. | |
477 | + */ | |
478 | +static inline void put_inotify_watch(struct inotify_watch *watch) | |
479 | +{ | |
480 | + if (atomic_dec_and_test(&watch->count)) { | |
481 | + put_inotify_dev(watch->dev); | |
482 | + iput(watch->inode); | |
483 | + kmem_cache_free(watch_cachep, watch); | |
484 | + } | |
485 | +} | |
486 | + | |
487 | +/* | |
488 | + * kernel_event - create a new kernel event with the given parameters | |
489 | + * | |
490 | + * This function can sleep. | |
491 | + */ | |
492 | +static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie, | |
493 | + const char *name) | |
494 | +{ | |
495 | + struct inotify_kernel_event *kevent; | |
496 | + | |
497 | + kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL); | |
498 | + if (unlikely(!kevent)) | |
499 | + return NULL; | |
500 | + | |
501 | + /* we hand this out to user-space, so zero it just in case */ | |
502 | + memset(&kevent->event, 0, sizeof(struct inotify_event)); | |
503 | + | |
504 | + kevent->event.wd = wd; | |
505 | + kevent->event.mask = mask; | |
506 | + kevent->event.cookie = cookie; | |
507 | + | |
508 | + INIT_LIST_HEAD(&kevent->list); | |
509 | + | |
510 | + if (name) { | |
511 | + size_t len, rem, event_size = sizeof(struct inotify_event); | |
512 | + | |
513 | + /* | |
514 | + * We need to pad the filename so as to properly align an | |
515 | + * array of inotify_event structures. Because the structure is | |
516 | + * small and the common case is a small filename, we just round | |
517 | + * up to the next multiple of the structure's sizeof. This is | |
518 | + * simple and safe for all architectures. | |
519 | + */ | |
520 | + len = strlen(name) + 1; | |
521 | + rem = event_size - len; | |
522 | + if (len > event_size) { | |
523 | + rem = event_size - (len % event_size); | |
524 | + if (len % event_size == 0) | |
525 | + rem = 0; | |
526 | + } | |
527 | + len += rem; | |
528 | + | |
529 | + kevent->name = kmalloc(len, GFP_KERNEL); | |
530 | + if (unlikely(!kevent->name)) { | |
531 | + kmem_cache_free(event_cachep, kevent); | |
532 | + return NULL; | |
533 | + } | |
534 | + memset(kevent->name, 0, len); | |
535 | + strncpy(kevent->name, name, strlen(name)); | |
536 | + kevent->event.len = len; | |
537 | + } else { | |
538 | + kevent->event.len = 0; | |
539 | + kevent->name = NULL; | |
540 | + } | |
541 | + | |
542 | + return kevent; | |
543 | +} | |
544 | + | |
545 | +/* | |
546 | + * inotify_dev_get_event - return the next event in the given dev's queue | |
547 | + * | |
548 | + * Caller must hold dev->sem. | |
549 | + */ | |
550 | +static inline struct inotify_kernel_event * | |
551 | +inotify_dev_get_event(struct inotify_device *dev) | |
552 | +{ | |
553 | + return list_entry(dev->events.next, struct inotify_kernel_event, list); | |
554 | +} | |
555 | + | |
556 | +/* | |
557 | + * inotify_dev_queue_event - add a new event to the given device | |
558 | + * | |
559 | + * Caller must hold dev->sem. Can sleep (calls kernel_event()). | |
560 | + */ | |
561 | +static void inotify_dev_queue_event(struct inotify_device *dev, | |
562 | + struct inotify_watch *watch, u32 mask, | |
563 | + u32 cookie, const char *name) | |
564 | +{ | |
565 | + struct inotify_kernel_event *kevent, *last; | |
566 | + | |
567 | + /* coalescing: drop this event if it is a dupe of the previous */ | |
568 | + last = inotify_dev_get_event(dev); | |
569 | + if (dev->event_count && last->event.mask == mask && | |
570 | + last->event.cookie == cookie && | |
571 | + last->event.wd == watch->wd) { | |
572 | + const char *lastname = last->name; | |
573 | + | |
574 | + if (!name && !lastname) | |
575 | + return; | |
576 | + if (name && lastname && !strcmp(lastname, name)) | |
577 | + return; | |
578 | + } | |
579 | + | |
580 | + /* the queue overflowed and we already sent the Q_OVERFLOW event */ | |
581 | + if (unlikely(dev->event_count > dev->max_events)) | |
582 | + return; | |
583 | + | |
584 | + /* if the queue overflows, we need to notify user space */ | |
585 | + if (unlikely(dev->event_count == dev->max_events)) | |
586 | + kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL); | |
587 | + else | |
588 | + kevent = kernel_event(watch->wd, mask, cookie, name); | |
589 | + | |
590 | + if (unlikely(!kevent)) | |
591 | + return; | |
592 | + | |
593 | + /* queue the event and wake up anyone waiting */ | |
594 | + dev->event_count++; | |
595 | + dev->queue_size += sizeof(struct inotify_event) + kevent->event.len; | |
596 | + list_add_tail(&kevent->list, &dev->events); | |
597 | + wake_up_interruptible(&dev->wq); | |
598 | +} | |
599 | + | |
600 | +/* | |
601 | + * remove_kevent - cleans up and ultimately frees the given kevent | |
602 | + * | |
603 | + * Caller must hold dev->sem. | |
604 | + */ | |
605 | +static void remove_kevent(struct inotify_device *dev, | |
606 | + struct inotify_kernel_event *kevent) | |
607 | +{ | |
608 | + list_del(&kevent->list); | |
609 | + | |
610 | + dev->event_count--; | |
611 | + dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len; | |
612 | + | |
613 | + kfree(kevent->name); | |
614 | + kmem_cache_free(event_cachep, kevent); | |
615 | +} | |
616 | + | |
617 | +/* | |
618 | + * inotify_dev_event_dequeue - destroy an event on the given device | |
619 | + * | |
620 | + * Caller must hold dev->sem. | |
621 | + */ | |
622 | +static void inotify_dev_event_dequeue(struct inotify_device *dev) | |
623 | +{ | |
624 | + if (!list_empty(&dev->events)) { | |
625 | + struct inotify_kernel_event *kevent; | |
626 | + kevent = inotify_dev_get_event(dev); | |
627 | + remove_kevent(dev, kevent); | |
628 | + } | |
629 | +} | |
630 | + | |
631 | +/* | |
632 | + * inotify_dev_get_wd - returns the next WD for use by the given dev | |
633 | + * | |
634 | + * Callers must hold dev->sem. This function can sleep. | |
635 | + */ | |
636 | +static int inotify_dev_get_wd(struct inotify_device *dev, | |
637 | + struct inotify_watch *watch) | |
638 | +{ | |
639 | + int ret; | |
640 | + | |
641 | + do { | |
642 | + if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL))) | |
643 | + return -ENOSPC; | |
644 | + ret = idr_get_new(&dev->idr, watch, &watch->wd); | |
645 | + } while (ret == -EAGAIN); | |
646 | + | |
647 | + return ret; | |
648 | +} | |
649 | + | |
650 | +/* | |
651 | + * create_watch - creates a watch on the given device. | |
652 | + * | |
653 | + * Callers must hold dev->sem. Calls inotify_dev_get_wd() so may sleep. | |
654 | + * Both 'dev' and 'inode' (by way of nameidata) need to be pinned. | |
655 | + */ | |
656 | +static struct inotify_watch *create_watch(struct inotify_device *dev, | |
657 | + u32 mask, struct inode *inode) | |
658 | +{ | |
659 | + struct inotify_watch *watch; | |
660 | + int ret; | |
661 | + | |
662 | + if (atomic_read(&dev->user->inotify_watches) >= max_user_watches) | |
663 | + return ERR_PTR(-ENOSPC); | |
664 | + | |
665 | + watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL); | |
666 | + if (unlikely(!watch)) | |
667 | + return ERR_PTR(-ENOMEM); | |
668 | + | |
669 | + ret = inotify_dev_get_wd(dev, watch); | |
670 | + if (unlikely(ret)) { | |
671 | + kmem_cache_free(watch_cachep, watch); | |
672 | + return ERR_PTR(ret); | |
673 | + } | |
674 | + | |
675 | + watch->mask = mask; | |
676 | + atomic_set(&watch->count, 0); | |
677 | + INIT_LIST_HEAD(&watch->d_list); | |
678 | + INIT_LIST_HEAD(&watch->i_list); | |
679 | + | |
680 | + /* save a reference to device and bump the count to make it official */ | |
681 | + get_inotify_dev(dev); | |
682 | + watch->dev = dev; | |
683 | + | |
684 | + /* | |
685 | + * Save a reference to the inode and bump the ref count to make it | |
686 | + * official. We hold a reference to nameidata, which makes this safe. | |
687 | + */ | |
688 | + watch->inode = igrab(inode); | |
689 | + | |
690 | + /* bump our own count, corresponding to our entry in dev->watches */ | |
691 | + get_inotify_watch(watch); | |
692 | + | |
693 | + atomic_inc(&dev->user->inotify_watches); | |
694 | + | |
695 | + return watch; | |
696 | +} | |
697 | + | |
698 | +/* | |
699 | + * inotify_find_dev - find the watch associated with the given inode and dev | |
700 | + * | |
701 | + * Callers must hold inode->inotify_sem. | |
702 | + */ | |
703 | +static struct inotify_watch *inode_find_dev(struct inode *inode, | |
704 | + struct inotify_device *dev) | |
705 | +{ | |
706 | + struct inotify_watch *watch; | |
707 | + | |
708 | + list_for_each_entry(watch, &inode->inotify_watches, i_list) { | |
709 | + if (watch->dev == dev) | |
710 | + return watch; | |
711 | + } | |
712 | + | |
713 | + return NULL; | |
714 | +} | |
715 | + | |
716 | +/* | |
717 | + * inotify_dev_is_watching_inode - is this device watching this inode? | |
718 | + * | |
719 | + * Callers must hold dev->sem. | |
720 | + */ | |
721 | +static inline int inotify_dev_is_watching_inode(struct inotify_device *dev, | |
722 | + struct inode *inode) | |
723 | +{ | |
724 | + struct inotify_watch *watch; | |
725 | + | |
726 | + list_for_each_entry(watch, &dev->watches, d_list) { | |
727 | + if (watch->inode == inode) | |
728 | + return 1; | |
729 | + } | |
730 | + | |
731 | + return 0; | |
732 | +} | |
733 | + | |
734 | +/* | |
735 | + * remove_watch_no_event - remove_watch() without the IN_IGNORED event. | |
736 | + */ | |
737 | +static void remove_watch_no_event(struct inotify_watch *watch, | |
738 | + struct inotify_device *dev) | |
739 | +{ | |
740 | + list_del(&watch->i_list); | |
741 | + list_del(&watch->d_list); | |
742 | + | |
743 | + atomic_dec(&dev->user->inotify_watches); | |
744 | + idr_remove(&dev->idr, watch->wd); | |
745 | + put_inotify_watch(watch); | |
746 | +} | |
747 | + | |
748 | +/* | |
749 | + * remove_watch - Remove a watch from both the device and the inode. Sends | |
750 | + * the IN_IGNORED event to the given device signifying that the inode is no | |
751 | + * longer watched. | |
752 | + * | |
753 | + * Callers must hold both inode->inotify_sem and dev->sem. We drop a | |
754 | + * reference to the inode before returning. | |
755 | + * | |
756 | + * The inode is not iput() so as to remain atomic. If the inode needs to be | |
757 | + * iput(), the call returns one. Otherwise, it returns zero. | |
758 | + */ | |
759 | +static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev) | |
760 | +{ | |
761 | + inotify_dev_queue_event(dev, watch, IN_IGNORED, 0, NULL); | |
762 | + remove_watch_no_event(watch, dev); | |
763 | +} | |
764 | + | |
765 | +/* Kernel API */ | |
766 | + | |
767 | +/** | |
768 | + * inotify_inode_queue_event - queue an event to all watches on this inode | |
769 | + * @inode: inode event is originating from | |
770 | + * @mask: event mask describing this event | |
771 | + * @cookie: cookie for synchronization, or zero | |
772 | + * @name: filename, if any | |
773 | + */ | |
774 | +void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie, | |
775 | + const char *name) | |
776 | +{ | |
777 | + struct inotify_watch *watch; | |
778 | + | |
779 | + down(&inode->inotify_sem); | |
780 | + list_for_each_entry(watch, &inode->inotify_watches, i_list) { | |
781 | + if (watch->mask & mask) { | |
782 | + struct inotify_device *dev = watch->dev; | |
783 | + down(&dev->sem); | |
784 | + inotify_dev_queue_event(dev, watch, mask, cookie, name); | |
785 | + up(&dev->sem); | |
786 | + } | |
787 | + } | |
788 | + up(&inode->inotify_sem); | |
789 | +} | |
790 | +EXPORT_SYMBOL_GPL(inotify_inode_queue_event); | |
791 | + | |
792 | +/** | |
793 | + * inotify_dentry_parent_queue_event - queue an event to a dentry's parent | |
794 | + * @dentry: the dentry in question, we queue against this dentry's parent | |
795 | + * @mask: event mask describing this event | |
796 | + * @cookie: cookie for synchronization, or zero | |
797 | + * @name: filename, if any | |
798 | + */ | |
799 | +void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask, | |
800 | + u32 cookie, const char *name) | |
801 | +{ | |
802 | + struct dentry *parent; | |
803 | + struct inode *inode; | |
804 | + | |
805 | + spin_lock(&dentry->d_lock); | |
806 | + parent = dentry->d_parent; | |
807 | + inode = parent->d_inode; | |
808 | + | |
809 | + /* We intentially do the list_empty() lockless. The race is fine. */ | |
810 | + if (!list_empty(&inode->inotify_watches)) { | |
811 | + dget(parent); | |
812 | + spin_unlock(&dentry->d_lock); | |
813 | + inotify_inode_queue_event(inode, mask, cookie, name); | |
814 | + dput(parent); | |
815 | + } else | |
816 | + spin_unlock(&dentry->d_lock); | |
817 | +} | |
818 | +EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event); | |
819 | + | |
820 | +/** | |
821 | + * inotify_get_cookie - return a unique cookie for use in synchronizing events. | |
822 | + */ | |
823 | +u32 inotify_get_cookie(void) | |
824 | +{ | |
825 | + return atomic_inc_return(&inotify_cookie); | |
826 | +} | |
827 | +EXPORT_SYMBOL_GPL(inotify_get_cookie); | |
828 | + | |
829 | +/** | |
830 | + * inotify_unmount_inodes - an sb is unmounting. handle any watched inodes. | |
831 | + * @list: list of inodes being unmounted (sb->s_inodes) | |
832 | + * | |
833 | + * Called with inode_lock held, protecting the unmounting super block's list | |
834 | + * of inodes, and with iprune_sem held, keeping shrink_icache_memory() at bay. | |
835 | + * We temporarily drop inode_lock, however, and CAN block. | |
836 | + */ | |
837 | +void inotify_unmount_inodes(struct list_head *list) | |
838 | +{ | |
839 | + struct inode *inode, *next_i; | |
840 | + | |
841 | + list_for_each_entry_safe(inode, next_i, list, i_sb_list) { | |
842 | + struct inotify_watch *watch, *next_w; | |
843 | + struct list_head *watches; | |
844 | + | |
845 | + /* | |
846 | + * We can safely drop inode_lock here because the per-sb list | |
847 | + * of inodes must not change during unmount and iprune_sem | |
848 | + * keeps shrink_icache_memory() away. | |
849 | + */ | |
850 | + spin_unlock(&inode_lock); | |
851 | + | |
852 | + /* for each watch, send IN_UNMOUNT and then remove it */ | |
853 | + down(&inode->inotify_sem); | |
854 | + watches = &inode->inotify_watches; | |
855 | + list_for_each_entry_safe(watch, next_w, watches, i_list) { | |
856 | + struct inotify_device *dev = watch->dev; | |
857 | + down(&dev->sem); | |
858 | + inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL); | |
859 | + remove_watch(watch, dev); | |
860 | + up(&dev->sem); | |
861 | + } | |
862 | + up(&inode->inotify_sem); | |
863 | + | |
864 | + spin_lock(&inode_lock); | |
865 | + } | |
866 | +} | |
867 | +EXPORT_SYMBOL_GPL(inotify_unmount_inodes); | |
868 | + | |
869 | +/** | |
870 | + * inotify_inode_is_dead - an inode has been deleted, cleanup any watches | |
871 | + * @inode: inode that is about to be removed | |
872 | + */ | |
873 | +void inotify_inode_is_dead(struct inode *inode) | |
874 | +{ | |
875 | + struct inotify_watch *watch, *next; | |
876 | + | |
877 | + down(&inode->inotify_sem); | |
878 | + list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { | |
879 | + struct inotify_device *dev = watch->dev; | |
880 | + down(&dev->sem); | |
881 | + remove_watch(watch, dev); | |
882 | + up(&dev->sem); | |
883 | + } | |
884 | + up(&inode->inotify_sem); | |
885 | +} | |
886 | +EXPORT_SYMBOL_GPL(inotify_inode_is_dead); | |
887 | + | |
888 | +/* Device Interface */ | |
889 | + | |
890 | +static unsigned int inotify_poll(struct file *file, poll_table *wait) | |
891 | +{ | |
892 | + struct inotify_device *dev = file->private_data; | |
893 | + int ret = 0; | |
894 | + | |
895 | + poll_wait(file, &dev->wq, wait); | |
896 | + down(&dev->sem); | |
897 | + if (!list_empty(&dev->events)) | |
898 | + ret = POLLIN | POLLRDNORM; | |
899 | + up(&dev->sem); | |
900 | + | |
901 | + return ret; | |
902 | +} | |
903 | + | |
904 | +static ssize_t inotify_read(struct file *file, char __user *buf, | |
905 | + size_t count, loff_t *pos) | |
906 | +{ | |
907 | + size_t event_size = sizeof (struct inotify_event); | |
908 | + struct inotify_device *dev; | |
909 | + char __user *start; | |
910 | + int ret; | |
911 | + DEFINE_WAIT(wait); | |
912 | + | |
913 | + start = buf; | |
914 | + dev = file->private_data; | |
915 | + | |
916 | + while (1) { | |
917 | + int events; | |
918 | + | |
919 | + prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE); | |
920 | + | |
921 | + down(&dev->sem); | |
922 | + events = !list_empty(&dev->events); | |
923 | + up(&dev->sem); | |
924 | + if (events) { | |
925 | + ret = 0; | |
926 | + break; | |
927 | + } | |
928 | + | |
929 | + if (file->f_flags & O_NONBLOCK) { | |
930 | + ret = -EAGAIN; | |
931 | + break; | |
932 | + } | |
933 | + | |
934 | + if (signal_pending(current)) { | |
935 | + ret = -EINTR; | |
936 | + break; | |
937 | + } | |
938 | + | |
939 | + schedule(); | |
940 | + } | |
941 | + | |
942 | + finish_wait(&dev->wq, &wait); | |
943 | + if (ret) | |
944 | + return ret; | |
945 | + | |
946 | + down(&dev->sem); | |
947 | + while (1) { | |
948 | + struct inotify_kernel_event *kevent; | |
949 | + | |
950 | + ret = buf - start; | |
951 | + if (list_empty(&dev->events)) | |
952 | + break; | |
953 | + | |
954 | + kevent = inotify_dev_get_event(dev); | |
955 | + if (event_size + kevent->event.len > count) | |
956 | + break; | |
957 | + | |
958 | + if (copy_to_user(buf, &kevent->event, event_size)) { | |
959 | + ret = -EFAULT; | |
960 | + break; | |
961 | + } | |
962 | + buf += event_size; | |
963 | + count -= event_size; | |
964 | + | |
965 | + if (kevent->name) { | |
966 | + if (copy_to_user(buf, kevent->name, kevent->event.len)){ | |
967 | + ret = -EFAULT; | |
968 | + break; | |
969 | + } | |
970 | + buf += kevent->event.len; | |
971 | + count -= kevent->event.len; | |
972 | + } | |
973 | + | |
974 | + remove_kevent(dev, kevent); | |
975 | + } | |
976 | + up(&dev->sem); | |
977 | + | |
978 | + return ret; | |
979 | +} | |
980 | + | |
981 | +static int inotify_open(struct inode *inode, struct file *file) | |
982 | +{ | |
983 | + struct inotify_device *dev; | |
984 | + struct user_struct *user; | |
985 | + int ret; | |
986 | + | |
987 | + user = get_uid(current->user); | |
988 | + | |
989 | + if (unlikely(atomic_read(&user->inotify_devs) >= max_user_devices)) { | |
990 | + ret = -EMFILE; | |
991 | + goto out_err; | |
992 | + } | |
993 | + | |
994 | + dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL); | |
995 | + if (unlikely(!dev)) { | |
996 | + ret = -ENOMEM; | |
997 | + goto out_err; | |
998 | + } | |
999 | + | |
1000 | + idr_init(&dev->idr); | |
1001 | + INIT_LIST_HEAD(&dev->events); | |
1002 | + INIT_LIST_HEAD(&dev->watches); | |
1003 | + init_waitqueue_head(&dev->wq); | |
1004 | + sema_init(&dev->sem, 1); | |
1005 | + | |
1006 | + dev->event_count = 0; | |
1007 | + dev->queue_size = 0; | |
1008 | + dev->max_events = max_queued_events; | |
1009 | + dev->user = user; | |
1010 | + atomic_set(&dev->count, 0); | |
1011 | + | |
1012 | + get_inotify_dev(dev); | |
1013 | + atomic_inc(¤t->user->inotify_devs); | |
1014 | + | |
1015 | + file->private_data = dev; | |
1016 | + | |
1017 | + return 0; | |
1018 | +out_err: | |
1019 | + free_uid(user); | |
1020 | + return ret; | |
1021 | +} | |
1022 | + | |
1023 | +static int inotify_release(struct inode *ignored, struct file *file) | |
1024 | +{ | |
1025 | + struct inotify_device *dev = file->private_data; | |
1026 | + | |
1027 | + /* | |
1028 | + * Destroy all of the watches on this device. Unfortunately, not very | |
1029 | + * pretty. We cannot do a simple iteration over the list, because we | |
1030 | + * do not know the inode until we iterate to the watch. But we need to | |
1031 | + * hold inode->inotify_sem before dev->sem. The following works. | |
1032 | + */ | |
1033 | + while (1) { | |
1034 | + struct inotify_watch *watch; | |
1035 | + struct list_head *watches; | |
1036 | + struct inode *inode; | |
1037 | + | |
1038 | + down(&dev->sem); | |
1039 | + watches = &dev->watches; | |
1040 | + if (list_empty(watches)) { | |
1041 | + up(&dev->sem); | |
1042 | + break; | |
1043 | + } | |
1044 | + watch = list_entry(watches->next, struct inotify_watch, d_list); | |
1045 | + get_inotify_watch(watch); | |
1046 | + up(&dev->sem); | |
1047 | + | |
1048 | + inode = watch->inode; | |
1049 | + down(&inode->inotify_sem); | |
1050 | + down(&dev->sem); | |
1051 | + remove_watch_no_event(watch, dev); | |
1052 | + up(&dev->sem); | |
1053 | + up(&inode->inotify_sem); | |
1054 | + put_inotify_watch(watch); | |
1055 | + } | |
1056 | + | |
1057 | + /* destroy all of the events on this device */ | |
1058 | + down(&dev->sem); | |
1059 | + while (!list_empty(&dev->events)) | |
1060 | + inotify_dev_event_dequeue(dev); | |
1061 | + up(&dev->sem); | |
1062 | + | |
1063 | + /* free this device: the put matching the get in inotify_open() */ | |
1064 | + put_inotify_dev(dev); | |
1065 | + | |
1066 | + return 0; | |
1067 | +} | |
1068 | + | |
1069 | +static int inotify_add_watch(struct inotify_device *dev, | |
1070 | + int fd, u32 mask) | |
1071 | +{ | |
1072 | + struct inotify_watch *watch, *old; | |
1073 | + struct inode *inode; | |
1074 | + struct file *filp; | |
1075 | + int ret; | |
1076 | + | |
1077 | + filp = fget(fd); | |
1078 | + if (!filp) | |
1079 | + return -EBADF; | |
1080 | + inode = filp->f_dentry->d_inode; | |
1081 | + | |
1082 | + down(&inode->inotify_sem); | |
1083 | + down(&dev->sem); | |
1084 | + | |
1085 | + /* | |
1086 | + * Handle the case of re-adding a watch on an (inode,dev) pair that we | |
1087 | + * are already watching. We just update the mask and return its wd. | |
1088 | + */ | |
1089 | + old = inode_find_dev(inode, dev); | |
1090 | + if (unlikely(old)) { | |
1091 | + old->mask = mask; | |
1092 | + ret = old->wd; | |
1093 | + goto out; | |
1094 | + } | |
1095 | + | |
1096 | + watch = create_watch(dev, mask, inode); | |
1097 | + if (unlikely(IS_ERR(watch))) { | |
1098 | + ret = PTR_ERR(watch); | |
1099 | + goto out; | |
1100 | + } | |
1101 | + | |
1102 | + /* Add the watch to the device's and the inode's list */ | |
1103 | + list_add(&watch->d_list, &dev->watches); | |
1104 | + list_add(&watch->i_list, &inode->inotify_watches); | |
1105 | + ret = watch->wd; | |
1106 | + | |
1107 | +out: | |
1108 | + up(&dev->sem); | |
1109 | + up(&inode->inotify_sem); | |
1110 | + fput(filp); | |
1111 | + | |
1112 | + return ret; | |
1113 | +} | |
1114 | + | |
1115 | +/* | |
1116 | + * inotify_ignore - handle the INOTIFY_IGNORE ioctl, asking that a given wd be | |
1117 | + * removed from the device. | |
1118 | + * | |
1119 | + * Can sleep. | |
1120 | + */ | |
1121 | +static int inotify_ignore(struct inotify_device *dev, s32 wd) | |
1122 | +{ | |
1123 | + struct inotify_watch *watch; | |
1124 | + struct inode *inode; | |
1125 | + | |
1126 | + down(&dev->sem); | |
1127 | + watch = idr_find(&dev->idr, wd); | |
1128 | + if (unlikely(!watch)) { | |
1129 | + up(&dev->sem); | |
1130 | + return -EINVAL; | |
1131 | + } | |
1132 | + get_inotify_watch(watch); | |
1133 | + up(&dev->sem); | |
1134 | + | |
1135 | + inode = watch->inode; | |
1136 | + down(&inode->inotify_sem); | |
1137 | + down(&dev->sem); | |
1138 | + remove_watch(watch, dev); | |
1139 | + up(&dev->sem); | |
1140 | + up(&inode->inotify_sem); | |
1141 | + put_inotify_watch(watch); | |
1142 | + | |
1143 | + return 0; | |
1144 | +} | |
1145 | + | |
1146 | +static long inotify_ioctl(struct file *file, unsigned int cmd, | |
1147 | + unsigned long arg) | |
1148 | +{ | |
1149 | + struct inotify_device *dev; | |
1150 | + struct inotify_watch_request request; | |
1151 | + void __user *p; | |
1152 | + int ret = -ENOTTY; | |
1153 | + s32 wd; | |
1154 | + | |
1155 | + dev = file->private_data; | |
1156 | + p = (void __user *) arg; | |
1157 | + | |
1158 | + switch (cmd) { | |
1159 | + case INOTIFY_WATCH: | |
1160 | + if (unlikely(copy_from_user(&request, p, sizeof (request)))) { | |
1161 | + ret = -EFAULT; | |
1162 | + break; | |
1163 | + } | |
1164 | + ret = inotify_add_watch(dev, request.fd, request.mask); | |
1165 | + break; | |
1166 | + case INOTIFY_IGNORE: | |
1167 | + if (unlikely(get_user(wd, (int __user *) p))) { | |
1168 | + ret = -EFAULT; | |
1169 | + break; | |
1170 | + } | |
1171 | + ret = inotify_ignore(dev, wd); | |
1172 | + break; | |
1173 | + case FIONREAD: | |
1174 | + ret = put_user(dev->queue_size, (int __user *) p); | |
1175 | + break; | |
1176 | + } | |
1177 | + | |
1178 | + return ret; | |
1179 | +} | |
1180 | + | |
1181 | +static struct file_operations inotify_fops = { | |
1182 | + .owner = THIS_MODULE, | |
1183 | + .poll = inotify_poll, | |
1184 | + .read = inotify_read, | |
1185 | + .open = inotify_open, | |
1186 | + .release = inotify_release, | |
1187 | + .unlocked_ioctl = inotify_ioctl, | |
1188 | + .compat_ioctl = inotify_ioctl, | |
1189 | +}; | |
1190 | + | |
1191 | +static struct miscdevice inotify_device = { | |
1192 | + .minor = MISC_DYNAMIC_MINOR, | |
1193 | + .name = "inotify", | |
1194 | + .fops = &inotify_fops, | |
1195 | +}; | |
1196 | + | |
1197 | +/* | |
1198 | + * inotify_init - Our initialization function. Note that we cannnot return | |
1199 | + * error because we have compiled-in VFS hooks. So an (unlikely) failure here | |
1200 | + * must result in panic(). | |
1201 | + */ | |
1202 | +static int __init inotify_init(void) | |
1203 | +{ | |
1204 | + struct class_device *class; | |
1205 | + int ret; | |
1206 | + | |
1207 | + ret = misc_register(&inotify_device); | |
1208 | + if (unlikely(ret)) | |
1209 | + panic("inotify: misc_register returned %d\n", ret); | |
1210 | + | |
1211 | + max_queued_events = 512; | |
1212 | + max_user_devices = 64; | |
1213 | + max_user_watches = 16384; | |
1214 | + | |
1215 | + class = inotify_device.class; | |
1216 | + class_device_create_file(class, &class_device_attr_max_queued_events); | |
1217 | + class_device_create_file(class, &class_device_attr_max_user_devices); | |
1218 | + class_device_create_file(class, &class_device_attr_max_user_watches); | |
1219 | + | |
1220 | + atomic_set(&inotify_cookie, 0); | |
1221 | + | |
1222 | + watch_cachep = kmem_cache_create("inotify_watch_cache", | |
1223 | + sizeof(struct inotify_watch), | |
1224 | + 0, SLAB_PANIC, NULL, NULL); | |
1225 | + event_cachep = kmem_cache_create("inotify_event_cache", | |
1226 | + sizeof(struct inotify_kernel_event), | |
1227 | + 0, SLAB_PANIC, NULL, NULL); | |
1228 | + | |
1229 | + printk(KERN_INFO "inotify device minor=%d\n", inotify_device.minor); | |
1230 | + | |
1231 | + return 0; | |
1232 | +} | |
1233 | + | |
1234 | +module_init(inotify_init); | |
1235 | diff -urN linux-2.6.12-rc2-mm1/fs/Kconfig linux/fs/Kconfig | |
1236 | --- linux-2.6.12-rc2-mm1/fs/Kconfig 2005-04-05 12:40:03.000000000 -0400 | |
1237 | +++ linux/fs/Kconfig 2005-04-05 12:41:51.000000000 -0400 | |
1238 | @@ -341,6 +341,19 @@ | |
1239 | If you don't know whether you need it, then you don't need it: | |
1240 | answer N. | |
1241 | ||
1242 | +config INOTIFY | |
1243 | + bool "Inotify file change notification support" | |
1244 | + default y | |
1245 | + ---help--- | |
1246 | + Say Y here to enable inotify support and the /dev/inotify character | |
1247 | + device. Inotify is a file change notification system and a | |
1248 | + replacement for dnotify. Inotify fixes numerous shortcomings in | |
1249 | + dnotify and introduces several new features. It allows monitoring | |
1250 | + of both files and directories via a single open fd. Multiple file | |
1251 | + events are supported. | |
1252 | + | |
1253 | + If unsure, say Y. | |
1254 | + | |
1255 | config QUOTA | |
1256 | bool "Quota support" | |
1257 | help | |
1258 | diff -urN linux-2.6.12-rc2-mm1/fs/Makefile linux/fs/Makefile | |
1259 | --- linux-2.6.12-rc2-mm1/fs/Makefile 2005-04-05 12:40:03.000000000 -0400 | |
1260 | +++ linux/fs/Makefile 2005-04-05 12:41:51.000000000 -0400 | |
1261 | @@ -12,6 +12,7 @@ | |
1262 | seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ | |
1263 | ioprio.o | |
1264 | ||
1265 | +obj-$(CONFIG_INOTIFY) += inotify.o | |
1266 | obj-$(CONFIG_EPOLL) += eventpoll.o | |
1267 | obj-$(CONFIG_COMPAT) += compat.o | |
1268 | ||
1269 | diff -urN linux-2.6.12-rc2-mm1/fs/namei.c linux/fs/namei.c | |
1270 | --- linux-2.6.12-rc2-mm1/fs/namei.c 2005-04-05 12:40:03.000000000 -0400 | |
1271 | +++ linux/fs/namei.c 2005-04-05 12:41:51.000000000 -0400 | |
1272 | @@ -21,7 +21,7 @@ | |
1273 | #include <linux/namei.h> | |
1274 | #include <linux/quotaops.h> | |
1275 | #include <linux/pagemap.h> | |
1276 | -#include <linux/dnotify.h> | |
1277 | +#include <linux/fsnotify.h> | |
1278 | #include <linux/smp_lock.h> | |
1279 | #include <linux/personality.h> | |
1280 | #include <linux/security.h> | |
1281 | @@ -1296,7 +1296,7 @@ | |
1282 | DQUOT_INIT(dir); | |
1283 | error = dir->i_op->create(dir, dentry, mode, nd); | |
1284 | if (!error) { | |
1285 | - inode_dir_notify(dir, DN_CREATE); | |
1286 | + fsnotify_create(dir, dentry->d_name.name); | |
1287 | security_inode_post_create(dir, dentry, mode); | |
1288 | } | |
1289 | return error; | |
1290 | @@ -1601,7 +1601,7 @@ | |
1291 | DQUOT_INIT(dir); | |
1292 | error = dir->i_op->mknod(dir, dentry, mode, dev); | |
1293 | if (!error) { | |
1294 | - inode_dir_notify(dir, DN_CREATE); | |
1295 | + fsnotify_create(dir, dentry->d_name.name); | |
1296 | security_inode_post_mknod(dir, dentry, mode, dev); | |
1297 | } | |
1298 | return error; | |
1299 | @@ -1674,7 +1674,7 @@ | |
1300 | DQUOT_INIT(dir); | |
1301 | error = dir->i_op->mkdir(dir, dentry, mode); | |
1302 | if (!error) { | |
1303 | - inode_dir_notify(dir, DN_CREATE); | |
1304 | + fsnotify_mkdir(dir, dentry->d_name.name); | |
1305 | security_inode_post_mkdir(dir,dentry, mode); | |
1306 | } | |
1307 | return error; | |
1308 | @@ -1765,7 +1765,7 @@ | |
1309 | } | |
1310 | up(&dentry->d_inode->i_sem); | |
1311 | if (!error) { | |
1312 | - inode_dir_notify(dir, DN_DELETE); | |
1313 | + fsnotify_rmdir(dentry, dentry->d_inode, dir); | |
1314 | d_delete(dentry); | |
1315 | } | |
1316 | dput(dentry); | |
1317 | @@ -1838,9 +1838,10 @@ | |
1318 | ||
1319 | /* We don't d_delete() NFS sillyrenamed files--they still exist. */ | |
1320 | if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { | |
1321 | + fsnotify_unlink(dentry, dir); | |
1322 | d_delete(dentry); | |
1323 | - inode_dir_notify(dir, DN_DELETE); | |
1324 | } | |
1325 | + | |
1326 | return error; | |
1327 | } | |
1328 | ||
1329 | @@ -1914,7 +1915,7 @@ | |
1330 | DQUOT_INIT(dir); | |
1331 | error = dir->i_op->symlink(dir, dentry, oldname); | |
1332 | if (!error) { | |
1333 | - inode_dir_notify(dir, DN_CREATE); | |
1334 | + fsnotify_create(dir, dentry->d_name.name); | |
1335 | security_inode_post_symlink(dir, dentry, oldname); | |
1336 | } | |
1337 | return error; | |
1338 | @@ -1987,7 +1988,7 @@ | |
1339 | error = dir->i_op->link(old_dentry, dir, new_dentry); | |
1340 | up(&old_dentry->d_inode->i_sem); | |
1341 | if (!error) { | |
1342 | - inode_dir_notify(dir, DN_CREATE); | |
1343 | + fsnotify_create(dir, new_dentry->d_name.name); | |
1344 | security_inode_post_link(old_dentry, dir, new_dentry); | |
1345 | } | |
1346 | return error; | |
1347 | @@ -2151,6 +2152,7 @@ | |
1348 | { | |
1349 | int error; | |
1350 | int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); | |
1351 | + char *old_name; | |
1352 | ||
1353 | if (old_dentry->d_inode == new_dentry->d_inode) | |
1354 | return 0; | |
1355 | @@ -2172,18 +2174,18 @@ | |
1356 | DQUOT_INIT(old_dir); | |
1357 | DQUOT_INIT(new_dir); | |
1358 | ||
1359 | + old_name = fsnotify_oldname_init(old_dentry); | |
1360 | + | |
1361 | if (is_dir) | |
1362 | error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); | |
1363 | else | |
1364 | error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); | |
1365 | if (!error) { | |
1366 | - if (old_dir == new_dir) | |
1367 | - inode_dir_notify(old_dir, DN_RENAME); | |
1368 | - else { | |
1369 | - inode_dir_notify(old_dir, DN_DELETE); | |
1370 | - inode_dir_notify(new_dir, DN_CREATE); | |
1371 | - } | |
1372 | + const char *new_name = old_dentry->d_name.name; | |
1373 | + fsnotify_move(old_dir, new_dir, old_name, new_name); | |
1374 | } | |
1375 | + fsnotify_oldname_free(old_name); | |
1376 | + | |
1377 | return error; | |
1378 | } | |
1379 | ||
1380 | diff -urN linux-2.6.12-rc2-mm1/fs/open.c linux/fs/open.c | |
1381 | --- linux-2.6.12-rc2-mm1/fs/open.c 2005-04-05 12:40:03.000000000 -0400 | |
1382 | +++ linux/fs/open.c 2005-04-05 12:41:51.000000000 -0400 | |
1383 | @@ -10,7 +10,7 @@ | |
1384 | #include <linux/file.h> | |
1385 | #include <linux/smp_lock.h> | |
1386 | #include <linux/quotaops.h> | |
1387 | -#include <linux/dnotify.h> | |
1388 | +#include <linux/fsnotify.h> | |
1389 | #include <linux/module.h> | |
1390 | #include <linux/slab.h> | |
1391 | #include <linux/tty.h> | |
1392 | @@ -944,9 +944,11 @@ | |
1393 | fd = get_unused_fd(); | |
1394 | if (fd >= 0) { | |
1395 | struct file *f = filp_open(tmp, flags, mode); | |
1396 | + | |
1397 | error = PTR_ERR(f); | |
1398 | if (IS_ERR(f)) | |
1399 | goto out_error; | |
1400 | + fsnotify_open(f->f_dentry); | |
1401 | fd_install(fd, f); | |
1402 | } | |
1403 | out: | |
1404 | @@ -998,7 +1000,7 @@ | |
1405 | retval = err; | |
1406 | } | |
1407 | ||
1408 | - dnotify_flush(filp, id); | |
1409 | + fsnotify_flush(filp, id); | |
1410 | locks_remove_posix(filp, id); | |
1411 | fput(filp); | |
1412 | return retval; | |
1413 | diff -urN linux-2.6.12-rc2-mm1/fs/read_write.c linux/fs/read_write.c | |
1414 | --- linux-2.6.12-rc2-mm1/fs/read_write.c 2005-04-05 12:40:03.000000000 -0400 | |
1415 | +++ linux/fs/read_write.c 2005-04-05 12:41:51.000000000 -0400 | |
1416 | @@ -10,7 +10,7 @@ | |
1417 | #include <linux/file.h> | |
1418 | #include <linux/uio.h> | |
1419 | #include <linux/smp_lock.h> | |
1420 | -#include <linux/dnotify.h> | |
1421 | +#include <linux/fsnotify.h> | |
1422 | #include <linux/security.h> | |
1423 | #include <linux/module.h> | |
1424 | #include <linux/syscalls.h> | |
1425 | @@ -239,7 +239,7 @@ | |
1426 | else | |
1427 | ret = do_sync_read(file, buf, count, pos); | |
1428 | if (ret > 0) { | |
1429 | - dnotify_parent(file->f_dentry, DN_ACCESS); | |
1430 | + fsnotify_access(file->f_dentry); | |
1431 | current->rchar += ret; | |
1432 | } | |
1433 | current->syscr++; | |
1434 | @@ -287,7 +287,7 @@ | |
1435 | else | |
1436 | ret = do_sync_write(file, buf, count, pos); | |
1437 | if (ret > 0) { | |
1438 | - dnotify_parent(file->f_dentry, DN_MODIFY); | |
1439 | + fsnotify_modify(file->f_dentry); | |
1440 | current->wchar += ret; | |
1441 | } | |
1442 | current->syscw++; | |
1443 | @@ -523,9 +523,12 @@ | |
1444 | out: | |
1445 | if (iov != iovstack) | |
1446 | kfree(iov); | |
1447 | - if ((ret + (type == READ)) > 0) | |
1448 | - dnotify_parent(file->f_dentry, | |
1449 | - (type == READ) ? DN_ACCESS : DN_MODIFY); | |
1450 | + if ((ret + (type == READ)) > 0) { | |
1451 | + if (type == READ) | |
1452 | + fsnotify_access(file->f_dentry); | |
1453 | + else | |
1454 | + fsnotify_modify(file->f_dentry); | |
1455 | + } | |
1456 | return ret; | |
1457 | Efault: | |
1458 | ret = -EFAULT; | |
1459 | diff -urN linux-2.6.12-rc2-mm1/include/linux/fs.h linux/include/linux/fs.h | |
1460 | --- linux-2.6.12-rc2-mm1/include/linux/fs.h 2005-04-05 12:40:03.000000000 -0400 | |
1461 | +++ linux/include/linux/fs.h 2005-04-05 12:41:51.000000000 -0400 | |
1462 | @@ -472,6 +472,11 @@ | |
1463 | struct dnotify_struct *i_dnotify; /* for directory notifications */ | |
1464 | #endif | |
1465 | ||
1466 | +#ifdef CONFIG_INOTIFY | |
1467 | + struct list_head inotify_watches; /* watches on this inode */ | |
1468 | + struct semaphore inotify_sem; /* protects the watches list */ | |
1469 | +#endif | |
1470 | + | |
1471 | unsigned long i_state; | |
1472 | unsigned long dirtied_when; /* jiffies of first dirtying */ | |
1473 | ||
1474 | @@ -1387,7 +1392,6 @@ | |
1475 | extern int do_remount_sb(struct super_block *sb, int flags, | |
1476 | void *data, int force); | |
1477 | extern sector_t bmap(struct inode *, sector_t); | |
1478 | -extern int setattr_mask(unsigned int); | |
1479 | extern int notify_change(struct dentry *, struct iattr *); | |
1480 | extern int permission(struct inode *, int, struct nameidata *); | |
1481 | extern int generic_permission(struct inode *, int, | |
1482 | diff -urN linux-2.6.12-rc2-mm1/include/linux/fsnotify.h linux/include/linux/fsnotify.h | |
1483 | --- linux-2.6.12-rc2-mm1/include/linux/fsnotify.h 1969-12-31 19:00:00.000000000 -0500 | |
1484 | +++ linux/include/linux/fsnotify.h 2005-04-05 12:41:51.000000000 -0400 | |
1485 | @@ -0,0 +1,228 @@ | |
1486 | +#ifndef _LINUX_FS_NOTIFY_H | |
1487 | +#define _LINUX_FS_NOTIFY_H | |
1488 | + | |
1489 | +/* | |
1490 | + * include/linux/fs_notify.h - generic hooks for filesystem notification, to | |
1491 | + * reduce in-source duplication from both dnotify and inotify. | |
1492 | + * | |
1493 | + * We don't compile any of this away in some complicated menagerie of ifdefs. | |
1494 | + * Instead, we rely on the code inside to optimize away as needed. | |
1495 | + * | |
1496 | + * (C) Copyright 2005 Robert Love | |
1497 | + */ | |
1498 | + | |
1499 | +#ifdef __KERNEL__ | |
1500 | + | |
1501 | +#include <linux/dnotify.h> | |
1502 | +#include <linux/inotify.h> | |
1503 | + | |
1504 | +/* | |
1505 | + * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir | |
1506 | + */ | |
1507 | +static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, | |
1508 | + const char *old_name, const char *new_name) | |
1509 | +{ | |
1510 | + u32 cookie; | |
1511 | + | |
1512 | + if (old_dir == new_dir) | |
1513 | + inode_dir_notify(old_dir, DN_RENAME); | |
1514 | + else { | |
1515 | + inode_dir_notify(old_dir, DN_DELETE); | |
1516 | + inode_dir_notify(new_dir, DN_CREATE); | |
1517 | + } | |
1518 | + | |
1519 | + cookie = inotify_get_cookie(); | |
1520 | + | |
1521 | + inotify_inode_queue_event(old_dir, IN_MOVED_FROM, cookie, old_name); | |
1522 | + inotify_inode_queue_event(new_dir, IN_MOVED_TO, cookie, new_name); | |
1523 | +} | |
1524 | + | |
1525 | +/* | |
1526 | + * fsnotify_unlink - file was unlinked | |
1527 | + */ | |
1528 | +static inline void fsnotify_unlink(struct dentry *dentry, struct inode *dir) | |
1529 | +{ | |
1530 | + struct inode *inode = dentry->d_inode; | |
1531 | + | |
1532 | + inode_dir_notify(dir, DN_DELETE); | |
1533 | + inotify_inode_queue_event(dir, IN_DELETE_FILE, 0, dentry->d_name.name); | |
1534 | + inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL); | |
1535 | + | |
1536 | + inotify_inode_is_dead(inode); | |
1537 | +} | |
1538 | + | |
1539 | +/* | |
1540 | + * fsnotify_rmdir - directory was removed | |
1541 | + */ | |
1542 | +static inline void fsnotify_rmdir(struct dentry *dentry, struct inode *inode, | |
1543 | + struct inode *dir) | |
1544 | +{ | |
1545 | + inode_dir_notify(dir, DN_DELETE); | |
1546 | + inotify_inode_queue_event(dir, IN_DELETE_SUBDIR,0,dentry->d_name.name); | |
1547 | + inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL); | |
1548 | + | |
1549 | + inotify_inode_is_dead(inode); | |
1550 | +} | |
1551 | + | |
1552 | +/* | |
1553 | + * fsnotify_create - filename was linked in | |
1554 | + */ | |
1555 | +static inline void fsnotify_create(struct inode *inode, const char *filename) | |
1556 | +{ | |
1557 | + inode_dir_notify(inode, DN_CREATE); | |
1558 | + inotify_inode_queue_event(inode, IN_CREATE_FILE, 0, filename); | |
1559 | +} | |
1560 | + | |
1561 | +/* | |
1562 | + * fsnotify_mkdir - directory 'name' was created | |
1563 | + */ | |
1564 | +static inline void fsnotify_mkdir(struct inode *inode, const char *name) | |
1565 | +{ | |
1566 | + inode_dir_notify(inode, DN_CREATE); | |
1567 | + inotify_inode_queue_event(inode, IN_CREATE_SUBDIR, 0, name); | |
1568 | +} | |
1569 | + | |
1570 | +/* | |
1571 | + * fsnotify_access - file was read | |
1572 | + */ | |
1573 | +static inline void fsnotify_access(struct dentry *dentry) | |
1574 | +{ | |
1575 | + dnotify_parent(dentry, DN_ACCESS); | |
1576 | + inotify_dentry_parent_queue_event(dentry, IN_ACCESS, 0, | |
1577 | + dentry->d_name.name); | |
1578 | + inotify_inode_queue_event(dentry->d_inode, IN_ACCESS, 0, NULL); | |
1579 | +} | |
1580 | + | |
1581 | +/* | |
1582 | + * fsnotify_modify - file was modified | |
1583 | + */ | |
1584 | +static inline void fsnotify_modify(struct dentry *dentry) | |
1585 | +{ | |
1586 | + dnotify_parent(dentry, DN_MODIFY); | |
1587 | + inotify_dentry_parent_queue_event(dentry, IN_MODIFY, 0, | |
1588 | + dentry->d_name.name); | |
1589 | + inotify_inode_queue_event(dentry->d_inode, IN_MODIFY, 0, NULL); | |
1590 | +} | |
1591 | + | |
1592 | +/* | |
1593 | + * fsnotify_open - file was opened | |
1594 | + */ | |
1595 | +static inline void fsnotify_open(struct dentry *dentry) | |
1596 | +{ | |
1597 | + inotify_inode_queue_event(dentry->d_inode, IN_OPEN, 0, NULL); | |
1598 | + inotify_dentry_parent_queue_event(dentry, IN_OPEN, 0, | |
1599 | + dentry->d_name.name); | |
1600 | +} | |
1601 | + | |
1602 | +/* | |
1603 | + * fsnotify_close - file was closed | |
1604 | + */ | |
1605 | +static inline void fsnotify_close(struct file *file) | |
1606 | +{ | |
1607 | + struct dentry *dentry = file->f_dentry; | |
1608 | + struct inode *inode = dentry->d_inode; | |
1609 | + const char *filename = dentry->d_name.name; | |
1610 | + mode_t mode = file->f_mode; | |
1611 | + u32 mask; | |
1612 | + | |
1613 | + mask = (mode & FMODE_WRITE) ? IN_CLOSE_WRITE : IN_CLOSE_NOWRITE; | |
1614 | + inotify_dentry_parent_queue_event(dentry, mask, 0, filename); | |
1615 | + inotify_inode_queue_event(inode, mask, 0, NULL); | |
1616 | +} | |
1617 | + | |
1618 | +/* | |
1619 | + * fsnotify_change - notify_change event. file was modified and/or metadata | |
1620 | + * was changed. | |
1621 | + */ | |
1622 | +static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) | |
1623 | +{ | |
1624 | + int dn_mask = 0; | |
1625 | + u32 in_mask = 0; | |
1626 | + | |
1627 | + if (ia_valid & ATTR_UID) { | |
1628 | + in_mask |= IN_ATTRIB; | |
1629 | + dn_mask |= DN_ATTRIB; | |
1630 | + } | |
1631 | + if (ia_valid & ATTR_GID) { | |
1632 | + in_mask |= IN_ATTRIB; | |
1633 | + dn_mask |= DN_ATTRIB; | |
1634 | + } | |
1635 | + if (ia_valid & ATTR_SIZE) { | |
1636 | + in_mask |= IN_MODIFY; | |
1637 | + dn_mask |= DN_MODIFY; | |
1638 | + } | |
1639 | + /* both times implies a utime(s) call */ | |
1640 | + if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) | |
1641 | + { | |
1642 | + in_mask |= IN_ATTRIB; | |
1643 | + dn_mask |= DN_ATTRIB; | |
1644 | + } else if (ia_valid & ATTR_ATIME) { | |
1645 | + in_mask |= IN_ACCESS; | |
1646 | + dn_mask |= DN_ACCESS; | |
1647 | + } else if (ia_valid & ATTR_MTIME) { | |
1648 | + in_mask |= IN_MODIFY; | |
1649 | + dn_mask |= DN_MODIFY; | |
1650 | + } | |
1651 | + if (ia_valid & ATTR_MODE) { | |
1652 | + in_mask |= IN_ATTRIB; | |
1653 | + dn_mask |= DN_ATTRIB; | |
1654 | + } | |
1655 | + | |
1656 | + if (dn_mask) | |
1657 | + dnotify_parent(dentry, dn_mask); | |
1658 | + if (in_mask) { | |
1659 | + inotify_inode_queue_event(dentry->d_inode, in_mask, 0, NULL); | |
1660 | + inotify_dentry_parent_queue_event(dentry, in_mask, 0, | |
1661 | + dentry->d_name.name); | |
1662 | + } | |
1663 | +} | |
1664 | + | |
1665 | +/* | |
1666 | + * fsnotify_flush - flush time! | |
1667 | + */ | |
1668 | +static inline void fsnotify_flush(struct file *filp, fl_owner_t id) | |
1669 | +{ | |
1670 | + dnotify_flush(filp, id); | |
1671 | +} | |
1672 | + | |
1673 | +#ifdef CONFIG_INOTIFY /* inotify helpers */ | |
1674 | + | |
1675 | +/* | |
1676 | + * fsnotify_oldname_init - save off the old filename before we change it | |
1677 | + * | |
1678 | + * this could be kstrdup if only we could add that to lib/string.c | |
1679 | + */ | |
1680 | +static inline char *fsnotify_oldname_init(struct dentry *old_dentry) | |
1681 | +{ | |
1682 | + char *old_name; | |
1683 | + | |
1684 | + old_name = kmalloc(strlen(old_dentry->d_name.name) + 1, GFP_KERNEL); | |
1685 | + if (old_name) | |
1686 | + strcpy(old_name, old_dentry->d_name.name); | |
1687 | + return old_name; | |
1688 | +} | |
1689 | + | |
1690 | +/* | |
1691 | + * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init | |
1692 | + */ | |
1693 | +static inline void fsnotify_oldname_free(const char *old_name) | |
1694 | +{ | |
1695 | + kfree(old_name); | |
1696 | +} | |
1697 | + | |
1698 | +#else /* CONFIG_INOTIFY */ | |
1699 | + | |
1700 | +static inline char *fsnotify_oldname_init(struct dentry *old_dentry) | |
1701 | +{ | |
1702 | + return NULL; | |
1703 | +} | |
1704 | + | |
1705 | +static inline void fsnotify_oldname_free(const char *old_name) | |
1706 | +{ | |
1707 | +} | |
1708 | + | |
1709 | +#endif /* ! CONFIG_INOTIFY */ | |
1710 | + | |
1711 | +#endif /* __KERNEL__ */ | |
1712 | + | |
1713 | +#endif /* _LINUX_FS_NOTIFY_H */ | |
1714 | diff -urN linux-2.6.12-rc2-mm1/include/linux/inotify.h linux/include/linux/inotify.h | |
1715 | --- linux-2.6.12-rc2-mm1/include/linux/inotify.h 1969-12-31 19:00:00.000000000 -0500 | |
1716 | +++ linux/include/linux/inotify.h 2005-04-05 12:41:51.000000000 -0400 | |
1717 | @@ -0,0 +1,111 @@ | |
1718 | +/* | |
1719 | + * Inode based directory notification for Linux | |
1720 | + * | |
1721 | + * Copyright (C) 2005 John McCutchan | |
1722 | + */ | |
1723 | + | |
1724 | +#ifndef _LINUX_INOTIFY_H | |
1725 | +#define _LINUX_INOTIFY_H | |
1726 | + | |
1727 | +#include <linux/types.h> | |
1728 | + | |
1729 | +/* | |
1730 | + * struct inotify_event - structure read from the inotify device for each event | |
1731 | + * | |
1732 | + * When you are watching a directory, you will receive the filename for events | |
1733 | + * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd. | |
1734 | + */ | |
1735 | +struct inotify_event { | |
1736 | + __s32 wd; /* watch descriptor */ | |
1737 | + __u32 mask; /* watch mask */ | |
1738 | + __u32 cookie; /* cookie to synchronize two events */ | |
1739 | + __u32 len; /* length (including nulls) of name */ | |
1740 | + char name[0]; /* stub for possible name */ | |
1741 | +}; | |
1742 | + | |
1743 | +/* | |
1744 | + * struct inotify_watch_request - represents a watch request | |
1745 | + * | |
1746 | + * Pass to the inotify device via the INOTIFY_WATCH ioctl | |
1747 | + */ | |
1748 | +struct inotify_watch_request { | |
1749 | + int fd; /* fd of filename to watch */ | |
1750 | + __u32 mask; /* event mask */ | |
1751 | +}; | |
1752 | + | |
1753 | +/* the following are legal, implemented events */ | |
1754 | +#define IN_ACCESS 0x00000001 /* File was accessed */ | |
1755 | +#define IN_MODIFY 0x00000002 /* File was modified */ | |
1756 | +#define IN_ATTRIB 0x00000004 /* File changed attributes */ | |
1757 | +#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ | |
1758 | +#define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */ | |
1759 | +#define IN_OPEN 0x00000020 /* File was opened */ | |
1760 | +#define IN_MOVED_FROM 0x00000040 /* File was moved from X */ | |
1761 | +#define IN_MOVED_TO 0x00000080 /* File was moved to Y */ | |
1762 | +#define IN_DELETE_SUBDIR 0x00000100 /* Subdir was deleted */ | |
1763 | +#define IN_DELETE_FILE 0x00000200 /* Subfile was deleted */ | |
1764 | +#define IN_CREATE_SUBDIR 0x00000400 /* Subdir was created */ | |
1765 | +#define IN_CREATE_FILE 0x00000800 /* Subfile was created */ | |
1766 | +#define IN_DELETE_SELF 0x00001000 /* Self was deleted */ | |
1767 | +#define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */ | |
1768 | +#define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ | |
1769 | +#define IN_IGNORED 0x00008000 /* File was ignored */ | |
1770 | + | |
1771 | +/* special flags */ | |
1772 | +#define IN_ALL_EVENTS 0xffffffff /* All the events */ | |
1773 | +#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) | |
1774 | + | |
1775 | +#define INOTIFY_IOCTL_MAGIC 'Q' | |
1776 | +#define INOTIFY_IOCTL_MAXNR 2 | |
1777 | + | |
1778 | +#define INOTIFY_WATCH _IOR(INOTIFY_IOCTL_MAGIC, 1, struct inotify_watch_request) | |
1779 | +#define INOTIFY_IGNORE _IOR(INOTIFY_IOCTL_MAGIC, 2, int) | |
1780 | + | |
1781 | +#ifdef __KERNEL__ | |
1782 | + | |
1783 | +#include <linux/dcache.h> | |
1784 | +#include <linux/fs.h> | |
1785 | +#include <linux/config.h> | |
1786 | + | |
1787 | +#ifdef CONFIG_INOTIFY | |
1788 | + | |
1789 | +extern void inotify_inode_queue_event(struct inode *, __u32, __u32, | |
1790 | + const char *); | |
1791 | +extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32, | |
1792 | + const char *); | |
1793 | +extern void inotify_unmount_inodes(struct list_head *); | |
1794 | +extern void inotify_inode_is_dead(struct inode *); | |
1795 | +extern u32 inotify_get_cookie(void); | |
1796 | + | |
1797 | +#else | |
1798 | + | |
1799 | +static inline void inotify_inode_queue_event(struct inode *inode, | |
1800 | + __u32 mask, __u32 cookie, | |
1801 | + const char *filename) | |
1802 | +{ | |
1803 | +} | |
1804 | + | |
1805 | +static inline void inotify_dentry_parent_queue_event(struct dentry *dentry, | |
1806 | + __u32 mask, __u32 cookie, | |
1807 | + const char *filename) | |
1808 | +{ | |
1809 | +} | |
1810 | + | |
1811 | +static inline void inotify_unmount_inodes(struct list_head *list) | |
1812 | +{ | |
1813 | +} | |
1814 | + | |
1815 | +static inline void inotify_inode_is_dead(struct inode *inode) | |
1816 | +{ | |
1817 | +} | |
1818 | + | |
1819 | +static inline u32 inotify_get_cookie(void) | |
1820 | +{ | |
1821 | + return 0; | |
1822 | +} | |
1823 | + | |
1824 | +#endif /* CONFIG_INOTIFY */ | |
1825 | + | |
1826 | +#endif /* __KERNEL __ */ | |
1827 | + | |
1828 | +#endif /* _LINUX_INOTIFY_H */ | |
1829 | diff -urN linux-2.6.12-rc2-mm1/include/linux/sched.h linux/include/linux/sched.h | |
1830 | --- linux-2.6.12-rc2-mm1/include/linux/sched.h 2005-04-05 12:40:03.000000000 -0400 | |
1831 | +++ linux/include/linux/sched.h 2005-04-05 12:41:51.000000000 -0400 | |
1832 | @@ -426,6 +426,10 @@ | |
1833 | atomic_t processes; /* How many processes does this user have? */ | |
1834 | atomic_t files; /* How many open files does this user have? */ | |
1835 | atomic_t sigpending; /* How many pending signals does this user have? */ | |
1836 | +#ifdef CONFIG_INOTIFY | |
1837 | + atomic_t inotify_watches; /* How many inotify watches does this user have? */ | |
1838 | + atomic_t inotify_devs; /* How many inotify devs does this user have opened? */ | |
1839 | +#endif | |
1840 | /* protected by mq_lock */ | |
1841 | unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ | |
1842 | unsigned long locked_shm; /* How many pages of mlocked shm ? */ | |
1843 | diff -urN linux-2.6.12-rc2-mm1/kernel/user.c linux/kernel/user.c | |
1844 | --- linux-2.6.12-rc2-mm1/kernel/user.c 2005-04-05 12:40:03.000000000 -0400 | |
1845 | +++ linux/kernel/user.c 2005-04-05 12:41:51.000000000 -0400 | |
1846 | @@ -120,6 +120,10 @@ | |
1847 | atomic_set(&new->processes, 0); | |
1848 | atomic_set(&new->files, 0); | |
1849 | atomic_set(&new->sigpending, 0); | |
1850 | +#ifdef CONFIG_INOTIFY | |
1851 | + atomic_set(&new->inotify_watches, 0); | |
1852 | + atomic_set(&new->inotify_devs, 0); | |
1853 | +#endif | |
1854 | ||
1855 | new->mq_bytes = 0; | |
1856 | new->locked_shm = 0; |