mount-notify_test.c 11 KB

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