Skip to content

Commit 0f46d81

Browse files
Miklos Szeredibrauner
authored andcommitted
fanotify: notify on mount attach and detach
Add notifications for attaching and detaching mounts. The following new event masks are added: FAN_MNT_ATTACH - Mount was attached FAN_MNT_DETACH - Mount was detached If a mount is moved, then the event is reported with (FAN_MNT_ATTACH | FAN_MNT_DETACH). These events add an info record of type FAN_EVENT_INFO_TYPE_MNT containing these fields identifying the affected mounts: __u64 mnt_id - the ID of the mount (see statmount(2)) FAN_REPORT_MNT must be supplied to fanotify_init() to receive these events and no other type of event can be received with this report type. Marks are added with FAN_MARK_MNTNS, which records the mount namespace from an nsfs file (e.g. /proc/self/ns/mnt). Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Link: https://lore.kernel.org/r/20250129165803.72138-3-mszeredi@redhat.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent b944249 commit 0f46d81

8 files changed

Lines changed: 164 additions & 24 deletions

File tree

fs/mount.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,5 @@ static inline struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
181181
{
182182
return container_of(ns, struct mnt_namespace, ns);
183183
}
184+
185+
struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry);

fs/namespace.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,16 +2145,24 @@ struct mnt_namespace *get_sequential_mnt_ns(struct mnt_namespace *mntns, bool pr
21452145
}
21462146
}
21472147

2148+
struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry)
2149+
{
2150+
if (!is_mnt_ns_file(dentry))
2151+
return NULL;
2152+
2153+
return to_mnt_ns(get_proc_ns(dentry->d_inode));
2154+
}
2155+
21482156
static bool mnt_ns_loop(struct dentry *dentry)
21492157
{
21502158
/* Could bind mounting the mount namespace inode cause a
21512159
* mount namespace loop?
21522160
*/
2153-
struct mnt_namespace *mnt_ns;
2154-
if (!is_mnt_ns_file(dentry))
2161+
struct mnt_namespace *mnt_ns = mnt_ns_from_dentry(dentry);
2162+
2163+
if (!mnt_ns)
21552164
return false;
21562165

2157-
mnt_ns = to_mnt_ns(get_proc_ns(dentry->d_inode));
21582166
return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
21592167
}
21602168

fs/notify/fanotify/fanotify.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ static bool fanotify_should_merge(struct fanotify_event *old,
166166
case FANOTIFY_EVENT_TYPE_FS_ERROR:
167167
return fanotify_error_event_equal(FANOTIFY_EE(old),
168168
FANOTIFY_EE(new));
169+
case FANOTIFY_EVENT_TYPE_MNT:
170+
return false;
169171
default:
170172
WARN_ON_ONCE(1);
171173
}
@@ -312,7 +314,10 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
312314
pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
313315
__func__, iter_info->report_mask, event_mask, data, data_type);
314316

315-
if (!fid_mode) {
317+
if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) {
318+
if (data_type != FSNOTIFY_EVENT_MNT)
319+
return 0;
320+
} else if (!fid_mode) {
316321
/* Do we have path to open a file descriptor? */
317322
if (!path)
318323
return 0;
@@ -557,6 +562,20 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
557562
return &pevent->fae;
558563
}
559564

565+
static struct fanotify_event *fanotify_alloc_mnt_event(u64 mnt_id, gfp_t gfp)
566+
{
567+
struct fanotify_mnt_event *pevent;
568+
569+
pevent = kmem_cache_alloc(fanotify_mnt_event_cachep, gfp);
570+
if (!pevent)
571+
return NULL;
572+
573+
pevent->fae.type = FANOTIFY_EVENT_TYPE_MNT;
574+
pevent->mnt_id = mnt_id;
575+
576+
return &pevent->fae;
577+
}
578+
560579
static struct fanotify_event *fanotify_alloc_perm_event(const void *data,
561580
int data_type,
562581
gfp_t gfp)
@@ -731,6 +750,7 @@ static struct fanotify_event *fanotify_alloc_event(
731750
fid_mode);
732751
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
733752
const struct path *path = fsnotify_data_path(data, data_type);
753+
u64 mnt_id = fsnotify_data_mnt_id(data, data_type);
734754
struct mem_cgroup *old_memcg;
735755
struct dentry *moved = NULL;
736756
struct inode *child = NULL;
@@ -826,8 +846,12 @@ static struct fanotify_event *fanotify_alloc_event(
826846
moved, &hash, gfp);
827847
} else if (fid_mode) {
828848
event = fanotify_alloc_fid_event(id, fsid, &hash, gfp);
829-
} else {
849+
} else if (path) {
830850
event = fanotify_alloc_path_event(path, &hash, gfp);
851+
} else if (mnt_id) {
852+
event = fanotify_alloc_mnt_event(mnt_id, gfp);
853+
} else {
854+
WARN_ON_ONCE(1);
831855
}
832856

833857
if (!event)
@@ -927,7 +951,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
927951
BUILD_BUG_ON(FAN_RENAME != FS_RENAME);
928952
BUILD_BUG_ON(FAN_PRE_ACCESS != FS_PRE_ACCESS);
929953

930-
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 22);
954+
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 24);
931955

932956
mask = fanotify_group_event_mask(group, iter_info, &match_mask,
933957
mask, data, data_type, dir);
@@ -1028,6 +1052,11 @@ static void fanotify_free_error_event(struct fsnotify_group *group,
10281052
mempool_free(fee, &group->fanotify_data.error_events_pool);
10291053
}
10301054

1055+
static void fanotify_free_mnt_event(struct fanotify_event *event)
1056+
{
1057+
kmem_cache_free(fanotify_mnt_event_cachep, FANOTIFY_ME(event));
1058+
}
1059+
10311060
static void fanotify_free_event(struct fsnotify_group *group,
10321061
struct fsnotify_event *fsn_event)
10331062
{
@@ -1054,6 +1083,9 @@ static void fanotify_free_event(struct fsnotify_group *group,
10541083
case FANOTIFY_EVENT_TYPE_FS_ERROR:
10551084
fanotify_free_error_event(group, event);
10561085
break;
1086+
case FANOTIFY_EVENT_TYPE_MNT:
1087+
fanotify_free_mnt_event(event);
1088+
break;
10571089
default:
10581090
WARN_ON_ONCE(1);
10591091
}

fs/notify/fanotify/fanotify.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extern struct kmem_cache *fanotify_mark_cache;
99
extern struct kmem_cache *fanotify_fid_event_cachep;
1010
extern struct kmem_cache *fanotify_path_event_cachep;
1111
extern struct kmem_cache *fanotify_perm_event_cachep;
12+
extern struct kmem_cache *fanotify_mnt_event_cachep;
1213

1314
/* Possible states of the permission event */
1415
enum {
@@ -244,6 +245,7 @@ enum fanotify_event_type {
244245
FANOTIFY_EVENT_TYPE_PATH_PERM,
245246
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
246247
FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */
248+
FANOTIFY_EVENT_TYPE_MNT,
247249
__FANOTIFY_EVENT_TYPE_NUM
248250
};
249251

@@ -409,12 +411,23 @@ struct fanotify_path_event {
409411
struct path path;
410412
};
411413

414+
struct fanotify_mnt_event {
415+
struct fanotify_event fae;
416+
u64 mnt_id;
417+
};
418+
412419
static inline struct fanotify_path_event *
413420
FANOTIFY_PE(struct fanotify_event *event)
414421
{
415422
return container_of(event, struct fanotify_path_event, fae);
416423
}
417424

425+
static inline struct fanotify_mnt_event *
426+
FANOTIFY_ME(struct fanotify_event *event)
427+
{
428+
return container_of(event, struct fanotify_mnt_event, fae);
429+
}
430+
418431
/*
419432
* Structure for permission fanotify events. It gets allocated and freed in
420433
* fanotify_handle_event() since we wait there for user response. When the
@@ -466,6 +479,11 @@ static inline bool fanotify_is_error_event(u32 mask)
466479
return mask & FAN_FS_ERROR;
467480
}
468481

482+
static inline bool fanotify_is_mnt_event(u32 mask)
483+
{
484+
return mask & (FAN_MNT_ATTACH | FAN_MNT_DETACH);
485+
}
486+
469487
static inline const struct path *fanotify_event_path(struct fanotify_event *event)
470488
{
471489
if (event->type == FANOTIFY_EVENT_TYPE_PATH)

fs/notify/fanotify/fanotify_user.c

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ struct kmem_cache *fanotify_mark_cache __ro_after_init;
113113
struct kmem_cache *fanotify_fid_event_cachep __ro_after_init;
114114
struct kmem_cache *fanotify_path_event_cachep __ro_after_init;
115115
struct kmem_cache *fanotify_perm_event_cachep __ro_after_init;
116+
struct kmem_cache *fanotify_mnt_event_cachep __ro_after_init;
116117

117118
#define FANOTIFY_EVENT_ALIGN 4
118119
#define FANOTIFY_FID_INFO_HDR_LEN \
@@ -123,6 +124,8 @@ struct kmem_cache *fanotify_perm_event_cachep __ro_after_init;
123124
(sizeof(struct fanotify_event_info_error))
124125
#define FANOTIFY_RANGE_INFO_LEN \
125126
(sizeof(struct fanotify_event_info_range))
127+
#define FANOTIFY_MNT_INFO_LEN \
128+
(sizeof(struct fanotify_event_info_mnt))
126129

127130
static int fanotify_fid_info_len(int fh_len, int name_len)
128131
{
@@ -178,6 +181,8 @@ static size_t fanotify_event_len(unsigned int info_mode,
178181
fh_len = fanotify_event_object_fh_len(event);
179182
event_len += fanotify_fid_info_len(fh_len, dot_len);
180183
}
184+
if (fanotify_is_mnt_event(event->mask))
185+
event_len += FANOTIFY_MNT_INFO_LEN;
181186

182187
if (info_mode & FAN_REPORT_PIDFD)
183188
event_len += FANOTIFY_PIDFD_INFO_LEN;
@@ -405,6 +410,25 @@ static int process_access_response(struct fsnotify_group *group,
405410
return -ENOENT;
406411
}
407412

413+
static size_t copy_mnt_info_to_user(struct fanotify_event *event,
414+
char __user *buf, int count)
415+
{
416+
struct fanotify_event_info_mnt info = { };
417+
418+
info.hdr.info_type = FAN_EVENT_INFO_TYPE_MNT;
419+
info.hdr.len = FANOTIFY_MNT_INFO_LEN;
420+
421+
if (WARN_ON(count < info.hdr.len))
422+
return -EFAULT;
423+
424+
info.mnt_id = FANOTIFY_ME(event)->mnt_id;
425+
426+
if (copy_to_user(buf, &info, sizeof(info)))
427+
return -EFAULT;
428+
429+
return info.hdr.len;
430+
}
431+
408432
static size_t copy_error_info_to_user(struct fanotify_event *event,
409433
char __user *buf, int count)
410434
{
@@ -700,6 +724,15 @@ static int copy_info_records_to_user(struct fanotify_event *event,
700724
total_bytes += ret;
701725
}
702726

727+
if (fanotify_is_mnt_event(event->mask)) {
728+
ret = copy_mnt_info_to_user(event, buf, count);
729+
if (ret < 0)
730+
return ret;
731+
buf += ret;
732+
count -= ret;
733+
total_bytes += ret;
734+
}
735+
703736
return total_bytes;
704737
}
705738

@@ -1508,6 +1541,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15081541
if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
15091542
return -EINVAL;
15101543

1544+
/* Don't allow mixing mnt events with inode events for now */
1545+
if (flags & FAN_REPORT_MNT) {
1546+
if (class != FAN_CLASS_NOTIF)
1547+
return -EINVAL;
1548+
if (flags & (FANOTIFY_FID_BITS | FAN_REPORT_FD_ERROR))
1549+
return -EINVAL;
1550+
}
1551+
15111552
if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
15121553
return -EINVAL;
15131554

@@ -1767,7 +1808,6 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
17671808
int dfd, const char __user *pathname)
17681809
{
17691810
struct inode *inode = NULL;
1770-
struct vfsmount *mnt = NULL;
17711811
struct fsnotify_group *group;
17721812
struct path path;
17731813
struct fan_fsid __fsid, *fsid = NULL;
@@ -1776,7 +1816,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
17761816
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
17771817
unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS;
17781818
unsigned int obj_type, fid_mode;
1779-
void *obj;
1819+
void *obj = NULL;
17801820
u32 umask = 0;
17811821
int ret;
17821822

@@ -1800,6 +1840,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18001840
case FAN_MARK_FILESYSTEM:
18011841
obj_type = FSNOTIFY_OBJ_TYPE_SB;
18021842
break;
1843+
case FAN_MARK_MNTNS:
1844+
obj_type = FSNOTIFY_OBJ_TYPE_MNTNS;
1845+
break;
18031846
default:
18041847
return -EINVAL;
18051848
}
@@ -1847,6 +1890,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18471890
return -EINVAL;
18481891
group = fd_file(f)->private_data;
18491892

1893+
/* Only report mount events on mnt namespace */
1894+
if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) {
1895+
if (mask & ~FANOTIFY_MOUNT_EVENTS)
1896+
return -EINVAL;
1897+
if (mark_type != FAN_MARK_MNTNS)
1898+
return -EINVAL;
1899+
} else {
1900+
if (mask & FANOTIFY_MOUNT_EVENTS)
1901+
return -EINVAL;
1902+
if (mark_type == FAN_MARK_MNTNS)
1903+
return -EINVAL;
1904+
}
1905+
18501906
/*
18511907
* An unprivileged user is not allowed to setup mount nor filesystem
18521908
* marks. This also includes setting up such marks by a group that
@@ -1888,7 +1944,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18881944
* point.
18891945
*/
18901946
fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
1891-
if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) &&
1947+
if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_MOUNT_EVENTS|FANOTIFY_EVENT_FLAGS) &&
18921948
(!fid_mode || mark_type == FAN_MARK_MOUNT))
18931949
return -EINVAL;
18941950

@@ -1938,28 +1994,32 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
19381994
}
19391995

19401996
/* inode held in place by reference to path; group by fget on fd */
1941-
if (mark_type == FAN_MARK_INODE) {
1997+
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
19421998
inode = path.dentry->d_inode;
19431999
obj = inode;
1944-
} else {
1945-
mnt = path.mnt;
1946-
if (mark_type == FAN_MARK_MOUNT)
1947-
obj = mnt;
1948-
else
1949-
obj = mnt->mnt_sb;
2000+
} else if (obj_type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
2001+
obj = path.mnt;
2002+
} else if (obj_type == FSNOTIFY_OBJ_TYPE_SB) {
2003+
obj = path.mnt->mnt_sb;
2004+
} else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) {
2005+
obj = mnt_ns_from_dentry(path.dentry);
19502006
}
19512007

2008+
ret = -EINVAL;
2009+
if (!obj)
2010+
goto path_put_and_out;
2011+
19522012
/*
19532013
* If some other task has this inode open for write we should not add
19542014
* an ignore mask, unless that ignore mask is supposed to survive
19552015
* modification changes anyway.
19562016
*/
19572017
if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) &&
19582018
!(flags & FAN_MARK_IGNORED_SURV_MODIFY)) {
1959-
ret = mnt ? -EINVAL : -EISDIR;
2019+
ret = !inode ? -EINVAL : -EISDIR;
19602020
/* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */
19612021
if (ignore == FAN_MARK_IGNORE &&
1962-
(mnt || S_ISDIR(inode->i_mode)))
2022+
(!inode || S_ISDIR(inode->i_mode)))
19632023
goto path_put_and_out;
19642024

19652025
ret = 0;
@@ -1968,7 +2028,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
19682028
}
19692029

19702030
/* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */
1971-
if (mnt || !S_ISDIR(inode->i_mode)) {
2031+
if (!inode || !S_ISDIR(inode->i_mode)) {
19722032
mask &= ~FAN_EVENT_ON_CHILD;
19732033
umask = FAN_EVENT_ON_CHILD;
19742034
/*
@@ -2042,7 +2102,7 @@ static int __init fanotify_user_setup(void)
20422102
FANOTIFY_DEFAULT_MAX_USER_MARKS);
20432103

20442104
BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
2045-
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13);
2105+
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 14);
20462106
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
20472107

20482108
fanotify_mark_cache = KMEM_CACHE(fanotify_mark,
@@ -2055,6 +2115,7 @@ static int __init fanotify_user_setup(void)
20552115
fanotify_perm_event_cachep =
20562116
KMEM_CACHE(fanotify_perm_event, SLAB_PANIC);
20572117
}
2118+
fanotify_mnt_event_cachep = KMEM_CACHE(fanotify_mnt_event, SLAB_PANIC);
20582119

20592120
fanotify_max_queued_events = FANOTIFY_DEFAULT_MAX_EVENTS;
20602121
init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS] =

fs/notify/fdinfo.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
121121

122122
seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n",
123123
sb->s_dev, mflags, mark->mask, mark->ignore_mask);
124+
} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_MNTNS) {
125+
struct mnt_namespace *mnt_ns = fsnotify_conn_mntns(mark->connector);
126+
127+
seq_printf(m, "fanotify mnt_ns:%u mflags:%x mask:%x ignored_mask:%x\n",
128+
mnt_ns->ns.inum, mflags, mark->mask, mark->ignore_mask);
124129
}
125130
}
126131

0 commit comments

Comments
 (0)