From: Darrick J. Wong Date: Tue, 3 Mar 2026 19:01:39 +0000 (-0800) Subject: libfrog: add wrappers for listmount and statmount X-Git-Tag: v7.0.0~52 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=67fc0c99ea5170ba82cbf33beda3207371c02a8f;p=xfsprogs-dev.git libfrog: add wrappers for listmount and statmount Add some wrappers for listmount and statmount so that we don't have to open-code the kernel ABI quirks in every utility program that uses it. Note that glibc seems to have discussed providing a wrapper in late 2023 but took no action; and the listmount manpage says that there is no glibc wrapper. Signed-off-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig --- diff --git a/configure.ac b/configure.ac index a9febabc..cffcaf37 100644 --- a/configure.ac +++ b/configure.ac @@ -183,6 +183,11 @@ AC_HAVE_BLKID_TOPO AC_HAVE_TRIVIAL_AUTO_VAR_INIT AC_STRERROR_R_RETURNS_STRING AC_HAVE_CLOSE_RANGE +AC_HAVE_LISTMOUNT +if test "$have_listmount" = "yes"; then + AC_HAVE_LISTMOUNT_NS_FD + AC_HAVE_STATMOUNT_SUPPORTED_MASK +fi if test "$enable_ubsan" = "yes" || test "$enable_ubsan" = "probe"; then AC_PACKAGE_CHECK_UBSAN diff --git a/include/builddefs.in b/include/builddefs.in index 4a2cb757..d2d25c8a 100644 --- a/include/builddefs.in +++ b/include/builddefs.in @@ -119,6 +119,10 @@ UDEV_RULE_DIR = @udev_rule_dir@ HAVE_LIBURCU_ATOMIC64 = @have_liburcu_atomic64@ STRERROR_R_RETURNS_STRING = @strerror_r_returns_string@ HAVE_CLOSE_RANGE = @have_close_range@ +HAVE_LISTMOUNT = @have_listmount@ +HAVE_LISTMOUNT_NS_FD = @have_listmount_ns_fd@ +HAVE_STATMOUNT_SUPPORTED_MASK = @have_statmount_supported_mask@ +NEED_INTERNAL_STATMOUNT = @need_internal_statmount@ GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall # -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl @@ -141,6 +145,9 @@ endif ifeq ($(NEED_INTERNAL_STATX),yes) PCFLAGS+= -DOVERRIDE_SYSTEM_STATX endif +ifeq ($(NEED_INTERNAL_STATMOUNT),yes) +PCFLAGS+= -DOVERRIDE_SYSTEM_STATMOUNT +endif ifeq ($(HAVE_GETFSMAP),yes) PCFLAGS+= -DHAVE_GETFSMAP endif diff --git a/include/linux.h b/include/linux.h index 3ea90162..8972c959 100644 --- a/include/linux.h +++ b/include/linux.h @@ -32,7 +32,13 @@ #ifdef OVERRIDE_SYSTEM_FSXATTR # define fsxattr sys_fsxattr #endif -#include /* fsxattr defintion for new kernels */ +#ifdef OVERRIDE_SYSTEM_STATMOUNT +# define statmount sys_statmount +#endif +#include /* fsxattr/statmount defintion for new kernels */ +#ifdef OVERRIDE_SYSTEM_STATMOUNT +# undef statmount +#endif #ifdef OVERRIDE_SYSTEM_FSXATTR # undef fsxattr #endif diff --git a/libfrog/Makefile b/libfrog/Makefile index 89a0332a..22668212 100644 --- a/libfrog/Makefile +++ b/libfrog/Makefile @@ -96,6 +96,15 @@ ifeq ($(HAVE_CLOSE_RANGE),yes) CFLAGS += -DHAVE_CLOSE_RANGE endif +ifeq ($(HAVE_LISTMOUNT),yes) +CFILES += statmount.c +HFILES += statmount.h +endif + +ifeq ($(HAVE_LISTMOUNT_NS_FD),yes) +CFLAGS+=-DHAVE_LISTMOUNT_NS_FD +endif + default: ltdepend $(LTLIBRARY) $(GETTEXT_PY) crc32table.h: gen_crc32table.c crc32defs.h diff --git a/libfrog/statmount.c b/libfrog/statmount.c new file mode 100644 index 00000000..edf17d60 --- /dev/null +++ b/libfrog/statmount.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 Oracle. All Rights Reserved. + * Author: Darrick J. Wong + */ +#include "xfs.h" + +#include + +int +libfrog_listmount( + uint64_t mnt_id, + int mnt_ns_fd, + uint64_t *cursor, + uint64_t *mnt_ids, + size_t nr_mnt_ids) +{ + struct mnt_id_req req = { + .size = sizeof(req), + .mnt_id = mnt_id, +#ifdef HAVE_LISTMOUNT_NS_FD + .mnt_ns_fd = mnt_ns_fd, +#else + .spare = mnt_ns_fd, +#endif + .param = *cursor, + }; + int ret = syscall(SYS_listmount, &req, mnt_ids, nr_mnt_ids, 0); + + if (ret > 0) + *cursor = mnt_ids[ret - 1]; + + return ret; +} + +int +libfrog_statmount( + uint64_t mnt_id, + int mnt_ns_fd, + uint64_t statmount_flags, + struct statmount *smbuf, + size_t smbuf_size) +{ + struct mnt_id_req req = { + .size = sizeof(req), + .mnt_id = mnt_id, +#ifdef HAVE_LISTMOUNT_NS_FD + .mnt_ns_fd = mnt_ns_fd, +#else + .spare = mnt_ns_fd, +#endif + .param = statmount_flags, + }; + + return syscall(SYS_statmount, &req, smbuf, smbuf_size, 0); +} + +int +libfrog_fstatmount( + int fd, + uint64_t statmount_flags, + struct statmount *smbuf, + size_t smbuf_size) +{ + struct mnt_id_req req = { + .size = sizeof(req), +#ifdef HAVE_LISTMOUNT_NS_FD + .mnt_ns_fd = fd, +#else + .spare = fd, +#endif + .param = statmount_flags, + }; + + return syscall(SYS_statmount, &req, smbuf, smbuf_size, STATMOUNT_BY_FD); +} diff --git a/libfrog/statmount.h b/libfrog/statmount.h new file mode 100644 index 00000000..7e281ce9 --- /dev/null +++ b/libfrog/statmount.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 Oracle. All rights reserved. + * All Rights Reserved. + */ +#ifndef __LIBFROG_STATMOUNT_H__ +#define __LIBFROG_STATMOUNT_H__ + +/* This is the path to the current process' mount namespace file */ +#define DEFAULT_MOUNTNS_FILE "/proc/self/ns/mnt" + +/* + * Believe it or not, listmount and statmount treat a zero value for mnt_ns_fd + * as if that means "use the current process' mount namespace" even though + * Linus Torvalds roared about that with the BPF people. + */ +#define DEFAULT_MOUNTNS_FD (0) + +#ifdef OVERRIDE_SYSTEM_STATMOUNT +struct statmount { + __u32 size; /* Total size, including strings */ + __u32 mnt_opts; /* [str] Options (comma separated, escaped) */ + __u64 mask; /* What results were written */ + __u32 sb_dev_major; /* Device ID */ + __u32 sb_dev_minor; + __u64 sb_magic; /* ..._SUPER_MAGIC */ + __u32 sb_flags; /* SB_{RDONLY,SYNCHRONOUS,DIRSYNC,LAZYTIME} */ + __u32 fs_type; /* [str] Filesystem type */ + __u64 mnt_id; /* Unique ID of mount */ + __u64 mnt_parent_id; /* Unique ID of parent (for root == mnt_id) */ + __u32 mnt_id_old; /* Reused IDs used in proc/.../mountinfo */ + __u32 mnt_parent_id_old; + __u64 mnt_attr; /* MOUNT_ATTR_... */ + __u64 mnt_propagation; /* MS_{SHARED,SLAVE,PRIVATE,UNBINDABLE} */ + __u64 mnt_peer_group; /* ID of shared peer group */ + __u64 mnt_master; /* Mount receives propagation from this ID */ + __u64 propagate_from; /* Propagation from in current namespace */ + __u32 mnt_root; /* [str] Root of mount relative to root of fs */ + __u32 mnt_point; /* [str] Mountpoint relative to current root */ + __u64 mnt_ns_id; /* ID of the mount namespace */ + __u32 fs_subtype; /* [str] Subtype of fs_type (if any) */ + __u32 sb_source; /* [str] Source string of the mount */ + __u32 opt_num; /* Number of fs options */ + __u32 opt_array; /* [str] Array of nul terminated fs options */ + __u32 opt_sec_num; /* Number of security options */ + __u32 opt_sec_array; /* [str] Array of nul terminated security options */ + __u64 supported_mask; /* Mask flags that this kernel supports */ + __u64 __spare2[45]; + char str[]; /* Variable size part containing strings */ +}; +#endif + +/* all the new flags added since the beginning of statmount */ + +#ifndef STATMOUNT_MNT_NS_ID +#define STATMOUNT_MNT_NS_ID 0x00000040U /* Want/got mnt_ns_id */ +#endif + +#ifndef STATMOUNT_MNT_OPTS +#define STATMOUNT_MNT_OPTS 0x00000080U /* Want/got mnt_opts */ +#endif + +#ifndef STATMOUNT_FS_SUBTYPE +#define STATMOUNT_FS_SUBTYPE 0x00000100U /* Want/got fs_subtype */ +#endif + +#ifndef STATMOUNT_SB_SOURCE +#define STATMOUNT_SB_SOURCE 0x00000200U /* Want/got sb_source */ +#endif + +#ifndef STATMOUNT_OPT_ARRAY +#define STATMOUNT_OPT_ARRAY 0x00000400U /* Want/got opt_... */ +#endif + +#ifndef STATMOUNT_OPT_SEC_ARRAY +#define STATMOUNT_OPT_SEC_ARRAY 0x00000800U /* Want/got opt_sec... */ +#endif + +#ifndef STATMOUNT_SUPPORTED_MASK +#define STATMOUNT_SUPPORTED_MASK 0x00001000U /* Want/got supported mask flags */ +#endif + +/* flag bits for statmount */ +#ifndef STATMOUNT_BY_FD +#define STATMOUNT_BY_FD 0x00000001U /* want mountinfo for given fd */ +#endif + +#define LISTMOUNT_INIT_CURSOR (0ULL) + +int libfrog_listmount(uint64_t mnt_id, int mnt_ns_fd, uint64_t *cursor, + uint64_t *mnt_ids, size_t nr_mnt_ids); + +int libfrog_statmount(uint64_t mnt_id, int mnt_ns_fd, uint64_t statmount_flags, + struct statmount *smbuf, size_t smbuf_size); + +int libfrog_fstatmount(int fd, uint64_t statmount_flags, + struct statmount *smbuf, size_t smbuf_size); + +static inline size_t libfrog_statmount_sizeof(size_t strings_bytes) +{ + return sizeof(struct statmount) + strings_bytes; +} + +#endif /* __LIBFROG_STATMOUNT_H__ */ diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4 index b3d87229..ec4a3ef4 100644 --- a/m4/package_libcdev.m4 +++ b/m4/package_libcdev.m4 @@ -366,3 +366,89 @@ close_range(0, 0, 0); AC_MSG_RESULT(no)) AC_SUBST(have_close_range) ]) + +# +# Check if listmount and statmount exist. Note that statmount came first (6.8) +# and listmount came later (6.9), so we'll refuse both if either is missing. +# +AC_DEFUN([AC_HAVE_LISTMOUNT], + [AC_MSG_CHECKING([for listmount]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#define _GNU_SOURCE +#include +#include +#include +#include + ]], [[ + struct mnt_id_req req = { + .size = sizeof(req), + }; + struct statmount smbuf; + + syscall(SYS_statmount, &req, &smbuf, 0, 0); + syscall(SYS_listmount, &req, NULL, 0, 0); + ]]) + ], have_listmount=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) + AC_SUBST(have_listmount) + ]) + +# +# Check if mnt_id_req::mnt_ns_fd exists. This replaced mnt_id_req::spare in +# 6.18, though earlier kernels allowed userspace to assign to spare. +# +AC_DEFUN([AC_HAVE_LISTMOUNT_NS_FD], + [AC_MSG_CHECKING([for struct mnt_id_req::mnt_ns_fd]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#define _GNU_SOURCE +#include +#include +#include +#include + ]], [[ + struct mnt_id_req req = { + .mnt_ns_fd = 555, + }; + + syscall(SYS_listmount, &req, NULL, 0, 0); + ]]) + ], have_listmount_ns_fd=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) + AC_SUBST(have_listmount_ns_fd) + ]) + +# +# Check if statmount::supported_mask (and hence sb_source) exists. We need +# sb_source for xfs_healer_start; and supported_mask for the xfs_io wrapper. +# sb_source was added in 6.13 and supported_mask in 6.15. +# +AC_DEFUN([AC_HAVE_STATMOUNT_SUPPORTED_MASK], + [AC_MSG_CHECKING([for struct statmount::supported_mask]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ +#define _GNU_SOURCE +#include +#include +#include +#include + ]], [[ + struct mnt_id_req req = { + .mnt_ns_fd = 555, + }; + struct statmount smbuf = { + .supported_mask = 1, + }; + + syscall(SYS_statmount, &req, &smbuf, 0, 0); + ]]) + ], have_statmount_supported_mask=yes + AC_MSG_RESULT(yes), + need_internal_statmount=yes + AC_MSG_RESULT(no)) + AC_SUBST(have_statmount_supported_mask) + AC_SUBST(need_internal_statmount) + ])