Signed-off-by: Alexey Zaytsev <alexey.zaytsev@xxxxxxxxx> --- fs/compat.c | 2 + fs/nfsd/vfs.c | 2 + fs/open.c | 4 +++ fs/read_write.c | 4 +-- include/linux/fs.h | 14 +++++++++ include/linux/fsnotify.h | 68 ++++++++++++++++++++++++++++------------------ 6 files changed, 64 insertions(+), 30 deletions(-) diff --git a/fs/compat.c b/fs/compat.c index c580c32..66eb689 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1171,7 +1171,7 @@ out: if (type == READ) fsnotify_access(file); else - fsnotify_modify(file); + fsnotify_modify(file, pos - ret, ret); } return ret; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 184938f..d781014 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1035,7 +1035,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, goto out_nfserr; *cnt = host_err; nfsdstats.io_write += host_err; - fsnotify_modify(file); + fsnotify_modify(file, offset - host_err, host_err); /* clear setuid/setgid flag after write */ if (inode->i_mode & (S_ISUID | S_ISGID)) diff --git a/fs/open.c b/fs/open.c index 4197b9e..17b0d79 100644 --- a/fs/open.c +++ b/fs/open.c @@ -675,6 +675,10 @@ static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, f->f_path.mnt = mnt; f->f_pos = 0; f->f_op = fops_get(inode->i_fop); +#ifdef CONFIG_FSNOTIFY + f->f_whatchanged.start = -1; + f->f_whatchanged.end = 0; +#endif file_sb_list_add(f, inode->i_sb); error = security_dentry_open(f, cred); diff --git a/fs/read_write.c b/fs/read_write.c index 5d431ba..86c60c2 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -383,7 +383,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { - fsnotify_modify(file); + fsnotify_modify(file, (*pos) - ret, ret); add_wchar(current, ret); } inc_syscw(current); @@ -699,7 +699,7 @@ out: if (type == READ) fsnotify_access(file); else - fsnotify_modify(file); + fsnotify_modify(file, (*pos) - ret, ret); } return ret; } diff --git a/include/linux/fs.h b/include/linux/fs.h index eedc00b..3337975 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -922,6 +922,16 @@ static inline int ra_has_index(struct file_ra_state *ra, pgoff_t index) index < ra->start + ra->size); } +/* + * fsnotify wants to know, what might have changed during the file's lifetime. + * We maintain a single range, so it might include non-modified gaps, but this + * should work good enough for the majoroty of use cases, and the rest + * shold listen to individual 'modify' events. */ +struct fsnotify_range { + loff_t start; /* The beginning of the possibly modified area.*/ + loff_t end; /* The first byte after the area. */ +}; + #define FILE_MNT_WRITE_TAKEN 1 #define FILE_MNT_WRITE_RELEASED 2 @@ -965,6 +975,10 @@ struct file { #ifdef CONFIG_DEBUG_WRITECOUNT unsigned long f_mnt_write_state; #endif + +#ifdef CONFIG_FSNOTIFY + struct fsnotify_range f_whatchanged; +#endif }; #define get_file(x) atomic_long_inc(&(x)->f_count) diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 5c185fa..49e7788 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -26,12 +26,13 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry, } /* Notify this dentry's parent about a child's events. */ -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) { if (!dentry) dentry = path->dentry; - return __fsnotify_parent(path, dentry, mask); + return __fsnotify_parent(path, dentry, mask, range); } /* simple call site for access decisions */ @@ -53,11 +54,12 @@ static inline int fsnotify_perm(struct file *file, int mask) else BUG(); - ret = fsnotify_parent(path, NULL, fsnotify_mask); + ret = fsnotify_parent(path, NULL, fsnotify_mask, NULL); if (ret) return ret; - return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, + NULL, 0, NULL); } /* @@ -78,7 +80,7 @@ static inline void fsnotify_d_move(struct dentry *dentry) */ static inline void fsnotify_link_count(struct inode *inode) { - fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE, NULL, 0, NULL); } /* @@ -102,14 +104,17 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, new_dir_mask |= FS_ISDIR; } - fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie); - fsnotify(new_dir, new_dir_mask, new_dir, FSNOTIFY_EVENT_INODE, new_name, fs_cookie); + fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, + old_name, fs_cookie, NULL); + fsnotify(new_dir, new_dir_mask, new_dir, FSNOTIFY_EVENT_INODE, + new_name, fs_cookie, NULL); if (target) fsnotify_link_count(target); if (source) - fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, + NULL, 0, NULL); audit_inode_child(moved, new_dir); } @@ -139,7 +144,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) if (isdir) mask |= FS_ISDIR; - fsnotify_parent(NULL, dentry, mask); + fsnotify_parent(NULL, dentry, mask, NULL); } /* @@ -147,7 +152,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) */ static inline void fsnotify_inoderemove(struct inode *inode) { - fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE, NULL, 0, NULL); __fsnotify_inode_delete(inode); } @@ -158,7 +163,8 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { audit_inode_child(dentry, inode); - fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); + fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, + dentry->d_name.name, 0, NULL); } /* @@ -171,7 +177,8 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct fsnotify_link_count(inode); audit_inode_child(new_dentry, dir); - fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0); + fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, + new_dentry->d_name.name, 0, NULL); } /* @@ -184,7 +191,8 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) audit_inode_child(dentry, inode); - fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); + fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, + dentry->d_name.name, 0, NULL); } /* @@ -200,26 +208,33 @@ static inline void fsnotify_access(struct file *file) mask |= FS_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(path, NULL, mask, NULL); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0, NULL); } } + + /* * fsnotify_modify - file was modified */ -static inline void fsnotify_modify(struct file *file) +static inline void fsnotify_modify(struct file *file, loff_t original, size_t count) { struct path *path = &file->f_path; struct inode *inode = path->dentry->d_inode; __u32 mask = FS_MODIFY; + struct fsnotify_range range = { + .start = original, + .end = original + count, + }; + fsnotify_update_range(&file->f_whatchanged, &range); if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(path, NULL, mask, &range); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0, &range); } } @@ -238,8 +253,8 @@ static inline void fsnotify_open(struct file *file) /* FMODE_NONOTIFY must never be set from user */ file->f_mode &= ~FMODE_NONOTIFY; - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(path, NULL, mask, NULL); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0, NULL); } /* @@ -256,8 +271,9 @@ static inline void fsnotify_close(struct file *file) mask |= FS_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(path, NULL, mask, &file->f_whatchanged); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, + NULL, 0, &file->f_whatchanged); } } @@ -272,8 +288,8 @@ static inline void fsnotify_xattr(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - fsnotify_parent(NULL, dentry, mask); - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify_parent(NULL, dentry, mask, NULL); + fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0, NULL); } /* @@ -307,8 +323,8 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - fsnotify_parent(NULL, dentry, mask); - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify_parent(NULL, dentry, mask, NULL); + fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0, NULL); } } -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html