@@ -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;
}
@@ -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;
}
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@gmail.com> --- fs/notify/fanotify/fanotify.c | 28 +++++++++++++++++++++------- fs/notify/fanotify/fanotify_user.c | 14 ++++++++++---- 2 files changed, 31 insertions(+), 11 deletions(-)