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