* [PATCH 0/2] Support fanotify FAN_REPORT_FID on all filesystems
@ 2023-11-18 18:30 Amir Goldstein
2023-11-18 18:30 ` [PATCH 1/2] fanotify: store fsid in mark instead of in connector Amir Goldstein
2023-11-18 18:30 ` [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem Amir Goldstein
0 siblings, 2 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-18 18:30 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
Jan,
In the vfs fanotify update for v6.7-rc1 [1], we considerably increased
the amount of filesystems that can setup inode marks with FAN_REPORT_FID:
- NFS export is no longer required for setting up inode marks
- All the simple fs gained a non-zero fsid
This leaves the following in-tree filesystems where inode marks with
FAN_REPORT_FID cannot be set:
- nfs, fuse, afs, coda (zero fsid)
- btrfs non-root subvol (fsid not a unique identifier of sb)
This patch set takes care of these remaining cases, by allowing inode
marks, as long as all inode marks in the group are contained to the same
filesystem and same fsid (i.e. subvol).
I've written some basic sanity tests [2] and a man-page update draft [3].
I've also tested fsnotifywait --recursive on fuse and on btrfs subvol.
It works as expected - if tree travesal crosses filesystem or subvol
boundary, setting the subdir mark fails with -EXDEV.
Thanks,
Amir.
[1] https://lore.kernel.org/linux-fsdevel/20231107-vfs-fsid-5037e344d215@brauner/
[2] https://github.com/amir73il/ltp/commits/fanotify_fsid
[3] https://github.com/amir73il/man-pages/commits/fanotify_fsid
Amir Goldstein (2):
fanotify: store fsid in mark instead of in connector
fanotify: allow "weak" fsid when watching a single filesystem
fs/notify/fanotify/fanotify.c | 34 +++------
fs/notify/fanotify/fanotify.h | 21 ++++++
fs/notify/fanotify/fanotify_user.c | 111 ++++++++++++++++++++++++-----
fs/notify/mark.c | 52 +++-----------
include/linux/fsnotify_backend.h | 14 ++--
5 files changed, 135 insertions(+), 97 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] fanotify: store fsid in mark instead of in connector
2023-11-18 18:30 [PATCH 0/2] Support fanotify FAN_REPORT_FID on all filesystems Amir Goldstein
@ 2023-11-18 18:30 ` Amir Goldstein
2023-11-30 14:25 ` Jan Kara
2023-11-18 18:30 ` [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem Amir Goldstein
1 sibling, 1 reply; 13+ messages in thread
From: Amir Goldstein @ 2023-11-18 18:30 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
Some filesystems like fuse and nfs have zero or non-unique fsid.
We would like to avoid reporting ambiguous fsid in events, so we need
to avoid marking objects with same fsid and different sb.
To make this easier to enforce, store the fsid in the marks of the group
instead of in the shared conenctor.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/notify/fanotify/fanotify.c | 19 +++--------
fs/notify/fanotify/fanotify.h | 15 +++++++++
fs/notify/fanotify/fanotify_user.c | 18 ++++++++---
fs/notify/mark.c | 52 +++++-------------------------
include/linux/fsnotify_backend.h | 13 +++-----
5 files changed, 47 insertions(+), 70 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 9dac7f6e72d2..aff1ab3c32aa 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -838,9 +838,8 @@ static struct fanotify_event *fanotify_alloc_event(
}
/*
- * Get cached fsid of the filesystem containing the object from any connector.
- * All connectors are supposed to have the same fsid, but we do not verify that
- * here.
+ * Get cached fsid of the filesystem containing the object from any mark.
+ * All marks are supposed to have the same fsid, but we do not verify that here.
*/
static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
{
@@ -849,17 +848,9 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
__kernel_fsid_t fsid = {};
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
- struct fsnotify_mark_connector *conn;
-
- conn = READ_ONCE(mark->connector);
- /* Mark is just getting destroyed or created? */
- if (!conn)
- continue;
- if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID))
+ if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
continue;
- /* Pairs with smp_wmb() in fsnotify_add_mark_list() */
- smp_rmb();
- fsid = conn->fsid;
+ fsid = FANOTIFY_MARK(mark)->fsid;
if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
continue;
return fsid;
@@ -1068,7 +1059,7 @@ static void fanotify_freeing_mark(struct fsnotify_mark *mark,
static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
{
- kmem_cache_free(fanotify_mark_cache, fsn_mark);
+ kmem_cache_free(fanotify_mark_cache, FANOTIFY_MARK(fsn_mark));
}
const struct fsnotify_ops fanotify_fsnotify_ops = {
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 6936671e148d..2847fa564298 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -489,6 +489,21 @@ static inline unsigned int fanotify_event_hash_bucket(
return event->hash & FANOTIFY_HTABLE_MASK;
}
+struct fanotify_mark {
+ struct fsnotify_mark fsn_mark;
+ __kernel_fsid_t fsid;
+};
+
+static inline struct fanotify_mark *FANOTIFY_MARK(struct fsnotify_mark *mark)
+{
+ return container_of(mark, struct fanotify_mark, fsn_mark);
+}
+
+static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
+{
+ return &FANOTIFY_MARK(mark)->fsid;
+}
+
static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
{
unsigned int mflags = 0;
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 4d765c72496f..e3d836d4d156 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1199,6 +1199,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
__kernel_fsid_t *fsid)
{
struct ucounts *ucounts = group->fanotify_data.ucounts;
+ struct fanotify_mark *fan_mark;
struct fsnotify_mark *mark;
int ret;
@@ -1211,17 +1212,26 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
!inc_ucount(ucounts->ns, ucounts->uid, UCOUNT_FANOTIFY_MARKS))
return ERR_PTR(-ENOSPC);
- mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
- if (!mark) {
+ fan_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
+ if (!fan_mark) {
ret = -ENOMEM;
goto out_dec_ucounts;
}
+ mark = &fan_mark->fsn_mark;
fsnotify_init_mark(mark, group);
if (fan_flags & FAN_MARK_EVICTABLE)
mark->flags |= FSNOTIFY_MARK_FLAG_NO_IREF;
- ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0, fsid);
+ /* Cache fsid of filesystem containing the marked object */
+ if (fsid) {
+ fan_mark->fsid = *fsid;
+ mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
+ } else {
+ fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
+ }
+
+ ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0);
if (ret) {
fsnotify_put_mark(mark);
goto out_dec_ucounts;
@@ -1935,7 +1945,7 @@ static int __init fanotify_user_setup(void)
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
- fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
+ fanotify_mark_cache = KMEM_CACHE(fanotify_mark,
SLAB_PANIC|SLAB_ACCOUNT);
fanotify_fid_event_cachep = KMEM_CACHE(fanotify_fid_event,
SLAB_PANIC);
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index c74ef947447d..d6944ff86ffa 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -537,8 +537,7 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
}
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
- unsigned int obj_type,
- __kernel_fsid_t *fsid)
+ unsigned int obj_type)
{
struct fsnotify_mark_connector *conn;
@@ -550,14 +549,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
conn->flags = 0;
conn->type = obj_type;
conn->obj = connp;
- /* Cache fsid of filesystem containing the object */
- if (fsid) {
- conn->fsid = *fsid;
- conn->flags = FSNOTIFY_CONN_FLAG_HAS_FSID;
- } else {
- conn->fsid.val[0] = conn->fsid.val[1] = 0;
- conn->flags = 0;
- }
+ conn->flags = 0;
fsnotify_get_sb_connectors(conn);
/*
@@ -608,8 +600,7 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector(
*/
static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
fsnotify_connp_t *connp,
- unsigned int obj_type,
- int add_flags, __kernel_fsid_t *fsid)
+ unsigned int obj_type, int add_flags)
{
struct fsnotify_mark *lmark, *last = NULL;
struct fsnotify_mark_connector *conn;
@@ -619,41 +610,15 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
if (WARN_ON(!fsnotify_valid_obj_type(obj_type)))
return -EINVAL;
- /* Backend is expected to check for zero fsid (e.g. tmpfs) */
- if (fsid && WARN_ON_ONCE(!fsid->val[0] && !fsid->val[1]))
- return -ENODEV;
-
restart:
spin_lock(&mark->lock);
conn = fsnotify_grab_connector(connp);
if (!conn) {
spin_unlock(&mark->lock);
- err = fsnotify_attach_connector_to_object(connp, obj_type,
- fsid);
+ err = fsnotify_attach_connector_to_object(connp, obj_type);
if (err)
return err;
goto restart;
- } else if (fsid && !(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID)) {
- conn->fsid = *fsid;
- /* Pairs with smp_rmb() in fanotify_get_fsid() */
- smp_wmb();
- conn->flags |= FSNOTIFY_CONN_FLAG_HAS_FSID;
- } else if (fsid && (conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID) &&
- (fsid->val[0] != conn->fsid.val[0] ||
- fsid->val[1] != conn->fsid.val[1])) {
- /*
- * Backend is expected to check for non uniform fsid
- * (e.g. btrfs), but maybe we missed something?
- * Only allow setting conn->fsid once to non zero fsid.
- * inotify and non-fid fanotify groups do not set nor test
- * conn->fsid.
- */
- pr_warn_ratelimited("%s: fsid mismatch on object of type %u: "
- "%x.%x != %x.%x\n", __func__, conn->type,
- fsid->val[0], fsid->val[1],
- conn->fsid.val[0], conn->fsid.val[1]);
- err = -EXDEV;
- goto out_err;
}
/* is mark the first mark? */
@@ -703,7 +668,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
*/
int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
fsnotify_connp_t *connp, unsigned int obj_type,
- int add_flags, __kernel_fsid_t *fsid)
+ int add_flags)
{
struct fsnotify_group *group = mark->group;
int ret = 0;
@@ -723,7 +688,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
fsnotify_get_mark(mark); /* for g_list */
spin_unlock(&mark->lock);
- ret = fsnotify_add_mark_list(mark, connp, obj_type, add_flags, fsid);
+ ret = fsnotify_add_mark_list(mark, connp, obj_type, add_flags);
if (ret)
goto err;
@@ -742,14 +707,13 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
}
int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp,
- unsigned int obj_type, int add_flags,
- __kernel_fsid_t *fsid)
+ unsigned int obj_type, int add_flags)
{
int ret;
struct fsnotify_group *group = mark->group;
fsnotify_group_lock(group);
- ret = fsnotify_add_mark_locked(mark, connp, obj_type, add_flags, fsid);
+ ret = fsnotify_add_mark_locked(mark, connp, obj_type, add_flags);
fsnotify_group_unlock(group);
return ret;
}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index c0892d75ce33..a80b525ca653 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -472,10 +472,8 @@ typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
struct fsnotify_mark_connector {
spinlock_t lock;
unsigned short type; /* Type of object [lock] */
-#define FSNOTIFY_CONN_FLAG_HAS_FSID 0x01
#define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02
unsigned short flags; /* flags [lock] */
- __kernel_fsid_t fsid; /* fsid of filesystem containing object */
union {
/* Object pointer [lock] */
fsnotify_connp_t *obj;
@@ -530,6 +528,7 @@ struct fsnotify_mark {
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
+#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
unsigned int flags; /* flags [mark->lock] */
};
@@ -763,11 +762,10 @@ extern struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
/* attach the mark to the object */
extern int fsnotify_add_mark(struct fsnotify_mark *mark,
fsnotify_connp_t *connp, unsigned int obj_type,
- int add_flags, __kernel_fsid_t *fsid);
+ int add_flags);
extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
fsnotify_connp_t *connp,
- unsigned int obj_type, int add_flags,
- __kernel_fsid_t *fsid);
+ unsigned int obj_type, int add_flags);
/* attach the mark to the inode */
static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
@@ -775,15 +773,14 @@ static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
int add_flags)
{
return fsnotify_add_mark(mark, &inode->i_fsnotify_marks,
- FSNOTIFY_OBJ_TYPE_INODE, add_flags, NULL);
+ FSNOTIFY_OBJ_TYPE_INODE, add_flags);
}
static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
struct inode *inode,
int add_flags)
{
return fsnotify_add_mark_locked(mark, &inode->i_fsnotify_marks,
- FSNOTIFY_OBJ_TYPE_INODE, add_flags,
- NULL);
+ FSNOTIFY_OBJ_TYPE_INODE, add_flags);
}
/* given a group and a mark, flag mark to be freed when all references are dropped */
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-18 18:30 [PATCH 0/2] Support fanotify FAN_REPORT_FID on all filesystems Amir Goldstein
2023-11-18 18:30 ` [PATCH 1/2] fanotify: store fsid in mark instead of in connector Amir Goldstein
@ 2023-11-18 18:30 ` Amir Goldstein
2023-11-20 7:42 ` Amir Goldstein
2023-11-30 15:47 ` Jan Kara
1 sibling, 2 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-18 18:30 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
non-uniform fsid (e.g. btrfs non-root subvol).
When group is watching inodes all from the same filesystem (or subvol),
allow adding inode marks with "weak" fsid, because there is no ambiguity
regarding which filesystem reports the event.
The first mark added to a group determines if this group is single or
multi filesystem, depending on the fsid at the path of the added mark.
If the first mark added has a "strong" fsid, marks with "weak" fsid
cannot be added and vice versa.
If the first mark added has a "weak" fsid, following marks must have
the same "weak" fsid and the same sb as the first mark.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
fs/notify/fanotify/fanotify.c | 15 +----
fs/notify/fanotify/fanotify.h | 6 ++
fs/notify/fanotify/fanotify_user.c | 97 ++++++++++++++++++++++++------
include/linux/fsnotify_backend.h | 1 +
4 files changed, 90 insertions(+), 29 deletions(-)
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index aff1ab3c32aa..1e4def21811e 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -29,12 +29,6 @@ static unsigned int fanotify_hash_path(const struct path *path)
hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
}
-static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
- __kernel_fsid_t *fsid2)
-{
- return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
-}
-
static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
{
return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
@@ -851,7 +845,8 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
continue;
fsid = FANOTIFY_MARK(mark)->fsid;
- if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
+ if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
+ WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
continue;
return fsid;
}
@@ -933,12 +928,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
return 0;
}
- if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
+ if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS))
fsid = fanotify_get_fsid(iter_info);
- /* Racing with mark destruction or creation? */
- if (!fsid.val[0] && !fsid.val[1])
- return 0;
- }
event = fanotify_alloc_event(group, mask, data, data_type, dir,
file_name, &fsid, match_mask);
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 2847fa564298..9c97ae638ac5 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -504,6 +504,12 @@ static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
return &FANOTIFY_MARK(mark)->fsid;
}
+static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
+ __kernel_fsid_t *fsid2)
+{
+ return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
+}
+
static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
{
unsigned int mflags = 0;
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index e3d836d4d156..1cc68ea56c17 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -23,7 +23,7 @@
#include <asm/ioctls.h>
-#include "../../mount.h"
+#include "../fsnotify.h"
#include "../fdinfo.h"
#include "fanotify.h"
@@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
return recalc;
}
+struct fan_fsid {
+ struct super_block *sb;
+ __kernel_fsid_t id;
+ bool weak;
+};
+
+static int fanotify_check_mark_fsid(struct fsnotify_group *group,
+ struct fsnotify_mark *mark,
+ struct fan_fsid *fsid)
+{
+ struct fsnotify_mark_connector *conn;
+ struct fsnotify_mark *old;
+ struct super_block *old_sb = NULL;
+
+ *fanotify_mark_fsid(mark) = fsid->id;
+ mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
+ if (fsid->weak)
+ mark->flags |= FSNOTIFY_MARK_FLAG_WEAK_FSID;
+
+ /* First mark added will determine if group is single or multi fsid */
+ if (list_empty(&group->marks_list))
+ return 0;
+
+ /* Find sb of an existing mark */
+ list_for_each_entry(old, &group->marks_list, g_list) {
+ conn = READ_ONCE(old->connector);
+ if (!conn)
+ continue;
+ old_sb = fsnotify_connector_sb(conn);
+ if (old_sb)
+ break;
+ }
+
+ /* Only detached marks left? */
+ if (!old_sb)
+ return 0;
+
+ /* Do not allow mixing of marks with weak and strong fsid */
+ if ((mark->flags ^ old->flags) & FSNOTIFY_MARK_FLAG_WEAK_FSID)
+ return -EXDEV;
+
+ /* Allow mixing of marks with strong fsid from different fs */
+ if (!fsid->weak)
+ return 0;
+
+ /* Do not allow mixing marks with weak fsid from different fs */
+ if (old_sb != fsid->sb)
+ return -EXDEV;
+
+ /* Do not allow mixing marks from different btrfs sub-volumes */
+ if (!fanotify_fsid_equal(fanotify_mark_fsid(old),
+ fanotify_mark_fsid(mark)))
+ return -EXDEV;
+
+ return 0;
+}
+
static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp,
unsigned int obj_type,
unsigned int fan_flags,
- __kernel_fsid_t *fsid)
+ struct fan_fsid *fsid)
{
struct ucounts *ucounts = group->fanotify_data.ucounts;
struct fanotify_mark *fan_mark;
@@ -1225,8 +1282,9 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
/* Cache fsid of filesystem containing the marked object */
if (fsid) {
- fan_mark->fsid = *fsid;
- mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
+ ret = fanotify_check_mark_fsid(group, mark, fsid);
+ if (ret)
+ goto out_dec_ucounts;
} else {
fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
}
@@ -1289,7 +1347,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, unsigned int obj_type,
__u32 mask, unsigned int fan_flags,
- __kernel_fsid_t *fsid)
+ struct fan_fsid *fsid)
{
struct fsnotify_mark *fsn_mark;
bool recalc;
@@ -1337,7 +1395,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
struct vfsmount *mnt, __u32 mask,
- unsigned int flags, __kernel_fsid_t *fsid)
+ unsigned int flags, struct fan_fsid *fsid)
{
return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
@@ -1345,7 +1403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
static int fanotify_add_sb_mark(struct fsnotify_group *group,
struct super_block *sb, __u32 mask,
- unsigned int flags, __kernel_fsid_t *fsid)
+ unsigned int flags, struct fan_fsid *fsid)
{
return fanotify_add_mark(group, &sb->s_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
@@ -1353,7 +1411,7 @@ static int fanotify_add_sb_mark(struct fsnotify_group *group,
static int fanotify_add_inode_mark(struct fsnotify_group *group,
struct inode *inode, __u32 mask,
- unsigned int flags, __kernel_fsid_t *fsid)
+ unsigned int flags, struct fan_fsid *fsid)
{
pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
@@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
return fd;
}
-static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
+static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
+ struct fan_fsid *fsid)
{
+ unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
__kernel_fsid_t root_fsid;
int err;
/*
* Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
*/
- err = vfs_get_fsid(dentry, fsid);
+ err = vfs_get_fsid(dentry, &fsid->id);
if (err)
return err;
- if (!fsid->val[0] && !fsid->val[1])
- return -ENODEV;
+ /* Allow weak fsid when marking inodes */
+ fsid->sb = dentry->d_sb;
+ fsid->weak = mark_type == FAN_MARK_INODE;
+ if (!fsid->id.val[0] && !fsid->id.val[1])
+ return fsid->weak ? 0 : -ENODEV;
/*
* Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
@@ -1587,10 +1650,10 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
if (err)
return err;
- if (root_fsid.val[0] != fsid->val[0] ||
- root_fsid.val[1] != fsid->val[1])
- return -EXDEV;
+ if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
+ return fsid->weak ? 0 : -EXDEV;
+ fsid->weak = false;
return 0;
}
@@ -1675,7 +1738,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
struct fsnotify_group *group;
struct fd f;
struct path path;
- __kernel_fsid_t __fsid, *fsid = NULL;
+ struct fan_fsid __fsid, *fsid = NULL;
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
@@ -1827,7 +1890,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
}
if (fid_mode) {
- ret = fanotify_test_fsid(path.dentry, &__fsid);
+ ret = fanotify_test_fsid(path.dentry, flags, &__fsid);
if (ret)
goto path_put_and_out;
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index a80b525ca653..7f63be5ca0f1 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -529,6 +529,7 @@ struct fsnotify_mark {
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
+#define FSNOTIFY_MARK_FLAG_WEAK_FSID 0x1000
unsigned int flags; /* flags [mark->lock] */
};
--
2.34.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-18 18:30 ` [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem Amir Goldstein
@ 2023-11-20 7:42 ` Amir Goldstein
2023-11-20 15:48 ` Christian Brauner
2023-11-21 13:33 ` Amir Goldstein
2023-11-30 15:47 ` Jan Kara
1 sibling, 2 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-20 7:42 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
On Sat, Nov 18, 2023 at 8:30 PM Amir Goldstein <amir73il@gmail.com> wrote:
>
> So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
> on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
> non-uniform fsid (e.g. btrfs non-root subvol).
>
> When group is watching inodes all from the same filesystem (or subvol),
> allow adding inode marks with "weak" fsid, because there is no ambiguity
> regarding which filesystem reports the event.
>
> The first mark added to a group determines if this group is single or
> multi filesystem, depending on the fsid at the path of the added mark.
>
> If the first mark added has a "strong" fsid, marks with "weak" fsid
> cannot be added and vice versa.
>
> If the first mark added has a "weak" fsid, following marks must have
> the same "weak" fsid and the same sb as the first mark.
>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> ---
> fs/notify/fanotify/fanotify.c | 15 +----
> fs/notify/fanotify/fanotify.h | 6 ++
> fs/notify/fanotify/fanotify_user.c | 97 ++++++++++++++++++++++++------
> include/linux/fsnotify_backend.h | 1 +
> 4 files changed, 90 insertions(+), 29 deletions(-)
>
> diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> index aff1ab3c32aa..1e4def21811e 100644
> --- a/fs/notify/fanotify/fanotify.c
> +++ b/fs/notify/fanotify/fanotify.c
> @@ -29,12 +29,6 @@ static unsigned int fanotify_hash_path(const struct path *path)
> hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
> }
>
> -static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> - __kernel_fsid_t *fsid2)
> -{
> - return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> -}
> -
> static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
> {
> return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
> @@ -851,7 +845,8 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
> if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
> continue;
> fsid = FANOTIFY_MARK(mark)->fsid;
> - if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> + if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
> + WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> continue;
> return fsid;
> }
> @@ -933,12 +928,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
> return 0;
> }
>
> - if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
> + if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS))
> fsid = fanotify_get_fsid(iter_info);
> - /* Racing with mark destruction or creation? */
> - if (!fsid.val[0] && !fsid.val[1])
> - return 0;
> - }
>
> event = fanotify_alloc_event(group, mask, data, data_type, dir,
> file_name, &fsid, match_mask);
> diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
> index 2847fa564298..9c97ae638ac5 100644
> --- a/fs/notify/fanotify/fanotify.h
> +++ b/fs/notify/fanotify/fanotify.h
> @@ -504,6 +504,12 @@ static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
> return &FANOTIFY_MARK(mark)->fsid;
> }
>
> +static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> + __kernel_fsid_t *fsid2)
> +{
> + return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> +}
> +
> static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
> {
> unsigned int mflags = 0;
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index e3d836d4d156..1cc68ea56c17 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -23,7 +23,7 @@
>
> #include <asm/ioctls.h>
>
> -#include "../../mount.h"
> +#include "../fsnotify.h"
> #include "../fdinfo.h"
> #include "fanotify.h"
>
> @@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
> return recalc;
> }
>
> +struct fan_fsid {
> + struct super_block *sb;
> + __kernel_fsid_t id;
> + bool weak;
> +};
> +
> +static int fanotify_check_mark_fsid(struct fsnotify_group *group,
> + struct fsnotify_mark *mark,
> + struct fan_fsid *fsid)
> +{
> + struct fsnotify_mark_connector *conn;
> + struct fsnotify_mark *old;
> + struct super_block *old_sb = NULL;
> +
> + *fanotify_mark_fsid(mark) = fsid->id;
> + mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> + if (fsid->weak)
> + mark->flags |= FSNOTIFY_MARK_FLAG_WEAK_FSID;
> +
> + /* First mark added will determine if group is single or multi fsid */
> + if (list_empty(&group->marks_list))
> + return 0;
> +
> + /* Find sb of an existing mark */
> + list_for_each_entry(old, &group->marks_list, g_list) {
> + conn = READ_ONCE(old->connector);
> + if (!conn)
> + continue;
> + old_sb = fsnotify_connector_sb(conn);
> + if (old_sb)
> + break;
> + }
> +
> + /* Only detached marks left? */
> + if (!old_sb)
> + return 0;
> +
> + /* Do not allow mixing of marks with weak and strong fsid */
> + if ((mark->flags ^ old->flags) & FSNOTIFY_MARK_FLAG_WEAK_FSID)
> + return -EXDEV;
> +
> + /* Allow mixing of marks with strong fsid from different fs */
> + if (!fsid->weak)
> + return 0;
> +
> + /* Do not allow mixing marks with weak fsid from different fs */
> + if (old_sb != fsid->sb)
> + return -EXDEV;
> +
> + /* Do not allow mixing marks from different btrfs sub-volumes */
> + if (!fanotify_fsid_equal(fanotify_mark_fsid(old),
> + fanotify_mark_fsid(mark)))
> + return -EXDEV;
> +
> + return 0;
> +}
> +
> static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
> fsnotify_connp_t *connp,
> unsigned int obj_type,
> unsigned int fan_flags,
> - __kernel_fsid_t *fsid)
> + struct fan_fsid *fsid)
> {
> struct ucounts *ucounts = group->fanotify_data.ucounts;
> struct fanotify_mark *fan_mark;
> @@ -1225,8 +1282,9 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
>
> /* Cache fsid of filesystem containing the marked object */
> if (fsid) {
> - fan_mark->fsid = *fsid;
> - mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> + ret = fanotify_check_mark_fsid(group, mark, fsid);
> + if (ret)
OOPS, missed fsnotify_put_mark(mark);
better add a goto target out_put_mark as this is the second case now.
> + goto out_dec_ucounts;
> } else {
> fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
> }
> @@ -1289,7 +1347,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
> static int fanotify_add_mark(struct fsnotify_group *group,
> fsnotify_connp_t *connp, unsigned int obj_type,
> __u32 mask, unsigned int fan_flags,
> - __kernel_fsid_t *fsid)
> + struct fan_fsid *fsid)
> {
> struct fsnotify_mark *fsn_mark;
> bool recalc;
> @@ -1337,7 +1395,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
>
> static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
> struct vfsmount *mnt, __u32 mask,
> - unsigned int flags, __kernel_fsid_t *fsid)
> + unsigned int flags, struct fan_fsid *fsid)
> {
> return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
> FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
> @@ -1345,7 +1403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
>
> static int fanotify_add_sb_mark(struct fsnotify_group *group,
> struct super_block *sb, __u32 mask,
> - unsigned int flags, __kernel_fsid_t *fsid)
> + unsigned int flags, struct fan_fsid *fsid)
> {
> return fanotify_add_mark(group, &sb->s_fsnotify_marks,
> FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
> @@ -1353,7 +1411,7 @@ static int fanotify_add_sb_mark(struct fsnotify_group *group,
>
> static int fanotify_add_inode_mark(struct fsnotify_group *group,
> struct inode *inode, __u32 mask,
> - unsigned int flags, __kernel_fsid_t *fsid)
> + unsigned int flags, struct fan_fsid *fsid)
> {
> pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
>
> @@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
> return fd;
> }
>
> -static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> +static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
> + struct fan_fsid *fsid)
> {
> + unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> __kernel_fsid_t root_fsid;
> int err;
>
> /*
> * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
> */
> - err = vfs_get_fsid(dentry, fsid);
> + err = vfs_get_fsid(dentry, &fsid->id);
> if (err)
> return err;
>
> - if (!fsid->val[0] && !fsid->val[1])
> - return -ENODEV;
> + /* Allow weak fsid when marking inodes */
> + fsid->sb = dentry->d_sb;
> + fsid->weak = mark_type == FAN_MARK_INODE;
> + if (!fsid->id.val[0] && !fsid->id.val[1])
> + return fsid->weak ? 0 : -ENODEV;
>
> /*
> * Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
> @@ -1587,10 +1650,10 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> if (err)
> return err;
>
> - if (root_fsid.val[0] != fsid->val[0] ||
> - root_fsid.val[1] != fsid->val[1])
> - return -EXDEV;
> + if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
> + return fsid->weak ? 0 : -EXDEV;
>
> + fsid->weak = false;
> return 0;
> }
>
> @@ -1675,7 +1738,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
> struct fsnotify_group *group;
> struct fd f;
> struct path path;
> - __kernel_fsid_t __fsid, *fsid = NULL;
> + struct fan_fsid __fsid, *fsid = NULL;
> u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
> unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
> @@ -1827,7 +1890,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
> }
>
> if (fid_mode) {
> - ret = fanotify_test_fsid(path.dentry, &__fsid);
> + ret = fanotify_test_fsid(path.dentry, flags, &__fsid);
> if (ret)
> goto path_put_and_out;
>
> diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
> index a80b525ca653..7f63be5ca0f1 100644
> --- a/include/linux/fsnotify_backend.h
> +++ b/include/linux/fsnotify_backend.h
> @@ -529,6 +529,7 @@ struct fsnotify_mark {
> #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
> #define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
> +#define FSNOTIFY_MARK_FLAG_WEAK_FSID 0x1000
> unsigned int flags; /* flags [mark->lock] */
> };
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-20 7:42 ` Amir Goldstein
@ 2023-11-20 15:48 ` Christian Brauner
2023-11-20 16:20 ` Amir Goldstein
2023-11-21 13:33 ` Amir Goldstein
1 sibling, 1 reply; 13+ messages in thread
From: Christian Brauner @ 2023-11-20 15:48 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Jan Kara, linux-fsdevel
> OOPS, missed fsnotify_put_mark(mark);
> better add a goto target out_put_mark as this is the second case now.
I want to point out that going forward we should be able to make use of
scoped cleanup macros. Please see include/linux/cleanup.h. I think we
should start making liberal use of this. I know that Peter Ziljstra is
already doing so for kernel/sched/.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-20 15:48 ` Christian Brauner
@ 2023-11-20 16:20 ` Amir Goldstein
0 siblings, 0 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-20 16:20 UTC (permalink / raw)
To: Christian Brauner; +Cc: Jan Kara, linux-fsdevel
On Mon, Nov 20, 2023 at 5:48 PM Christian Brauner <brauner@kernel.org> wrote:
>
> > OOPS, missed fsnotify_put_mark(mark);
> > better add a goto target out_put_mark as this is the second case now.
>
> I want to point out that going forward we should be able to make use of
> scoped cleanup macros. Please see include/linux/cleanup.h. I think we
> should start making liberal use of this. I know that Peter Ziljstra is
> already doing so for kernel/sched/.
Yeh, I like them, but not sure if I would use them in this case.
Thanks,
Amir.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-20 7:42 ` Amir Goldstein
2023-11-20 15:48 ` Christian Brauner
@ 2023-11-21 13:33 ` Amir Goldstein
2023-11-30 12:42 ` Amir Goldstein
1 sibling, 1 reply; 13+ messages in thread
From: Amir Goldstein @ 2023-11-21 13:33 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
On Mon, Nov 20, 2023 at 9:42 AM Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Sat, Nov 18, 2023 at 8:30 PM Amir Goldstein <amir73il@gmail.com> wrote:
> >
> > So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
> > on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
> > non-uniform fsid (e.g. btrfs non-root subvol).
> >
> > When group is watching inodes all from the same filesystem (or subvol),
> > allow adding inode marks with "weak" fsid, because there is no ambiguity
> > regarding which filesystem reports the event.
> >
> > The first mark added to a group determines if this group is single or
> > multi filesystem, depending on the fsid at the path of the added mark.
> >
> > If the first mark added has a "strong" fsid, marks with "weak" fsid
> > cannot be added and vice versa.
> >
> > If the first mark added has a "weak" fsid, following marks must have
> > the same "weak" fsid and the same sb as the first mark.
> >
> > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > ---
> > fs/notify/fanotify/fanotify.c | 15 +----
> > fs/notify/fanotify/fanotify.h | 6 ++
> > fs/notify/fanotify/fanotify_user.c | 97 ++++++++++++++++++++++++------
> > include/linux/fsnotify_backend.h | 1 +
> > 4 files changed, 90 insertions(+), 29 deletions(-)
> >
> > diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> > index aff1ab3c32aa..1e4def21811e 100644
> > --- a/fs/notify/fanotify/fanotify.c
> > +++ b/fs/notify/fanotify/fanotify.c
> > @@ -29,12 +29,6 @@ static unsigned int fanotify_hash_path(const struct path *path)
> > hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
> > }
> >
> > -static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> > - __kernel_fsid_t *fsid2)
> > -{
> > - return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> > -}
> > -
> > static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
> > {
> > return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
> > @@ -851,7 +845,8 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
> > if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
> > continue;
> > fsid = FANOTIFY_MARK(mark)->fsid;
> > - if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> > + if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
> > + WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> > continue;
> > return fsid;
> > }
> > @@ -933,12 +928,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
> > return 0;
> > }
> >
> > - if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
> > + if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS))
> > fsid = fanotify_get_fsid(iter_info);
> > - /* Racing with mark destruction or creation? */
> > - if (!fsid.val[0] && !fsid.val[1])
> > - return 0;
> > - }
> >
> > event = fanotify_alloc_event(group, mask, data, data_type, dir,
> > file_name, &fsid, match_mask);
> > diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
> > index 2847fa564298..9c97ae638ac5 100644
> > --- a/fs/notify/fanotify/fanotify.h
> > +++ b/fs/notify/fanotify/fanotify.h
> > @@ -504,6 +504,12 @@ static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
> > return &FANOTIFY_MARK(mark)->fsid;
> > }
> >
> > +static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> > + __kernel_fsid_t *fsid2)
> > +{
> > + return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> > +}
> > +
> > static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
> > {
> > unsigned int mflags = 0;
> > diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> > index e3d836d4d156..1cc68ea56c17 100644
> > --- a/fs/notify/fanotify/fanotify_user.c
> > +++ b/fs/notify/fanotify/fanotify_user.c
> > @@ -23,7 +23,7 @@
> >
> > #include <asm/ioctls.h>
> >
> > -#include "../../mount.h"
> > +#include "../fsnotify.h"
> > #include "../fdinfo.h"
> > #include "fanotify.h"
> >
> > @@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
> > return recalc;
> > }
> >
> > +struct fan_fsid {
> > + struct super_block *sb;
> > + __kernel_fsid_t id;
> > + bool weak;
> > +};
> > +
> > +static int fanotify_check_mark_fsid(struct fsnotify_group *group,
> > + struct fsnotify_mark *mark,
> > + struct fan_fsid *fsid)
> > +{
> > + struct fsnotify_mark_connector *conn;
> > + struct fsnotify_mark *old;
> > + struct super_block *old_sb = NULL;
> > +
> > + *fanotify_mark_fsid(mark) = fsid->id;
> > + mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> > + if (fsid->weak)
> > + mark->flags |= FSNOTIFY_MARK_FLAG_WEAK_FSID;
> > +
> > + /* First mark added will determine if group is single or multi fsid */
> > + if (list_empty(&group->marks_list))
> > + return 0;
> > +
> > + /* Find sb of an existing mark */
> > + list_for_each_entry(old, &group->marks_list, g_list) {
> > + conn = READ_ONCE(old->connector);
> > + if (!conn)
> > + continue;
> > + old_sb = fsnotify_connector_sb(conn);
> > + if (old_sb)
> > + break;
> > + }
> > +
> > + /* Only detached marks left? */
> > + if (!old_sb)
> > + return 0;
> > +
> > + /* Do not allow mixing of marks with weak and strong fsid */
> > + if ((mark->flags ^ old->flags) & FSNOTIFY_MARK_FLAG_WEAK_FSID)
> > + return -EXDEV;
> > +
> > + /* Allow mixing of marks with strong fsid from different fs */
> > + if (!fsid->weak)
> > + return 0;
> > +
> > + /* Do not allow mixing marks with weak fsid from different fs */
> > + if (old_sb != fsid->sb)
> > + return -EXDEV;
> > +
> > + /* Do not allow mixing marks from different btrfs sub-volumes */
> > + if (!fanotify_fsid_equal(fanotify_mark_fsid(old),
> > + fanotify_mark_fsid(mark)))
> > + return -EXDEV;
> > +
> > + return 0;
> > +}
> > +
> > static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
> > fsnotify_connp_t *connp,
> > unsigned int obj_type,
> > unsigned int fan_flags,
> > - __kernel_fsid_t *fsid)
> > + struct fan_fsid *fsid)
> > {
> > struct ucounts *ucounts = group->fanotify_data.ucounts;
> > struct fanotify_mark *fan_mark;
> > @@ -1225,8 +1282,9 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
> >
> > /* Cache fsid of filesystem containing the marked object */
> > if (fsid) {
> > - fan_mark->fsid = *fsid;
> > - mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> > + ret = fanotify_check_mark_fsid(group, mark, fsid);
> > + if (ret)
>
> OOPS, missed fsnotify_put_mark(mark);
> better add a goto target out_put_mark as this is the second case now.
FYI, I pushed the fix to:
https://github.com/amir73il/linux/commits/fanotify_fsid
Let me know if you want me to post v2 for this.
Thanks,
Amir.
>
> > + goto out_dec_ucounts;
> > } else {
> > fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
> > }
> > @@ -1289,7 +1347,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
> > static int fanotify_add_mark(struct fsnotify_group *group,
> > fsnotify_connp_t *connp, unsigned int obj_type,
> > __u32 mask, unsigned int fan_flags,
> > - __kernel_fsid_t *fsid)
> > + struct fan_fsid *fsid)
> > {
> > struct fsnotify_mark *fsn_mark;
> > bool recalc;
> > @@ -1337,7 +1395,7 @@ static int fanotify_add_mark(struct fsnotify_group *group,
> >
> > static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
> > struct vfsmount *mnt, __u32 mask,
> > - unsigned int flags, __kernel_fsid_t *fsid)
> > + unsigned int flags, struct fan_fsid *fsid)
> > {
> > return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
> > FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
> > @@ -1345,7 +1403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
> >
> > static int fanotify_add_sb_mark(struct fsnotify_group *group,
> > struct super_block *sb, __u32 mask,
> > - unsigned int flags, __kernel_fsid_t *fsid)
> > + unsigned int flags, struct fan_fsid *fsid)
> > {
> > return fanotify_add_mark(group, &sb->s_fsnotify_marks,
> > FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
> > @@ -1353,7 +1411,7 @@ static int fanotify_add_sb_mark(struct fsnotify_group *group,
> >
> > static int fanotify_add_inode_mark(struct fsnotify_group *group,
> > struct inode *inode, __u32 mask,
> > - unsigned int flags, __kernel_fsid_t *fsid)
> > + unsigned int flags, struct fan_fsid *fsid)
> > {
> > pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
> >
> > @@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
> > return fd;
> > }
> >
> > -static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> > +static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
> > + struct fan_fsid *fsid)
> > {
> > + unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> > __kernel_fsid_t root_fsid;
> > int err;
> >
> > /*
> > * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
> > */
> > - err = vfs_get_fsid(dentry, fsid);
> > + err = vfs_get_fsid(dentry, &fsid->id);
> > if (err)
> > return err;
> >
> > - if (!fsid->val[0] && !fsid->val[1])
> > - return -ENODEV;
> > + /* Allow weak fsid when marking inodes */
> > + fsid->sb = dentry->d_sb;
> > + fsid->weak = mark_type == FAN_MARK_INODE;
> > + if (!fsid->id.val[0] && !fsid->id.val[1])
> > + return fsid->weak ? 0 : -ENODEV;
> >
> > /*
> > * Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
> > @@ -1587,10 +1650,10 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> > if (err)
> > return err;
> >
> > - if (root_fsid.val[0] != fsid->val[0] ||
> > - root_fsid.val[1] != fsid->val[1])
> > - return -EXDEV;
> > + if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
> > + return fsid->weak ? 0 : -EXDEV;
> >
> > + fsid->weak = false;
> > return 0;
> > }
> >
> > @@ -1675,7 +1738,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
> > struct fsnotify_group *group;
> > struct fd f;
> > struct path path;
> > - __kernel_fsid_t __fsid, *fsid = NULL;
> > + struct fan_fsid __fsid, *fsid = NULL;
> > u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
> > unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> > unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
> > @@ -1827,7 +1890,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
> > }
> >
> > if (fid_mode) {
> > - ret = fanotify_test_fsid(path.dentry, &__fsid);
> > + ret = fanotify_test_fsid(path.dentry, flags, &__fsid);
> > if (ret)
> > goto path_put_and_out;
> >
> > diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
> > index a80b525ca653..7f63be5ca0f1 100644
> > --- a/include/linux/fsnotify_backend.h
> > +++ b/include/linux/fsnotify_backend.h
> > @@ -529,6 +529,7 @@ struct fsnotify_mark {
> > #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> > #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
> > #define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
> > +#define FSNOTIFY_MARK_FLAG_WEAK_FSID 0x1000
> > unsigned int flags; /* flags [mark->lock] */
> > };
> >
> > --
> > 2.34.1
> >
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-21 13:33 ` Amir Goldstein
@ 2023-11-30 12:42 ` Amir Goldstein
0 siblings, 0 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-30 12:42 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
On Tue, Nov 21, 2023 at 3:33 PM Amir Goldstein <amir73il@gmail.com> wrote:
>
> On Mon, Nov 20, 2023 at 9:42 AM Amir Goldstein <amir73il@gmail.com> wrote:
> >
> > On Sat, Nov 18, 2023 at 8:30 PM Amir Goldstein <amir73il@gmail.com> wrote:
> > >
> > > So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
> > > on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
> > > non-uniform fsid (e.g. btrfs non-root subvol).
> > >
> > > When group is watching inodes all from the same filesystem (or subvol),
> > > allow adding inode marks with "weak" fsid, because there is no ambiguity
> > > regarding which filesystem reports the event.
> > >
> > > The first mark added to a group determines if this group is single or
> > > multi filesystem, depending on the fsid at the path of the added mark.
> > >
> > > If the first mark added has a "strong" fsid, marks with "weak" fsid
> > > cannot be added and vice versa.
> > >
> > > If the first mark added has a "weak" fsid, following marks must have
> > > the same "weak" fsid and the same sb as the first mark.
> > >
> > > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
> > > ---
> > > fs/notify/fanotify/fanotify.c | 15 +----
> > > fs/notify/fanotify/fanotify.h | 6 ++
> > > fs/notify/fanotify/fanotify_user.c | 97 ++++++++++++++++++++++++------
> > > include/linux/fsnotify_backend.h | 1 +
> > > 4 files changed, 90 insertions(+), 29 deletions(-)
> > >
> > > diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> > > index aff1ab3c32aa..1e4def21811e 100644
> > > --- a/fs/notify/fanotify/fanotify.c
> > > +++ b/fs/notify/fanotify/fanotify.c
> > > @@ -29,12 +29,6 @@ static unsigned int fanotify_hash_path(const struct path *path)
> > > hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
> > > }
> > >
> > > -static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> > > - __kernel_fsid_t *fsid2)
> > > -{
> > > - return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> > > -}
> > > -
> > > static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
> > > {
> > > return hash_32(fsid->val[0], FANOTIFY_EVENT_HASH_BITS) ^
> > > @@ -851,7 +845,8 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
> > > if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
> > > continue;
> > > fsid = FANOTIFY_MARK(mark)->fsid;
> > > - if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> > > + if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
> > > + WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
> > > continue;
> > > return fsid;
> > > }
> > > @@ -933,12 +928,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
> > > return 0;
> > > }
> > >
> > > - if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
> > > + if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS))
> > > fsid = fanotify_get_fsid(iter_info);
> > > - /* Racing with mark destruction or creation? */
> > > - if (!fsid.val[0] && !fsid.val[1])
> > > - return 0;
> > > - }
> > >
> > > event = fanotify_alloc_event(group, mask, data, data_type, dir,
> > > file_name, &fsid, match_mask);
> > > diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
> > > index 2847fa564298..9c97ae638ac5 100644
> > > --- a/fs/notify/fanotify/fanotify.h
> > > +++ b/fs/notify/fanotify/fanotify.h
> > > @@ -504,6 +504,12 @@ static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
> > > return &FANOTIFY_MARK(mark)->fsid;
> > > }
> > >
> > > +static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
> > > + __kernel_fsid_t *fsid2)
> > > +{
> > > + return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
> > > +}
> > > +
> > > static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
> > > {
> > > unsigned int mflags = 0;
> > > diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> > > index e3d836d4d156..1cc68ea56c17 100644
> > > --- a/fs/notify/fanotify/fanotify_user.c
> > > +++ b/fs/notify/fanotify/fanotify_user.c
> > > @@ -23,7 +23,7 @@
> > >
> > > #include <asm/ioctls.h>
> > >
> > > -#include "../../mount.h"
> > > +#include "../fsnotify.h"
> > > #include "../fdinfo.h"
> > > #include "fanotify.h"
> > >
> > > @@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
> > > return recalc;
> > > }
> > >
> > > +struct fan_fsid {
> > > + struct super_block *sb;
> > > + __kernel_fsid_t id;
> > > + bool weak;
> > > +};
> > > +
> > > +static int fanotify_check_mark_fsid(struct fsnotify_group *group,
> > > + struct fsnotify_mark *mark,
> > > + struct fan_fsid *fsid)
> > > +{
> > > + struct fsnotify_mark_connector *conn;
> > > + struct fsnotify_mark *old;
> > > + struct super_block *old_sb = NULL;
> > > +
> > > + *fanotify_mark_fsid(mark) = fsid->id;
> > > + mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> > > + if (fsid->weak)
> > > + mark->flags |= FSNOTIFY_MARK_FLAG_WEAK_FSID;
> > > +
> > > + /* First mark added will determine if group is single or multi fsid */
> > > + if (list_empty(&group->marks_list))
> > > + return 0;
> > > +
> > > + /* Find sb of an existing mark */
> > > + list_for_each_entry(old, &group->marks_list, g_list) {
> > > + conn = READ_ONCE(old->connector);
> > > + if (!conn)
> > > + continue;
> > > + old_sb = fsnotify_connector_sb(conn);
> > > + if (old_sb)
> > > + break;
> > > + }
> > > +
> > > + /* Only detached marks left? */
> > > + if (!old_sb)
> > > + return 0;
> > > +
> > > + /* Do not allow mixing of marks with weak and strong fsid */
> > > + if ((mark->flags ^ old->flags) & FSNOTIFY_MARK_FLAG_WEAK_FSID)
> > > + return -EXDEV;
> > > +
> > > + /* Allow mixing of marks with strong fsid from different fs */
> > > + if (!fsid->weak)
> > > + return 0;
> > > +
> > > + /* Do not allow mixing marks with weak fsid from different fs */
> > > + if (old_sb != fsid->sb)
> > > + return -EXDEV;
> > > +
> > > + /* Do not allow mixing marks from different btrfs sub-volumes */
> > > + if (!fanotify_fsid_equal(fanotify_mark_fsid(old),
> > > + fanotify_mark_fsid(mark)))
> > > + return -EXDEV;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
> > > fsnotify_connp_t *connp,
> > > unsigned int obj_type,
> > > unsigned int fan_flags,
> > > - __kernel_fsid_t *fsid)
> > > + struct fan_fsid *fsid)
> > > {
> > > struct ucounts *ucounts = group->fanotify_data.ucounts;
> > > struct fanotify_mark *fan_mark;
> > > @@ -1225,8 +1282,9 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
> > >
> > > /* Cache fsid of filesystem containing the marked object */
> > > if (fsid) {
> > > - fan_mark->fsid = *fsid;
> > > - mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
> > > + ret = fanotify_check_mark_fsid(group, mark, fsid);
> > > + if (ret)
> >
> > OOPS, missed fsnotify_put_mark(mark);
> > better add a goto target out_put_mark as this is the second case now.
>
> FYI, I pushed the fix to:
> https://github.com/amir73il/linux/commits/fanotify_fsid
>
> Let me know if you want me to post v2 for this.
>
Hi Jan,
Ping.
Reminder, I have LTP tests for testing fs with "weak" fsid [2].
After your LTP patches are merged, I will rebase them.
The way that I tested fs with "weak" fsid is that LTP tests
the ntfs-3g FUSE filesystem with .all_filesystems = 1.
With this work, the tests started to run on FUSE and skip
the test cases of sb/mount marks.
Thanks,
Amir.
[2] https://github.com/amir73il/tlp/commits/fanotify_fsid
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] fanotify: store fsid in mark instead of in connector
2023-11-18 18:30 ` [PATCH 1/2] fanotify: store fsid in mark instead of in connector Amir Goldstein
@ 2023-11-30 14:25 ` Jan Kara
2023-11-30 15:29 ` Amir Goldstein
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kara @ 2023-11-30 14:25 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Jan Kara, Christian Brauner, linux-fsdevel
On Sat 18-11-23 20:30:17, Amir Goldstein wrote:
> Some filesystems like fuse and nfs have zero or non-unique fsid.
> We would like to avoid reporting ambiguous fsid in events, so we need
> to avoid marking objects with same fsid and different sb.
>
> To make this easier to enforce, store the fsid in the marks of the group
> instead of in the shared conenctor.
>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Very nice! I like the result. Just a few nits below.
> +static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
> +{
> + return &FANOTIFY_MARK(mark)->fsid;
> +}
I guess, there's no big win in using this helper compared to using
FANOTIFY_MARK(mark)->fsid so I'd just drop this helper.
> @@ -530,6 +528,7 @@ struct fsnotify_mark {
> #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
> #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
> +#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
> unsigned int flags; /* flags [mark->lock] */
> };
So this flag is in fact private to fanotify notification framework. Either
we could just drop this flag and use
FANOTIFY_MARK(mark)->fsid[0] != 0 || FANOTIFY_MARK(mark)->fsid[1] != 0
instead or we could at least add a comment that this flags is in fact
private to fanotify?
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] fanotify: store fsid in mark instead of in connector
2023-11-30 14:25 ` Jan Kara
@ 2023-11-30 15:29 ` Amir Goldstein
2023-11-30 15:50 ` Jan Kara
0 siblings, 1 reply; 13+ messages in thread
From: Amir Goldstein @ 2023-11-30 15:29 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
On Thu, Nov 30, 2023 at 4:25 PM Jan Kara <jack@suse.cz> wrote:
>
> On Sat 18-11-23 20:30:17, Amir Goldstein wrote:
> > Some filesystems like fuse and nfs have zero or non-unique fsid.
> > We would like to avoid reporting ambiguous fsid in events, so we need
> > to avoid marking objects with same fsid and different sb.
> >
> > To make this easier to enforce, store the fsid in the marks of the group
> > instead of in the shared conenctor.
> >
> > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
>
> Very nice! I like the result. Just a few nits below.
>
> > +static inline __kernel_fsid_t *fanotify_mark_fsid(struct fsnotify_mark *mark)
> > +{
> > + return &FANOTIFY_MARK(mark)->fsid;
> > +}
>
> I guess, there's no big win in using this helper compared to using
> FANOTIFY_MARK(mark)->fsid so I'd just drop this helper.
ok.
>
> > @@ -530,6 +528,7 @@ struct fsnotify_mark {
> > #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
> > #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> > #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
> > +#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
> > unsigned int flags; /* flags [mark->lock] */
> > };
>
> So this flag is in fact private to fanotify notification framework. Either
> we could just drop this flag and use
>
> FANOTIFY_MARK(mark)->fsid[0] != 0 || FANOTIFY_MARK(mark)->fsid[1] != 0
Cannot.
Zero fsid is now a valid fsid in an inode mark (e.g. fuse).
The next patch also adds the flag FSNOTIFY_MARK_FLAG_WEAK_FSID
>
> instead or we could at least add a comment that this flags is in fact
> private to fanotify?
There is already a comment, because all the flags above are fanotify flags:
/* fanotify mark flags */
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
Thanks,
Amir.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-18 18:30 ` [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem Amir Goldstein
2023-11-20 7:42 ` Amir Goldstein
@ 2023-11-30 15:47 ` Jan Kara
2023-11-30 16:28 ` Amir Goldstein
1 sibling, 1 reply; 13+ messages in thread
From: Jan Kara @ 2023-11-30 15:47 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Jan Kara, Christian Brauner, linux-fsdevel
On Sat 18-11-23 20:30:18, Amir Goldstein wrote:
> So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
> on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
> non-uniform fsid (e.g. btrfs non-root subvol).
>
> When group is watching inodes all from the same filesystem (or subvol),
> allow adding inode marks with "weak" fsid, because there is no ambiguity
> regarding which filesystem reports the event.
>
> The first mark added to a group determines if this group is single or
> multi filesystem, depending on the fsid at the path of the added mark.
>
> If the first mark added has a "strong" fsid, marks with "weak" fsid
> cannot be added and vice versa.
>
> If the first mark added has a "weak" fsid, following marks must have
> the same "weak" fsid and the same sb as the first mark.
>
> Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Looks mostly good to me, some small comments below.
> @@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
> return recalc;
> }
>
> +struct fan_fsid {
> + struct super_block *sb;
> + __kernel_fsid_t id;
> + bool weak;
> +};
> +
> +static int fanotify_check_mark_fsid(struct fsnotify_group *group,
> + struct fsnotify_mark *mark,
> + struct fan_fsid *fsid)
I'd call this fanotify_set_mark_fsid() because that's what it does and
as part of that it also checks whether it can actually set the fsid...
> @@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
> return fd;
> }
>
> -static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> +static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
> + struct fan_fsid *fsid)
> {
> + unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> __kernel_fsid_t root_fsid;
> int err;
>
> /*
> * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
> */
> - err = vfs_get_fsid(dentry, fsid);
> + err = vfs_get_fsid(dentry, &fsid->id);
> if (err)
> return err;
>
> - if (!fsid->val[0] && !fsid->val[1])
> - return -ENODEV;
> + /* Allow weak fsid when marking inodes */
> + fsid->sb = dentry->d_sb;
> + fsid->weak = mark_type == FAN_MARK_INODE;
> + if (!fsid->id.val[0] && !fsid->id.val[1])
> + return fsid->weak ? 0 : -ENODEV;
>
> /*
> * Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
> @@ -1587,10 +1650,10 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> if (err)
> return err;
>
> - if (root_fsid.val[0] != fsid->val[0] ||
> - root_fsid.val[1] != fsid->val[1])
> - return -EXDEV;
> + if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
> + return fsid->weak ? 0 : -EXDEV;
>
> + fsid->weak = false;
> return 0;
> }
So the handling of 'weak' confuses me here as it combines determining
whether fsid is weak with determining whether we accept it or not. Maybe my
brain is just fried... Can we maybe simplify to something like:
fsid->sb = dentry->d_sb;
if (!fsid->id.val[0] && !fsid->id.val[1]) {
fsid->weak = true;
goto check;
}
... fetch root_fsid ...
if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
fsid->weak = true;
check:
/* Allow weak fsids only for inode marks... */
if (fsid->weak && mark_type != FAN_MARK_INODE)
return -EXDEV;
return 0;
This is how I understand the logic from your description but I'm not 100%
sure this is equivalent to your code above ;).
Thanks!
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] fanotify: store fsid in mark instead of in connector
2023-11-30 15:29 ` Amir Goldstein
@ 2023-11-30 15:50 ` Jan Kara
0 siblings, 0 replies; 13+ messages in thread
From: Jan Kara @ 2023-11-30 15:50 UTC (permalink / raw)
To: Amir Goldstein; +Cc: Jan Kara, Christian Brauner, linux-fsdevel
On Thu 30-11-23 17:29:02, Amir Goldstein wrote:
> On Thu, Nov 30, 2023 at 4:25 PM Jan Kara <jack@suse.cz> wrote:
> > > @@ -530,6 +528,7 @@ struct fsnotify_mark {
> > > #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
> > > #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> > > #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
> > > +#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
> > > unsigned int flags; /* flags [mark->lock] */
> > > };
> >
> > So this flag is in fact private to fanotify notification framework. Either
> > we could just drop this flag and use
> >
> > FANOTIFY_MARK(mark)->fsid[0] != 0 || FANOTIFY_MARK(mark)->fsid[1] != 0
>
> Cannot.
> Zero fsid is now a valid fsid in an inode mark (e.g. fuse).
> The next patch also adds the flag FSNOTIFY_MARK_FLAG_WEAK_FSID
Yeah, I've realized that once I've digested the second patch.
> > instead or we could at least add a comment that this flags is in fact
> > private to fanotify?
>
> There is already a comment, because all the flags above are fanotify flags:
>
> /* fanotify mark flags */
> #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
> #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
> #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
Right, I should have checked more that the diff context ;) Sorry for the
noise.
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem
2023-11-30 15:47 ` Jan Kara
@ 2023-11-30 16:28 ` Amir Goldstein
0 siblings, 0 replies; 13+ messages in thread
From: Amir Goldstein @ 2023-11-30 16:28 UTC (permalink / raw)
To: Jan Kara; +Cc: Christian Brauner, linux-fsdevel
On Thu, Nov 30, 2023 at 5:47 PM Jan Kara <jack@suse.cz> wrote:
>
> On Sat 18-11-23 20:30:18, Amir Goldstein wrote:
> > So far, fanotify returns -ENODEV or -EXDEV when trying to set a mark
> > on a filesystem with a "weak" fsid, namely, zero fsid (e.g. fuse), or
> > non-uniform fsid (e.g. btrfs non-root subvol).
> >
> > When group is watching inodes all from the same filesystem (or subvol),
> > allow adding inode marks with "weak" fsid, because there is no ambiguity
> > regarding which filesystem reports the event.
> >
> > The first mark added to a group determines if this group is single or
> > multi filesystem, depending on the fsid at the path of the added mark.
> >
> > If the first mark added has a "strong" fsid, marks with "weak" fsid
> > cannot be added and vice versa.
> >
> > If the first mark added has a "weak" fsid, following marks must have
> > the same "weak" fsid and the same sb as the first mark.
> >
> > Signed-off-by: Amir Goldstein <amir73il@gmail.com>
>
> Looks mostly good to me, some small comments below.
>
> > @@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
> > return recalc;
> > }
> >
> > +struct fan_fsid {
> > + struct super_block *sb;
> > + __kernel_fsid_t id;
> > + bool weak;
> > +};
> > +
> > +static int fanotify_check_mark_fsid(struct fsnotify_group *group,
> > + struct fsnotify_mark *mark,
> > + struct fan_fsid *fsid)
>
> I'd call this fanotify_set_mark_fsid() because that's what it does and
> as part of that it also checks whether it can actually set the fsid...
ok.
>
> > @@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
> > return fd;
> > }
> >
> > -static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> > +static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
> > + struct fan_fsid *fsid)
> > {
> > + unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
> > __kernel_fsid_t root_fsid;
> > int err;
> >
> > /*
> > * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
> > */
> > - err = vfs_get_fsid(dentry, fsid);
> > + err = vfs_get_fsid(dentry, &fsid->id);
> > if (err)
> > return err;
> >
> > - if (!fsid->val[0] && !fsid->val[1])
> > - return -ENODEV;
> > + /* Allow weak fsid when marking inodes */
> > + fsid->sb = dentry->d_sb;
> > + fsid->weak = mark_type == FAN_MARK_INODE;
> > + if (!fsid->id.val[0] && !fsid->id.val[1])
> > + return fsid->weak ? 0 : -ENODEV;
> >
> > /*
> > * Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
> > @@ -1587,10 +1650,10 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
> > if (err)
> > return err;
> >
> > - if (root_fsid.val[0] != fsid->val[0] ||
> > - root_fsid.val[1] != fsid->val[1])
> > - return -EXDEV;
> > + if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
> > + return fsid->weak ? 0 : -EXDEV;
> >
> > + fsid->weak = false;
> > return 0;
> > }
>
> So the handling of 'weak' confuses me here as it combines determining
> whether fsid is weak with determining whether we accept it or not. Maybe my
> brain is just fried... Can we maybe simplify to something like:
>
> fsid->sb = dentry->d_sb;
> if (!fsid->id.val[0] && !fsid->id.val[1]) {
> fsid->weak = true;
> goto check;
> }
>
> ... fetch root_fsid ...
>
> if (!fanotify_fsid_equal(&root_fsid, &fsid->id))
> fsid->weak = true;
>
> check:
> /* Allow weak fsids only for inode marks... */
> if (fsid->weak && mark_type != FAN_MARK_INODE)
> return -EXDEV;
> return 0;
>
> This is how I understand the logic from your description but I'm not 100%
> sure this is equivalent to your code above ;).
Almost.
The return value for FUSE + sb mark is ENODEV.
The code below should work and be readable.
Thanks,
Amir.
fsid->sb = dentry->d_sb;
if (!fsid->id.val[0] && !fsid->id.val[1]) {
err = -ENODEV;
goto weak;
}
... fetch root_fsid ...
if (!fanotify_fsid_equal(&root_fsid, &fsid->id)) {
err = -EXDEV;
goto weak;
}
return 0;
weak:
/* Allow weak fsid when marking inodes */
fsid->weak = true;
return (mark_type == FAN_MARK_INODE) ? 0 : err;
}
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2023-11-30 16:29 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-11-18 18:30 [PATCH 0/2] Support fanotify FAN_REPORT_FID on all filesystems Amir Goldstein
2023-11-18 18:30 ` [PATCH 1/2] fanotify: store fsid in mark instead of in connector Amir Goldstein
2023-11-30 14:25 ` Jan Kara
2023-11-30 15:29 ` Amir Goldstein
2023-11-30 15:50 ` Jan Kara
2023-11-18 18:30 ` [PATCH 2/2] fanotify: allow "weak" fsid when watching a single filesystem Amir Goldstein
2023-11-20 7:42 ` Amir Goldstein
2023-11-20 15:48 ` Christian Brauner
2023-11-20 16:20 ` Amir Goldstein
2023-11-21 13:33 ` Amir Goldstein
2023-11-30 12:42 ` Amir Goldstein
2023-11-30 15:47 ` Jan Kara
2023-11-30 16:28 ` Amir Goldstein
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).