Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
29271 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
// Copyright (c) 2025 Miklos Szeredi <[email protected]>
3
4
#define _GNU_SOURCE
5
6
// Needed for linux/fanotify.h
7
typedef struct {
8
int val[2];
9
} __kernel_fsid_t;
10
#define __kernel_fsid_t __kernel_fsid_t
11
12
#include <fcntl.h>
13
#include <sched.h>
14
#include <stdio.h>
15
#include <string.h>
16
#include <sys/stat.h>
17
#include <sys/mount.h>
18
#include <unistd.h>
19
#include <sys/syscall.h>
20
#include <sys/fanotify.h>
21
22
#include "../../kselftest_harness.h"
23
#include "../statmount/statmount.h"
24
#include "../utils.h"
25
26
static const char root_mntpoint_templ[] = "/tmp/mount-notify_test_root.XXXXXX";
27
28
static const int mark_cmds[] = {
29
FAN_MARK_ADD,
30
FAN_MARK_REMOVE,
31
FAN_MARK_FLUSH
32
};
33
34
#define NUM_FAN_FDS ARRAY_SIZE(mark_cmds)
35
36
FIXTURE(fanotify) {
37
int fan_fd[NUM_FAN_FDS];
38
char buf[256];
39
unsigned int rem;
40
void *next;
41
char root_mntpoint[sizeof(root_mntpoint_templ)];
42
int orig_root;
43
int ns_fd;
44
uint64_t root_id;
45
};
46
47
FIXTURE_SETUP(fanotify)
48
{
49
int i, ret;
50
51
ASSERT_EQ(unshare(CLONE_NEWNS), 0);
52
53
self->ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
54
ASSERT_GE(self->ns_fd, 0);
55
56
ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0);
57
58
strcpy(self->root_mntpoint, root_mntpoint_templ);
59
ASSERT_NE(mkdtemp(self->root_mntpoint), NULL);
60
61
self->orig_root = open("/", O_PATH | O_CLOEXEC);
62
ASSERT_GE(self->orig_root, 0);
63
64
ASSERT_EQ(mount("tmpfs", self->root_mntpoint, "tmpfs", 0, NULL), 0);
65
66
ASSERT_EQ(chroot(self->root_mntpoint), 0);
67
68
ASSERT_EQ(chdir("/"), 0);
69
70
ASSERT_EQ(mkdir("a", 0700), 0);
71
72
ASSERT_EQ(mkdir("b", 0700), 0);
73
74
self->root_id = get_unique_mnt_id("/");
75
ASSERT_NE(self->root_id, 0);
76
77
for (i = 0; i < NUM_FAN_FDS; i++) {
78
self->fan_fd[i] = fanotify_init(FAN_REPORT_MNT | FAN_NONBLOCK,
79
0);
80
ASSERT_GE(self->fan_fd[i], 0);
81
ret = fanotify_mark(self->fan_fd[i], FAN_MARK_ADD |
82
FAN_MARK_MNTNS,
83
FAN_MNT_ATTACH | FAN_MNT_DETACH,
84
self->ns_fd, NULL);
85
ASSERT_EQ(ret, 0);
86
// On fd[0] we do an extra ADD that changes nothing.
87
// On fd[1]/fd[2] we REMOVE/FLUSH which removes the mark.
88
ret = fanotify_mark(self->fan_fd[i], mark_cmds[i] |
89
FAN_MARK_MNTNS,
90
FAN_MNT_ATTACH | FAN_MNT_DETACH,
91
self->ns_fd, NULL);
92
ASSERT_EQ(ret, 0);
93
}
94
95
self->rem = 0;
96
}
97
98
FIXTURE_TEARDOWN(fanotify)
99
{
100
int i;
101
102
ASSERT_EQ(self->rem, 0);
103
for (i = 0; i < NUM_FAN_FDS; i++)
104
close(self->fan_fd[i]);
105
106
ASSERT_EQ(fchdir(self->orig_root), 0);
107
108
ASSERT_EQ(chroot("."), 0);
109
110
EXPECT_EQ(umount2(self->root_mntpoint, MNT_DETACH), 0);
111
EXPECT_EQ(chdir(self->root_mntpoint), 0);
112
EXPECT_EQ(chdir("/"), 0);
113
EXPECT_EQ(rmdir(self->root_mntpoint), 0);
114
}
115
116
static uint64_t expect_notify(struct __test_metadata *const _metadata,
117
FIXTURE_DATA(fanotify) *self,
118
uint64_t *mask)
119
{
120
struct fanotify_event_metadata *meta;
121
struct fanotify_event_info_mnt *mnt;
122
unsigned int thislen;
123
124
if (!self->rem) {
125
ssize_t len;
126
int i;
127
128
for (i = NUM_FAN_FDS - 1; i >= 0; i--) {
129
len = read(self->fan_fd[i], self->buf,
130
sizeof(self->buf));
131
if (i > 0) {
132
// Groups 1,2 should get EAGAIN
133
ASSERT_EQ(len, -1);
134
ASSERT_EQ(errno, EAGAIN);
135
} else {
136
// Group 0 should get events
137
ASSERT_GT(len, 0);
138
}
139
}
140
141
self->rem = len;
142
self->next = (void *) self->buf;
143
}
144
145
meta = self->next;
146
ASSERT_TRUE(FAN_EVENT_OK(meta, self->rem));
147
148
thislen = meta->event_len;
149
self->rem -= thislen;
150
self->next += thislen;
151
152
*mask = meta->mask;
153
thislen -= sizeof(*meta);
154
155
mnt = ((void *) meta) + meta->event_len - thislen;
156
157
ASSERT_EQ(thislen, sizeof(*mnt));
158
159
return mnt->mnt_id;
160
}
161
162
static void expect_notify_n(struct __test_metadata *const _metadata,
163
FIXTURE_DATA(fanotify) *self,
164
unsigned int n, uint64_t mask[], uint64_t mnts[])
165
{
166
unsigned int i;
167
168
for (i = 0; i < n; i++)
169
mnts[i] = expect_notify(_metadata, self, &mask[i]);
170
}
171
172
static uint64_t expect_notify_mask(struct __test_metadata *const _metadata,
173
FIXTURE_DATA(fanotify) *self,
174
uint64_t expect_mask)
175
{
176
uint64_t mntid, mask;
177
178
mntid = expect_notify(_metadata, self, &mask);
179
ASSERT_EQ(expect_mask, mask);
180
181
return mntid;
182
}
183
184
185
static void expect_notify_mask_n(struct __test_metadata *const _metadata,
186
FIXTURE_DATA(fanotify) *self,
187
uint64_t mask, unsigned int n, uint64_t mnts[])
188
{
189
unsigned int i;
190
191
for (i = 0; i < n; i++)
192
mnts[i] = expect_notify_mask(_metadata, self, mask);
193
}
194
195
static void verify_mount_ids(struct __test_metadata *const _metadata,
196
const uint64_t list1[], const uint64_t list2[],
197
size_t num)
198
{
199
unsigned int i, j;
200
201
// Check that neither list has any duplicates
202
for (i = 0; i < num; i++) {
203
for (j = 0; j < num; j++) {
204
if (i != j) {
205
ASSERT_NE(list1[i], list1[j]);
206
ASSERT_NE(list2[i], list2[j]);
207
}
208
}
209
}
210
// Check that all list1 memebers can be found in list2. Together with
211
// the above it means that the list1 and list2 represent the same sets.
212
for (i = 0; i < num; i++) {
213
for (j = 0; j < num; j++) {
214
if (list1[i] == list2[j])
215
break;
216
}
217
ASSERT_NE(j, num);
218
}
219
}
220
221
static void check_mounted(struct __test_metadata *const _metadata,
222
const uint64_t mnts[], size_t num)
223
{
224
ssize_t ret;
225
uint64_t *list;
226
227
list = malloc((num + 1) * sizeof(list[0]));
228
ASSERT_NE(list, NULL);
229
230
ret = listmount(LSMT_ROOT, 0, 0, list, num + 1, 0);
231
ASSERT_EQ(ret, num);
232
233
verify_mount_ids(_metadata, mnts, list, num);
234
235
free(list);
236
}
237
238
static void setup_mount_tree(struct __test_metadata *const _metadata,
239
int log2_num)
240
{
241
int ret, i;
242
243
ret = mount("", "/", NULL, MS_SHARED, NULL);
244
ASSERT_EQ(ret, 0);
245
246
for (i = 0; i < log2_num; i++) {
247
ret = mount("/", "/", NULL, MS_BIND, NULL);
248
ASSERT_EQ(ret, 0);
249
}
250
}
251
252
TEST_F(fanotify, bind)
253
{
254
int ret;
255
uint64_t mnts[2] = { self->root_id };
256
257
ret = mount("/", "/", NULL, MS_BIND, NULL);
258
ASSERT_EQ(ret, 0);
259
260
mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
261
ASSERT_NE(mnts[0], mnts[1]);
262
263
check_mounted(_metadata, mnts, 2);
264
265
// Cleanup
266
uint64_t detach_id;
267
ret = umount("/");
268
ASSERT_EQ(ret, 0);
269
270
detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
271
ASSERT_EQ(detach_id, mnts[1]);
272
273
check_mounted(_metadata, mnts, 1);
274
}
275
276
TEST_F(fanotify, move)
277
{
278
int ret;
279
uint64_t mnts[2] = { self->root_id };
280
uint64_t move_id;
281
282
ret = mount("/", "/a", NULL, MS_BIND, NULL);
283
ASSERT_EQ(ret, 0);
284
285
mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
286
ASSERT_NE(mnts[0], mnts[1]);
287
288
check_mounted(_metadata, mnts, 2);
289
290
ret = move_mount(AT_FDCWD, "/a", AT_FDCWD, "/b", 0);
291
ASSERT_EQ(ret, 0);
292
293
move_id = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH);
294
ASSERT_EQ(move_id, mnts[1]);
295
296
// Cleanup
297
ret = umount("/b");
298
ASSERT_EQ(ret, 0);
299
300
check_mounted(_metadata, mnts, 1);
301
}
302
303
TEST_F(fanotify, propagate)
304
{
305
const unsigned int log2_num = 4;
306
const unsigned int num = (1 << log2_num);
307
uint64_t mnts[num];
308
309
setup_mount_tree(_metadata, log2_num);
310
311
expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, num - 1, mnts + 1);
312
313
mnts[0] = self->root_id;
314
check_mounted(_metadata, mnts, num);
315
316
// Cleanup
317
int ret;
318
uint64_t mnts2[num];
319
ret = umount2("/", MNT_DETACH);
320
ASSERT_EQ(ret, 0);
321
322
ret = mount("", "/", NULL, MS_PRIVATE, NULL);
323
ASSERT_EQ(ret, 0);
324
325
mnts2[0] = self->root_id;
326
expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, num - 1, mnts2 + 1);
327
verify_mount_ids(_metadata, mnts, mnts2, num);
328
329
check_mounted(_metadata, mnts, 1);
330
}
331
332
TEST_F(fanotify, fsmount)
333
{
334
int ret, fs, mnt;
335
uint64_t mnts[2] = { self->root_id };
336
337
fs = fsopen("tmpfs", 0);
338
ASSERT_GE(fs, 0);
339
340
ret = fsconfig(fs, FSCONFIG_CMD_CREATE, 0, 0, 0);
341
ASSERT_EQ(ret, 0);
342
343
mnt = fsmount(fs, 0, 0);
344
ASSERT_GE(mnt, 0);
345
346
close(fs);
347
348
ret = move_mount(mnt, "", AT_FDCWD, "/a", MOVE_MOUNT_F_EMPTY_PATH);
349
ASSERT_EQ(ret, 0);
350
351
close(mnt);
352
353
mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
354
ASSERT_NE(mnts[0], mnts[1]);
355
356
check_mounted(_metadata, mnts, 2);
357
358
// Cleanup
359
uint64_t detach_id;
360
ret = umount("/a");
361
ASSERT_EQ(ret, 0);
362
363
detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
364
ASSERT_EQ(detach_id, mnts[1]);
365
366
check_mounted(_metadata, mnts, 1);
367
}
368
369
TEST_F(fanotify, reparent)
370
{
371
uint64_t mnts[6] = { self->root_id };
372
uint64_t dmnts[3];
373
uint64_t masks[3];
374
unsigned int i;
375
int ret;
376
377
// Create setup with a[1] -> b[2] propagation
378
ret = mount("/", "/a", NULL, MS_BIND, NULL);
379
ASSERT_EQ(ret, 0);
380
381
ret = mount("", "/a", NULL, MS_SHARED, NULL);
382
ASSERT_EQ(ret, 0);
383
384
ret = mount("/a", "/b", NULL, MS_BIND, NULL);
385
ASSERT_EQ(ret, 0);
386
387
ret = mount("", "/b", NULL, MS_SLAVE, NULL);
388
ASSERT_EQ(ret, 0);
389
390
expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
391
392
check_mounted(_metadata, mnts, 3);
393
394
// Mount on a[3], which is propagated to b[4]
395
ret = mount("/", "/a", NULL, MS_BIND, NULL);
396
ASSERT_EQ(ret, 0);
397
398
expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 3);
399
400
check_mounted(_metadata, mnts, 5);
401
402
// Mount on b[5], not propagated
403
ret = mount("/", "/b", NULL, MS_BIND, NULL);
404
ASSERT_EQ(ret, 0);
405
406
mnts[5] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
407
408
check_mounted(_metadata, mnts, 6);
409
410
// Umount a[3], which is propagated to b[4], but not b[5]
411
// This will result in b[5] "falling" on b[2]
412
ret = umount("/a");
413
ASSERT_EQ(ret, 0);
414
415
expect_notify_n(_metadata, self, 3, masks, dmnts);
416
verify_mount_ids(_metadata, mnts + 3, dmnts, 3);
417
418
for (i = 0; i < 3; i++) {
419
if (dmnts[i] == mnts[5]) {
420
ASSERT_EQ(masks[i], FAN_MNT_ATTACH | FAN_MNT_DETACH);
421
} else {
422
ASSERT_EQ(masks[i], FAN_MNT_DETACH);
423
}
424
}
425
426
mnts[3] = mnts[5];
427
check_mounted(_metadata, mnts, 4);
428
429
// Cleanup
430
ret = umount("/b");
431
ASSERT_EQ(ret, 0);
432
433
ret = umount("/a");
434
ASSERT_EQ(ret, 0);
435
436
ret = umount("/b");
437
ASSERT_EQ(ret, 0);
438
439
expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 3, dmnts);
440
verify_mount_ids(_metadata, mnts + 1, dmnts, 3);
441
442
check_mounted(_metadata, mnts, 1);
443
}
444
445
TEST_F(fanotify, rmdir)
446
{
447
uint64_t mnts[3] = { self->root_id };
448
int ret;
449
450
ret = mount("/", "/a", NULL, MS_BIND, NULL);
451
ASSERT_EQ(ret, 0);
452
453
ret = mount("/", "/a/b", NULL, MS_BIND, NULL);
454
ASSERT_EQ(ret, 0);
455
456
expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
457
458
check_mounted(_metadata, mnts, 3);
459
460
ret = chdir("/a");
461
ASSERT_EQ(ret, 0);
462
463
ret = fork();
464
ASSERT_GE(ret, 0);
465
466
if (ret == 0) {
467
chdir("/");
468
unshare(CLONE_NEWNS);
469
mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
470
umount2("/a", MNT_DETACH);
471
// This triggers a detach in the other namespace
472
rmdir("/a");
473
exit(0);
474
}
475
wait(NULL);
476
477
expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 2, mnts + 1);
478
check_mounted(_metadata, mnts, 1);
479
480
// Cleanup
481
ret = chdir("/");
482
ASSERT_EQ(ret, 0);
483
}
484
485
TEST_F(fanotify, pivot_root)
486
{
487
uint64_t mnts[3] = { self->root_id };
488
uint64_t mnts2[3];
489
int ret;
490
491
ret = mount("tmpfs", "/a", "tmpfs", 0, NULL);
492
ASSERT_EQ(ret, 0);
493
494
mnts[2] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
495
496
ret = mkdir("/a/new", 0700);
497
ASSERT_EQ(ret, 0);
498
499
ret = mkdir("/a/old", 0700);
500
ASSERT_EQ(ret, 0);
501
502
ret = mount("/a", "/a/new", NULL, MS_BIND, NULL);
503
ASSERT_EQ(ret, 0);
504
505
mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
506
check_mounted(_metadata, mnts, 3);
507
508
ret = syscall(SYS_pivot_root, "/a/new", "/a/new/old");
509
ASSERT_EQ(ret, 0);
510
511
expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH, 2, mnts2);
512
verify_mount_ids(_metadata, mnts, mnts2, 2);
513
check_mounted(_metadata, mnts, 3);
514
515
// Cleanup
516
ret = syscall(SYS_pivot_root, "/old", "/old/a/new");
517
ASSERT_EQ(ret, 0);
518
519
ret = umount("/a/new");
520
ASSERT_EQ(ret, 0);
521
522
ret = umount("/a");
523
ASSERT_EQ(ret, 0);
524
525
check_mounted(_metadata, mnts, 1);
526
}
527
528
TEST_HARNESS_MAIN
529
530