From: Karel Zak Date: Wed, 14 Aug 2024 15:10:45 +0000 (+0200) Subject: libmount: add support for statmount() X-Git-Tag: v2.42-start~97^2~29 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0affb7ae7b2ffdc66daab293e0f8289a133f3a9e;p=thirdparty%2Futil-linux.git libmount: add support for statmount() * introduce libmnt_statmnt object to store statmount mask and buffer (etc.) * add API for libmnt_fs to retrieve data from the kernel using statmount() * support on-demand statmount() from old mnt_fs_get_...() functions * allow libmnt_statmnt to be shared and reused between libmnt_fs instances * allow libmnt_statmnt to be assigned and used for filesystems in the table (assign libmnt_statmnt to the table to make it usable for all filesystems). * allow temporary disabling of fetching data from the kernel to avoid unwanted recursion in certain use-cases * support namespaces for statmount() based on the libmnt_fs namespace ID setting * allow user-defined statmount masks to overwrite the default * add a sample program Signed-off-by: Karel Zak --- diff --git a/libmount/docs/libmount-sections.txt b/libmount/docs/libmount-sections.txt index b72229c58..79c6055e5 100644 --- a/libmount/docs/libmount-sections.txt +++ b/libmount/docs/libmount-sections.txt @@ -281,10 +281,27 @@ mnt_fs_strdup_options 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 +
+statmount +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 +
+
init mnt_init_debug diff --git a/libmount/meson.build b/libmount/meson.build index a7145c5b4..335523d26 100644 --- a/libmount/meson.build +++ b/libmount/meson.build @@ -43,6 +43,7 @@ lib_mount_sources = ''' if LINUX lib_mount_sources += ''' + src/fs_statmount.c src/hooks.c src/monitor.c src/optlist.c diff --git a/libmount/samples/Makemodule.am b/libmount/samples/Makemodule.am index b9201e925..dc9f641f4 100644 --- a/libmount/samples/Makemodule.am +++ b/libmount/samples/Makemodule.am @@ -1,13 +1,14 @@ 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) diff --git a/libmount/samples/statmount.c b/libmount/samples/statmount.c new file mode 100644 index 000000000..c480206cf --- /dev/null +++ b/libmount/samples/statmount.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Karel Zak + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + */ +#include + +#include "c.h" +#include "strutils.h" +#include "libmount.h" + +#include /* 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 ", + 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; +} diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am index 66963bb09..9c87e0abe 100644 --- a/libmount/src/Makemodule.am +++ b/libmount/src/Makemodule.am @@ -30,6 +30,7 @@ libmount_la_SOURCES += \ 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 \ diff --git a/libmount/src/fs.c b/libmount/src/fs.c index d8886dc38..e466bca13 100644 --- a/libmount/src/fs.c +++ b/libmount/src/fs.c @@ -102,6 +102,10 @@ void mnt_reset_fs(struct libmnt_fs *fs) 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; @@ -611,7 +615,11 @@ 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; + if (!fs->target && mnt_fs_want_statmount(fs, STATMOUNT_MNT_POINT)) + mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_POINT); + return fs->target;; } /** @@ -657,6 +665,8 @@ int mnt_fs_get_propagation(struct libmnt_fs *fs, unsigned long *flags) { 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) { /* @@ -707,6 +717,11 @@ int mnt_fs_is_swaparea(struct libmnt_fs *fs) */ 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; } @@ -718,6 +733,11 @@ int mnt_fs_is_pseudofs(struct libmnt_fs *fs) */ 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; } @@ -744,7 +764,13 @@ 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; + + 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 */ @@ -866,6 +892,8 @@ char *mnt_fs_strdup_options(struct libmnt_fs *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); errno = 0; if (fs->optstr) @@ -890,10 +918,14 @@ 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); + 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; } /** @@ -1062,6 +1094,8 @@ const char *mnt_fs_get_fs_options(struct libmnt_fs *fs) 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; } @@ -1078,6 +1112,8 @@ const char *mnt_fs_get_vfs_options(struct libmnt_fs *fs) 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; } @@ -1154,6 +1190,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) @@ -1258,7 +1297,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; + if (!fs->root && mnt_fs_want_statmount(fs, STATMOUNT_MNT_ROOT)) + mnt_fs_fetch_statmount(fs, STATMOUNT_MNT_ROOT); + + return fs->root; } /** @@ -1368,7 +1412,12 @@ int mnt_fs_set_bindsrc(struct libmnt_fs *fs, const char *src) */ 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; } /** @@ -1384,7 +1433,12 @@ int mnt_fs_get_id(struct libmnt_fs *fs) */ 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; } /** @@ -1415,7 +1469,12 @@ int mnt_fs_set_uniq_id(struct libmnt_fs *fs, uint64_t 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; } /** @@ -1428,9 +1487,51 @@ int mnt_fs_get_parent_id(struct libmnt_fs *fs) */ 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 @@ -1439,7 +1540,12 @@ uint64_t mnt_fs_get_parent_uniq_id(struct libmnt_fs *fs) */ 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; } /** @@ -1472,6 +1578,8 @@ int mnt_fs_get_option(struct libmnt_fs *fs, const char *name, 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); @@ -1576,7 +1684,11 @@ 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; + 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 */ @@ -1693,7 +1805,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); } /** @@ -1720,16 +1832,26 @@ 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)); + 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)); @@ -1744,6 +1866,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)); @@ -1770,6 +1898,7 @@ int mnt_fs_print_debug(struct libmnt_fs *fs, FILE *file) 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))); @@ -1778,6 +1907,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 000000000..0fad5bd4b --- /dev/null +++ b/libmount/src/fs_statmount.c @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * This file is part of libmount from util-linux project. + * + * Copyright (C) 2024 Karel Zak + * + * 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 */ diff --git a/libmount/src/libmount.h.in b/libmount/src/libmount.h.in index 146240630..89bae3a0c 100644 --- a/libmount/src/libmount.h.in +++ b/libmount/src/libmount.h.in @@ -141,6 +141,13 @@ struct libmnt_tabdiff; */ struct libmnt_ns; +/** + * libmnt_statmnt + * + * Setting for statmount() + */ +struct libmnt_statmnt; + /* * Actions */ @@ -541,6 +548,9 @@ 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); @@ -571,6 +581,17 @@ 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); +/* 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)); @@ -599,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); diff --git a/libmount/src/libmount.sym b/libmount/src/libmount.sym index af7b2ac21..761eabdc9 100644 --- a/libmount/src/libmount.sym +++ b/libmount/src/libmount.sym @@ -383,8 +383,19 @@ MOUNT_2_40 { } 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; diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h index b1374ceb2..6ea1d89cf 100644 --- a/libmount/src/mountP.h +++ b/libmount/src/mountP.h @@ -52,6 +52,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 @@ -189,6 +190,20 @@ struct libmnt_iter { } 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...) @@ -204,6 +219,8 @@ struct libmnt_fs { 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 */ @@ -241,6 +258,9 @@ struct libmnt_fs { int flags; /* MNT_FS_* flags */ pid_t tid; /* /proc//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 */ @@ -255,6 +275,9 @@ 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 */ +#define mnt_fs_want_statmount(_x, _m) \ + ((_x)->stmnt && !(_x)->stmnt->disabled && !((_x)->stmnt_done & (_m))) + /* * fstab/mountinfo file */ @@ -274,6 +297,8 @@ struct libmnt_table { 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) */ diff --git a/libmount/src/tab.c b/libmount/src/tab.c index 2b89552eb..ebb804328 100644 --- a/libmount/src/tab.c +++ b/libmount/src/tab.c @@ -172,6 +172,10 @@ void mnt_free_table(struct libmnt_table *tb) mnt_unref_cache(tb->cache); free(tb->comm_intro); free(tb->comm_tail); + + mnt_unref_statmnt(tb->stmnt); + tb->stmnt = NULL; + free(tb); } @@ -395,6 +399,40 @@ struct libmnt_cache *mnt_table_get_cache(struct libmnt_table *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 @@ -455,6 +493,10 @@ 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; } @@ -474,6 +516,10 @@ static int __table_insert_fs( 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; }