aboutsummaryrefslogtreecommitdiffstats
path: root/libmount/src
diff options
context:
space:
mode:
authorKarel Zak <kzak@redhat.com>2025-01-09 11:13:49 +0100
committerKarel Zak <kzak@redhat.com>2025-01-09 11:13:49 +0100
commitb4ea6459da5767de34fde33c5e2751788203b989 (patch)
tree29e7395055b5181b76700ade205aa6b8f19d9d07 /libmount/src
parenta5d0e6a87328049ecb8aab01a5d5248cb989f07d (diff)
parente1190d466394fefaa89962b300332813e68bfe6c (diff)
downloadutil-linux-b4ea6459da5767de34fde33c5e2751788203b989.tar.gz
Merge branch 'PR/libmount-statmount' of https://github.com/karelzak/util-linux-work
* 'PR/libmount-statmount' of https://github.com/karelzak/util-linux-work: (40 commits) libmount: map unsupported LISTMOUNT_REVERSE to ENOSYS findmnt: add --id and --uniq-id options findmnt: improve --help output findmnt: improve reliability of match testing libmount: add mnt_table_find_[uniq]_id() function findmnt: add UNIQ-ID column findmnt: add docs for --kernel tests: add findmnt --kernel=listmount libmount: fix mnt_fs_match_target() libmount: improve fs->stmnt_done mask use libmount: improve how library generates fs->optstr findmnt: add --kernel=listmount findmnt: add optional argument to --kernel meson: fix after rebase libmount: remove unnecessary include test_sysinfo; fix fsopen() ifdef libmount: Add integer type headers to private header file libmount: use __unused__ for dummy get_mnt_id() libmount: update tests include/mount-api-utils: fix typo ...
Diffstat (limited to 'libmount/src')
-rw-r--r--libmount/src/Makemodule.am2
-rw-r--r--libmount/src/btrfs.c1
-rw-r--r--libmount/src/fs.c315
-rw-r--r--libmount/src/fs_statmount.c399
-rw-r--r--libmount/src/fuzz.c1
-rw-r--r--libmount/src/hook_idmap.c2
-rw-r--r--libmount/src/hook_mount.c3
-rw-r--r--libmount/src/hook_subdir.c1
-rw-r--r--libmount/src/hooks.c1
-rw-r--r--libmount/src/libmount.h.in44
-rw-r--r--libmount/src/libmount.sym22
-rw-r--r--libmount/src/mountP.h53
-rw-r--r--libmount/src/optlist.c1
-rw-r--r--libmount/src/tab.c144
-rw-r--r--libmount/src/tab_listmount.c424
-rw-r--r--libmount/src/tab_parse.c19
-rw-r--r--libmount/src/tab_update.c6
-rw-r--r--libmount/src/utils.c67
-rw-r--r--libmount/src/version.c4
19 files changed, 1426 insertions, 83 deletions
diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am
index 66963bb094..49f6d6f03d 100644
--- a/libmount/src/Makemodule.am
+++ b/libmount/src/Makemodule.am
@@ -30,6 +30,8 @@ libmount_la_SOURCES += \
libmount/src/context.c \
libmount/src/context_mount.c \
libmount/src/context_umount.c \
+ libmount/src/fs_statmount.c \
+ libmount/src/tab_listmount.c \
libmount/src/hooks.c \
libmount/src/hook_mount.c \
libmount/src/hook_mount_legacy.c \
diff --git a/libmount/src/btrfs.c b/libmount/src/btrfs.c
index a831ce8375..eb569f2582 100644
--- a/libmount/src/btrfs.c
+++ b/libmount/src/btrfs.c
@@ -15,7 +15,6 @@
#include <dirent.h>
#include <sys/ioctl.h>
#include <stdlib.h>
-#include <stdint.h>
#include <linux/btrfs.h>
#include "mountP.h"
diff --git a/libmount/src/fs.c b/libmount/src/fs.c
index 26f2c69e90..999fec49b7 100644
--- a/libmount/src/fs.c
+++ b/libmount/src/fs.c
@@ -100,6 +100,11 @@ void mnt_reset_fs(struct libmnt_fs *fs)
fs->optlist = NULL;
fs->opts_age = 0;
+ fs->propagation = 0;
+
+ mnt_unref_statmnt(fs->stmnt);
+ fs->stmnt = NULL;
+ fs->stmnt_done = 0;
memset(fs, 0, sizeof(*fs));
INIT_LIST_HEAD(&fs->ents);
@@ -610,7 +615,12 @@ int mnt_fs_get_tag(struct libmnt_fs *fs, const char **name, const char **value)
*/
const char *mnt_fs_get_target(struct libmnt_fs *fs)
{
- return fs ? fs->target : NULL;
+ if (!fs)
+ return NULL;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, target, STATMOUNT_MNT_POINT);
+#endif
+ return fs->target;;
}
/**
@@ -656,22 +666,24 @@ int mnt_fs_get_propagation(struct libmnt_fs *fs, unsigned long *flags)
{
if (!fs || !flags)
return -EINVAL;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, propagation, STATMOUNT_MNT_BASIC);
+#endif
+ if (!fs->propagation && fs->opt_fields) {
+ /*
+ * The optional fields format is incompatible with mount options
+ * ... we have to parse the field here.
+ */
+ fs->propagation |= strstr(fs->opt_fields, "shared:") ?
+ MS_SHARED : MS_PRIVATE;
+
+ if (strstr(fs->opt_fields, "master:"))
+ fs->propagation |= MS_SLAVE;
+ if (strstr(fs->opt_fields, "unbindable"))
+ fs->propagation |= MS_UNBINDABLE;
+ }
- *flags = 0;
-
- if (!fs->opt_fields)
- return 0;
-
- /*
- * The optional fields format is incompatible with mount options
- * ... we have to parse the field here.
- */
- *flags |= strstr(fs->opt_fields, "shared:") ? MS_SHARED : MS_PRIVATE;
-
- if (strstr(fs->opt_fields, "master:"))
- *flags |= MS_SLAVE;
- if (strstr(fs->opt_fields, "unbindable"))
- *flags |= MS_UNBINDABLE;
+ *flags = fs->propagation;
return 0;
}
@@ -706,6 +718,11 @@ int mnt_fs_is_swaparea(struct libmnt_fs *fs)
*/
int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
{
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, fstype, STATMOUNT_FS_TYPE);
+#endif
return mnt_fs_get_flags(fs) & MNT_FS_PSEUDO ? 1 : 0;
}
@@ -717,6 +734,11 @@ int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
*/
int mnt_fs_is_netfs(struct libmnt_fs *fs)
{
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, fstype, STATMOUNT_FS_TYPE);
+#endif
return mnt_fs_get_flags(fs) & MNT_FS_NET ? 1 : 0;
}
@@ -743,7 +765,12 @@ int mnt_fs_is_regularfs(struct libmnt_fs *fs)
*/
const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
{
- return fs ? fs->fstype : NULL;
+ if (!fs)
+ return NULL;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, fstype, STATMOUNT_FS_TYPE);
+#endif
+ return fs->fstype;
}
/* Used by the struct libmnt_file parser only */
@@ -795,18 +822,8 @@ int mnt_fs_set_fstype(struct libmnt_fs *fs, const char *fstype)
}
/*
- * Merges @vfs and @fs options strings into a new string.
- * This function cares about 'ro/rw' options. The 'ro' is
- * always used if @vfs or @fs is read-only.
- * For example:
- *
- * mnt_merge_optstr("rw,noexec", "ro,journal=update")
- *
- * returns: "ro,noexec,journal=update"
- *
- * mnt_merge_optstr("rw,noexec", "rw,journal=update")
- *
- * returns: "rw,noexec,journal=update"
+ * Merges @vfs and @fs options strings into a new string. This function cares
+ * about 'ro/rw' options. The 'ro' is always used if @vfs or @fs is read-only.
*/
static char *merge_optstr(const char *vfs, const char *fs)
{
@@ -848,24 +865,10 @@ static char *merge_optstr(const char *vfs, const char *fs)
return res;
}
-/**
- * mnt_fs_strdup_options:
- * @fs: fstab/mtab/mountinfo entry pointer
- *
- * Merges all mount options (VFS, FS and userspace) to one options string
- * and returns the result. This function does not modify @fs.
- *
- * Returns: pointer to string (can be freed by free(3)) or NULL in case of error.
- */
-char *mnt_fs_strdup_options(struct libmnt_fs *fs)
+static char *fs_strdup_options(struct libmnt_fs *fs)
{
char *res;
- if (!fs)
- return NULL;
- if (fs->optlist)
- sync_opts_from_optlist(fs, fs->optlist);
-
errno = 0;
if (fs->optstr)
return strdup(fs->optstr);
@@ -882,6 +885,30 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
}
/**
+ * mnt_fs_strdup_options:
+ * @fs: fstab/mtab/mountinfo entry pointer
+ *
+ * Merges all mount options (VFS, FS and userspace) to one options string
+ * and returns the result. This function does not modify @fs.
+ *
+ * Returns: pointer to string (can be freed by free(3)) or NULL in case of error.
+ */
+char *mnt_fs_strdup_options(struct libmnt_fs *fs)
+{
+ if (!fs)
+ return NULL;
+ if (fs->optlist)
+ sync_opts_from_optlist(fs, fs->optlist);
+#ifdef HAVE_STATMOUNT_API
+ else
+ mnt_fs_try_statmount(fs, optstr, STATMOUNT_SB_BASIC
+ | STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS);
+#endif
+ return fs_strdup_options(fs);
+
+}
+
+/**
* mnt_fs_get_options:
* @fs: fstab/mtab/mountinfo entry pointer
*
@@ -889,10 +916,19 @@ char *mnt_fs_strdup_options(struct libmnt_fs *fs)
*/
const char *mnt_fs_get_options(struct libmnt_fs *fs)
{
- if (fs && fs->optlist)
+ if (!fs)
+ return NULL;
+ if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
-
- return fs ? fs->optstr : NULL;
+#ifdef HAVE_STATMOUNT_API
+ else {
+ mnt_fs_try_statmount(fs, optstr, STATMOUNT_SB_BASIC
+ | STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS);
+ if (!fs->optstr)
+ fs->optstr = fs_strdup_options(fs);
+ }
+#endif
+ return fs->optstr;
}
/**
@@ -1061,7 +1097,10 @@ const char *mnt_fs_get_fs_options(struct libmnt_fs *fs)
return NULL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
-
+#ifdef HAVE_STATMOUNT_API
+ else
+ mnt_fs_try_statmount(fs, fs_optstr, STATMOUNT_SB_BASIC | STATMOUNT_MNT_OPTS);
+#endif
return fs->fs_optstr;
}
@@ -1077,7 +1116,10 @@ const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs)
return NULL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
-
+#ifdef HAVE_STATMOUNT_API
+ else
+ mnt_fs_try_statmount(fs, vfs_optstr, STATMOUNT_MNT_BASIC);
+#endif
return fs->vfs_optstr;
}
@@ -1153,6 +1195,9 @@ const char *mnt_fs_get_attributes(struct libmnt_fs *fs)
* that information stored in userspace will not be available for libmount
* after CLONE_FS unshare. Be careful, and don't use attributes if possible.
*
+ * Please note that the new mount kernel API calls some VFS flags "mount attributes"
+ * (MOUNT_ATTR_*), but these flags are not related to the old libmount functionality.
+ *
* Returns: 0 on success or negative number in case of error.
*/
int mnt_fs_set_attributes(struct libmnt_fs *fs, const char *optstr)
@@ -1257,7 +1302,12 @@ int mnt_fs_set_passno(struct libmnt_fs *fs, int passno)
*/
const char *mnt_fs_get_root(struct libmnt_fs *fs)
{
- return fs ? fs->root : NULL;
+ if (!fs)
+ return NULL;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, root, STATMOUNT_MNT_ROOT);
+#endif
+ return fs->root;
}
/**
@@ -1360,11 +1410,60 @@ int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src)
* mnt_fs_get_id:
* @fs: /proc/self/mountinfo entry
*
- * Returns: mount ID (unique identifier of the mount) or negative number in case of error.
+ * This ID is "old" and used in mountinfo only. Since Linux v6.8 there is also unique
+ * 64-bit ID, see mnt_fs_get_uniq_id().
+ *
+ * Returns: mount ID or negative number in case of error.
*/
int mnt_fs_get_id(struct libmnt_fs *fs)
{
- return fs ? fs->id : -EINVAL;
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, id, STATMOUNT_MNT_BASIC);
+#endif
+ return fs->id;
+}
+
+/**
+ * mnt_fs_get_uniq_id:
+ * @fs: filesystem instance
+ *
+ * This ID is provided by statmount() or statx(STATX_MNT_ID_UNIQUE) since Linux
+ * kernel since v6.8.
+ *
+ * Returns: unique mount ID
+ *
+ * Since: 2.41
+ */
+uint64_t mnt_fs_get_uniq_id(struct libmnt_fs *fs)
+{
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, uniq_id, STATMOUNT_MNT_BASIC);
+#endif
+ return fs->uniq_id;
+}
+
+/**
+ * mnt_fs_set_uniq_id:
+ * @fs: filesystem instance
+ * @id: mount node ID
+ *
+ * This ID is provided by statmount() or statx(STATX_MNT_ID_UNIQUE) since Linux
+ * kernel since v6.8.
+ *
+ * Returns: 0 or negative number in case of error.
+ *
+ * Since: 2.41
+ */
+int mnt_fs_set_uniq_id(struct libmnt_fs *fs, uint64_t id)
+{
+ if (!fs)
+ return -EINVAL;
+ fs->uniq_id = id;
+ return 0;
}
/**
@@ -1375,9 +1474,70 @@ int mnt_fs_get_id(struct libmnt_fs *fs)
*/
int mnt_fs_get_parent_id(struct libmnt_fs *fs)
{
- return fs ? fs->parent : -EINVAL;
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, parent, STATMOUNT_MNT_BASIC);
+#endif
+ return fs->parent;
+}
+
+/**
+ * mnt_fs_get_parent_uniq_id:
+ * @fs: filesystem instance
+ *
+ * This ID is provided by statmount() since Linux kernel since v6.8.
+ *
+ * Returns: parent mount ID or 0 if not avalable
+ */
+uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs)
+{
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, uniq_parent, STATMOUNT_MNT_BASIC);
+#endif
+ return fs->uniq_parent;
+}
+
+/**
+ * mnt_fs_get_ns:
+ * @fs: filesystem instance
+ *
+ * This ID is provided by statmount() since Linux kernel since v6.10
+ *
+ * Returns: parent namespace ID or 0 if not avalable.
+ *
+ * Since: 2.41
+ */
+uint64_t mnt_fs_get_ns(struct libmnt_fs *fs)
+{
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, ns_id, STATMOUNT_MNT_NS_ID);
+#endif
+ return fs->ns_id;
+}
+
+/**
+ * mnt_fs_set_ns:
+ * @fs: filesystem instance
+ * @id: namespace ID (or 0)
+ *
+ * Returns: 0 or <0 in case of error.
+ *
+ * Sinse: 2.41
+ */
+int mnt_fs_set_ns(struct libmnt_fs *fs, uint64_t id)
+{
+ if (!fs)
+ return -EINVAL;
+ fs->ns_id = id;
+ return 0;
}
+
/**
* mnt_fs_get_devno:
* @fs: /proc/self/mountinfo entry
@@ -1386,7 +1546,12 @@ int mnt_fs_get_parent_id(struct libmnt_fs *fs)
*/
dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
{
- return fs ? fs->devno : 0;
+ if (!fs)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, devno, STATMOUNT_SB_BASIC);
+#endif
+ return fs->devno;
}
/**
@@ -1419,7 +1584,10 @@ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name,
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
-
+#ifdef HAVE_STATMOUNT_API
+ else
+ mnt_fs_try_statmount(fs, vfs_optstr, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC);
+#endif
if (fs->fs_optstr)
rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
if (rc == 1 && fs->vfs_optstr)
@@ -1523,7 +1691,12 @@ int mnt_fs_match_target(struct libmnt_fs *fs, const char *target,
{
int rc = 0;
- if (!fs || !target || !fs->target)
+ if (!fs || !target)
+ return 0;
+#ifdef HAVE_STATMOUNT_API
+ mnt_fs_try_statmount(fs, target, STATMOUNT_MNT_POINT);
+#endif
+ if (!fs->target)
return 0;
/* 1) native paths */
@@ -1640,7 +1813,7 @@ int mnt_fs_match_source(struct libmnt_fs *fs, const char *source,
*/
int mnt_fs_match_fstype(struct libmnt_fs *fs, const char *types)
{
- return mnt_match_fstype(fs->fstype, types);
+ return mnt_match_fstype(mnt_fs_get_fstype(fs), types);
}
/**
@@ -1667,16 +1840,25 @@ int mnt_fs_match_options(struct libmnt_fs *fs, const char *options)
*/
int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
{
+ unsigned long pro = 0;
+ int stmnt_disabled = 1;
+
if (!fs || !file)
return -EINVAL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
+ if (fs->stmnt)
+ stmnt_disabled = mnt_statmnt_disable_fetching(fs->stmnt, 1);
+
fprintf(file, "------ fs:\n");
- fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
- fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
- fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
+ if (mnt_fs_get_source(fs))
+ fprintf(file, "source: %s\n", mnt_fs_get_source(fs));
+ if (mnt_fs_get_target(fs))
+ fprintf(file, "target: %s\n", mnt_fs_get_target(fs));
+ if (mnt_fs_get_fstype(fs))
+ fprintf(file, "fstype: %s\n", mnt_fs_get_fstype(fs));
if (mnt_fs_get_options(fs))
fprintf(file, "optstr: %s\n", mnt_fs_get_options(fs));
@@ -1691,6 +1873,12 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
if (mnt_fs_get_attributes(fs))
fprintf(file, "attributes: %s\n", mnt_fs_get_attributes(fs));
+ if (mnt_fs_get_propagation(fs, &pro) == 0 && pro)
+ fprintf(file, "propagation: %s %s %s\n",
+ pro & MS_SHARED ? "shared" : "private",
+ pro & MS_SLAVE ? "slave" : "",
+ pro & MS_UNBINDABLE ? "unbindable" : "");
+
if (mnt_fs_get_root(fs))
fprintf(file, "root: %s\n", mnt_fs_get_root(fs));
@@ -1713,6 +1901,11 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
fprintf(file, "id: %d\n", mnt_fs_get_id(fs));
if (mnt_fs_get_parent_id(fs))
fprintf(file, "parent: %d\n", mnt_fs_get_parent_id(fs));
+ if (mnt_fs_get_uniq_id(fs))
+ fprintf(file, "uniq-id: %" PRIu64 "\n", mnt_fs_get_uniq_id(fs));
+ if (mnt_fs_get_parent_uniq_id(fs))
+ fprintf(file, "uniq-parent: %" PRIu64 "\n", mnt_fs_get_parent_uniq_id(fs));
+
if (mnt_fs_get_devno(fs))
fprintf(file, "devno: %d:%d\n", major(mnt_fs_get_devno(fs)),
minor(mnt_fs_get_devno(fs)));
@@ -1721,6 +1914,8 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file)
if (mnt_fs_get_comment(fs))
fprintf(file, "comment: '%s'\n", mnt_fs_get_comment(fs));
+ if (fs->stmnt)
+ mnt_statmnt_disable_fetching(fs->stmnt, stmnt_disabled);
return 0;
}
diff --git a/libmount/src/fs_statmount.c b/libmount/src/fs_statmount.c
new file mode 100644
index 0000000000..c44e822995
--- /dev/null
+++ b/libmount/src/fs_statmount.c
@@ -0,0 +1,399 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2024 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+/**
+ * SECTION: statmount
+ * @title: statmount setting
+ * @short_description: Fetches information about mount node from the kernel.
+ */
+#include "mountP.h"
+
+#include "mangle.h"
+
+/**
+ * mnt_new_statmnt:
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the filesystem.
+ *
+ * Returns: newly allocated struct libmnt_statmnt.
+ */
+struct libmnt_statmnt *mnt_new_statmnt(void)
+{
+#ifdef HAVE_STATMOUNT_API
+ struct libmnt_statmnt *sm;
+
+ errno = 0;
+ if (ul_statmount(0, 0, 0, NULL, 0, 0) < 0 && errno == ENOSYS) {
+ DBG(FS, ul_debug("statmount: unsuppported"));
+ return NULL;
+ }
+
+ sm = calloc(1, sizeof(*sm));
+ if (!sm)
+ return NULL;
+
+ sm->refcount = 1;
+ DBG(STATMNT, ul_debugobj(sm, "alloc"));
+ return sm;
+#else
+ errno = ENOSYS;
+ return NULL;
+#endif
+}
+
+/**
+ * mnt_ref_statmount:
+ * @sm: statmount setting
+ *
+ * Increments reference counter.
+ */
+void mnt_ref_statmnt(struct libmnt_statmnt *sm)
+{
+ if (sm) {
+ sm->refcount++;
+ /*DBG(STATMNT, ul_debugobj(sm, "ref=%d", sm->refcount));*/
+ }
+}
+
+/**
+ * mnt_unref_statmnt:
+ * @sm: statmount setting
+ *
+ * De-increments reference counter, on zero the @sm is automatically
+ * deallocated.
+ */
+void mnt_unref_statmnt(struct libmnt_statmnt *sm)
+{
+ if (sm) {
+ sm->refcount--;
+ /*DBG(STATMNT, ul_debugobj(sm, "unref=%d", sm->refcount));*/
+ if (sm->refcount <= 0) {
+ free(sm->buf);
+ free(sm);
+ }
+ }
+}
+
+/**
+ * mnt_statmnt_set_mask:
+ * @sm: statmount setting
+ * @mask: default mask for statmount() or 0
+ *
+ * Returns: 0 on succees or or <0 on error.
+ */
+int mnt_statmnt_set_mask(struct libmnt_statmnt *sm, uint64_t mask)
+{
+ if (!sm)
+ return -EINVAL;
+ sm->mask = mask;
+
+ DBG(STATMNT, ul_debugobj(sm, "mask=0x%" PRIx64, sm->mask));
+ return 0;
+}
+
+/**
+ * mnt_statmnt_disable_fetching:
+ * @sm: statmount setting
+ * @disable: 0 or 1
+ *
+ * Disable or enable on-demand statmount() in all libmnt_table of libmnt_fs
+ * onjects that references this @sm.
+ *
+ * Returns: current setting (0 or 1) or <0 on error.
+ *
+ * Since: 2.41
+ */
+int mnt_statmnt_disable_fetching(struct libmnt_statmnt *sm, int disable)
+{
+ int old;
+
+ if (!sm)
+ return -EINVAL;
+ old = sm->disabled;
+ sm->disabled = disable ? 1 : 0;
+
+ /*
+ DBG(STATMNT, ul_debugobj(sm, "statmount() %s",
+ sm->disabled ? "off" : "on"));
+ */
+ return old;
+}
+
+/**
+ * mnt_fs_refer_statmnt:
+ * @fs: filesystem
+ * @sm: statmount() setting
+ *
+ * Add a reference to the statmount() setting. This setting can be overwritten
+ * if you add @fs into a table that uses a different statmount() setting. See
+ * mnt_table_refer_statmnt(). It is recommended to use the statmount() setting
+ * on a table level if you do not want to work with complete mount table.
+ *
+ * The function mnt_reset_fs() removes this reference too.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ *
+ * Since: 2.41
+ */
+int mnt_fs_refer_statmnt(struct libmnt_fs *fs, struct libmnt_statmnt *sm)
+{
+ if (!fs)
+ return -EINVAL;
+ if (fs->stmnt == sm)
+ return 0;
+
+ mnt_unref_statmnt(fs->stmnt);
+ mnt_ref_statmnt(sm);
+
+ fs->stmnt = sm;
+ return 0;
+}
+
+/**
+ * mnt_fs_get_statmnt:
+ * @fs: filesystem
+ *
+ * Returns: pointer to linmnt_statmnt instance used for the filesystem or NULL
+ *
+ * Since: 2.41
+ */
+struct libmnt_statmnt *mnt_fs_get_statmnt(struct libmnt_fs *fs)
+{
+ return fs ? fs->stmnt : NULL;
+}
+
+#ifdef HAVE_STATMOUNT_API
+
+static inline const char *sm_str(struct ul_statmount *sm, uint32_t offset)
+{
+ return sm->str + offset;
+}
+
+static int apply_statmount(struct libmnt_fs *fs, struct ul_statmount *sm)
+{
+ int rc = 0;
+
+ if (!sm || !sm->size || !fs)
+ return -EINVAL;
+
+ if ((sm->mask & STATMOUNT_FS_TYPE) && !fs->fstype)
+ rc = mnt_fs_set_fstype(fs, sm_str(sm, sm->fs_type));
+
+ if (!rc && (sm->mask & STATMOUNT_MNT_POINT) && !fs->target)
+ rc = mnt_fs_set_target(fs, sm_str(sm, sm->mnt_point));
+
+ if (!rc && (sm->mask & STATMOUNT_MNT_ROOT) && !fs->root)
+ rc = mnt_fs_set_root(fs, sm_str(sm, sm->mnt_root));
+
+ if (!rc && (sm->mask & STATMOUNT_MNT_BASIC)) {
+ if (!fs->propagation)
+ fs->propagation = sm->mnt_propagation;
+ if (!fs->parent)
+ fs->parent = sm->mnt_parent_id_old;
+ if (!fs->uniq_parent)
+ fs->uniq_parent = sm->mnt_parent_id;
+ if (!fs->id)
+ fs->id = sm->mnt_id_old;
+ if (!fs->uniq_id)
+ fs->uniq_id = sm->mnt_id;
+ if (!fs->vfs_optstr) {
+ rc = mnt_optstr_append_option(&fs->vfs_optstr,
+ sm->mnt_attr & MOUNT_ATTR_RDONLY ? "ro" : "rw", NULL);
+ if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOSUID))
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "nosuid", NULL);
+ if (!rc && (sm->mnt_attr & MOUNT_ATTR_NODEV))
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "nodev", NULL);
+ if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOEXEC))
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "noexec", NULL);
+ if (!rc && (sm->mnt_attr & MOUNT_ATTR_NODIRATIME))
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "nodiratime", NULL);
+ if (!rc && (sm->mnt_attr & MOUNT_ATTR_NOSYMFOLLOW))
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "nosymfollow", NULL);
+
+ switch (sm->mnt_attr & MOUNT_ATTR__ATIME) {
+ case MOUNT_ATTR_STRICTATIME:
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "strictatime", NULL);
+ break;
+ case MOUNT_ATTR_NOATIME:
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "noatime", NULL);
+ break;
+ case MOUNT_ATTR_RELATIME:
+ rc = mnt_optstr_append_option(&fs->vfs_optstr, "relatime", NULL);
+ break;
+ }
+ free(fs->optstr);
+ fs->optstr = NULL;
+ }
+ }
+
+ if (!rc && (sm->mask & STATMOUNT_MNT_NS_ID) && !fs->ns_id)
+ fs->ns_id = sm->mnt_ns_id;
+
+ if (!rc && (sm->mask & STATMOUNT_MNT_OPTS) && !fs->fs_optstr) {
+ fs->fs_optstr = unmangle(sm_str(sm, sm->mnt_opts), NULL);
+ free(fs->optstr);
+ fs->optstr = NULL;
+ }
+
+ if (!rc && (sm->mask & STATMOUNT_SB_BASIC)) {
+ if (!fs->devno)
+ fs->devno = makedev(sm->sb_dev_major, sm->sb_dev_minor);
+ if (!fs->fs_optstr) {
+ rc = mnt_optstr_append_option(&fs->fs_optstr,
+ sm->sb_flags & SB_RDONLY ? "ro" : "rw", NULL);
+ if (!rc && (sm->sb_flags & SB_SYNCHRONOUS))
+ rc = mnt_optstr_append_option(&fs->fs_optstr, "sync", NULL);
+ if (!rc && (sm->sb_flags & SB_DIRSYNC))
+ rc = mnt_optstr_append_option(&fs->fs_optstr, "dirsync", NULL);
+ if (!rc && (sm->sb_flags & SB_LAZYTIME))
+ rc = mnt_optstr_append_option(&fs->fs_optstr, "lazytime", NULL);
+ free(fs->optstr);
+ fs->optstr = NULL;
+ }
+ }
+
+ fs->flags |= MNT_FS_KERNEL;
+
+ return rc;
+}
+
+/**
+ * mnt_fs_fetch_statmount:
+ * @fs: filesystem instance
+ * @mask: extends the default statmount() mask.
+ *
+ * This function retrieves mount node information from the kernel and applies it
+ * to the @fs. If the @fs is associated with any libmnt_statmnt object (see
+ * mnt_fs_refer_statmnt()), then this object is used to reduce the overhead of
+ * allocating statmount buffer and to define default statmount() mask.
+ *
+ * The @mask is extended (bitwise-OR) by the mask specified for
+ * mnt_statmnt_set_mask() if on-demand fetching is enabled. If the mask is
+ * still 0, then a mask is generated for all missing data in @fs.
+ *
+ * The default namespace is the current namespace. This default can be
+ * overwritten by mnt_fs_set_ns_id(). The namespace ID is also set when @fs
+ * has been created by mnt_table_fetch_statmount() or on-demand (see
+ * mnt_table_enable_listmount()).
+ *
+ * Returns: 0 or negative number in case of error (if @fs is NULL).
+ *
+ * Since: 2.41
+ */
+int mnt_fs_fetch_statmount(struct libmnt_fs *fs, uint64_t mask)
+{
+ int rc = 0, status = 0;
+ struct ul_statmount *buf = NULL;
+ size_t bufsiz = 0;
+ uint64_t ns = 0;
+
+ if (!fs)
+ return -EINVAL;
+
+ DBG(FS, ul_debugobj(fs, "statmount fetch"));
+
+ /* add default mask if on-demand enabled */
+ if (fs->stmnt
+ && !fs->stmnt->disabled
+ && fs->stmnt->mask)
+ mask |= fs->stmnt->mask;
+
+ /* call only for missing stuff */
+ if (mask && fs->stmnt_done) {
+ mask &= ~fs->stmnt_done; /* remove what is already done */
+ if (!mask)
+ return 0;
+ }
+
+ /* ignore repeated requests */
+ if (mask && fs->stmnt_done & mask)
+ return 0;
+
+ /* temporary disable statmount() to avoid recursive
+ * mnt_fs_fetch_statmount() from mnt_fs_get...() functions */
+ if (fs->stmnt)
+ status = mnt_statmnt_disable_fetching(fs->stmnt, 1);
+
+ if (!fs->uniq_id) {
+ if (!fs->target) {
+ rc = -EINVAL;
+ goto done;
+ }
+ rc = mnt_id_from_path(fs->target, &fs->uniq_id, NULL);
+ if (rc)
+ goto done;
+ DBG(FS, ul_debugobj(fs, " uniq-ID=%" PRIu64, fs->uniq_id));
+ }
+
+ /* fetch all missing information by default */
+ if (!mask) {
+ mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC;
+ if (!fs->fstype)
+ mask |= STATMOUNT_FS_TYPE;
+ if (!fs->target)
+ mask |= STATMOUNT_MNT_POINT;
+ if (!fs->root)
+ mask |= STATMOUNT_MNT_ROOT;
+ if (!fs->fs_optstr)
+ mask |= STATMOUNT_MNT_OPTS;
+ if (!fs->ns_id)
+ mask |= STATMOUNT_MNT_NS_ID;
+ }
+ if (!mask)
+ goto done;
+
+ if (fs->ns_id)
+ ns = fs->ns_id;
+
+ if (fs->stmnt) {
+ DBG(FS, ul_debugobj(fs, " reuse libmnt_stmnt"));
+ memset(fs->stmnt->buf, 0, fs->stmnt->bufsiz);
+ rc = sys_statmount(fs->uniq_id, 0, mask,
+ &fs->stmnt->buf, &fs->stmnt->bufsiz, 0);
+ buf = fs->stmnt->buf;
+ bufsiz = fs->stmnt->bufsiz;
+ } else {
+ DBG(FS, ul_debugobj(fs, " use private buffer"));
+ rc = sys_statmount(fs->uniq_id, 0, mask, &buf, &bufsiz, 0);
+ }
+ DBG(FS, ul_debugobj(fs, " statmount [rc=%d bufsiz=%zu ns=%" PRIu64 " mask: %s%s%s%s%s%s]",
+ rc, bufsiz, ns,
+ mask & STATMOUNT_SB_BASIC ? "sb-basic " : "",
+ mask & STATMOUNT_MNT_BASIC ? "mnt-basic " : "",
+ mask & STATMOUNT_MNT_ROOT ? "mnt-root " : "",
+ mask & STATMOUNT_MNT_POINT ? "mnt-point " : "",
+ mask & STATMOUNT_FS_TYPE ? "fs-type " : "",
+ mask & STATMOUNT_MNT_OPTS ? "mnt-opts " : ""));
+
+ if (!rc)
+ rc = apply_statmount(fs, buf);
+done:
+
+ if (fs->stmnt)
+ mnt_statmnt_disable_fetching(fs->stmnt, status);
+ else
+ free(buf);
+
+ fs->stmnt_done |= mask;
+ return rc;
+}
+
+#else /* HAVE_STATMOUNT_API */
+
+int mnt_fs_fetch_statmount(struct libmnt_fs *fs __attribute__((__unused__)),
+ uint64_t mask __attribute__((__unused__)))
+{
+ return -ENOTSUP;
+}
+
+#endif /* HAVE_STATMOUNT_API */
diff --git a/libmount/src/fuzz.c b/libmount/src/fuzz.c
index 2c84714430..f4c0f8ae66 100644
--- a/libmount/src/fuzz.c
+++ b/libmount/src/fuzz.c
@@ -4,7 +4,6 @@
#include <stdlib.h>
#include <stddef.h>
-#include <stdint.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
struct libmnt_table *tb = NULL;
diff --git a/libmount/src/hook_idmap.c b/libmount/src/hook_idmap.c
index 0fa9335597..4592f5ef6a 100644
--- a/libmount/src/hook_idmap.c
+++ b/libmount/src/hook_idmap.c
@@ -20,12 +20,10 @@
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
-#include <inttypes.h>
#include "strutils.h"
#include "all-io.h"
#include "namespace.h"
-#include "mount-api-utils.h"
#include "mountP.h"
diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c
index a2a4aa6361..e128c4a680 100644
--- a/libmount/src/hook_mount.c
+++ b/libmount/src/hook_mount.c
@@ -46,11 +46,8 @@
#include "mountP.h"
#include "fileutils.h" /* statx() fallback */
#include "strutils.h"
-#include "mount-api-utils.h"
#include "linux_version.h"
-#include <inttypes.h>
-
#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
#define get_sysapi(_cxt) mnt_context_get_sysapi(_cxt)
diff --git a/libmount/src/hook_subdir.c b/libmount/src/hook_subdir.c
index 1ccf7b520c..5949af7d82 100644
--- a/libmount/src/hook_subdir.c
+++ b/libmount/src/hook_subdir.c
@@ -19,7 +19,6 @@
#include "mountP.h"
#include "fileutils.h"
-#include "mount-api-utils.h"
struct hookset_data {
char *subdir;
diff --git a/libmount/src/hooks.c b/libmount/src/hooks.c
index 6b9bd4bb25..23eca4efdc 100644
--- a/libmount/src/hooks.c
+++ b/libmount/src/hooks.c
@@ -27,7 +27,6 @@
* Usually implemented by locally defined 'struct hook_data' in hook_*.c.
*/
#include "mountP.h"
-#include "mount-api-utils.h"
/* built-in hooksets */
static const struct libmnt_hookset *const hooksets[] =
diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in
index 0300584ecc..33c6381c86 100644
--- a/libmount/src/libmount.h.in
+++ b/libmount/src/libmount.h.in
@@ -31,6 +31,7 @@ extern "C" {
#include <stdio.h>
#include <mntent.h>
#include <sys/types.h>
+#include <stdint.h>
/* Make sure libc MS_* definitions are used by default. Note that MS_* flags
* may be already defined by linux/fs.h or another file -- in this case we
@@ -140,6 +141,13 @@ struct libmnt_tabdiff;
*/
struct libmnt_ns;
+/**
+ * libmnt_statmnt
+ *
+ * Setting for statmount()
+ */
+struct libmnt_statmnt;
+
/*
* Actions
*/
@@ -368,6 +376,8 @@ extern char *mnt_get_mountpoint(const char *path)
extern int mnt_guess_system_root(dev_t devno, struct libmnt_cache *cache, char **path)
__ul_attribute__((nonnull(3)));
+extern int mnt_id_from_path(const char *path, uint64_t *uniq_id, int *id);
+
/* cache.c */
extern struct libmnt_cache *mnt_new_cache(void)
__ul_attribute__((warn_unused_result));
@@ -530,8 +540,17 @@ extern const char *mnt_fs_get_root(struct libmnt_fs *fs);
extern int mnt_fs_set_root(struct libmnt_fs *fs, const char *path);
extern const char *mnt_fs_get_bindsrc(struct libmnt_fs *fs);
extern int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src);
+
extern int mnt_fs_get_id(struct libmnt_fs *fs);
+extern uint64_t mnt_fs_get_uniq_id(struct libmnt_fs *fs);
+extern int mnt_fs_set_uniq_id(struct libmnt_fs *fs, uint64_t id);
+
extern int mnt_fs_get_parent_id(struct libmnt_fs *fs);
+extern uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs);
+
+extern uint64_t mnt_fs_get_ns(struct libmnt_fs *fs);
+extern int mnt_fs_set_ns(struct libmnt_fs *fs, uint64_t id);
+
extern dev_t mnt_fs_get_devno(struct libmnt_fs *fs);
extern pid_t mnt_fs_get_tid(struct libmnt_fs *fs);
@@ -562,7 +581,18 @@ extern int mnt_fs_is_regularfs(struct libmnt_fs *fs);
extern void mnt_free_mntent(struct mntent *mnt);
extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt);
-/* tab-parse.c */
+/* fs_statmount.c */
+extern struct libmnt_statmnt *mnt_new_statmnt(void);
+extern void mnt_ref_statmnt(struct libmnt_statmnt *sm);
+extern void mnt_unref_statmnt(struct libmnt_statmnt *sm);
+extern int mnt_statmnt_set_mask(struct libmnt_statmnt *sm, uint64_t mask);
+extern int mnt_statmnt_disable_fetching(struct libmnt_statmnt *sm, int disable);
+
+extern int mnt_fs_refer_statmnt(struct libmnt_fs *fs, struct libmnt_statmnt *sm);
+extern struct libmnt_statmnt *mnt_fs_get_statmnt(struct libmnt_fs *fs);
+extern int mnt_fs_fetch_statmount(struct libmnt_fs *fs, uint64_t mask);
+
+/* tab_parse.c */
extern struct libmnt_table *mnt_new_table_from_file(const char *filename)
__ul_attribute__((warn_unused_result));
extern struct libmnt_table *mnt_new_table_from_dir(const char *dirname)
@@ -590,6 +620,8 @@ extern int mnt_reset_table(struct libmnt_table *tb);
extern int mnt_table_get_nents(struct libmnt_table *tb);
extern int mnt_table_is_empty(struct libmnt_table *tb);
+extern int mnt_table_refer_statmnt(struct libmnt_table *tb, struct libmnt_statmnt *sm);
+
extern int mnt_table_set_userdata(struct libmnt_table *tb, void *data);
extern void *mnt_table_get_userdata(struct libmnt_table *tb);
@@ -649,6 +681,8 @@ extern struct libmnt_fs *mnt_table_find_pair(struct libmnt_table *tb,
const char *target, int direction);
extern struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
dev_t devno, int direction);
+extern struct libmnt_fs *mnt_table_find_uniq_id(struct libmnt_table *tb, uint64_t id);
+extern struct libmnt_fs *mnt_table_find_id(struct libmnt_table *tb, int id);
extern int mnt_table_find_next_fs(struct libmnt_table *tb,
struct libmnt_iter *itr,
@@ -658,6 +692,14 @@ extern int mnt_table_find_next_fs(struct libmnt_table *tb,
extern int mnt_table_is_fs_mounted(struct libmnt_table *tb, struct libmnt_fs *fstab_fs);
+/* tab_listmount.c */
+extern int mnt_table_listmount_set_id(struct libmnt_table *tb, uint64_t id);
+extern int mnt_table_listmount_set_ns(struct libmnt_table *tb, uint64_t ns);
+extern int mnt_table_listmount_set_stepsiz(struct libmnt_table *tb, size_t sz);
+
+extern int mnt_table_enable_listmount(struct libmnt_table *tb, int enable);
+extern int mnt_table_fetch_listmount(struct libmnt_table *tb);
+
/* tab_update.c */
extern struct libmnt_update *mnt_new_update(void)
__ul_attribute__((warn_unused_result));
diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym
index ed5acbb2f6..284869a357 100644
--- a/libmount/src/libmount.sym
+++ b/libmount/src/libmount.sym
@@ -385,4 +385,26 @@ MOUNT_2_40 {
MOUNT_2_41 {
mnt_context_get_nmesgs;
mnt_context_get_mesgs;
+ mnt_fs_fetch_statmount;
+ mnt_fs_get_ns;
+ mnt_fs_get_parent_uniq_id;
+ mnt_fs_get_statmnt;
+ mnt_fs_get_uniq_id;
+ mnt_fs_refer_statmnt;
+ mnt_fs_set_ns;
+ mnt_fs_set_uniq_id;
+ mnt_id_from_path;
+ mnt_new_statmnt;
+ mnt_ref_statmnt;
+ mnt_statmnt_disable_fetching;
+ mnt_statmnt_set_mask;
+ mnt_table_enable_listmount;
+ mnt_table_fetch_listmount;
+ mnt_table_find_id;
+ mnt_table_find_uniq_id;
+ mnt_table_listmount_set_id;
+ mnt_table_listmount_set_ns;
+ mnt_table_listmount_set_stepsiz;
+ mnt_table_refer_statmnt;
+ mnt_unref_statmnt;
} MOUNT_2_40;
diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
index 365a6e0f0f..47007d6c8c 100644
--- a/libmount/src/mountP.h
+++ b/libmount/src/mountP.h
@@ -23,13 +23,18 @@
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
+#include <stdint.h>
+#include <inttypes.h>
#include "c.h"
+
#include "list.h"
#include "debug.h"
#include "buffer.h"
#include "libmount.h"
+#include "mount-api-utils.h"
+
/*
* Debug
*/
@@ -50,6 +55,7 @@
#define MNT_DEBUG_VERITY (1 << 14)
#define MNT_DEBUG_HOOK (1 << 15)
#define MNT_DEBUG_OPTLIST (1 << 16)
+#define MNT_DEBUG_STATMNT (1 << 17)
#define MNT_DEBUG_ALL 0xFFFFFF
@@ -96,6 +102,9 @@ struct libmnt_test {
extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]);
#endif
+/* private tab_listmount.c */
+struct libmnt_listmnt;
+
/* utils.c */
extern int mnt_valid_tagname(const char *tagname);
@@ -134,6 +143,8 @@ extern int mnt_is_path(const char *target);
extern int mnt_tmptgt_unshare(int *old_ns_fd);
extern int mnt_tmptgt_cleanup(int old_ns_fd);
+extern int mnt_id_from_fd(int fd, uint64_t *uniq_id, int *id);
+
/* tab.c */
extern int is_mountinfo(struct libmnt_table *tb);
extern int mnt_table_set_parser_fltrcb( struct libmnt_table *tb,
@@ -156,6 +167,11 @@ extern int __mnt_table_is_fs_mounted( struct libmnt_table *tb,
extern int mnt_table_enable_noautofs(struct libmnt_table *tb, int ignore);
extern int mnt_table_is_noautofs(struct libmnt_table *tb);
+/* tab_listmount.c */
+extern int mnt_table_next_lsmnt(struct libmnt_table *tb, int direction);
+extern int mnt_table_reset_listmount(struct libmnt_table *tb);
+extern int mnt_table_want_listmount(struct libmnt_table *tb);
+
/*
* Generic iterator
*/
@@ -186,6 +202,20 @@ struct libmnt_iter {
/*
+ * statmount setting; shared between tables and filesystems
+ */
+struct libmnt_statmnt {
+ int refcount;
+ uint64_t mask; /* default statmount() mask */
+
+ struct ul_statmount *buf;
+ size_t bufsiz;
+
+ unsigned int disabled: 1; /* enable or disable statmount() */
+};
+
+
+/*
* This struct represents one entry in a fstab/mountinfo file.
* (note that fstab[1] means the first column from fstab, and so on...)
*/
@@ -199,7 +229,11 @@ struct libmnt_fs {
struct libmnt_optlist *optlist;
int id; /* mountinfo[1]: ID */
+ uint64_t uniq_id; /* unique node ID; statx(STATX_MNT_ID_UNIQUE); statmount->mnt_id */
+ uint64_t ns_id; /* namespace ID; statmount->mnt_ns_id */
+
int parent; /* mountinfo[2]: parent */
+ uint64_t uniq_parent; /* unique parent ID; statmount->mnt_parent_id */
dev_t devno; /* mountinfo[3]: st_dev */
char *bindsrc; /* utab, full path from fstab[1] for bind mounts */
@@ -215,7 +249,10 @@ struct libmnt_fs {
char *optstr; /* fstab[4], merged options */
char *vfs_optstr; /* mountinfo[6]: fs-independent (VFS) options */
+
char *opt_fields; /* mountinfo[7]: optional fields */
+ uint64_t propagation; /* statmmount() or parsed opt_fields */
+
char *fs_optstr; /* mountinfo[11]: fs-dependent options */
char *user_optstr; /* userspace mount options */
char *attrs; /* mount attributes */
@@ -232,6 +269,9 @@ struct libmnt_fs {
int flags; /* MNT_FS_* flags */
pid_t tid; /* /proc/<tid>/mountinfo otherwise zero */
+ uint64_t stmnt_done; /* mask of already called masks */
+ struct libmnt_statmnt *stmnt; /* statmount() stuff */
+
char *comment; /* fstab comment */
void *userdata; /* library independent data */
@@ -246,6 +286,16 @@ struct libmnt_fs {
#define MNT_FS_KERNEL (1 << 4) /* data from /proc/{mounts,self/mountinfo} */
#define MNT_FS_MERGED (1 << 5) /* already merged data from /run/mount/utab */
+#ifdef HAVE_STATMOUNT_API
+# define mnt_fs_try_statmount(FS, MEMBER, FLAGS) __extension__ ({ \
+ if (!(FS)->MEMBER \
+ && (FS)->stmnt \
+ && !(FS)->stmnt->disabled \
+ && ((FLAGS) & ~((FS)->stmnt_done))) \
+ mnt_fs_fetch_statmount((FS), (FLAGS)); })
+#endif
+
+
/*
* fstab/mountinfo file
*/
@@ -265,6 +315,9 @@ struct libmnt_table {
int (*fltrcb)(struct libmnt_fs *fs, void *data);
void *fltrcb_data;
+ struct libmnt_listmnt *lsmnt; /* listmount() stuff */
+ struct libmnt_statmnt *stmnt; /* statmount() stuff */
+
int noautofs; /* ignore autofs mounts */
struct list_head ents; /* list of entries (libmnt_fs) */
diff --git a/libmount/src/optlist.c b/libmount/src/optlist.c
index 4ca3ef4b41..256c592fde 100644
--- a/libmount/src/optlist.c
+++ b/libmount/src/optlist.c
@@ -16,7 +16,6 @@
#include "strutils.h"
#include "list.h"
#include "mountP.h"
-#include "mount-api-utils.h"
#define MNT_OL_MAXMAPS 8
diff --git a/libmount/src/tab.c b/libmount/src/tab.c
index 2b89552eb2..8508715eab 100644
--- a/libmount/src/tab.c
+++ b/libmount/src/tab.c
@@ -116,6 +116,8 @@ int mnt_reset_table(struct libmnt_table *tb)
}
tb->nents = 0;
+ mnt_table_reset_listmount(tb);
+
return 0;
}
@@ -172,6 +174,13 @@ void mnt_free_table(struct libmnt_table *tb)
mnt_unref_cache(tb->cache);
free(tb->comm_intro);
free(tb->comm_tail);
+
+ free(tb->lsmnt);
+ tb->lsmnt = NULL;
+
+ mnt_unref_statmnt(tb->stmnt);
+ tb->stmnt = NULL;
+
free(tb);
}
@@ -396,6 +405,39 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *tb)
}
/**
+ * mnt_table_refer_statmnt:
+ * @tb: pointer to tab
+ * @sm: statmount setting or NULL
+ *
+ * Add a reference to the statmount() setting in the table (see
+ * mnt_new_statmnt() function, etc.). This reference will automatically be
+ * used for any newly added filesystems in the @tb, eliminating the need for
+ * extra mnt_fs_refer_statmnt() calls for each filesystem.
+ *
+ * The reference is not removed by mnt_reset_table(), use NULL as @sm to
+ * remove the reference.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ *
+ * Since: 2.41
+ */
+int mnt_table_refer_statmnt(struct libmnt_table *tb, struct libmnt_statmnt *sm)
+{
+ if (!tb)
+ return -EINVAL;
+ if (tb->stmnt == sm)
+ return 0;
+
+ mnt_unref_statmnt(tb->stmnt);
+ mnt_ref_statmnt(sm);
+
+ DBG(TAB, ul_debugobj(tb, "refer statmnt"));
+
+ tb->stmnt = sm;
+ return 0;
+}
+
+/**
* mnt_table_find_fs:
* @tb: tab pointer
* @fs: entry to look for
@@ -455,6 +497,9 @@ int mnt_table_add_fs(struct libmnt_table *tb, struct libmnt_fs *fs)
DBG(TAB, ul_debugobj(tb, "add entry: %s %s",
mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+ if (tb->stmnt)
+ mnt_fs_refer_statmnt(fs, tb->stmnt);
+
return 0;
}
@@ -462,18 +507,26 @@ static int __table_insert_fs(
struct libmnt_table *tb, int before,
struct libmnt_fs *pos, struct libmnt_fs *fs)
{
- struct list_head *head = pos ? &pos->ents : &tb->ents;
-
- if (before)
- list_add(&fs->ents, head);
+ if (!pos)
+ list_add_tail(&fs->ents, &tb->ents);
+ else if (before)
+ list_add_tail(&fs->ents, &pos->ents);
else
- list_add_tail(&fs->ents, head);
+ list_add(&fs->ents, &pos->ents);
fs->tab = tb;
tb->nents++;
- DBG(TAB, ul_debugobj(tb, "insert entry: %s %s",
+ if (mnt_fs_get_uniq_id(fs)) {
+ DBG(TAB, ul_debugobj(tb, "insert entry: %" PRIu64, mnt_fs_get_uniq_id(fs)));
+ } else {
+ DBG(TAB, ul_debugobj(tb, "insert entry: %s %s",
mnt_fs_get_source(fs), mnt_fs_get_target(fs)));
+ }
+
+ if (tb->stmnt)
+ mnt_fs_refer_statmnt(fs, tb->stmnt);
+
return 0;
}
@@ -802,7 +855,23 @@ int mnt_table_next_fs(struct libmnt_table *tb, struct libmnt_iter *itr, struct l
return -EINVAL;
if (fs)
*fs = NULL;
-
+#ifdef HAVE_STATMOUNT_API
+ if (mnt_table_want_listmount(tb) &&
+ (list_empty(&tb->ents) || itr->p == itr->head)) {
+ struct list_head *prev = NULL;
+
+ if (itr->p)
+ prev = IS_ITER_FORWARD(itr) ? itr->p->prev : itr->p->next;
+ rc = mnt_table_next_lsmnt(tb, itr->direction);
+ if (rc)
+ return rc;
+ MNT_ITER_INIT(itr, &tb->ents);
+ if (prev) {
+ itr->p = prev;
+ MNT_ITER_ITERATE(itr);
+ }
+ }
+#endif
if (!itr->head)
MNT_ITER_INIT(itr, &tb->ents);
if (itr->p != itr->head) {
@@ -1472,6 +1541,67 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
return NULL;
}
+/**
+ * mnt_table_find_id:
+ * @tb: mount table
+ * @id: classic mount ID
+ *
+ * See also mnt_id_from_path().
+ *
+ * Returns: a tab entry or NULL.
+ *
+ * Since: 2.41
+ */
+struct libmnt_fs *mnt_table_find_id(struct libmnt_table *tb, int id)
+{
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_iter itr;
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "lookup ID: %d", id));
+ mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
+
+ while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_get_id(fs) == id)
+ return fs;
+ }
+
+ return NULL;
+}
+
+/**
+ * mnt_table_find_uniq_id:
+ * @tb: mount table
+ * @id: uniqie 64-bit mount ID
+ *
+ * See also mnt_id_from_path().
+ *
+ * Returns: a tab entry or NULL.
+ *
+ * Since: 2.41
+ */
+struct libmnt_fs *mnt_table_find_uniq_id(struct libmnt_table *tb, uint64_t id)
+{
+ struct libmnt_fs *fs = NULL;
+ struct libmnt_iter itr;
+
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "lookup uniq-ID: %" PRIu64, id));
+ mnt_reset_iter(&itr, MNT_ITER_BACKWARD);
+
+ while (mnt_table_next_fs(tb, &itr, &fs) == 0) {
+ if (mnt_fs_get_uniq_id(fs) == id)
+ return fs;
+ }
+
+ return NULL;
+}
+
+
static char *remove_mountpoint_from_path(const char *path, const char *mnt)
{
char *res;
diff --git a/libmount/src/tab_listmount.c b/libmount/src/tab_listmount.c
new file mode 100644
index 0000000000..e55786f278
--- /dev/null
+++ b/libmount/src/tab_listmount.c
@@ -0,0 +1,424 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * This file is part of libmount from util-linux project.
+ *
+ * Copyright (C) 2024 Karel Zak <kzak@redhat.com>
+ *
+ * libmount is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+#include "mountP.h"
+
+#ifndef HAVE_STATMOUNT_API
+
+int mnt_table_listmount_set_id(
+ struct libmnt_table *tb __attribute__((__unused__)),
+ uint64_t id __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_listmount_set_ns(
+ struct libmnt_table *tb __attribute__((__unused__)),
+ uint64_t ns __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_listmount_set_stepsiz(
+ struct libmnt_table *tb __attribute__((__unused__)),
+ size_t sz __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_enable_listmount(
+ struct libmnt_table *tb __attribute__((__unused__)),
+ int enable __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_fetch_listmount(struct libmnt_table *tb __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_reset_listmount(struct libmnt_table *tb __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+int mnt_table_next_lsmnt(struct libmnt_table *tb __attribute__((__unused__)),
+ int direction __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+
+#else /* HAVE_STATMOUNT_API */
+
+/*
+* This struct is not shared between multiple tables, so reference counting is
+* not used for it.
+ */
+struct libmnt_listmnt {
+
+ uint64_t id; /* node ID (LSMT_ROOT for "/") */
+ uint64_t ns; /* namespce ID or zero for the current */
+ uint64_t last; /* last ID from previous listmount() call */
+ size_t stepsiz; /* how many IDs read in one step */
+ uint64_t *list; /* buffer for IDs */
+
+ unsigned int enabled : 1, /* on-demand listmount status */
+ done : 1, /* we already have all data */
+ reverse : 1; /* current setting */
+};
+
+/* default number of IDs read by one listmount() call */
+#define MNT_LSMNT_STEPSIZ 512
+
+static int table_init_listmount(struct libmnt_table *tb, size_t stepsiz)
+{
+ struct libmnt_listmnt *ls = NULL;;
+
+ if (!tb)
+ return -EINVAL;
+ if (!stepsiz)
+ stepsiz = MNT_LSMNT_STEPSIZ;
+
+ ls = tb->lsmnt;
+
+ /* check if supported by current kernel */
+ if (!ls) {
+ uint64_t dummy;
+
+ errno = 0;
+ if (ul_listmount(LSMT_ROOT, 0, 0, &dummy, 1, LISTMOUNT_REVERSE) != 1) {
+ if (errno == ENOSYS)
+ DBG(TAB, ul_debugobj(tb, "listmount: unsuppported"));
+ if (errno == EINVAL)
+ DBG(TAB, ul_debugobj(tb, "listmount: reverse unsuppported"));
+ errno = ENOSYS;
+ return -ENOSYS;
+ }
+ }
+
+ /* reset if allocated for a different size */
+ if (ls && ls->stepsiz != stepsiz)
+ ls = NULL;
+
+ /* alloc libmnt_listmnt together with list buffer */
+ if (!ls) {
+ char *x = calloc(1, sizeof(struct libmnt_listmnt)
+ + (sizeof(uint64_t) * stepsiz));
+ if (!x)
+ return -ENOMEM;
+
+ ls = (struct libmnt_listmnt *) x;
+ ls->list = (uint64_t *) (x + sizeof(struct libmnt_listmnt));
+ ls->stepsiz = stepsiz;
+ ls->id = LSMT_ROOT; /* default */
+ }
+
+ /* reuse old setting */
+ if (tb->lsmnt) {
+ ls->id = tb->lsmnt->id;
+ ls->ns = tb->lsmnt->ns;
+ ls->last = tb->lsmnt->last;
+ ls->enabled = tb->lsmnt->enabled;
+ ls->reverse = tb->lsmnt->reverse;
+ free(tb->lsmnt);
+ }
+
+ tb->lsmnt = ls;
+
+ DBG(TAB, ul_debugobj(tb, "listmount: init [step=%zu]", ls->stepsiz));
+ return 0;
+}
+
+/**
+ * mnt_table_listmount_set_id:
+ * @tb: mount table
+ * @id: root ID
+ *
+ * Set root ID for the table if the table is read from kernel by
+ * listmount() syscall. The default is to read all filesystems; use
+ * statx(STATX_MNT_ID_UNIQUE) for subdirectory.
+ *
+ * Returns: 0 on sucess, < 0 on error
+ * Since: 2.41
+ */
+int mnt_table_listmount_set_id(struct libmnt_table *tb, uint64_t id)
+{
+ int rc = 0;
+
+ if (!tb)
+ return -EINVAL;
+ if (!tb->lsmnt && (rc = table_init_listmount(tb, 0)) != 0)
+ return rc;
+ tb->lsmnt->id = id;
+ return 0;
+}
+
+/**
+ * mnt_table_listmount_set_ns:
+ * @tb: mount table
+ * @id: namespace ID
+ *
+ * Set namespace ID for listmount().
+ *
+ * Returns: 0 on sucess, < 0 on error
+ * Since: 2.41
+ */
+int mnt_table_listmount_set_ns(struct libmnt_table *tb, uint64_t ns)
+{
+ int rc = 0;
+
+ if (!tb)
+ return -EINVAL;
+ if (!tb->lsmnt && (rc = table_init_listmount(tb, 0)) != 0)
+ return rc;
+ tb->lsmnt->ns = ns;
+ return 0;
+}
+
+/**
+ * mnt_table_listmount_set_stepsiz:
+ * @tb: mount table
+ * @sz: number of nodes read by one libmount() call
+ *
+ * Returns: 0 on sucess, < 0 on error
+ * Since: 2.41
+ */
+int mnt_table_listmount_set_stepsiz(struct libmnt_table *tb, size_t sz)
+{
+ if (!tb)
+ return -EINVAL;
+
+ return table_init_listmount(tb, sz);
+}
+
+/*
+ * This function is called by mnt_reset_table() and the table must already be
+ * empty.
+ *
+ * Private; not export to library API!
+ **/
+int mnt_table_reset_listmount(struct libmnt_table *tb)
+{
+ if (!tb || !tb->lsmnt)
+ return 0;
+ if (tb->nents)
+ return -EINVAL;
+
+ tb->lsmnt->done = 0;
+ tb->lsmnt->reverse = 0;
+ tb->lsmnt->last = 0;
+ return 0;
+}
+
+/**
+ * mnt_table_enable_listmount:
+ * @tb: table
+ * @enable: 0 or 1
+ *
+ * Enable or disable on-demand listmount() to make it usable by
+ * mnt_table_next_fs(). This function does not affect
+ * mnt_table_fetch_listmont().
+ *
+ * Returns: old status (1 or 0)
+ * Since: 2.41
+ */
+int mnt_table_enable_listmount(struct libmnt_table *tb, int enable)
+{
+ int old = 0;
+
+ if (tb && tb->lsmnt) {
+ old = tb->lsmnt->enabled;
+ tb->lsmnt->enabled = enable;
+ DBG(TAB, ul_debugobj(tb, "listmount() %s",
+ enable ? "on" : "off"));
+ }
+ return old;
+}
+
+/* private; returns 1 if on-demand listmount() possible */
+int mnt_table_want_listmount(struct libmnt_table *tb)
+{
+ return tb && tb->lsmnt && tb->lsmnt->enabled;
+}
+
+/* add new entries from list[] to table */
+static int lsmnt_to_table(
+ struct libmnt_table *tb, struct libmnt_listmnt *ls,
+ size_t nitems, int reverse)
+{
+ int rc = 0;
+ size_t i;
+ struct libmnt_fs *prev = NULL;
+
+ if (reverse)
+ mnt_table_first_fs(tb, &prev);
+ else
+ mnt_table_last_fs(tb, &prev);
+ if (prev)
+ mnt_ref_fs(prev);
+
+ DBG(TAB, ul_debugobj(tb, "listmount: insert %zu", nitems));
+
+ for (i = 0; rc == 0 && i < nitems; i++) {
+ struct libmnt_fs *fs;
+ uint64_t id = ls->list[i];
+
+ if (!id)
+ continue;
+
+ fs = mnt_new_fs();
+ if (fs) {
+ fs->flags |= MNT_FS_KERNEL;
+ mnt_fs_set_uniq_id(fs, id);
+ if (ls && ls->ns)
+ mnt_fs_set_ns(fs, ls->ns);
+
+ rc = mnt_table_insert_fs(tb, reverse, prev, fs);
+ } else
+ rc = -ENOMEM;
+
+ mnt_unref_fs(prev);
+ prev = fs;
+ }
+
+ mnt_unref_fs(prev);
+ return rc;
+}
+
+/*
+ * Private function, backed of mnt_table_next_fs().
+ *
+ * Return: 0 on success, 1 if not more data, <0 on error.
+ */
+int mnt_table_next_lsmnt(struct libmnt_table *tb, int direction)
+{
+ ssize_t n;
+ int reverse = direction == MNT_ITER_BACKWARD;
+ struct libmnt_listmnt *ls = NULL;
+ int rc = 0;
+
+ if (!tb || !tb->lsmnt)
+ return -EINVAL;
+ if (tb->lsmnt->done || !tb->lsmnt->enabled)
+ return 1;
+
+ ls = tb->lsmnt;
+
+ /* disable on-demand fetching */
+ mnt_table_enable_listmount(tb, 0);
+
+ /* read all to avoid mixing order in the table */
+ if (!mnt_table_is_empty(tb) && ls->reverse != reverse) {
+ rc = mnt_table_fetch_listmount(tb);
+ goto done;
+ }
+
+ ls->reverse = reverse;
+
+ DBG(TAB, ul_debugobj(tb, "listmount: call "
+ "[id=%" PRIu64", ns=%" PRIu64
+ "last=%" PRIu64", sz=%zu %s]",
+ ls->id, ls->ns,
+ ls->last, ls->stepsiz,
+ ls->reverse ? "reverse" : ""));
+
+ n = ul_listmount(ls->id, ls->ns, ls->last, ls->list, ls->stepsiz,
+ reverse ? LISTMOUNT_REVERSE : 0);
+ if (n < 0) {
+ rc = -errno;
+ goto done;
+ }
+
+ if (n < (ssize_t) ls->stepsiz)
+ ls->done = 1;
+ if (n > 0) {
+ ls->last = ls->list[ n - 1 ];
+ rc = lsmnt_to_table(tb, ls, n, reverse);
+ } else
+ rc = 0;
+done:
+ mnt_table_enable_listmount(tb, 1);
+
+ DBG(TAB, ul_debugobj(tb, "listmount: on-demand done [rc=%d]", rc));
+ return rc; /* nothing */
+}
+
+/**
+ * mnt_table_fetch_listmount:
+ * @tb: table instance
+ *
+ * By default, this function reads all mount nodes in the current namespace
+ * from the kernel and adds them to the @tb table. This default behavior can
+ * be modified using mnt_table_listmount_set_...().
+ *
+ * The table is reset (all file systems removed) before new data is added.
+ *
+ * Return: 0 on success, <0 on error.
+ * Since: 2.41
+ */
+int mnt_table_fetch_listmount(struct libmnt_table *tb)
+{
+ int rc = 0, stmnt_status = 0, lsmnt_status = 0;
+ struct libmnt_listmnt *ls = NULL;
+ ssize_t n;
+
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "listmount: fetching all"));
+
+ if (!tb->lsmnt && (rc = table_init_listmount(tb, 0)) != 0)
+ return rc;
+
+ /* disable on-demand statmount() */
+ if (tb->stmnt)
+ stmnt_status = mnt_statmnt_disable_fetching(tb->stmnt, 1);
+ /* disable on-demand listmount() */
+ lsmnt_status = mnt_table_enable_listmount(tb, 0);
+
+ mnt_reset_table(tb);
+
+ ls = tb->lsmnt;
+
+ do {
+ DBG(TAB, ul_debugobj(tb, "listmount: call "
+ "[id=%" PRIu64", ns=%" PRIu64
+ "last=%" PRIu64", sz=%zu]",
+ ls->id, ls->ns,
+ ls->last, ls->stepsiz));
+
+ n = ul_listmount(ls->id, ls->ns, ls->last,
+ ls->list, ls->stepsiz, 0);
+ if (n < 0) {
+ rc = -errno;
+ break;
+ }
+ ls->last = ls->list[ n - 1 ];
+ rc = lsmnt_to_table(tb, ls, n, 0);
+
+ } while (rc == 0 && n == (ssize_t) ls->stepsiz);
+
+ /* Avoid using on-demand mnt_table_next_lsmnt() if we already
+ * have all the necessary data (or on error) */
+ tb->lsmnt->done = 1;
+
+ /* restore */
+ if (tb->stmnt)
+ mnt_statmnt_disable_fetching(tb->stmnt, stmnt_status);
+ mnt_table_enable_listmount(tb, lsmnt_status);
+
+ DBG(TAB, ul_debugobj(tb, "listmount: fetching done [rc=%d]", rc));
+
+ return rc;
+}
+
+#endif /* HAVE_STATMOUNT_API */
diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c
index b8ec244f79..e5db3e385c 100644
--- a/libmount/src/tab_parse.c
+++ b/libmount/src/tab_parse.c
@@ -323,7 +323,14 @@ static int mnt_parse_utab_line(struct libmnt_fs *fs, const char *s)
if (!*p)
break;
- if (!fs->id && !strncmp(p, "ID=", 3)) {
+ if (!fs->uniq_id && !strncmp(p, "UNIQID=", 7)) {
+ int rc = 0;
+
+ end = next_u64(p + 7, &fs->uniq_id, &rc);
+ if (!end || rc)
+ return rc;
+
+ } else if (!fs->id && !strncmp(p, "ID=", 3)) {
int rc = 0;
end = next_s32(p + 3, &fs->id, &rc);
@@ -1178,6 +1185,7 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct
struct libmnt_iter itr;
const char *optstr, *src, *target, *root, *attrs;
int id;
+ uint64_t uniq_id;
if (!tb || !uf)
return NULL;
@@ -1190,6 +1198,7 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct
attrs = mnt_fs_get_attributes(uf);
root = mnt_fs_get_root(uf);
id = mnt_fs_get_id(uf);
+ uniq_id = mnt_fs_get_uniq_id(uf);
if (!src || !target || !root || (!attrs && !optstr))
return NULL;
@@ -1202,10 +1211,16 @@ static struct libmnt_fs *mnt_table_merge_user_fs(struct libmnt_table *tb, struct
if (fs->flags & MNT_FS_MERGED)
continue;
- if (id > 0 && mnt_fs_get_id(fs)) {
+ if (uniq_id > 0 && mnt_fs_get_uniq_id(fs)) {
+ DBG(TAB, ul_debugobj(tb, " using uniq ID"));
+ if (mnt_fs_get_uniq_id(fs) == uniq_id)
+ break;
+
+ } else if (id > 0 && mnt_fs_get_id(fs)) {
DBG(TAB, ul_debugobj(tb, " using ID"));
if (mnt_fs_get_id(fs) == id)
break;
+
} else if (r && strcmp(r, root) == 0
&& mnt_fs_streq_target(fs, target)
&& mnt_fs_streq_srcpath(fs, src))
diff --git a/libmount/src/tab_update.c b/libmount/src/tab_update.c
index fa07dc0d9f..a256edf858 100644
--- a/libmount/src/tab_update.c
+++ b/libmount/src/tab_update.c
@@ -446,9 +446,11 @@ static int fprintf_utab_fs(FILE *f, struct libmnt_fs *fs)
if (!fs || !f)
return -EINVAL;
- if (mnt_fs_get_id(fs) > 0) {
+ if (mnt_fs_get_id(fs) > 0)
rc = fprintf(f, "ID=%d ", mnt_fs_get_id(fs));
- }
+ if (mnt_fs_get_uniq_id(fs) > 0)
+ rc = fprintf(f, "UNIQID=%" PRIu64, mnt_fs_get_uniq_id(fs));
+
if (rc >= 0) {
p = mangle(mnt_fs_get_source(fs));
if (p) {
diff --git a/libmount/src/utils.c b/libmount/src/utils.c
index 94a877cf05..0601def97c 100644
--- a/libmount/src/utils.c
+++ b/libmount/src/utils.c
@@ -266,6 +266,73 @@ int mnt_is_readonly(const char *path)
return 0;
}
+#if defined(HAVE_STATX) && defined(HAVE_STRUCT_STATX) && defined(HAVE_STRUCT_STATX_STX_MNT_ID)
+static int get_mnt_id( int fd, const char *path,
+ uint64_t *uniq_id, int *id)
+{
+ int rc;
+ struct statx sx = { 0 };
+ int flags = AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT;
+
+ if (!path || !*path)
+ flags |= AT_EMPTY_PATH;
+
+ if (id) {
+ rc = statx(fd, path ? path : "", flags,
+ STATX_MNT_ID, &sx);
+ if (rc)
+ return rc;
+ *id = sx.stx_mnt_id;
+ }
+ if (uniq_id) {
+# ifdef STATX_MNT_ID_UNIQUE
+ errno = 0;
+ rc = statx(fd, path ? path : "", flags,
+ STATX_MNT_ID_UNIQUE, &sx);
+
+ if (rc && errno == EINVAL)
+ return -ENOSYS; /* *_ID_UNIQUE unsupported? */
+ if (rc)
+ return rc;
+ *uniq_id = sx.stx_mnt_id;
+# else
+ return -ENOSYS;
+# endif
+ }
+ return 0;
+}
+#else /* HAVE_STATX && HAVE_STRUCT_STATX && AVE_STRUCT_STATX_STX_MNT_ID */
+static int get_mnt_id( int fd __attribute__((__unused__)),
+ const char *path __attribute__((__unused__)),
+ uint64_t *uniq_id __attribute__((__unused__)),
+ int *id __attribute__((__unused__)))
+{
+ return -ENOSYS;
+}
+#endif
+
+int mnt_id_from_fd(int fd, uint64_t *uniq_id, int *id)
+{
+ return get_mnt_id(fd, NULL, uniq_id, id);
+}
+
+/**
+ * mnt_id_from_path:
+ * @path: mountpoint
+ * @uniq_id: returns STATX_MNT_ID_UNIQUE (optional)
+ * @id: returns STATX_MNT_ID (optional)
+ *
+ * Converts @path to ID.
+ *
+ * Returns: 0 on success, <0 on error
+ *
+ * Since: 2.41
+ */
+int mnt_id_from_path(const char *path, uint64_t *uniq_id, int *id)
+{
+ return get_mnt_id(-1, path, uniq_id, id);
+}
+
/**
* mnt_mangle:
* @str: string
diff --git a/libmount/src/version.c b/libmount/src/version.c
index b1e34327e8..3b61618b55 100644
--- a/libmount/src/version.c
+++ b/libmount/src/version.c
@@ -19,7 +19,6 @@
#include <ctype.h>
#include "mountP.h"
-#include "mount-api-utils.h"
static const char *lib_version = LIBMOUNT_VERSION;
static const char *lib_features[] = {
@@ -44,6 +43,9 @@ static const char *lib_features[] = {
#ifdef USE_LIBMOUNT_MOUNTFD_SUPPORT
"fd-based-mount",
#endif
+#ifdef HAVE_STATMOUNT_API
+ "statmount",
+#endif
#if defined(HAVE_STATX) && defined(HAVE_STRUCT_STATX) && defined(AT_STATX_DONT_SYNC)
"statx",
#endif