[PATCH 3/4] fsnotify: Handle the file change ranges

From: Alexey Zaytsev
Date: Sun Nov 21 2010 - 19:32:25 EST


Signed-off-by: Alexey Zaytsev <alexey.zaytsev@xxxxxxxxx>
---
fs/notify/fsnotify.c | 24 ++++++++++++++----------
fs/notify/inode_mark.c | 2 +-
fs/notify/inotify/inotify_user.c | 2 +-
fs/notify/notification.c | 18 ++++++++++++++++--
include/linux/fsnotify_backend.h | 31 ++++++++++++++++++++++++++-----
5 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 20dc218..7cabc1d 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -84,7 +84,8 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
}

/* Notify this dentry's parent about a child's events. */
-int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
+int __fsnotify_parent(struct path *path, struct dentry *dentry,
+ __u32 mask, struct fsnotify_range *range)
{
struct dentry *parent;
struct inode *p_inode;
@@ -108,10 +109,10 @@ int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)

if (path)
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
- dentry->d_name.name, 0);
+ dentry->d_name.name, 0, range);
else
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
- dentry->d_name.name, 0);
+ dentry->d_name.name, 0, range);
}

dput(parent);
@@ -126,6 +127,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
__u32 mask, void *data,
int data_is, u32 cookie,
const unsigned char *file_name,
+ struct fsnotify_range *range,
struct fsnotify_event **event)
{
struct fsnotify_group *group = NULL;
@@ -167,10 +169,11 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,

pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x inode_mark=%p"
" inode_test_mask=%x vfsmount_mark=%p vfsmount_test_mask=%x"
- " data=%p data_is=%d cookie=%d event=%p\n",
+ " data=%p data_is=%d cookie=%d range = {%lld, %lld}, event=%p\n",
__func__, group, to_tell, mnt, mask, inode_mark,
inode_test_mask, vfsmount_mark, vfsmount_test_mask, data,
- data_is, cookie, *event);
+ data_is, cookie, range ? range->start : -1,
+ range ? range->end : -1, *event);

if (!inode_test_mask && !vfsmount_test_mask)
return 0;
@@ -183,7 +186,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
if (!*event) {
*event = fsnotify_create_event(to_tell, mask, data,
data_is, file_name,
- cookie, GFP_KERNEL);
+ cookie, range, GFP_KERNEL);
if (!*event)
return -ENOMEM;
}
@@ -197,7 +200,8 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
* notification event in whatever means they feel necessary.
*/
int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
- const unsigned char *file_name, u32 cookie)
+ const unsigned char *file_name, u32 cookie,
+ struct fsnotify_range *range)
{
struct hlist_node *inode_node = NULL, *vfsmount_node = NULL;
struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
@@ -256,17 +260,17 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
if (inode_group > vfsmount_group) {
/* handle inode */
ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
- data_is, cookie, file_name, &event);
+ data_is, cookie, file_name, range, &event);
/* we didn't use the vfsmount_mark */
vfsmount_group = NULL;
} else if (vfsmount_group > inode_group) {
ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
- data_is, cookie, file_name, &event);
+ data_is, cookie, file_name, range, &event);
inode_group = NULL;
} else {
ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
mask, data, data_is, cookie, file_name,
- &event);
+ range, &event);
}

if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 4c29fcf..cd39df7 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -295,7 +295,7 @@ void fsnotify_unmount_inodes(struct list_head *list)
iput(need_iput_tmp);

/* for each watch, send FS_UNMOUNT and then remove it */
- fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
+ fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0, NULL);

fsnotify_inode_delete(inode);

diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 444c305..a5c2c69 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -524,7 +524,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,

ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL,
FSNOTIFY_EVENT_NONE, NULL, 0,
- GFP_NOFS);
+ NULL, GFP_NOFS);
if (!ignored_event)
return;

diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index f39260f..20b86a0 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -395,7 +395,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event)
*/
struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, void *data,
int data_type, const unsigned char *name,
- u32 cookie, gfp_t gfp)
+ u32 cookie, struct fsnotify_range *range,
+ gfp_t gfp)
{
struct fsnotify_event *event;

@@ -422,6 +423,19 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
event->to_tell = to_tell;
event->data_type = data_type;

+ /* The range might be allocated on stack. */
+ if (mask & FS_MODIFY) {
+ event->mod_range = *range;
+ } else {
+ event->mod_range.start = -1;
+ }
+
+ if (mask & FS_CLOSE_WRITE) {
+ event->cw_range = *range;
+ } else {
+ event->cw_range.start = -1;
+ }
+
switch (data_type) {
case FSNOTIFY_EVENT_PATH: {
struct path *path = data;
@@ -453,7 +467,7 @@ __init int fsnotify_notification_init(void)
fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC);

q_overflow_event = fsnotify_create_event(NULL, FS_Q_OVERFLOW, NULL,
- FSNOTIFY_EVENT_NONE, NULL, 0,
+ FSNOTIFY_EVENT_NONE, NULL, 0, NULL,
GFP_KERNEL);
if (!q_overflow_event)
panic("unable to allocate fsnotify q_overflow_event\n");
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 0a68f92..63237c5 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -240,6 +240,9 @@ struct fsnotify_event {
size_t name_len;
struct pid *tgid;

+ struct fsnotify_range mod_range; /* What has been modified last time */
+ struct fsnotify_range cw_range; /* What has been modified since the file was opened */
+
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
__u32 response; /* userspace answer to question */
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
@@ -305,8 +308,10 @@ struct fsnotify_mark {

/* main fsnotify call to send events */
extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
- const unsigned char *name, u32 cookie);
-extern int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
+ const unsigned char *name, u32 cookie,
+ struct fsnotify_range *range);
+extern int __fsnotify_parent(struct path *path, struct dentry *dentry,
+ __u32 mask, struct fsnotify_range *range);
extern void __fsnotify_inode_delete(struct inode *inode);
extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
extern u32 fsnotify_get_cookie(void);
@@ -420,22 +425,34 @@ extern void fsnotify_unmount_inodes(struct list_head *list);
extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
void *data, int data_is,
const unsigned char *name,
- u32 cookie, gfp_t gfp);
+ u32 cookie,
+ struct fsnotify_range *range,
+ gfp_t gfp);

/* fanotify likes to change events after they are on lists... */
extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event);
extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
struct fsnotify_event *new_event);

+static inline void fsnotify_update_range(struct fsnotify_range *new,
+ struct fsnotify_range *old)
+{
+ /* Cast because an empty range starts at -1. */
+ new->start = min((unsigned long long) old->start, (unsigned long long) new->start);
+ new->end = max(old->end, new->end);
+}
+
#else

static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
- const unsigned char *name, u32 cookie)
+ const unsigned char *name, u32 cookie,
+ struct fsnotify_range *range)
{
return 0;
}

-static inline int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
+static inline int __fsnotify_parent(struct path *path, struct dentry *dentry,
+ __u32 mask, struct fsnotify_range *range)
{
return 0;
}
@@ -460,6 +477,10 @@ static inline u32 fsnotify_get_cookie(void)
static inline void fsnotify_unmount_inodes(struct list_head *list)
{}

+static inline void fsnotify_update_range(struct fsnotify_range *new,
+ struct fsnotify_range *old)
+{}
+
#endif /* CONFIG_FSNOTIFY */

#endif /* __KERNEL __ */

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/