diff options
| author | Karel Zak <kzak@redhat.com> | 2025-01-09 11:13:49 +0100 |
|---|---|---|
| committer | Karel Zak <kzak@redhat.com> | 2025-01-09 11:13:49 +0100 |
| commit | b4ea6459da5767de34fde33c5e2751788203b989 (patch) | |
| tree | 29e7395055b5181b76700ade205aa6b8f19d9d07 /libmount/src | |
| parent | a5d0e6a87328049ecb8aab01a5d5248cb989f07d (diff) | |
| parent | e1190d466394fefaa89962b300332813e68bfe6c (diff) | |
| download | util-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.am | 2 | ||||
| -rw-r--r-- | libmount/src/btrfs.c | 1 | ||||
| -rw-r--r-- | libmount/src/fs.c | 315 | ||||
| -rw-r--r-- | libmount/src/fs_statmount.c | 399 | ||||
| -rw-r--r-- | libmount/src/fuzz.c | 1 | ||||
| -rw-r--r-- | libmount/src/hook_idmap.c | 2 | ||||
| -rw-r--r-- | libmount/src/hook_mount.c | 3 | ||||
| -rw-r--r-- | libmount/src/hook_subdir.c | 1 | ||||
| -rw-r--r-- | libmount/src/hooks.c | 1 | ||||
| -rw-r--r-- | libmount/src/libmount.h.in | 44 | ||||
| -rw-r--r-- | libmount/src/libmount.sym | 22 | ||||
| -rw-r--r-- | libmount/src/mountP.h | 53 | ||||
| -rw-r--r-- | libmount/src/optlist.c | 1 | ||||
| -rw-r--r-- | libmount/src/tab.c | 144 | ||||
| -rw-r--r-- | libmount/src/tab_listmount.c | 424 | ||||
| -rw-r--r-- | libmount/src/tab_parse.c | 19 | ||||
| -rw-r--r-- | libmount/src/tab_update.c | 6 | ||||
| -rw-r--r-- | libmount/src/utils.c | 67 | ||||
| -rw-r--r-- | libmount/src/version.c | 4 |
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 |
