Skip to content

Commit 30ad193

Browse files
amir73iljankara
authored andcommitted
fanotify: allow "weak" fsid when watching a single filesystem
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> Signed-off-by: Jan Kara <jack@suse.cz> Message-Id: <20231130165619.3386452-3-amir73il@gmail.com>
1 parent 7232522 commit 30ad193

4 files changed

Lines changed: 101 additions & 33 deletions

File tree

fs/notify/fanotify/fanotify.c

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ static unsigned int fanotify_hash_path(const struct path *path)
2929
hash_ptr(path->mnt, FANOTIFY_EVENT_HASH_BITS);
3030
}
3131

32-
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
33-
__kernel_fsid_t *fsid2)
34-
{
35-
return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
36-
}
37-
3832
static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
3933
{
4034
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)
851845
if (!(mark->flags & FSNOTIFY_MARK_FLAG_HAS_FSID))
852846
continue;
853847
fsid = FANOTIFY_MARK(mark)->fsid;
854-
if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
848+
if (!(mark->flags & FSNOTIFY_MARK_FLAG_WEAK_FSID) &&
849+
WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
855850
continue;
856851
return fsid;
857852
}
@@ -933,12 +928,8 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
933928
return 0;
934929
}
935930

936-
if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) {
931+
if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS))
937932
fsid = fanotify_get_fsid(iter_info);
938-
/* Racing with mark destruction or creation? */
939-
if (!fsid.val[0] && !fsid.val[1])
940-
return 0;
941-
}
942933

943934
event = fanotify_alloc_event(group, mask, data, data_type, dir,
944935
file_name, &fsid, match_mask);

fs/notify/fanotify/fanotify.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,12 @@ static inline struct fanotify_mark *FANOTIFY_MARK(struct fsnotify_mark *mark)
499499
return container_of(mark, struct fanotify_mark, fsn_mark);
500500
}
501501

502+
static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1,
503+
__kernel_fsid_t *fsid2)
504+
{
505+
return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1];
506+
}
507+
502508
static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
503509
{
504510
unsigned int mflags = 0;

fs/notify/fanotify/fanotify_user.c

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
#include <asm/ioctls.h>
2525

26-
#include "../../mount.h"
26+
#include "../fsnotify.h"
2727
#include "../fdinfo.h"
2828
#include "fanotify.h"
2929

@@ -1192,11 +1192,68 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
11921192
return recalc;
11931193
}
11941194

1195+
struct fan_fsid {
1196+
struct super_block *sb;
1197+
__kernel_fsid_t id;
1198+
bool weak;
1199+
};
1200+
1201+
static int fanotify_set_mark_fsid(struct fsnotify_group *group,
1202+
struct fsnotify_mark *mark,
1203+
struct fan_fsid *fsid)
1204+
{
1205+
struct fsnotify_mark_connector *conn;
1206+
struct fsnotify_mark *old;
1207+
struct super_block *old_sb = NULL;
1208+
1209+
FANOTIFY_MARK(mark)->fsid = fsid->id;
1210+
mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
1211+
if (fsid->weak)
1212+
mark->flags |= FSNOTIFY_MARK_FLAG_WEAK_FSID;
1213+
1214+
/* First mark added will determine if group is single or multi fsid */
1215+
if (list_empty(&group->marks_list))
1216+
return 0;
1217+
1218+
/* Find sb of an existing mark */
1219+
list_for_each_entry(old, &group->marks_list, g_list) {
1220+
conn = READ_ONCE(old->connector);
1221+
if (!conn)
1222+
continue;
1223+
old_sb = fsnotify_connector_sb(conn);
1224+
if (old_sb)
1225+
break;
1226+
}
1227+
1228+
/* Only detached marks left? */
1229+
if (!old_sb)
1230+
return 0;
1231+
1232+
/* Do not allow mixing of marks with weak and strong fsid */
1233+
if ((mark->flags ^ old->flags) & FSNOTIFY_MARK_FLAG_WEAK_FSID)
1234+
return -EXDEV;
1235+
1236+
/* Allow mixing of marks with strong fsid from different fs */
1237+
if (!fsid->weak)
1238+
return 0;
1239+
1240+
/* Do not allow mixing marks with weak fsid from different fs */
1241+
if (old_sb != fsid->sb)
1242+
return -EXDEV;
1243+
1244+
/* Do not allow mixing marks from different btrfs sub-volumes */
1245+
if (!fanotify_fsid_equal(&FANOTIFY_MARK(old)->fsid,
1246+
&FANOTIFY_MARK(mark)->fsid))
1247+
return -EXDEV;
1248+
1249+
return 0;
1250+
}
1251+
11951252
static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
11961253
fsnotify_connp_t *connp,
11971254
unsigned int obj_type,
11981255
unsigned int fan_flags,
1199-
__kernel_fsid_t *fsid)
1256+
struct fan_fsid *fsid)
12001257
{
12011258
struct ucounts *ucounts = group->fanotify_data.ucounts;
12021259
struct fanotify_mark *fan_mark;
@@ -1225,20 +1282,21 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
12251282

12261283
/* Cache fsid of filesystem containing the marked object */
12271284
if (fsid) {
1228-
fan_mark->fsid = *fsid;
1229-
mark->flags |= FSNOTIFY_MARK_FLAG_HAS_FSID;
1285+
ret = fanotify_set_mark_fsid(group, mark, fsid);
1286+
if (ret)
1287+
goto out_put_mark;
12301288
} else {
12311289
fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
12321290
}
12331291

12341292
ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0);
1235-
if (ret) {
1236-
fsnotify_put_mark(mark);
1237-
goto out_dec_ucounts;
1238-
}
1293+
if (ret)
1294+
goto out_put_mark;
12391295

12401296
return mark;
12411297

1298+
out_put_mark:
1299+
fsnotify_put_mark(mark);
12421300
out_dec_ucounts:
12431301
if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS))
12441302
dec_ucount(ucounts, UCOUNT_FANOTIFY_MARKS);
@@ -1289,7 +1347,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
12891347
static int fanotify_add_mark(struct fsnotify_group *group,
12901348
fsnotify_connp_t *connp, unsigned int obj_type,
12911349
__u32 mask, unsigned int fan_flags,
1292-
__kernel_fsid_t *fsid)
1350+
struct fan_fsid *fsid)
12931351
{
12941352
struct fsnotify_mark *fsn_mark;
12951353
bool recalc;
@@ -1337,23 +1395,23 @@ static int fanotify_add_mark(struct fsnotify_group *group,
13371395

13381396
static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
13391397
struct vfsmount *mnt, __u32 mask,
1340-
unsigned int flags, __kernel_fsid_t *fsid)
1398+
unsigned int flags, struct fan_fsid *fsid)
13411399
{
13421400
return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
13431401
FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
13441402
}
13451403

13461404
static int fanotify_add_sb_mark(struct fsnotify_group *group,
13471405
struct super_block *sb, __u32 mask,
1348-
unsigned int flags, __kernel_fsid_t *fsid)
1406+
unsigned int flags, struct fan_fsid *fsid)
13491407
{
13501408
return fanotify_add_mark(group, &sb->s_fsnotify_marks,
13511409
FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
13521410
}
13531411

13541412
static int fanotify_add_inode_mark(struct fsnotify_group *group,
13551413
struct inode *inode, __u32 mask,
1356-
unsigned int flags, __kernel_fsid_t *fsid)
1414+
unsigned int flags, struct fan_fsid *fsid)
13571415
{
13581416
pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
13591417

@@ -1564,20 +1622,25 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15641622
return fd;
15651623
}
15661624

1567-
static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
1625+
static int fanotify_test_fsid(struct dentry *dentry, unsigned int flags,
1626+
struct fan_fsid *fsid)
15681627
{
1628+
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
15691629
__kernel_fsid_t root_fsid;
15701630
int err;
15711631

15721632
/*
15731633
* Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
15741634
*/
1575-
err = vfs_get_fsid(dentry, fsid);
1635+
err = vfs_get_fsid(dentry, &fsid->id);
15761636
if (err)
15771637
return err;
15781638

1579-
if (!fsid->val[0] && !fsid->val[1])
1580-
return -ENODEV;
1639+
fsid->sb = dentry->d_sb;
1640+
if (!fsid->id.val[0] && !fsid->id.val[1]) {
1641+
err = -ENODEV;
1642+
goto weak;
1643+
}
15811644

15821645
/*
15831646
* Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
@@ -1587,11 +1650,18 @@ static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
15871650
if (err)
15881651
return err;
15891652

1590-
if (root_fsid.val[0] != fsid->val[0] ||
1591-
root_fsid.val[1] != fsid->val[1])
1592-
return -EXDEV;
1653+
if (!fanotify_fsid_equal(&root_fsid, &fsid->id)) {
1654+
err = -EXDEV;
1655+
goto weak;
1656+
}
15931657

1658+
fsid->weak = false;
15941659
return 0;
1660+
1661+
weak:
1662+
/* Allow weak fsid when marking inodes */
1663+
fsid->weak = true;
1664+
return (mark_type == FAN_MARK_INODE) ? 0 : err;
15951665
}
15961666

15971667
/* Check if filesystem can encode a unique fid */
@@ -1675,7 +1745,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
16751745
struct fsnotify_group *group;
16761746
struct fd f;
16771747
struct path path;
1678-
__kernel_fsid_t __fsid, *fsid = NULL;
1748+
struct fan_fsid __fsid, *fsid = NULL;
16791749
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
16801750
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
16811751
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
@@ -1827,7 +1897,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18271897
}
18281898

18291899
if (fid_mode) {
1830-
ret = fanotify_test_fsid(path.dentry, &__fsid);
1900+
ret = fanotify_test_fsid(path.dentry, flags, &__fsid);
18311901
if (ret)
18321902
goto path_put_and_out;
18331903

include/linux/fsnotify_backend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ struct fsnotify_mark {
529529
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
530530
#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
531531
#define FSNOTIFY_MARK_FLAG_HAS_FSID 0x0800
532+
#define FSNOTIFY_MARK_FLAG_WEAK_FSID 0x1000
532533
unsigned int flags; /* flags [mark->lock] */
533534
};
534535

0 commit comments

Comments
 (0)