inotify for 2.6.12-rc3.
Subsystems (5)
PATCH 1/1 inotify for 2.6.12-rc3.
Live from linux.conf.au, below is inotify against 2.6.12-rc3.
Cheers,
Robert Love
inotify!
inotify is intended to correct the deficiencies of dnotify, particularly its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory that you intend to watch. This quickly results in too many open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to directories. Sure, a change to a file in a directory affects the directory, but you are then forced to keep a cache of stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change notification:
* inotify's interface is a device node, not SIGIO. You open a single fd to the device node, which is select()-able.
* inotify has an event that says "the filesystem that the item you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure) and Gamin (a FAM replacement).
Documentation/filesystems/inotify.txt | 81 ++ fs/Kconfig | 13 fs/Makefile | 1 fs/attr.c | 33 - fs/compat.c | 12 fs/file_table.c | 3 fs/inode.c | 6 fs/inotify.c | 961 ++++++++++++++++++++++++++++++++++ fs/namei.c | 30 - fs/open.c | 6 fs/read_write.c | 15 include/linux/fs.h | 6 include/linux/fsnotify.h | 228 ++++++++ include/linux/inotify.h | 111 +++ include/linux/sched.h | 4 kernel/user.c | 4 16 files changed, 1458 insertions(+), 56 deletions(-)
Documentation/filesystems/inotify.txt +81 -0
@@ -0,0 +1,81 @@ + inotify + a powerful yet simple file change notification system + + + +Document started 15 Mar 2005 by Robert Love <[email protected]> + +(i) User Interface + +Inotify is controlled by a device node, /dev/inotify. If you do not use udev, +this device may need to be created manually. First step, open it + + int dev_fd = open ("/dev/inotify", O_RDONLY); + +Change events are managed by "watches". A watch is an (object,mask) pair where +the object is a file or directory and the mask is a bitmask of one or more +inotify events that the application wishes to receive. See <linux/inotify.h> +for valid events. A watch is referenced by a watch descriptor, or wd. + +Watches are added via a file descriptor. + +Watches on a directory will return events on any files inside of the directory. + +Adding a watch is simple, + + /* 'wd' represents the watch on fd with mask */ + struct inotify_request req = { fd, mask }; + int wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + +You can add a large number of files via something like + + for each file to watch { + struct inotify_request req; + int file_fd; + + file_fd = open (file, O_RDONLY); + if (fd < 0) { + perror ("open"); + break; + } + + req.fd = file_fd; + req.mask = mask; + + wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + + close (fd); + } + +You can update an existing watch in the same manner, by passing in a new mask. + +An existing watch is removed via the INOTIFY_IGNORE ioctl, for example + + ioctl (dev_fd, INOTIFY_IGNORE, wd); + +Events are provided in the form of an inotify_event structure that is read(2) +from /dev/inotify. The filename is of dynamic length and follows the struct. +It is of size len. The filename is padded with null bytes to ensure proper +alignment. This padding is reflected in len. + +You can slurp multiple events by passing a large buffer, for example + + size_t len = read (fd, buf, BUF_LEN); + +Will return as many events as are available and fit in BUF_LEN. + +/dev/inotify is also select() and poll() able. + +You can find the size of the current event queue via the FIONREAD ioctl. + +All watches are destroyed and cleaned up on close. + + +(ii) Internal Kernel Implementation + +Each open inotify device is associated with an inotify_device structure. + +Each watch is associated with an inotify_watch structure. Watches are chained +off of each associated device and each associated inode. + +See fs/inotify.c for the locking and lifetime rules.
fs/attr.c +4 -29
@@ -10,7 +10,7 @@#include <linux/mm.h> #include <linux/string.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/fcntl.h> #include <linux/quotaops.h> #include <linux/security.h>
@@ -107,31 +107,8 @@ out:return error; } -EXPORT_SYMBOL (inode_setattr); -int setattr_mask (unsigned int ia_valid) -{ -unsigned long dn_mask =0 ; - -if (ia_valid &ATTR_UID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_GID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_SIZE ) - dn_mask |=DN_MODIFY ; -/* both times implies a utime(s) call */ -if ((ia_valid & (ATTR_ATIME |ATTR_MTIME )) == (ATTR_ATIME |ATTR_MTIME )) - dn_mask |=DN_ATTRIB ; -else if (ia_valid &ATTR_ATIME ) - dn_mask |=DN_ACCESS ; -else if (ia_valid &ATTR_MTIME ) - dn_mask |=DN_MODIFY ; -if (ia_valid &ATTR_MODE ) - dn_mask |=DN_ATTRIB ; -return dn_mask; -} -int notify_change (struct dentry * dentry,struct iattr * attr) {struct inode *inode = dentry->d_inode;
@@ -197,11 +174,9 @@if (ia_valid &ATTR_SIZE )up_write (&dentry->d_inode->i_alloc_sem); -if (!error) { -unsigned long dn_mask =setattr_mask (ia_valid); -if (dn_mask) -dnotify_parent (dentry, dn_mask); - } +if (!error) +fsnotify_change (dentry, ia_valid); +return error; }
fs/compat.c +8 -4
@@ -37,7 +37,7 @@#include <linux/ctype.h> #include <linux/module.h> #include <linux/dirent.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/highuid.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h>
@@ -1307,9 +1307,13 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +struct dentry *dentry = file->f_dentry; +if (type ==READ ) +fsnotify_access (dentry); +else +fsnotify_modify (dentry); + }return ret; }
fs/file_table.c +3 -0
@@ -16,6 +16,7 @@#include <linux/eventpoll.h> #include <linux/mount.h> #include <linux/cdev.h> +#include <linux/fsnotify.h> /* sysctl tunables... */ struct files_stat_struct files_stat = {
@@ -123,6 +124,8 @@struct inode *inode = dentry->d_inode;might_sleep (); + +fsnotify_close (file);/* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +6 -0
@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/inotify.h> /* * This is needed for the following functions:
@@ -129,6 +130,10 @@#ifdef CONFIG_QUOTA memset (&inode->i_dquot,0 ,sizeof (inode->i_dquot));#endif +#ifdef CONFIG_INOTIFY +INIT_LIST_HEAD (&inode->inotify_watches); +sema_init (&inode->inotify_sem,1 ); +#endif inode->i_pipe =NULL ; inode->i_bdev =NULL ; inode->i_cdev =NULL ;
@@ -355,6 +360,7 @@down (&iprune_sem);spin_lock (&inode_lock); +inotify_unmount_inodes (&sb->s_inodes); busy =invalidate_list (&sb->s_inodes, &throw_away);spin_unlock (&inode_lock);
fs/inotify.c +961 -0
961 lines not on this page · raw message
fs/Kconfig +13 -0
@@ -339,6 +339,19 @@ If you don't know whether you need it, then you don't need it: answer N. +config INOTIFY + bool "Inotify file change notification support" + default y + ---help--- + Say Y here to enable inotify support and the /dev/inotify character + device. Inotify is a file change notification system and a + replacement for dnotify. Inotify fixes numerous shortcomings in + dnotify and introduces several new features. It allows monitoring + of both files and directories via a single open fd. Multiple file + events are supported. + + If unsure, say Y. + config QUOTA bool "Quota support" help
fs/Makefile +1 -0
@@ -11,6 +11,7 @@ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ +obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_COMPAT) += compat.o
fs/namei.c +16 -14
@@ -21,7 +21,7 @@#include <linux/namei.h> #include <linux/quotaops.h> #include <linux/pagemap.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/smp_lock.h> #include <linux/personality.h> #include <linux/security.h>
@@ -1292,7 +1292,7 @@DQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_create (dir, dentry, mode); }return error;
@@ -1597,7 +1597,7 @@DQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_mknod (dir, dentry, mode, dev); }return error;
@@ -1670,7 +1670,7 @@DQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_mkdir (dir, dentry->d_name.name);security_inode_post_mkdir (dir,dentry, mode); }return error;
@@ -1761,7 +1761,7 @@ }up (&dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_DELETE ); +fsnotify_rmdir (dentry, dentry->d_inode, dir);d_delete (dentry); }dput (dentry);
@@ -1834,9 +1834,10 @@/* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags &DCACHE_NFSFS_RENAMED )) { +fsnotify_unlink (dentry, dir);d_delete (dentry); -inode_dir_notify (dir,DN_DELETE ); } +return error; }
@@ -1910,7 +1911,7 @@DQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_symlink (dir, dentry, oldname); }return error;
@@ -1983,7 +1984,7 @@ error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, new_dentry->d_name.name);security_inode_post_link (old_dentry, dir, new_dentry); }return error;
@@ -2147,6 +2148,7 @@ {int error;int is_dir =S_ISDIR (old_dentry->d_inode->i_mode); +char *old_name;if (old_dentry->d_inode == new_dentry->d_inode)return 0 ;
@@ -2168,18 +2170,18 @@DQUOT_INIT (old_dir);DQUOT_INIT (new_dir); + old_name =fsnotify_oldname_init (old_dentry); +if (is_dir) error =vfs_rename_dir (old_dir,old_dentry,new_dir,new_dentry);else error =vfs_rename_other (old_dir,old_dentry,new_dir,new_dentry);if (!error) { -if (old_dir == new_dir) -inode_dir_notify (old_dir,DN_RENAME ); -else { -inode_dir_notify (old_dir,DN_DELETE ); -inode_dir_notify (new_dir,DN_CREATE ); - } +const char *new_name = old_dentry->d_name.name; +fsnotify_move (old_dir, new_dir, old_name, new_name); } +fsnotify_oldname_free (old_name); +return error; }
fs/open.c +4 -2
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/tty.h>
@@ -944,9 +944,11 @@ fd =get_unused_fd ();if (fd >=0 ) {struct file *f =filp_open (tmp, flags, mode); + error =PTR_ERR (f);if (IS_ERR (f)) goto out_error; +fsnotify_open (f->f_dentry);fd_install (fd, f); } out:
@@ -998,7 +1000,7 @@ retval = err; } -dnotify_flush (filp, id); +fsnotify_flush (filp, id);locks_remove_posix (filp, id);fput (filp);return retval;
fs/read_write.c +9 -6
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/uio.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/security.h> #include <linux/module.h> #include <linux/syscalls.h>
@@ -239,7 +239,7 @@else ret =do_sync_read (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_ACCESS ); +fsnotify_access (file->f_dentry); current->rchar += ret; } current->syscr++;
@@ -287,7 +287,7 @@else ret =do_sync_write (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_MODIFY ); +fsnotify_modify (file->f_dentry); current->wchar += ret; } current->syscw++;
@@ -523,9 +523,12 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +if (type ==READ ) +fsnotify_access (file->f_dentry); +else +fsnotify_modify (file->f_dentry); + }return ret; Efault: ret = -EFAULT ;
include/linux/fs.h +5 -1
@@ -471,6 +471,11 @@struct dnotify_struct *i_dnotify;/* for directory notifications */ #endif +#ifdef CONFIG_INOTIFY +struct list_head inotify_watches;/* watches on this inode */ +struct semaphore inotify_sem;/* protects the watches list */ +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -1365,7 +1370,6 @@extern int do_remount_sb (struct super_block *sb,int flags,void *data,int force);extern sector_t bmap (struct inode *,sector_t ); -extern int setattr_mask (unsigned int );extern int notify_change (struct dentry *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *); extern int generic_permission(struct inode *, int,
include/linux/fsnotify.h +228 -0
228 lines not on this page · raw message
include/linux/inotify.h +111 -0
111 lines not on this page · raw message
include/linux/sched.h +4 -0
@@ -404,6 +404,10 @@atomic_t processes;/* How many processes does this user have? */ atomic_t files;/* How many open files does this user have? */ atomic_t sigpending;/* How many pending signals does this user have? */ +#ifdef CONFIG_INOTIFY +atomic_t inotify_watches;/* How many inotify watches does this user have? */ +atomic_t inotify_devs;/* How many inotify devs does this user have opened? */ +#endif /* protected by mq_lock */ unsigned long mq_bytes;/* How many bytes can be allocated to mqueue? */ unsigned long locked_shm;/* How many pages of mlocked shm ? */
kernel/user.c +4 -0
@@ -120,6 +120,10 @@atomic_set (&new->processes,0 );atomic_set (&new->files,0 );atomic_set (&new->sigpending,0 ); +#ifdef CONFIG_INOTIFY +atomic_set (&new->inotify_watches,0 ); +atomic_set (&new->inotify_devs,0 ); +#endif new->mq_bytes =0 ; new->locked_shm =0 ;
Quoted 1 line · Robert Love
Live from linux.conf.au, below is inotify against 2.6.12-rc3.
G'day mates! By popular request!
Cheers,
Robert Love
Send an event on xattr change. Just use the existing metadata change event, IN_ATTRIB, instead of adding a new event. While here, do not wrap dnotify_flush(), no need.
fs/open.c | 2 +- fs/xattr.c | 5 ++++- include/linux/fsnotify.h | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-)
fs/open.c +1 -1
@@ -1000,7 +1000,7 @@ retval = err; } -fsnotify_flush (filp, id); +dnotify_flush (filp, id);locks_remove_posix (filp, id);fput (filp);return retval;
fs/xattr.c +4 -1
@@ -16,6 +16,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/module.h> +#include <linux/fsnotify.h> #include <asm/uaccess.h> /*
@@ -57,8 +58,10 @@if (error) goto out; error = d->d_inode->i_op->setxattr (d, kname, kvalue, size, flags); -if (!error) +if (!error) { +fsnotify_xattr (d);security_inode_post_setxattr (d, kname, kvalue, size, flags); + } out:up (&d->d_inode->i_sem); }
include/linux/fsnotify.h +10 -8
@@ -131,6 +131,16 @@ }/* +* fsnotify_xattr - extended attributes were changed +*/ +static inline void fsnotify_xattr (struct dentry *dentry) +{ +inotify_dentry_parent_queue_event (dentry,IN_ATTRIB ,0 , + dentry->d_name.name); +inotify_inode_queue_event (dentry->d_inode,IN_ATTRIB ,0 ,NULL ); +} + +/* * fsnotify_change - notify_change event. file was modified and/or metadata * was changed. */
@@ -177,14 +187,6 @@ } } -/* -* fsnotify_flush - flush time! -*/ -static inline void fsnotify_flush (struct file *filp,fl_owner_t id) -{ -dnotify_flush (filp, id); -} -#ifdef CONFIG_INOTIFY /* inotify helpers */ /*
PATCH 1/1 oneshot for inotify.
In reply to Robert LoveThe Samba guys want dnotify-like oneshot/multishot support.
That is not hard to add, so the following patch adds "oneshot" support to inotify. If IN_ONESHOT is set on a watch, the watch is automatically removed after the first event. Default behavior remains "multishot."
Best,
Robert Love
fs/inotify.c | 2 ++ include/linux/inotify.h | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-)
fs/inotify.c +2 -0
@@ -509,6 +509,8 @@struct inotify_device *dev = watch->dev;down (&dev->sem);inotify_dev_queue_event (dev, watch, mask, cookie, name); +if (watch->mask &IN_ONESHOT ) +remove_watch_no_event (watch, dev);up (&dev->sem); } }
include/linux/inotify.h +4 -3
@@ -49,11 +49,12 @@#define IN_DELETE_SELF 0x00001000/* Self was deleted */ #define IN_UNMOUNT 0x00002000/* Backing fs was unmounted */ #define IN_Q_OVERFLOW 0x00004000/* Event queued overflowed */ -#define IN_IGNORED 0x00008000/* File was ignored */ /* special flags */ -#define IN_ALL_EVENTS 0xffffffff/* All the events */ -#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) +#define IN_IGNORED 0x00008000/* File was ignored */ +#define IN_ONESHOT 0x80000000/* only send event once */ +#define IN_ALL_EVENTS (0xffffffff & ~IN_ONESHOT)/* All the events */ +#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)/* close */ #define INOTIFY_IOCTL_MAGIC 'Q'#define INOTIFY_IOCTL_MAXNR 2
Quoted 1 line · Robert Love
Live from linux.conf.au, below is inotify against 2.6.12-rc3.
Mark the open inotify device as nonseekable, so lseek() and such do not work.
fs/inotify.c | 2 ++ 1 files changed, 2 insertions(+)
fs/inotify.c +2 -0
@@ -743,6 +743,8 @@ file->private_data = dev; +nonseekable_open (inode, file); +return 0 ; out_err:free_uid (user);
PATCH 1/1 updated inotify for 2.6.12-rc3.
In reply to Robert LoveQuoted 1 line · Robert Love
Live from linux.conf.au, below is inotify against 2.6.12-rc3.
Here is an updated rediff for 2.6.12-rc3, with the changes from the last day or so added:
- Add oneshot support for Tridge and Jeremy.
- Send IN_ATTRIB event on xattr change.
- Mark the open device as nonseekable.
Andrew, can you please replace the current inotify patch in 2.6-mm with this?
Best,
Robert Love
inotify!
inotify is intended to correct the deficiencies of dnotify, particularly its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory that you intend to watch. This quickly results in too many open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to directories. Sure, a change to a file in a directory affects the directory, but you are then forced to keep a cache of stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change notification:
* inotify's interface is a device node, not SIGIO. You open a single fd to the device node, which is select()-able.
* inotify has an event that says "the filesystem that the item you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure), Gamin (a FAM replacement), and other projects.
Documentation/filesystems/inotify.txt | 81 ++ fs/Kconfig | 13 fs/Makefile | 1 fs/attr.c | 33 - fs/compat.c | 12 fs/file_table.c | 3 fs/inode.c | 6 fs/inotify.c | 965 ++++++++++++++++++++++++++++++++++ fs/namei.c | 30 - fs/open.c | 4 fs/read_write.c | 15 fs/xattr.c | 5 include/linux/fs.h | 6 include/linux/fsnotify.h | 230 ++++++++ include/linux/inotify.h | 112 +++ include/linux/sched.h | 4 kernel/user.c | 4 17 files changed, 1468 insertions(+), 56 deletions(-)
Documentation/filesystems/inotify.txt +81 -0
@@ -0,0 +1,81 @@ + inotify + a powerful yet simple file change notification system + + + +Document started 15 Mar 2005 by Robert Love <[email protected]> + +(i) User Interface + +Inotify is controlled by a device node, /dev/inotify. If you do not use udev, +this device may need to be created manually. First step, open it + + int dev_fd = open ("/dev/inotify", O_RDONLY); + +Change events are managed by "watches". A watch is an (object,mask) pair where +the object is a file or directory and the mask is a bitmask of one or more +inotify events that the application wishes to receive. See <linux/inotify.h> +for valid events. A watch is referenced by a watch descriptor, or wd. + +Watches are added via a file descriptor. + +Watches on a directory will return events on any files inside of the directory. + +Adding a watch is simple, + + /* 'wd' represents the watch on fd with mask */ + struct inotify_request req = { fd, mask }; + int wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + +You can add a large number of files via something like + + for each file to watch { + struct inotify_request req; + int file_fd; + + file_fd = open (file, O_RDONLY); + if (fd < 0) { + perror ("open"); + break; + } + + req.fd = file_fd; + req.mask = mask; + + wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + + close (fd); + } + +You can update an existing watch in the same manner, by passing in a new mask. + +An existing watch is removed via the INOTIFY_IGNORE ioctl, for example + + ioctl (dev_fd, INOTIFY_IGNORE, wd); + +Events are provided in the form of an inotify_event structure that is read(2) +from /dev/inotify. The filename is of dynamic length and follows the struct. +It is of size len. The filename is padded with null bytes to ensure proper +alignment. This padding is reflected in len. + +You can slurp multiple events by passing a large buffer, for example + + size_t len = read (fd, buf, BUF_LEN); + +Will return as many events as are available and fit in BUF_LEN. + +/dev/inotify is also select() and poll() able. + +You can find the size of the current event queue via the FIONREAD ioctl. + +All watches are destroyed and cleaned up on close. + + +(ii) Internal Kernel Implementation + +Each open inotify device is associated with an inotify_device structure. + +Each watch is associated with an inotify_watch structure. Watches are chained +off of each associated device and each associated inode. + +See fs/inotify.c for the locking and lifetime rules.
fs/attr.c +4 -29
@@ -10,7 +10,7 @@#include <linux/mm.h> #include <linux/string.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/fcntl.h> #include <linux/quotaops.h> #include <linux/security.h>
@@ -107,31 +107,8 @@ out:return error; } -EXPORT_SYMBOL (inode_setattr); -int setattr_mask (unsigned int ia_valid) -{ -unsigned long dn_mask =0 ; - -if (ia_valid &ATTR_UID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_GID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_SIZE ) - dn_mask |=DN_MODIFY ; -/* both times implies a utime(s) call */ -if ((ia_valid & (ATTR_ATIME |ATTR_MTIME )) == (ATTR_ATIME |ATTR_MTIME )) - dn_mask |=DN_ATTRIB ; -else if (ia_valid &ATTR_ATIME ) - dn_mask |=DN_ACCESS ; -else if (ia_valid &ATTR_MTIME ) - dn_mask |=DN_MODIFY ; -if (ia_valid &ATTR_MODE ) - dn_mask |=DN_ATTRIB ; -return dn_mask; -} -int notify_change (struct dentry * dentry,struct iattr * attr) {struct inode *inode = dentry->d_inode;
@@ -197,11 +174,9 @@if (ia_valid &ATTR_SIZE )up_write (&dentry->d_inode->i_alloc_sem); -if (!error) { -unsigned long dn_mask =setattr_mask (ia_valid); -if (dn_mask) -dnotify_parent (dentry, dn_mask); - } +if (!error) +fsnotify_change (dentry, ia_valid); +return error; }
fs/compat.c +8 -4
@@ -37,7 +37,7 @@#include <linux/ctype.h> #include <linux/module.h> #include <linux/dirent.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/highuid.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h>
@@ -1307,9 +1307,13 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +struct dentry *dentry = file->f_dentry; +if (type ==READ ) +fsnotify_access (dentry); +else +fsnotify_modify (dentry); + }return ret; }
fs/file_table.c +3 -0
@@ -16,6 +16,7 @@#include <linux/eventpoll.h> #include <linux/mount.h> #include <linux/cdev.h> +#include <linux/fsnotify.h> /* sysctl tunables... */ struct files_stat_struct files_stat = {
@@ -123,6 +124,8 @@struct inode *inode = dentry->d_inode;might_sleep (); + +fsnotify_close (file);/* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +6 -0
@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/inotify.h> /* * This is needed for the following functions:
@@ -129,6 +130,10 @@#ifdef CONFIG_QUOTA memset (&inode->i_dquot,0 ,sizeof (inode->i_dquot));#endif +#ifdef CONFIG_INOTIFY +INIT_LIST_HEAD (&inode->inotify_watches); +sema_init (&inode->inotify_sem,1 ); +#endif inode->i_pipe =NULL ; inode->i_bdev =NULL ; inode->i_cdev =NULL ;
@@ -355,6 +360,7 @@down (&iprune_sem);spin_lock (&inode_lock); +inotify_unmount_inodes (&sb->s_inodes); busy =invalidate_list (&sb->s_inodes, &throw_away);spin_unlock (&inode_lock);
fs/inotify.c +965 -0
965 lines not on this page · raw message
fs/Kconfig +13 -0
@@ -339,6 +339,19 @@ If you don't know whether you need it, then you don't need it: answer N. +config INOTIFY + bool "Inotify file change notification support" + default y + ---help--- + Say Y here to enable inotify support and the /dev/inotify character + device. Inotify is a file change notification system and a + replacement for dnotify. Inotify fixes numerous shortcomings in + dnotify and introduces several new features. It allows monitoring + of both files and directories via a single open fd. Multiple file + events are supported. + + If unsure, say Y. + config QUOTA bool "Quota support" help
fs/Makefile +1 -0
@@ -11,6 +11,7 @@ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ +obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_COMPAT) += compat.o
fs/namei.c +16 -14
@@ -21,7 +21,7 @@#include <linux/namei.h> #include <linux/quotaops.h> #include <linux/pagemap.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/smp_lock.h> #include <linux/personality.h> #include <linux/security.h>
@@ -1292,7 +1292,7 @@DQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_create (dir, dentry, mode); }return error;
@@ -1597,7 +1597,7 @@DQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_mknod (dir, dentry, mode, dev); }return error;
@@ -1670,7 +1670,7 @@DQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_mkdir (dir, dentry->d_name.name);security_inode_post_mkdir (dir,dentry, mode); }return error;
@@ -1761,7 +1761,7 @@ }up (&dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_DELETE ); +fsnotify_rmdir (dentry, dentry->d_inode, dir);d_delete (dentry); }dput (dentry);
@@ -1834,9 +1834,10 @@/* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags &DCACHE_NFSFS_RENAMED )) { +fsnotify_unlink (dentry, dir);d_delete (dentry); -inode_dir_notify (dir,DN_DELETE ); } +return error; }
@@ -1910,7 +1911,7 @@DQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_symlink (dir, dentry, oldname); }return error;
@@ -1983,7 +1984,7 @@ error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, new_dentry->d_name.name);security_inode_post_link (old_dentry, dir, new_dentry); }return error;
@@ -2147,6 +2148,7 @@ {int error;int is_dir =S_ISDIR (old_dentry->d_inode->i_mode); +char *old_name;if (old_dentry->d_inode == new_dentry->d_inode)return 0 ;
@@ -2168,18 +2170,18 @@DQUOT_INIT (old_dir);DQUOT_INIT (new_dir); + old_name =fsnotify_oldname_init (old_dentry); +if (is_dir) error =vfs_rename_dir (old_dir,old_dentry,new_dir,new_dentry);else error =vfs_rename_other (old_dir,old_dentry,new_dir,new_dentry);if (!error) { -if (old_dir == new_dir) -inode_dir_notify (old_dir,DN_RENAME ); -else { -inode_dir_notify (old_dir,DN_DELETE ); -inode_dir_notify (new_dir,DN_CREATE ); - } +const char *new_name = old_dentry->d_name.name; +fsnotify_move (old_dir, new_dir, old_name, new_name); } +fsnotify_oldname_free (old_name); +return error; }
fs/open.c +3 -1
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/tty.h>
@@ -944,9 +944,11 @@ fd =get_unused_fd ();if (fd >=0 ) {struct file *f =filp_open (tmp, flags, mode); + error =PTR_ERR (f);if (IS_ERR (f)) goto out_error; +fsnotify_open (f->f_dentry);fd_install (fd, f); } out:
fs/read_write.c +9 -6
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/uio.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/security.h> #include <linux/module.h> #include <linux/syscalls.h>
@@ -239,7 +239,7 @@else ret =do_sync_read (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_ACCESS ); +fsnotify_access (file->f_dentry); current->rchar += ret; } current->syscr++;
@@ -287,7 +287,7 @@else ret =do_sync_write (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_MODIFY ); +fsnotify_modify (file->f_dentry); current->wchar += ret; } current->syscw++;
@@ -523,9 +523,12 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +if (type ==READ ) +fsnotify_access (file->f_dentry); +else +fsnotify_modify (file->f_dentry); + }return ret; Efault: ret = -EFAULT ;
fs/xattr.c +4 -1
@@ -16,6 +16,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/module.h> +#include <linux/fsnotify.h> #include <asm/uaccess.h> /*
@@ -57,8 +58,10 @@if (error) goto out; error = d->d_inode->i_op->setxattr (d, kname, kvalue, size, flags); -if (!error) +if (!error) { +fsnotify_xattr (d);security_inode_post_setxattr (d, kname, kvalue, size, flags); + } out:up (&d->d_inode->i_sem); }
include/linux/fs.h +5 -1
@@ -471,6 +471,11 @@struct dnotify_struct *i_dnotify;/* for directory notifications */ #endif +#ifdef CONFIG_INOTIFY +struct list_head inotify_watches;/* watches on this inode */ +struct semaphore inotify_sem;/* protects the watches list */ +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -1365,7 +1370,6 @@extern int do_remount_sb (struct super_block *sb,int flags,void *data,int force);extern sector_t bmap (struct inode *,sector_t ); -extern int setattr_mask (unsigned int );extern int notify_change (struct dentry *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *); extern int generic_permission(struct inode *, int,
include/linux/fsnotify.h +230 -0
230 lines not on this page · raw message
include/linux/inotify.h +112 -0
112 lines not on this page · raw message
include/linux/sched.h +4 -0
@@ -404,6 +404,10 @@atomic_t processes;/* How many processes does this user have? */ atomic_t files;/* How many open files does this user have? */ atomic_t sigpending;/* How many pending signals does this user have? */ +#ifdef CONFIG_INOTIFY +atomic_t inotify_watches;/* How many inotify watches does this user have? */ +atomic_t inotify_devs;/* How many inotify devs does this user have opened? */ +#endif /* protected by mq_lock */ unsigned long mq_bytes;/* How many bytes can be allocated to mqueue? */ unsigned long locked_shm;/* How many pages of mlocked shm ? */
kernel/user.c +4 -0
@@ -120,6 +120,10 @@atomic_set (&new->processes,0 );atomic_set (&new->files,0 );atomic_set (&new->sigpending,0 ); +#ifdef CONFIG_INOTIFY +atomic_set (&new->inotify_watches,0 ); +atomic_set (&new->inotify_devs,0 ); +#endif new->mq_bytes =0 ; new->locked_shm =0 ;
Re: [patch] updated inotify for 2.6.12-rc3.
Quoted 14 lines
fs/inotify.c +14
-0+static int inotify_ignore (struct inotify_device *dev,s32 wd) +{ +struct inotify_watch *watch; +struct inode *inode; + +down (&dev->sem); + watch =idr_find (&dev->idr, wd); +if (unlikely (!watch)) { +up (&dev->sem); +return -EINVAL ; + } +get_inotify_watch (watch); +up (&dev->sem); +
fs/inotify.c line 875
Quoted 10 lines
fs/inotify.c +10
-0+ inode = watch->inode; +down (&inode->inotify_sem); +down (&dev->sem); +remove_watch (watch, dev); +up (&dev->sem); +up (&inode->inotify_sem); +put_inotify_watch (watch); + +return 0 ; +}
So what happens if
* something is holding inotify_sem right now
* ten threads call that on the same watch
* all of them get to down(&inode->inotify_sem); and block there,
having acquired ten references to the watch
* after whatever had been holding ->inotify_sem in the first place
releases it, they will one by one go through the rest of function. And drop _20_ references to the watch. 9 of those - after we kfree() the watch...
Re: [patch] updated inotify for 2.6.12-rc3.
Quoted 26 lines · Al Viro
Quoted 14 lines
fs/inotify.c +14
-0+static int inotify_ignore (struct inotify_device *dev,s32 wd) +{ +struct inotify_watch *watch; +struct inode *inode; + +down (&dev->sem); + watch =idr_find (&dev->idr, wd); +if (unlikely (!watch)) { +up (&dev->sem); +return -EINVAL ; + } +get_inotify_watch (watch); +up (&dev->sem); +fs/inotify.c line 875
Quoted 10 lines
fs/inotify.c +10
-0+ inode = watch->inode; +down (&inode->inotify_sem); +down (&dev->sem); +remove_watch (watch, dev); +up (&dev->sem); +up (&inode->inotify_sem); +put_inotify_watch (watch); + +return 0 ; +}So what happens if
Quoted 8 lines
* something is holding
inotify_semright now
* ten threads call that on the same watch
* all of them get todown(&inode->inotify_sem); and block there,
having acquired ten references to the watch
* after whatever had been holding ->inotify_sem in the first place
releases it, they will one by one go through the rest of function. And drop _20_ references to the watch. 9 of those - after wekfree()the watch...
In create_watch () we call get_inotify_watch (), which maps to the put_inotify_watch() in remove_watch(). As far as I can tell the ref counting is 1 for 1.
Signature · 1 line
John McCutchan <[email protected]>
Re: [patch] updated inotify for 2.6.12-rc3.
Quoted 11 lines · John McCutchan
Quoted 9 lines · Al Viro
So what happens if
* something is holdinginotify_semright now
* ten threads call that on the same watch
* all of them get todown(&inode->inotify_sem); and block there,
having acquired ten references to the watch
* after whatever had been holding ->inotify_sem in the first place
releases it, they will one by one go through the rest of function. And drop _20_ references to the watch. 9 of those - after wekfree()the watch...
Quoted 3 lines
In
create_watch() we callget_inotify_watch(), which maps to theput_inotify_watch()inremove_watch(). As far as I can tell the ref counting is 1 for 1.
Or it would, if remove_watch() had been called only once. In the scenario above that will not be true.
Re: [patch] updated inotify for 2.6.12-rc3.
Quoted 2 lines · Al Viro
Or it would, if
remove_watch()had been called only once. In the scenario above that will not be true.
Thanks.
Robert Love
Double check that we don't race.
fs/inotify.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-)
fs/inotify.c +7 -2
@@ -861,12 +861,17 @@return -EINVAL ; }get_inotify_watch (watch); + inode = watch->inode;up (&dev->sem); - inode = watch->inode;down (&inode->inotify_sem);down (&dev->sem); -remove_watch (watch, dev); + +/* make sure we did not race */ + watch =idr_find (&dev->idr, wd); +if (likely (watch)) +remove_watch (watch, dev); +up (&dev->sem);up (&inode->inotify_sem);put_inotify_watch (watch);
PATCH 1/1 latest inotify.
In reply to Robert LoveBelow is the latest inotify, against 2.6.12-rc3.
Changes since the last post:
- Explicitly define IN_ALL_EVENTS
- Couple of bug fixes related to the recent changes
- Fix for Viro's race
- Add some rationale to the documentation
- Misc. cleanup and such
Enjoy.
Robert Love
inotify!
inotify is intended to correct the deficiencies of dnotify, particularly its inability to scale and its terrible user interface:
* dnotify requires the opening of one fd per each directory that you intend to watch. This quickly results in too many open files and pins removable media, preventing unmount.
* dnotify is directory-based. You only learn about changes to directories. Sure, a change to a file in a directory affects the directory, but you are then forced to keep a cache of stat structures.
* dnotify's interface to user-space is awful. Signals?
inotify provides a more usable, simple, powerful solution to file change notification:
* inotify's interface is a device node, not SIGIO. You open a single fd to the device node, which is select()-able.
* inotify has an event that says "the filesystem that the item you were watching is on was unmounted."
* inotify can watch directories or files.
Inotify is currently used by Beagle (a desktop search infrastructure), Gamin (a FAM replacement), and other projects.
Documentation/filesystems/inotify.txt | 123 ++++ fs/Kconfig | 13 fs/Makefile | 1 fs/attr.c | 33 - fs/compat.c | 12 fs/file_table.c | 3 fs/inode.c | 6 fs/inotify.c | 971 ++++++++++++++++++++++++++++++++++ fs/namei.c | 30 - fs/open.c | 4 fs/read_write.c | 15 fs/xattr.c | 5 include/linux/fs.h | 6 include/linux/fsnotify.h | 230 ++++++++ include/linux/inotify.h | 126 ++++ include/linux/sched.h | 4 kernel/user.c | 4 17 files changed, 1530 insertions(+), 56 deletions(-)
Documentation/filesystems/inotify.txt +123 -0
@@ -0,0 +1,123 @@ + inotify + a powerful yet simple file change notification system + + + +Document started 15 Mar 2005 by Robert Love <[email protected]> + +(i) User Interface + +Inotify is controlled by a device node, /dev/inotify. If you do not use udev, +this device may need to be created manually. First step, open it + + int dev_fd = open ("/dev/inotify", O_RDONLY); + +Change events are managed by "watches". A watch is an (object,mask) pair where +the object is a file or directory and the mask is a bitmask of one or more +inotify events that the application wishes to receive. See <linux/inotify.h> +for valid events. A watch is referenced by a watch descriptor, or wd. + +Watches are added via a file descriptor. + +Watches on a directory will return events on any files inside of the directory. + +Adding a watch is simple, + + /* 'wd' represents the watch on fd with mask */ + struct inotify_request req = { fd, mask }; + int wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + +You can add a large number of files via something like + + for each file to watch { + struct inotify_request req; + int file_fd; + + file_fd = open (file, O_RDONLY); + if (fd < 0) { + perror ("open"); + break; + } + + req.fd = file_fd; + req.mask = mask; + + wd = ioctl (dev_fd, INOTIFY_WATCH, &req); + + close (fd); + } + +You can update an existing watch in the same manner, by passing in a new mask. + +An existing watch is removed via the INOTIFY_IGNORE ioctl, for example + + ioctl (dev_fd, INOTIFY_IGNORE, wd); + +Events are provided in the form of an inotify_event structure that is read(2) +from /dev/inotify. The filename is of dynamic length and follows the struct. +It is of size len. The filename is padded with null bytes to ensure proper +alignment. This padding is reflected in len. + +You can slurp multiple events by passing a large buffer, for example + + size_t len = read (fd, buf, BUF_LEN); + +Will return as many events as are available and fit in BUF_LEN. + +/dev/inotify is also select() and poll() able. + +You can find the size of the current event queue via the FIONREAD ioctl. + +All watches are destroyed and cleaned up on close. + + +(ii) Internal Kernel Implementation + +Each open inotify device is associated with an inotify_device structure. + +Each watch is associated with an inotify_watch structure. Watches are chained +off of each associated device and each associated inode. + +See fs/inotify.c for the locking and lifetime rules. + + +(iii) Rationale + +Q: What is the design decision behind not tying the watch to the +open fd of the watched object? + +A: Watches are associated with an open inotify device, not an +open file. This solves the primary problem with dnotify: +keeping the file open pins the file and thus, worse, pins the +mount. Dnotify is therefore infeasible for use on a desktop +system with removable media as the media cannot be unmounted. + +Q: What is the design decision behind using an-fd-per-device as +opposed to an fd-per-watch? + +A: An fd-per-watch quickly consumes more file descriptors than +are allowed, more fd's than are feasible to manage, and more +fd's than are ideally select()-able. Yes, root can bump the +per-process fd limit and yes, users can use epoll, but requiring +both is silly and an extraneous requirement. A watch consumes +less memory than an open file, separating the number spaces is +thus sensible. The current design is what user-space developers +want: Users open the device, once, and add n watches, requiring +but one fd and no twiddling with fd limits. +Opening /dev/inotify two thousand times is silly. If we can +implement user-space's preferences cleanly--and we can, the idr +layer makes stuff like this trivial--then we should. + +Q: Why a device node? + +A: The second biggest problem with dnotify is that the user +interface sucks ass. Signals are a terrible, terrible interface +for file notification. Or for anything, for that matter. The +idea solution, from all perspectives, is a file descriptor based +one that allows basic file I/O and poll/select. Obtaining the +fd and managing the watches could of been done either via a +device file or a family of new system calls. We decided to +implement a device file because adding three or four new system +calls that mirrored open, close, and ioctl seemed silly. A +character device makes sense from user-space and was easy to +implement inside of the kernel.
fs/attr.c +4 -29
@@ -10,7 +10,7 @@#include <linux/mm.h> #include <linux/string.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/fcntl.h> #include <linux/quotaops.h> #include <linux/security.h>
@@ -107,31 +107,8 @@ out:return error; } -EXPORT_SYMBOL (inode_setattr); -int setattr_mask (unsigned int ia_valid) -{ -unsigned long dn_mask =0 ; - -if (ia_valid &ATTR_UID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_GID ) - dn_mask |=DN_ATTRIB ; -if (ia_valid &ATTR_SIZE ) - dn_mask |=DN_MODIFY ; -/* both times implies a utime(s) call */ -if ((ia_valid & (ATTR_ATIME |ATTR_MTIME )) == (ATTR_ATIME |ATTR_MTIME )) - dn_mask |=DN_ATTRIB ; -else if (ia_valid &ATTR_ATIME ) - dn_mask |=DN_ACCESS ; -else if (ia_valid &ATTR_MTIME ) - dn_mask |=DN_MODIFY ; -if (ia_valid &ATTR_MODE ) - dn_mask |=DN_ATTRIB ; -return dn_mask; -} -int notify_change (struct dentry * dentry,struct iattr * attr) {struct inode *inode = dentry->d_inode;
@@ -197,11 +174,9 @@if (ia_valid &ATTR_SIZE )up_write (&dentry->d_inode->i_alloc_sem); -if (!error) { -unsigned long dn_mask =setattr_mask (ia_valid); -if (dn_mask) -dnotify_parent (dentry, dn_mask); - } +if (!error) +fsnotify_change (dentry, ia_valid); +return error; }
fs/compat.c +8 -4
@@ -37,7 +37,7 @@#include <linux/ctype.h> #include <linux/module.h> #include <linux/dirent.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/highuid.h> #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h>
@@ -1307,9 +1307,13 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +struct dentry *dentry = file->f_dentry; +if (type ==READ ) +fsnotify_access (dentry); +else +fsnotify_modify (dentry); + }return ret; }
fs/file_table.c +3 -0
@@ -16,6 +16,7 @@#include <linux/eventpoll.h> #include <linux/mount.h> #include <linux/cdev.h> +#include <linux/fsnotify.h> /* sysctl tunables... */ struct files_stat_struct files_stat = {
@@ -123,6 +124,8 @@struct inode *inode = dentry->d_inode;might_sleep (); + +fsnotify_close (file);/* * The function eventpoll_release() should be the first called * in the file cleanup chain.
fs/inode.c +6 -0
@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/inotify.h> /* * This is needed for the following functions:
@@ -129,6 +130,10 @@#ifdef CONFIG_QUOTA memset (&inode->i_dquot,0 ,sizeof (inode->i_dquot));#endif +#ifdef CONFIG_INOTIFY +INIT_LIST_HEAD (&inode->inotify_watches); +sema_init (&inode->inotify_sem,1 ); +#endif inode->i_pipe =NULL ; inode->i_bdev =NULL ; inode->i_cdev =NULL ;
@@ -355,6 +360,7 @@down (&iprune_sem);spin_lock (&inode_lock); +inotify_unmount_inodes (&sb->s_inodes); busy =invalidate_list (&sb->s_inodes, &throw_away);spin_unlock (&inode_lock);
fs/inotify.c +971 -0
971 lines not on this page · raw message
fs/Kconfig +13 -0
@@ -339,6 +339,19 @@ If you don't know whether you need it, then you don't need it: answer N. +config INOTIFY + bool "Inotify file change notification support" + default y + ---help--- + Say Y here to enable inotify support and the /dev/inotify character + device. Inotify is a file change notification system and a + replacement for dnotify. Inotify fixes numerous shortcomings in + dnotify and introduces several new features. It allows monitoring + of both files and directories via a single open fd. Multiple file + events are supported. + + If unsure, say Y. + config QUOTA bool "Quota support" help
fs/Makefile +1 -0
@@ -11,6 +11,7 @@ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o mpage.o direct-io.o \ +obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_EPOLL) += eventpoll.o obj-$(CONFIG_COMPAT) += compat.o
fs/namei.c +16 -14
@@ -21,7 +21,7 @@#include <linux/namei.h> #include <linux/quotaops.h> #include <linux/pagemap.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/smp_lock.h> #include <linux/personality.h> #include <linux/security.h>
@@ -1292,7 +1292,7 @@DQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_create (dir, dentry, mode); }return error;
@@ -1597,7 +1597,7 @@DQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_mknod (dir, dentry, mode, dev); }return error;
@@ -1670,7 +1670,7 @@DQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_mkdir (dir, dentry->d_name.name);security_inode_post_mkdir (dir,dentry, mode); }return error;
@@ -1761,7 +1761,7 @@ }up (&dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_DELETE ); +fsnotify_rmdir (dentry, dentry->d_inode, dir);d_delete (dentry); }dput (dentry);
@@ -1834,9 +1834,10 @@/* We don't d_delete() NFS sillyrenamed files--they still exist. */ if (!error && !(dentry->d_flags &DCACHE_NFSFS_RENAMED )) { +fsnotify_unlink (dentry, dir);d_delete (dentry); -inode_dir_notify (dir,DN_DELETE ); } +return error; }
@@ -1910,7 +1911,7 @@DQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, dentry->d_name.name);security_inode_post_symlink (dir, dentry, oldname); }return error;
@@ -1983,7 +1984,7 @@ error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem);if (!error) { -inode_dir_notify (dir,DN_CREATE ); +fsnotify_create (dir, new_dentry->d_name.name);security_inode_post_link (old_dentry, dir, new_dentry); }return error;
@@ -2147,6 +2148,7 @@ {int error;int is_dir =S_ISDIR (old_dentry->d_inode->i_mode); +char *old_name;if (old_dentry->d_inode == new_dentry->d_inode)return 0 ;
@@ -2168,18 +2170,18 @@DQUOT_INIT (old_dir);DQUOT_INIT (new_dir); + old_name =fsnotify_oldname_init (old_dentry); +if (is_dir) error =vfs_rename_dir (old_dir,old_dentry,new_dir,new_dentry);else error =vfs_rename_other (old_dir,old_dentry,new_dir,new_dentry);if (!error) { -if (old_dir == new_dir) -inode_dir_notify (old_dir,DN_RENAME ); -else { -inode_dir_notify (old_dir,DN_DELETE ); -inode_dir_notify (new_dir,DN_CREATE ); - } +const char *new_name = old_dentry->d_name.name; +fsnotify_move (old_dir, new_dir, old_name, new_name); } +fsnotify_oldname_free (old_name); +return error; }
fs/open.c +3 -1
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/smp_lock.h> #include <linux/quotaops.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/tty.h>
@@ -944,9 +944,11 @@ fd =get_unused_fd ();if (fd >=0 ) {struct file *f =filp_open (tmp, flags, mode); + error =PTR_ERR (f);if (IS_ERR (f)) goto out_error; +fsnotify_open (f->f_dentry);fd_install (fd, f); } out:
fs/read_write.c +9 -6
@@ -10,7 +10,7 @@#include <linux/file.h> #include <linux/uio.h> #include <linux/smp_lock.h> -#include <linux/dnotify.h> +#include <linux/fsnotify.h> #include <linux/security.h> #include <linux/module.h> #include <linux/syscalls.h>
@@ -239,7 +239,7 @@else ret =do_sync_read (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_ACCESS ); +fsnotify_access (file->f_dentry); current->rchar += ret; } current->syscr++;
@@ -287,7 +287,7 @@else ret =do_sync_write (file, buf, count, pos);if (ret >0 ) { -dnotify_parent (file->f_dentry,DN_MODIFY ); +fsnotify_modify (file->f_dentry); current->wchar += ret; } current->syscw++;
@@ -523,9 +523,12 @@ out:if (iov != iovstack)kfree (iov); -if ((ret + (type ==READ )) >0 ) -dnotify_parent (file->f_dentry, - (type ==READ ) ?DN_ACCESS :DN_MODIFY ); +if ((ret + (type ==READ )) >0 ) { +if (type ==READ ) +fsnotify_access (file->f_dentry); +else +fsnotify_modify (file->f_dentry); + }return ret; Efault: ret = -EFAULT ;
fs/xattr.c +4 -1
@@ -16,6 +16,7 @@#include <linux/security.h> #include <linux/syscalls.h> #include <linux/module.h> +#include <linux/fsnotify.h> #include <asm/uaccess.h> /*
@@ -57,8 +58,10 @@if (error) goto out; error = d->d_inode->i_op->setxattr (d, kname, kvalue, size, flags); -if (!error) +if (!error) { +fsnotify_xattr (d);security_inode_post_setxattr (d, kname, kvalue, size, flags); + } out:up (&d->d_inode->i_sem); }
include/linux/fs.h +5 -1
@@ -471,6 +471,11 @@struct dnotify_struct *i_dnotify;/* for directory notifications */ #endif +#ifdef CONFIG_INOTIFY +struct list_head inotify_watches;/* watches on this inode */ +struct semaphore inotify_sem;/* protects the watches list */ +#endif +unsigned long i_state;unsigned long dirtied_when;/* jiffies of first dirtying */
@@ -1365,7 +1370,6 @@extern int do_remount_sb (struct super_block *sb,int flags,void *data,int force);extern sector_t bmap (struct inode *,sector_t ); -extern int setattr_mask (unsigned int );extern int notify_change (struct dentry *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *); extern int generic_permission(struct inode *, int,
include/linux/fsnotify.h +230 -0
230 lines not on this page · raw message
include/linux/inotify.h +126 -0
126 lines not on this page · raw message
include/linux/sched.h +4 -0
@@ -404,6 +404,10 @@atomic_t processes;/* How many processes does this user have? */ atomic_t files;/* How many open files does this user have? */ atomic_t sigpending;/* How many pending signals does this user have? */ +#ifdef CONFIG_INOTIFY +atomic_t inotify_watches;/* How many inotify watches does this user have? */ +atomic_t inotify_devs;/* How many inotify devs does this user have opened? */ +#endif /* protected by mq_lock */ unsigned long mq_bytes;/* How many bytes can be allocated to mqueue? */ unsigned long locked_shm;/* How many pages of mlocked shm ? */
kernel/user.c +4 -0
@@ -120,6 +120,10 @@atomic_set (&new->processes,0 );atomic_set (&new->files,0 );atomic_set (&new->sigpending,0 ); +#ifdef CONFIG_INOTIFY +atomic_set (&new->inotify_watches,0 ); +atomic_set (&new->inotify_devs,0 ); +#endif new->mq_bytes =0 ; new->locked_shm =0 ;