-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEq1nRK9aeMoq1VSgcnJ2qBz9kQNkFAmWb/aIACgkQnJ2qBz9k
 QNkV4AgAvsnFBOX3/jkZTA/2a+8AOJXx0uMdgupfMBzNkKXhhfWictaqWsiw7IzZ
 pypnHV1GL9dbFt7z+mEfApvXby5ZBY245Tn5Qx2rPUYZVAFDtLR4ELT4+9RBrrdJ
 DWxI8DgKetHeQLeP8neXVdU7ec/gP3J28lsYzqFAkJXA/ik13pWh16sLSZfYk7WX
 gmdSW+Ize0YIfPJdQYMy0sJLnzB4iOvv4CbwbZqAm7ZesXHuisSolfrnvLLPU5ju
 s7hf/dt46zakT6n2uYckhcv8Q7NnBpPnmgWRJI/utNtCAjLyZJJXbycUKFIGdBpm
 jn/oeDwiYb/tXIB39ISt4QNZodfv5Q==
 =rbo3
 -----END PGP SIGNATURE-----

Merge tag 'fsnotify_for_v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull fsnotify updates from Jan Kara:
 "fanotify changes allowing use of fanotify directory events even for
  filesystems such as FUSE which don't report proper fsid"

* tag 'fsnotify_for_v6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fanotify: allow "weak" fsid when watching a single filesystem
  fanotify: store fsid in mark instead of in connector
This commit is contained in:
Linus Torvalds 2024-01-10 08:38:33 -08:00
commit 32720aca90
5 changed files with 140 additions and 100 deletions

View File

@ -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) ^
@ -838,9 +832,8 @@ out:
}
/*
* 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,18 +842,11 @@ 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)
if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
continue;
if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID))
continue;
/* Pairs with smp_wmb() in fsnotify_add_mark_list() */
smp_rmb();
fsid = conn->fsid;
if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
fsid = FANOTIFY_MARK(mark)->fsid;
if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
continue;
return fsid;
}
@ -942,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);
@ -1068,7 +1050,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 = {

View File

@ -489,6 +489,22 @@ 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 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;

View File

@ -23,7 +23,7 @@
#include <asm/ioctls.h>
#include "../../mount.h"
#include "../fsnotify.h"
#include "../fdinfo.h"
#include "fanotify.h"
@ -1192,13 +1192,71 @@ 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_set_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(mark)->fsid = 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(old)->fsid,
&FANOTIFY_MARK(mark)->fsid))
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;
struct fsnotify_mark *mark;
int ret;
@ -1211,24 +1269,34 @@ 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);
if (ret) {
fsnotify_put_mark(mark);
goto out_dec_ucounts;
/* Cache fsid of filesystem containing the marked object */
if (fsid) {
ret = fanotify_set_mark_fsid(group, mark, fsid);
if (ret)
goto out_put_mark;
} else {
fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
}
ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0);
if (ret)
goto out_put_mark;
return mark;
out_put_mark:
fsnotify_put_mark(mark);
out_dec_ucounts:
if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS))
dec_ucount(ucounts, UCOUNT_FANOTIFY_MARKS);
@ -1279,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;
@ -1327,7 +1395,7 @@ out:
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);
@ -1335,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);
@ -1343,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);
@ -1554,20 +1622,25 @@ out_destroy_group:
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;
fsid->sb = dentry->d_sb;
if (!fsid->id.val[0] && !fsid->id.val[1]) {
err = -ENODEV;
goto weak;
}
/*
* Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
@ -1577,11 +1650,18 @@ 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)) {
err = -EXDEV;
goto weak;
}
fsid->weak = false;
return 0;
weak:
/* Allow weak fsid when marking inodes */
fsid->weak = true;
return (mark_type == FAN_MARK_INODE) ? 0 : err;
}
/* Check if filesystem can encode a unique fid */
@ -1665,7 +1745,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;
@ -1817,7 +1897,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;
@ -1935,7 +2015,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);

View File

@ -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 @@ out:
*/
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 @@ out_err:
*/
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 @@ err:
}
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;
}

View File

@ -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,8 @@ 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
#define FSNOTIFY_MARK_FLAG_WEAK_FSID 0x1000
unsigned int flags; /* flags [mark->lock] */
};
@ -763,11 +763,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 +774,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 */