--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+
+#include "libfrog/flagmap.h"
+#include "libfrog/statmount.h"
+#include "command.h"
+#include "input.h"
+#include "init.h"
+#include "io.h"
+
+static const struct flag_map statmount_funcs[] = {
+ { STATMOUNT_SB_BASIC, N_("sb_basic") },
+ { STATMOUNT_MNT_BASIC, N_("mnt_basic") },
+ { STATMOUNT_PROPAGATE_FROM, N_("propagate_from") },
+ { STATMOUNT_MNT_ROOT, N_("mnt_root") },
+ { STATMOUNT_MNT_POINT, N_("mnt_point") },
+ { STATMOUNT_FS_TYPE, N_("fs_type") },
+ { STATMOUNT_MNT_NS_ID, N_("mnt_ns_id") },
+ { STATMOUNT_MNT_OPTS, N_("mnt_opts") },
+ { STATMOUNT_FS_SUBTYPE, N_("fs_subtype") },
+ { STATMOUNT_SB_SOURCE, N_("sb_source") },
+ { STATMOUNT_OPT_ARRAY, N_("opt_array") },
+ { STATMOUNT_OPT_SEC_ARRAY, N_("opt_sec_array") },
+ { STATMOUNT_SUPPORTED_MASK, N_("supported_mask") },
+ {0, NULL},
+};
+
+static const struct flag_map mount_attrs[] = {
+ { MOUNT_ATTR_RDONLY, N_("rdonly") },
+ { MOUNT_ATTR_NOSUID, N_("nosuid") },
+ { MOUNT_ATTR_NODEV, N_("nodev") },
+ { MOUNT_ATTR_NOEXEC, N_("noexec") },
+ { MOUNT_ATTR__ATIME, N_("atime") },
+ { MOUNT_ATTR_RELATIME, N_("relatime") },
+ { MOUNT_ATTR_NOATIME, N_("noatime") },
+ { MOUNT_ATTR_STRICTATIME, N_("strictatime") },
+ { MOUNT_ATTR_NODIRATIME, N_("nodiratime") },
+ { MOUNT_ATTR_IDMAP, N_("idmap") },
+ { MOUNT_ATTR_NOSYMFOLLOW, N_("nosymfollow") },
+ {0, NULL},
+};
+
+static const struct flag_map mount_prop_flags[] = {
+ { MS_SHARED, N_("shared") },
+ { MS_SLAVE, N_("nopeer") },
+ { MS_PRIVATE, N_("private") },
+ { MS_UNBINDABLE, N_("unbindable") },
+ {0, NULL},
+};
+
+static void
+dump_mountinfo(
+ const struct statmount *smbuf,
+ bool rawflag)
+{
+ char buf[4096];
+
+ if (rawflag) {
+ printf("\tmask: 0x%llx\n", (unsigned long long)smbuf->mask);
+ } else {
+ mask_to_string(statmount_funcs, smbuf->mask, ",", buf,
+ sizeof(buf));
+ printf("\tmask: {%s}\n", buf);
+ }
+
+ if (smbuf->mask & STATMOUNT_SB_BASIC) {
+ printf("\tsb_dev_major: %u\n", smbuf->sb_dev_major);
+ printf("\tsb_dev_minor: %u\n", smbuf->sb_dev_minor);
+ printf("\tsb_magic: 0x%llx\n",
+ (unsigned long long)smbuf->sb_magic);
+ printf("\tsb_flags: 0x%x\n", smbuf->sb_flags);
+ }
+
+ if (smbuf->mask & STATMOUNT_MNT_BASIC) {
+ printf("\tmnt_id: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_id);
+ printf("\tmnt_parent_id: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_parent_id);
+ printf("\tmnt_id_old: %u\n", smbuf->mnt_id_old);
+ printf("\tmnt_parent_id_old: %u\n", smbuf->mnt_parent_id_old);
+ if (rawflag) {
+ printf("\tmnt_attr: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_attr);
+ printf("\tmnt_propagation: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_propagation);
+ } else {
+ mask_to_string(mount_attrs, smbuf->mnt_attr, ",", buf,
+ sizeof(buf));
+ printf("\tmnt_attr: {%s}\n", buf);
+ mask_to_string(mount_prop_flags, smbuf->mnt_propagation,
+ ",", buf, sizeof(buf));
+ printf("\tmnt_propagation: {%s}\n", buf);
+ }
+ printf("\tmnt_peer_group: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_peer_group);
+ printf("\tmnt_master: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_master);
+ }
+
+ if (smbuf->mask & STATMOUNT_PROPAGATE_FROM)
+ printf("\tpropagate_from: 0x%llx\n",
+ (unsigned long long)smbuf->propagate_from);
+
+ if (smbuf->mask & STATMOUNT_MNT_ROOT)
+ printf("\tmnt_root: %s\n", smbuf->str + smbuf->mnt_root);
+ if (smbuf->mask & STATMOUNT_MNT_POINT)
+ printf("\tmnt_point: %s\n", smbuf->str + smbuf->mnt_point);
+ if (smbuf->mask & STATMOUNT_FS_TYPE)
+ printf("\tfs_type: %s\n", smbuf->str + smbuf->fs_type);
+ if (smbuf->mask & STATMOUNT_FS_SUBTYPE)
+ printf("\tfs_subtype: %s\n", smbuf->str + smbuf->fs_subtype);
+
+ if (smbuf->mask & STATMOUNT_MNT_NS_ID)
+ printf("\tmnt_ns_id: 0x%llx\n",
+ (unsigned long long)smbuf->mnt_ns_id);
+
+ if (smbuf->mask & STATMOUNT_MNT_OPTS)
+ printf("\tmnt_opts: %s\n", smbuf->str + smbuf->mnt_opts);
+ if (smbuf->mask & STATMOUNT_SB_SOURCE)
+ printf("\tsb_source: %s\n", smbuf->str + smbuf->sb_source);
+
+ if (smbuf->mask & STATMOUNT_SUPPORTED_MASK) {
+ if (rawflag) {
+ printf("\tsupported_mask: 0x%llx\n",
+ (unsigned long long)smbuf->supported_mask);
+ } else {
+ mask_to_string(statmount_funcs, smbuf->supported_mask,
+ ",", buf, sizeof(buf));
+ printf("\tsupported_mask: {%s}\n", buf);
+ }
+ }
+}
+
+static inline bool
+match_mount(
+ const struct statmount *smbuf,
+ const char *fstype)
+{
+ char real_fstype[256];
+
+ if (!fstype)
+ return true;
+
+ if (!(smbuf->mask & STATMOUNT_FS_TYPE))
+ return false;
+
+ if (smbuf->mask & STATMOUNT_FS_SUBTYPE)
+ snprintf(real_fstype, sizeof(real_fstype), "%s.%s",
+ smbuf->str + smbuf->fs_type,
+ smbuf->str + smbuf->fs_subtype);
+ else
+ snprintf(real_fstype, sizeof(real_fstype), "%s",
+ smbuf->str + smbuf->fs_type);
+
+ return strcmp(fstype, real_fstype) == 0;
+}
+
+static void
+listmount_help(void)
+{
+ printf(_(
+"\n"
+" List all mounted filesystems.\n"
+"\n"
+" -f -- statmount mask flags to set. Defaults to all possible flags.\n"
+" -i -- only list mounts below this mount id. Defaults to the rootdir.\n"
+" -n -- path to a procfs mount namespace file.\n"
+" -r -- do not decode flags fields into strings.\n"
+" -t -- only display mount info for this fs type.\n"
+));
+}
+
+#define NR_MNT_IDS 7
+
+static int
+listmount_f(
+ int argc,
+ char **argv)
+{
+ uint64_t mnt_ids[NR_MNT_IDS];
+ uint64_t cursor = LISTMOUNT_INIT_CURSOR;
+ uint64_t statmount_flags = -1ULL;
+ uint64_t mnt_id = LSMT_ROOT;
+ struct statmount *smbuf;
+ const char *fstype = NULL;
+ unsigned long long rows = 0;
+ const size_t smbuf_size = libfrog_statmount_sizeof(65536);
+ int mnt_ns_fd = DEFAULT_MOUNTNS_FD;
+ int rawflag = 0;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "f:i:n:rt:")) > 0) {
+ switch (c) {
+ case 'f':
+ errno = 0;
+ statmount_flags = strtoull(optarg, NULL, 0);
+ if (errno) {
+ perror(optarg);
+ return 1;
+ }
+ break;
+ case 'i':
+ errno = 0;
+ mnt_id = strtoull(optarg, NULL, 0);
+ if (errno) {
+ perror(optarg);
+ return 1;
+ }
+ break;
+ case 'n':
+ mnt_ns_fd = open(optarg, O_RDONLY);
+ if (mnt_ns_fd < 0) {
+ perror(optarg);
+ return 1;
+ }
+ break;
+ case 'r':
+ rawflag++;
+ break;
+ case 't':
+ fstype = optarg;
+ break;
+ default:
+ listmount_help();
+ return 1;
+ }
+ }
+
+ smbuf = malloc(smbuf_size);
+ if (!smbuf) {
+ perror("malloc");
+ return 1;
+ }
+
+ if (fstype)
+ statmount_flags |= STATMOUNT_FS_TYPE | STATMOUNT_FS_SUBTYPE;
+
+ while ((ret = libfrog_listmount(mnt_id, mnt_ns_fd, &cursor,
+ mnt_ids, NR_MNT_IDS)) > 0) {
+ for (c = 0; c < ret; c++) {
+ ret = libfrog_statmount(mnt_ids[c], mnt_ns_fd,
+ statmount_flags, smbuf, smbuf_size);
+ if (ret) {
+ perror("statmount");
+ exitcode = 1;
+ goto out_smbuf;
+ }
+
+ if (!match_mount(smbuf, fstype))
+ continue;
+
+ printf("mnt_id[%llu]: 0x%llx\n",
+ (unsigned long long)rows++,
+ (unsigned long long)mnt_ids[c]);
+
+ dump_mountinfo(smbuf, rawflag);
+ }
+ }
+
+ if (ret < 0) {
+ perror("listmount");
+ exitcode = 1;
+ }
+
+out_smbuf:
+ free(smbuf);
+ if (mnt_ns_fd != DEFAULT_MOUNTNS_FD)
+ close(mnt_ns_fd);
+ return 0;
+}
+
+static const struct cmdinfo listmount_cmd = {
+ .name = "listmount",
+ .cfunc = listmount_f,
+ .argmin = -1,
+ .argmax = -1,
+ .flags = CMD_FLAG_ONESHOT | CMD_NOFILE_OK | CMD_FOREIGN_OK | CMD_NOMAP_OK,
+ .oneline = N_("list mounted filesystems"),
+ .help = listmount_help,
+};
+
+static void
+statmount_help(void)
+{
+ printf(_(
+"\n"
+" Print statmount information for the open file.\n"
+"\n"
+" -f -- statmount mask flags to set. Defaults to all possible flags.\n"
+" -r -- do not decode flags fields into strings.\n"
+));
+}
+
+static int
+statmount_f(
+ int argc,
+ char **argv)
+{
+ uint64_t statmount_flags = -1ULL;
+ struct statmount *smbuf;
+ const size_t smbuf_size = libfrog_statmount_sizeof(65536);
+ int rawflag = 0;
+ int c;
+ int ret;
+
+ while ((c = getopt(argc, argv, "f:r")) > 0) {
+ switch (c) {
+ case 'f':
+ errno = 0;
+ statmount_flags = strtoull(optarg, NULL, 0);
+ if (errno) {
+ perror(optarg);
+ return 1;
+ }
+ break;
+ case 'r':
+ rawflag++;
+ break;
+ default:
+ listmount_help();
+ return 1;
+ }
+ }
+
+ smbuf = malloc(smbuf_size);
+ if (!smbuf) {
+ perror("malloc");
+ return 1;
+ }
+
+ ret = libfrog_fstatmount(file->fd, statmount_flags, smbuf, smbuf_size);
+ if (ret) {
+ perror("statmount");
+ exitcode = 1;
+ goto out_smbuf;
+ }
+
+ printf("path: %s\n", file->name);
+
+ dump_mountinfo(smbuf, rawflag);
+
+out_smbuf:
+ free(smbuf);
+ return 0;
+}
+
+static const struct cmdinfo statmount_cmd = {
+ .name = "statmount",
+ .cfunc = statmount_f,
+ .argmin = -1,
+ .argmax = -1,
+ .flags = CMD_FOREIGN_OK | CMD_NOMAP_OK,
+ .oneline = N_("statmount the open file"),
+ .help = statmount_help,
+};
+
+void
+listmount_init(void)
+{
+ add_command(&listmount_cmd);
+ add_command(&statmount_cmd);
+}