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