@@ -731,6 +731,7 @@ linkat02 linkat02
listen01 listen01
listmount01 listmount01
+listmount02 listmount02
listxattr01 listxattr01
listxattr02 listxattr02
@@ -1 +1,2 @@
listmount01
+listmount02
new file mode 100644
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
+ */
+
+/*\
+ * [Description]
+ *
+ * This test verifies that listmount() is properly reading groups of mount IDs,
+ * checking that both oneshoot and iterator API for listmount() return the same
+ * array.
+ *
+ * [Algorithm]
+ *
+ * - move into a new unshared namespace
+ * - mount() our new root inside temporary folder
+ * - generate a full mounts tree inside root folder, doubling the number of
+ * mounted filesystems each bind mount
+ * - read the full list of mounted IDs using listmount(LSMT_ROOT, ..)
+ * - read the list of mounted IDs using groups of fixed size
+ * - compare the first mount list with the second mount list
+ */
+
+#include "listmount.h"
+#include "lapi/sched.h"
+
+#define MNTPOINT "mntpoint"
+#define BIND_MOUNTS 7
+#define GROUPS_SIZE 3
+#define LISTSIZE (1 << BIND_MOUNTS)
+
+static void run(void)
+{
+ ssize_t ret;
+ size_t id, tot_ids, count = 0;
+ uint64_t mount_ids[LISTSIZE];
+ uint64_t list[LISTSIZE];
+
+ for (int i = 0; i < BIND_MOUNTS; i++)
+ SAFE_MOUNT("/", "/", NULL, MS_BIND, NULL);
+
+ tst_res(TINFO, "Reading all %d mount IDs in once", LISTSIZE);
+
+ TST_EXP_POSITIVE(listmount(LSMT_ROOT, 0, mount_ids, LISTSIZE, 0));
+ if (TST_RET == -1)
+ goto end;
+
+ tot_ids = (size_t)TST_RET;
+
+ if (tot_ids != LISTSIZE) {
+ tst_res(TFAIL, "listmount() returned %lu but %d was expected",
+ tot_ids, LISTSIZE);
+ goto end;
+ }
+
+ tst_res(TINFO, "Reading groups of %d mount IDs", GROUPS_SIZE);
+
+ while (count < LISTSIZE) {
+ id = count ? list[count - 1] : 0;
+ ret = listmount(LSMT_ROOT, id, list + count, GROUPS_SIZE, 0);
+
+ tst_res(TDEBUG, "listmount(LSMT_ROOT, %lu, list + %lu, %d, 0)",
+ id, count, GROUPS_SIZE);
+
+ if (ret == -1) {
+ tst_res(TFAIL, "listmount() failed with %s", tst_strerrno(errno));
+ goto end;
+ }
+
+ count += ret;
+
+ if (TST_RET < GROUPS_SIZE)
+ break;
+ }
+
+ for (size_t i = 0; i < LISTSIZE; i++) {
+ if (mount_ids[i] != list[i]) {
+ tst_res(TFAIL, "Mount ID differs at %ld index", i);
+ goto end;
+ }
+ }
+
+ tst_res(TPASS, "All mount IDs have been correctly read");
+
+end:
+ for (int i = 0; i < BIND_MOUNTS; i++)
+ SAFE_UMOUNT("/");
+}
+
+static void setup(void)
+{
+ SAFE_UNSHARE(CLONE_NEWNS);
+ SAFE_CHROOT(MNTPOINT);
+
+ SAFE_MOUNT("", "/", NULL, MS_REC | MS_SHARED, NULL);
+}
+
+static struct tst_test test = {
+ .test_all = run,
+ .setup = setup,
+ .forks_child = 1,
+ .min_kver = "6.8",
+ .mount_device = 1,
+ .mntpoint = MNTPOINT,
+};