Setting fanotify marks that report events with fid on btrfs sub-volumes is not supported, because in the case of btrfs sub-volumes, there is no uniform fsid that can be cached in the sb connector. If filesystem supports the cheap ->get_fsid() operation, do not use the cached fsid in connector and query fsid from inode on every event. This allows setting fanotify marks that report events with fid on btrfs sub-volumes. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify.c | 28 +++++++++++++++++++++------- fs/notify/fanotify/fanotify_user.c | 14 ++++++++++---- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 9dac7f6e72d2..25282ca0173d 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -535,6 +535,21 @@ static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data, return dir; } +/* + * If @inode is available and if filesystem supports ->get_fsid(), return the + * fsid associated with the inode. Otherwise, return the pre cached fsid. + */ +static __kernel_fsid_t fanotify_inode_fsid(struct inode *inode, + __kernel_fsid_t *fsid) +{ + __kernel_fsid_t __fsid; + + if (inode && inode_get_fsid(inode, &__fsid) == 0) + return __fsid; + + return *fsid; +} + static struct fanotify_event *fanotify_alloc_path_event(const struct path *path, unsigned int *hash, gfp_t gfp) @@ -586,8 +601,8 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id, return NULL; ffe->fae.type = FANOTIFY_EVENT_TYPE_FID; - ffe->fsid = *fsid; - *hash ^= fanotify_hash_fsid(fsid); + ffe->fsid = fanotify_inode_fsid(id, fsid); + *hash ^= fanotify_hash_fsid(&ffe->fsid); fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id), hash, gfp); @@ -627,8 +642,8 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir, return NULL; fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; - fne->fsid = *fsid; - *hash ^= fanotify_hash_fsid(fsid); + fne->fsid = fanotify_inode_fsid(dir, fsid); + *hash ^= fanotify_hash_fsid(&fne->fsid); info = &fne->info; fanotify_info_init(info); if (dir_fh_len) { @@ -691,9 +706,10 @@ static struct fanotify_event *fanotify_alloc_error_event( fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR; fee->error = report->error; fee->err_count = 1; - fee->fsid = *fsid; inode = report->inode; + fee->fsid = fanotify_inode_fsid(inode, fsid); + *hash ^= fanotify_hash_fsid(&fee->fsid); fh_len = fanotify_encode_fh_len(inode); /* Bad fh_len. Fallback to using an invalid fh. Should never happen. */ @@ -702,8 +718,6 @@ static struct fanotify_event *fanotify_alloc_error_event( fanotify_encode_fh(&fee->object_fh, inode, fh_len, NULL, 0); - *hash ^= fanotify_hash_fsid(fsid); - return &fee->fae; } diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 0eb9622e8a9f..ed67e5f973ab 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1570,16 +1570,22 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid) return -ENODEV; /* - * Make sure dentry is not of a filesystem subvolume (e.g. btrfs) - * which uses a different fsid than sb root. + * If dentry is on a filesystem subvolume (e.g. btrfs), which uses a + * different fsid than sb root, then make sure that filesytem supports + * getting fsid from inode. */ err = vfs_get_fsid(dentry->d_sb->s_root, &root_fsid); if (err) return err; if (root_fsid.val[0] != fsid->val[0] || - root_fsid.val[1] != fsid->val[1]) - return -EXDEV; + root_fsid.val[1] != fsid->val[1]) { + if (!dentry->d_sb->s_op->get_fsid) + return -EXDEV; + + /* Cache root fsid in case inode is not available on event */ + *fsid = root_fsid; + } return 0; } -- 2.34.1