mnt_fs_streq_srcpath
mnt_fs_streq_target
mnt_fs_to_mntent
+mnt_fs_fetch_statmount
+mnt_fs_get_ns
+mnt_fs_set_ns
mnt_new_fs
mnt_reset_fs
</SECTION>
+<SECTION>
+<FILE>statmount</FILE>
+libmnt_statmnt
+mnt_new_statmnt
+mnt_ref_statmnt
+mnt_unref_statmnt
+mnt_statmnt_disable_fetching
+mnt_statmnt_set_mask
+mnt_fs_fetch_statmount
+mnt_fs_get_statmnt
+mnt_fs_refer_statmnt
+mnt_table_refer_statmnt
+</SECTION>
+
<SECTION>
<FILE>init</FILE>
mnt_init_debug
if LINUX
lib_mount_sources += '''
+ src/fs_statmount.c
src/hooks.c
src/monitor.c
src/optlist.c
if LINUX
check_PROGRAMS += \
- sample-mount-overwrite
+ sample-mount-overwrite \
+ sample-mount-statmount
endif
-sample_mount_cflags = $(AM_CFLAGS) -I$(ul_libmount_incdir)
-sample_mount_ldadd = libmount.la $(LDADD)
-
-
sample_mount_overwrite_SOURCES = libmount/samples/overwrite.c
-sample_mount_overwrite_LDADD = $(sample_mount_ldadd)
-sample_mount_overwrite_CFLAGS = $(sample_mount_cflags)
+sample_mount_overwrite_LDADD = libmount.la $(LDADD)
+sample_mount_overwrite_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
+
+sample_mount_statmount_SOURCES = libmount/samples/statmount.c
+sample_mount_statmount_LDADD = libmount.la $(LDADD) libcommon.la
+sample_mount_statmount_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir)
--- /dev/null
+/*
+ * Copyright (C) 2023 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 "libmount.h"
+
+#include <linux/mount.h> /* STATMOUNT_* masks */
+
+#include "mount-api-utils.h" /* fallback for old linux/mount.h */
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_fs *fs;
+ struct libmnt_statmnt *sm;
+ const char *mnt;
+ uint64_t id = 0;
+
+ if (argc != 2)
+ errx(EXIT_FAILURE, "usage: %s <mountpoint | id>",
+ program_invocation_short_name);
+
+ mnt_init_debug(0);
+
+ fs = mnt_new_fs();
+ if (!fs)
+ err(EXIT_FAILURE, "failed to allocate fs handler");
+
+ /* define target (mountpoint) or mount ID */
+ mnt = argv[1];
+ if (isdigit_string(mnt)) {
+ id = strtou64_or_err(mnt, "cannot ID");
+ mnt_fs_set_uniq_id(fs, id);
+ } else
+ mnt_fs_set_target(fs, mnt);
+
+ /*
+ * A) fetch all data without reference to libmnt_statmnt
+ */
+ if (mnt_fs_fetch_statmount(fs, 0) != 0)
+ warn("failed to read data by statmount()");
+ mnt_fs_print_debug(fs, stdout);
+
+ /* reset */
+ id = mnt_fs_get_uniq_id(fs);
+ mnt_reset_fs(fs);
+ mnt_fs_set_uniq_id(fs, id);
+
+ /*
+ * B) fetch data by on-demand way
+ */
+ sm = mnt_new_statmnt();
+ if (!sm)
+ err(EXIT_FAILURE, "failed to allocate statmount handler");
+
+ mnt_fs_refer_statmnt(fs, sm);
+
+ /* read fs type, but nothing else */
+ mnt_fs_get_fstype(fs);
+ mnt_fs_print_debug(fs, stdout);
+
+ /* read fs root, but nothing else */
+ mnt_fs_get_root(fs);
+ mnt_fs_print_debug(fs, stdout);
+
+ /* read all mising data */
+ mnt_fs_fetch_statmount(fs, 0);
+ mnt_fs_print_debug(fs, stdout);
+
+ /* see debug, this is no-op for statmount() */
+ mnt_fs_get_fstype(fs);
+
+ mnt_unref_fs(fs);
+ mnt_unref_statmnt(sm);
+
+ return EXIT_SUCCESS;
+}
libmount/src/context.c \
libmount/src/context_mount.c \
libmount/src/context_umount.c \
+ libmount/src/fs_statmount.c \
libmount/src/hooks.c \
libmount/src/hook_mount.c \
libmount/src/hook_mount_legacy.c \
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);
fs->refcount = ref;
*/
const char *mnt_fs_get_target(struct libmnt_fs *fs)
{
- return fs ? fs->target : NULL;
+ if (!fs)
+ return NULL;
+ if (!fs->target && mnt_fs_want_statmount(fs, STATMOUNT_MNT_POINT))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT);
+ return fs->target;;
}
/**
{
if (!fs || !flags)
return -EINVAL;
+ if (!fs->propagation && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
if (!fs->propagation && fs->opt_fields) {
/*
*/
int mnt_fs_is_pseudofs(struct libmnt_fs *fs)
{
+ if (!fs)
+ return 0;
+ if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
return mnt_fs_get_flags(fs) & MNT_FS_PSEUDO ? 1 : 0;
}
*/
int mnt_fs_is_netfs(struct libmnt_fs *fs)
{
+ if (!fs)
+ return 0;
+ if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
return mnt_fs_get_flags(fs) & MNT_FS_NET ? 1 : 0;
}
*/
const char *mnt_fs_get_fstype(struct libmnt_fs *fs)
{
- return fs ? fs->fstype : NULL;
+ if (!fs)
+ return NULL;
+
+ if (!fs->fstype && mnt_fs_want_statmount(fs, STATMOUNT_FS_TYPE))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_FS_TYPE);
+
+ return fs->fstype;
}
/* Used by the struct libmnt_file parser only */
return NULL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
+ else if (!fs->optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC);
errno = 0;
if (fs->optstr)
*/
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);
+ else if (!fs->optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC);
- return fs ? fs->optstr : NULL;
+ return fs->optstr;
}
/**
return NULL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
+ else if (!fs->fs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC);
return fs->fs_optstr;
}
return NULL;
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
+ else if (!fs->vfs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
return fs->vfs_optstr;
}
* 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)
*/
const char *mnt_fs_get_root(struct libmnt_fs *fs)
{
- return fs ? fs->root : NULL;
+ if (!fs)
+ return NULL;
+ if (!fs->root && mnt_fs_want_statmount(fs, STATMOUNT_MNT_ROOT))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_ROOT);
+
+ return fs->root;
}
/**
*/
int mnt_fs_get_id(struct libmnt_fs *fs)
{
- return fs ? fs->id : -EINVAL;
+ if (!fs)
+ return 0;
+ if (!fs->id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+ return fs->id;
}
/**
*/
uint64_t mnt_fs_get_uniq_id(struct libmnt_fs *fs)
{
- return fs ? fs->uniq_id : 0;
+ if (!fs)
+ return 0;
+ if (!fs->uniq_id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+ return fs->uniq_id;
}
/**
*/
int mnt_fs_get_parent_id(struct libmnt_fs *fs)
{
- return fs ? fs->parent : -EINVAL;
+ if (!fs)
+ return 0;
+ if (!fs->parent && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+ return fs->parent;
}
/**
*/
uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs)
{
- return fs ? fs->uniq_parent : 0;
+ if (!fs)
+ return 0;
+ if (!fs->uniq_parent && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC);
+
+ 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;
+ if (!fs->ns_id && mnt_fs_want_statmount(fs, STATMOUNT_MNT_NS_ID))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_NS_ID);
+ 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
*/
dev_t mnt_fs_get_devno(struct libmnt_fs *fs)
{
- return fs ? fs->devno : 0;
+ if (!fs)
+ return 0;
+ if (!fs->devno && mnt_fs_want_statmount(fs, STATMOUNT_SB_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_SB_BASIC);
+
+ return fs->devno;
}
/**
if (fs->optlist)
sync_opts_from_optlist(fs, fs->optlist);
+ else if (!fs->vfs_optstr && mnt_fs_want_statmount(fs, STATMOUNT_MNT_BASIC | STATMOUNT_SB_BASIC))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_BASIC | STATMOUNT_SB_BASIC);
if (fs->fs_optstr)
rc = mnt_optstr_get_option(fs->fs_optstr, name, value, valsz);
{
int rc = 0;
- if (!fs || !target || !fs->target)
+ if (!fs || !target)
+ return 0;
+ if (!fs->target && mnt_fs_want_statmount(fs, STATMOUNT_MNT_POINT))
+ mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT);
+ if (!fs->target)
return 0;
/* 1) native paths */
*/
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);
}
/**
*/
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));
+ fprintf(file, "auto-statmount: %s\n", stmnt_disabled ? "off" : "on");
+ 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));
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));
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)));
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;
}
--- /dev/null
+/* 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)
+{
+ 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;
+}
+
+/**
+ * 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, merge = 0;
+
+ if (!sm || !sm->size || !fs)
+ return -EINVAL;
+
+ merge = !fs->vfs_optstr || fs->fs_optstr;
+
+ 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;
+ }
+
+ }
+ }
+
+ 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);
+
+ 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);
+ }
+ }
+
+ if (!rc && merge) {
+ free(fs->optstr);
+
+ /* merge VFS and FS options to one string (we do the same in mountinfo parser) */
+ fs->optstr = mnt_fs_strdup_options(fs);
+ }
+
+ 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
+ && !(fs->stmnt_done & fs->stmnt->mask))
+ mask |= fs->stmnt->mask;
+
+ /* 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 */
*/
struct libmnt_ns;
+/**
+ * libmnt_statmnt
+ *
+ * Setting for statmount()
+ */
+struct libmnt_statmnt;
+
/*
* Actions
*/
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);
extern void mnt_free_mntent(struct mntent *mnt);
extern int mnt_fs_to_mntent(struct libmnt_fs *fs, struct mntent **mnt);
+/* 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 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);
} MOUNT_2_39;
MOUNT_2_41 {
- mnt_fs_get_uniq_id;
+ mnt_fs_fetch_statmount;
+ mnt_fs_get_ns_id;
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_refer_statmnt;
+ mnt_unref_statmnt;
} MOUNT_2_40;
#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
} while(0)
+/*
+ * 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...)
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 */
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 */
#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 */
+#define mnt_fs_want_statmount(_x, _m) \
+ ((_x)->stmnt && !(_x)->stmnt->disabled && !((_x)->stmnt_done & (_m)))
+
/*
* fstab/mountinfo file
*/
int (*fltrcb)(struct libmnt_fs *fs, void *data);
void *fltrcb_data;
+ struct libmnt_statmnt *stmnt; /* statmount() stuff */
+
int noautofs; /* ignore autofs mounts */
struct list_head ents; /* list of entries (libmnt_fs) */
mnt_unref_cache(tb->cache);
free(tb->comm_intro);
free(tb->comm_tail);
+
+ mnt_unref_statmnt(tb->stmnt);
+ tb->stmnt = NULL;
+
free(tb);
}
return tb ? tb->cache : NULL;
}
+/**
+ * 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
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;
}
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;
}