--- /dev/null
+/*
+ * Copyright (C) 2024 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdlib.h>
+
+#include "c.h"
+#include "strutils.h"
+#include "timeutils.h"
+#include "pathnames.h"
+
+#include "libmount.h"
+
+#include "mount-api-utils.h" /* fallback for old linux/mount.h */
+
+static void __iter_table( struct libmnt_table *tb,
+ struct libmnt_iter *itr, int output, int reverse)
+{
+ struct libmnt_fs *fs = NULL;
+ int rc;
+
+ mnt_reset_iter(itr, reverse ? MNT_ITER_BACKWARD : MNT_ITER_FORWARD);
+ while ((rc = mnt_table_next_fs(tb, itr, &fs)) == 0) {
+ const char *tgt = mnt_fs_get_target(fs);
+ const char *type = mnt_fs_get_fstype(fs);
+
+ if (output)
+ printf (" %15s %s\n", type, tgt);
+ }
+ if (rc < 0)
+ warn("cannot iterate on filesystems");
+}
+
+#define fetch_data(t, i) __iter_table(t, i, 0, 0)
+#define print_table(t, i) __iter_table(t, i, 1, 0)
+#define print_table_reverse(t, i) __iter_table(t, i, 1, 1)
+
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_table *tb;
+ struct libmnt_statmnt *sm;
+ struct libmnt_iter *itr;
+ struct timeval start, end;
+ uint64_t id = 0;
+ double sec_lsmnt, sec_lsstmnt, sec_mountinfo;
+
+ mnt_init_debug(0);
+
+
+ if (argc == 2) {
+ const char *arg = argv[1];
+ if (!isdigit_string(arg))
+ mnt_id_from_path(arg, &id, NULL);
+ else
+ id = strtou64_or_err(arg, "cannot ID");
+ }
+
+ itr = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!itr)
+ err(MNT_EX_SYSERR, "failed to initialize libmount iterator");
+
+ tb = mnt_new_table();
+ if (!tb)
+ err(EXIT_FAILURE, "failed to allocate table handler");
+ if (id)
+ mnt_table_listmount_set_id(tb, id);
+
+ /*
+ * A) listmount() and statmount() based table
+ */
+ sm = mnt_new_statmnt();
+ if (!sm)
+ err(EXIT_FAILURE, "failed to allocate statmnt handler");
+
+ /* Without this mask setting, the library will make two statmount() calls
+ * for each node. */
+ mnt_statmnt_set_mask(sm, STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE);
+
+ /* force fetch all data
+ mnt_statmnt_set_mask(sm,
+ STATMOUNT_SB_BASIC |
+ STATMOUNT_MNT_BASIC |
+ STATMOUNT_PROPAGATE_FROM |
+ STATMOUNT_MNT_ROOT |
+ STATMOUNT_MNT_POINT |
+ STATMOUNT_FS_TYPE
+ STATMOUNT_MNT_OPTS);
+ */
+
+ /* enable don-demand statmount() call for all filesystems in the table */
+ mnt_table_refer_statmnt(tb, sm);
+
+ /* listmount() only */
+ gettimeofday(&start, NULL);
+ if (mnt_table_fetch_listmount(tb) != 0)
+ warn("failed to read mount table by listmount()");
+ gettimeofday(&end, NULL);
+ sec_lsmnt = time_diff(&end, &start);
+
+ /* force statmount() for all nodes */
+ fetch_data(tb, itr);
+ gettimeofday(&end, NULL);
+ sec_lsstmnt = time_diff(&end, &start);
+
+ fprintf(stdout, "listmount() based table:\n");
+ print_table(tb, itr);
+
+
+ /* disable statmount() and listmount(); reset table */
+ mnt_statmnt_disable_fetching(sm, 1);
+ mnt_table_enable_listmount(tb, 0);
+ mnt_reset_table(tb);
+
+ /*
+ * B) /proc/self/mountinfo based table
+ */
+ gettimeofday(&start, NULL);
+ mnt_table_parse_file(tb, _PATH_PROC_MOUNTINFO);
+ gettimeofday(&end, NULL);
+ sec_mountinfo = time_diff(&end, &start);
+
+ fprintf(stdout, "\nprocfs based table:\n");
+ print_table(tb, itr);
+
+ fprintf(stdout, "\n"
+ "%f sec listmount()\n"
+ "%f sec listmount()+statmount()\n"
+ "%f sec /proc/sef/mountinfo"
+ "\n\n",
+ sec_lsmnt, sec_lsstmnt, sec_mountinfo);
+
+
+ mnt_reset_table(tb);
+
+ /*
+ * C) Instead of reading the entire mount table with one listmount() call, read
+ * it in smaller steps. This is particularly useful for systems with large
+ * mount tables, where we may only need to access one specific node (usually the
+ * last one).
+ *
+ * By default, libmount reads 512 nodes in one call. To test this sample
+ * on normal systems, we will reduce this number to 5 nodes. The listmount()
+ * function is used as a backend for regular mnt_table_next_fs().
+ * There is no need to make any changes to the application, just enable
+ * on-demand listmount() by using mnt_table_enable_listmount().
+ */
+ if (mnt_table_listmount_set_stepsiz(tb, 5) != 0)
+ warn("failed to initialize listmount()");
+ mnt_table_enable_listmount(tb, 1);
+
+ /* enable also statmount() */
+ mnt_statmnt_disable_fetching(sm, 0);
+
+ fprintf(stdout, "\nlistmount() - small steps (reverse):\n");
+ print_table_reverse(tb, itr);
+
+
+ mnt_unref_table(tb);
+ mnt_unref_statmnt(sm);
+ mnt_free_iter(itr);
+
+ return EXIT_SUCCESS;
+}