When adding an inode watch, and when FAN_EVENT_INFO_PARENT flag was requested at fanotify_init(), store the mount point from which the watch was added. Events reported for this inode mark may report the path of this mount point, even if the events actually happened on this inode, or its children, but from another mount point. An inode mark needs to have both a reference to the watched inode and a reference to the original mount point. For this purpose, the fsnotify_mark fields .inode and .mnt are now independent memebers and not a union. This has a side effect of extending the size of struct fsnotify_mark by another pointer, but this effect is insignificant compared to the fact that every inode mark pins a whole struct inode. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify_user.c | 14 +++++++++++--- fs/notify/fdinfo.c | 4 ++-- fs/notify/inode_mark.c | 1 + fs/notify/mark.c | 15 ++++++++++++--- include/linux/fsnotify_backend.h | 18 ++++++++++++++---- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 5f038f4..0696d46 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -653,6 +653,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, } static int fanotify_add_inode_mark(struct fsnotify_group *group, + struct vfsmount *mnt, struct inode *inode, __u32 mask, unsigned int flags) { @@ -674,7 +675,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, mutex_lock(&group->mark_mutex); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { - fsn_mark = fanotify_add_new_mark(group, inode, NULL); + fsn_mark = fanotify_add_new_mark(group, inode, mnt); if (IS_ERR(fsn_mark)) { mutex_unlock(&group->mark_mutex); return PTR_ERR(fsn_mark); @@ -891,7 +892,14 @@ SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, /* inode held in place by reference to path; group by fget on fd */ if (!(flags & FAN_MARK_MOUNT)) inode = path.dentry->d_inode; - else + + /* + * Store the mount point from which the watch was added also for + * inode marks. This mount point may be used to report dentry events, + * even if the events happened on another mount point. + */ + if ((flags & FAN_MARK_MOUNT) || + group->fanotify_data.flags & FAN_EVENT_INFO_PARENT) mnt = path.mnt; /* create/update an inode mark */ @@ -900,7 +908,7 @@ SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, if (flags & FAN_MARK_MOUNT) ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags); else - ret = fanotify_add_inode_mark(group, inode, mask, flags); + ret = fanotify_add_inode_mark(group, mnt, inode, mask, flags); break; case FAN_MARK_REMOVE: if (flags & FAN_MARK_MOUNT) diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index fd98e51..6dd776b 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -119,7 +119,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY) mflags |= FAN_MARK_IGNORED_SURV_MODIFY; - if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { + if (FSNOTIFY_IS_INODE_MARK(mark)) { inode = igrab(mark->inode); if (!inode) return; @@ -129,7 +129,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) show_mark_fhandle(m, inode); seq_putc(m, '\n'); iput(inode); - } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) { + } else if (FSNOTIFY_IS_VFSMOUNT_MARK(mark)) { struct mount *mnt = real_mount(mark->mnt); seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 741077d..2471229 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -54,6 +54,7 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) hlist_del_init_rcu(&mark->obj_list); mark->inode = NULL; + mark->mnt = NULL; /* * this mark is now off the inode->i_fsnotify_marks list and we diff --git a/fs/notify/mark.c b/fs/notify/mark.c index d3fea0b..2ebabb4 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -148,10 +148,10 @@ void fsnotify_detach_mark(struct fsnotify_mark *mark) mark->flags &= ~FSNOTIFY_MARK_FLAG_ATTACHED; - if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { + if (FSNOTIFY_IS_INODE_MARK(mark)) { inode = mark->inode; fsnotify_destroy_inode_mark(mark); - } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) + } else if (FSNOTIFY_IS_VFSMOUNT_MARK(mark)) fsnotify_destroy_vfsmount_mark(mark); else BUG(); @@ -352,6 +352,11 @@ int fsnotify_add_mark_list(struct hlist_head *head, struct fsnotify_mark *mark, * Attach an initialized mark to a given group and fs object. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group. + * + * Either @inode or @mnt MUST be non NULL. + * @inode may be NULL implying this is a mount watch. + * @inode and @mnt both non NULL imply this is an inode watch + * and @mnt is the mount point from which the watch was added. */ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, @@ -359,7 +364,6 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, { int ret = 0; - BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); BUG_ON(!mutex_is_locked(&group->mark_mutex)); @@ -382,6 +386,11 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); if (ret) goto err; + /* Store the mount point from which the inode watch was added */ + if (mnt) { + mark->mnt = mnt; + mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; + } } else if (mnt) { ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups); if (ret) diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 5722e4f..c36e8e5 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -227,10 +227,9 @@ struct fsnotify_mark { spinlock_t lock; /* List of marks for inode / vfsmount [obj_lock] */ struct hlist_node obj_list; - union { /* Object pointer [mark->lock, group->mark_mutex] */ - struct inode *inode; /* inode this mark is associated with */ - struct vfsmount *mnt; /* vfsmount this mark is associated with */ - }; + /* Object pointer [mark->lock, group->mark_mutex] */ + struct inode *inode; /* inode this mark is associated with */ + struct vfsmount *mnt; /* vfsmount this mark is associated with */ /* Events types to ignore [mark->lock, group->mark_mutex] */ __u32 ignored_mask; #define FSNOTIFY_MARK_FLAG_INODE 0x01 @@ -243,6 +242,17 @@ struct fsnotify_mark { void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; +/* + * A mark may have a reference to both an inode and a vfsmount, + * indicating that this is an inode mark with a reference to + * the mount point from which that inode mark was added + */ +#define FSNOTIFY_MARK_HAS_INODE(m) ((m)->flags & FSNOTIFY_MARK_FLAG_INODE) +#define FSNOTIFY_MARK_HAS_VFSMOUNT(m) ((m)->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) +#define FSNOTIFY_IS_INODE_MARK(m) (FSNOTIFY_MARK_HAS_INODE(m)) +#define FSNOTIFY_IS_VFSMOUNT_MARK(m) (!FSNOTIFY_MARK_HAS_INODE(m) && \ + FSNOTIFY_MARK_HAS_VFSMOUNT(m)) + #ifdef CONFIG_FSNOTIFY /* called from the vfs helpers */ -- 2.7.4 -- 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