From 9154cc7e80787e0c42b547c4305941b316d772d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 24 Jul 2025 14:31:46 +0200 Subject: [PATCH 1/4] getty-generator: improve debugging messages We generally log what happens, but we didn't log anything about checking credentials, so add that. The error message when we cannot acces /proc/1/environ was confusing. --- src/getty-generator/getty-generator.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index cd9aae51e3..bedfda81bd 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -137,6 +137,8 @@ static int add_credential_gettys(void) { }; int r; + log_debug("Checking credentials for configuration..."); + FOREACH_ELEMENT(t, table) { _cleanup_free_ char *b = NULL; size_t sz = 0; @@ -247,11 +249,12 @@ static void parse_env(void) { r = getenv_for_pid(1, "SYSTEMD_GETTY_AUTO", &value); if (r < 0) - log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO environment variable, ignoring: %m"); + log_warning_errno(r, "Failed to read $SYSTEMD_GETTY_AUTO in PID 1's environment, ignoring: %m"); else if (r > 0) { r = parse_getty_sources(value, &arg_getty_sources); if (r < 0) - log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO environment variable, ignoring: %s", value); + log_warning_errno(r, "Failed to parse $SYSTEMD_GETTY_AUTO from PID 1's environment, ignoring: %s", + value); } } @@ -314,8 +317,8 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) log_warning_errno(r, "Failed to get active kernel consoles, ignoring: %m"); else if (r > 0) STRV_FOREACH(i, consoles) { - /* We assume that gettys on virtual terminals are started via manual configuration - * and do this magic only for non-VC terminals. */ + /* We assume that gettys on virtual terminals are started via manual + * configuration and do this magic only for non-VC terminals. */ if (tty_is_vc(*i)) continue; From c136be3f04bf2c96e99a7466deb4eb491fb117b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 24 Jul 2025 15:09:58 +0200 Subject: [PATCH 2/4] basic/stat-util: avoid access syscall I was looking at strace for systemd-getty-generator and noticed the call to faccessat2(3, "", W_OK, AT_EMPTY_PATH), even though we already did fstatfs(3), which should give us all the necessary information. Let's only do this additional check when it's likely to yield something useful, i.e. for network fses and otherwise skip the syscall. The call to statvfs is replaced by statfs because that gives us the .f_type field and allows is_network_fs() to be called. I'm a bit worried that the is_network_fs() is somewhat costly. This will be improved in later commits. --- src/basic/stat-util.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 72360daadf..5582c59037 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -220,20 +220,22 @@ int null_or_empty_path_with_root(const char *fn, const char *root) { } int fd_is_read_only_fs(int fd) { - struct statvfs st; + struct statfs st; assert(fd >= 0); - if (fstatvfs(fd, &st) < 0) + if (fstatfs(fd, &st) < 0) return -errno; - if (st.f_flag & ST_RDONLY) + if (st.f_flags & ST_RDONLY) return true; - /* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try - * again with access(W_OK) which is more reliable, at least sometimes. */ - if (access_fd(fd, W_OK) == -EROFS) - return true; + if (is_network_fs(&st)) { + /* On NFS, fstatfs() might not reflect whether we can actually write to the remote share. + * Let's try again with access(W_OK) which is more reliable, at least sometimes. */ + if (access_fd(fd, W_OK) == -EROFS) + return true; + } return false; } From b0123576f4d2603e721e77932e8ed9b9e37b4a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 25 Jul 2025 16:29:10 +0200 Subject: [PATCH 3/4] meson: rework generation of file system lookup helpers Previously, the gperf table was the main "source of truth", and additional information (e.g. which file system names are obsolete) was scattered in various files. We would then parse the gperf file, using python, awk, grep, and bash, and use the results in various ways. This is hard to understand and maintain. Let's replace all of this with a single python script that generates the requested outputs as appropriate. $ diff -u <(git show @{u}:src/basic/filesystems-gperf.gperf) \ <(src/basic/filesystem-sets.py gperf) shows that the outputs are the same except for comments. Similarly, 'src/basic/filesystem-sets.py fs-type-to-string' and 'src/basic/filesystem-sets.py filesystem-sets' can be used to view the generated code. The check that the kernel doesn't define any new file system magics is converted into a normal test. It doesn't seem necessary to fail the build when that happens. --- src/basic/check-filesystems.sh | 31 -- src/basic/filesystem-sets.py | 352 +++++++++++++++++++ src/basic/filesystems-gperf.gperf | 135 ------- src/basic/filesystems.c | 127 +------ src/basic/generate-filesystem-list.py | 15 - src/basic/generate-filesystem-switch-case.py | 53 --- src/basic/meson.build | 49 ++- 7 files changed, 373 insertions(+), 389 deletions(-) delete mode 100755 src/basic/check-filesystems.sh create mode 100755 src/basic/filesystem-sets.py delete mode 100644 src/basic/filesystems-gperf.gperf delete mode 100755 src/basic/generate-filesystem-list.py delete mode 100755 src/basic/generate-filesystem-switch-case.py diff --git a/src/basic/check-filesystems.sh b/src/basic/check-filesystems.sh deleted file mode 100755 index e36890cca1..0000000000 --- a/src/basic/check-filesystems.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eu -set -o pipefail - -cpp="${1:?}" -filesystems_gperf="${2:?}" -shift 2 - -error=false - -# shellcheck disable=SC2086 -for fs in $($cpp -dM -include linux/magic.h "$@" - /dev/null; then - # STACK_END_MAGIC doesn't refer to a filesystem - # mtd_inode was removed in 2015 - # futexfs was removed in 2018 - if [[ "$fs" =~ ^(STACK_END_MAGIC|MTD_INODE_FS_MAGIC|FUTEXFS_SUPER_MAGIC)$ ]]; then - continue - fi - echo "Filesystem found in kernel header but not in $(basename "$filesystems_gperf"): $fs"; - error=true - fi -done - -if $error; then - exit 1 -fi diff --git a/src/basic/filesystem-sets.py b/src/basic/filesystem-sets.py new file mode 100755 index 0000000000..d374ac41ff --- /dev/null +++ b/src/basic/filesystem-sets.py @@ -0,0 +1,352 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +import os +import subprocess +import sys + +NAME_TO_MAGIC = { + 'apparmorfs': ['AAFS_MAGIC'], + 'adfs': ['ADFS_SUPER_MAGIC'], + 'affs': ['AFFS_SUPER_MAGIC'], + 'afs': ['AFS_FS_MAGIC', + 'AFS_SUPER_MAGIC'], + 'anon_inodefs': ['ANON_INODE_FS_MAGIC'], + 'autofs': ['AUTOFS_SUPER_MAGIC'], + 'balloon-kvm': ['BALLOON_KVM_MAGIC'], + 'bcachefs': ['BCACHEFS_SUPER_MAGIC'], + 'bdev': ['BDEVFS_MAGIC'], + 'binder': ['BINDERFS_SUPER_MAGIC'], + 'binfmt_misc': ['BINFMTFS_MAGIC'], + 'bpf': ['BPF_FS_MAGIC'], + 'btrfs': ['BTRFS_SUPER_MAGIC'], + 'btrfs_test_fs': ['BTRFS_TEST_MAGIC'], + # cpuset's magic got reassigned to cgroupfs + 'cpuset': ['CGROUP_SUPER_MAGIC'], + 'ceph': ['CEPH_SUPER_MAGIC'], + 'cgroup2': ['CGROUP2_SUPER_MAGIC'], + # note that the cgroupfs magic got reassigned from cpuset + 'cgroup': ['CGROUP_SUPER_MAGIC'], + 'cifs': ['CIFS_SUPER_MAGIC', + 'SMB2_SUPER_MAGIC'], + 'coda': ['CODA_SUPER_MAGIC'], + 'configfs': ['CONFIGFS_MAGIC'], + 'cramfs': ['CRAMFS_MAGIC'], + 'dax': ['DAXFS_MAGIC'], + 'debugfs': ['DEBUGFS_MAGIC'], + 'devmem': ['DEVMEM_MAGIC'], + 'devpts': ['DEVPTS_SUPER_MAGIC'], + # devtmpfs is just a special instance of tmpfs, hence it reports its magic + 'devtmpfs': ['TMPFS_MAGIC'], + 'dmabuf': ['DMA_BUF_MAGIC'], + 'ecryptfs': ['ECRYPTFS_SUPER_MAGIC'], + 'efivarfs': ['EFIVARFS_MAGIC'], + 'efs': ['EFS_SUPER_MAGIC'], + 'erofs': ['EROFS_SUPER_MAGIC_V1'], + # ext2 + ext3 + ext4 use the same magic + 'ext2': ['EXT2_SUPER_MAGIC'], + 'ext3': ['EXT3_SUPER_MAGIC'], + 'ext4': ['EXT4_SUPER_MAGIC'], + 'exfat': ['EXFAT_SUPER_MAGIC'], + 'f2fs': ['F2FS_SUPER_MAGIC'], + # fuseblk is so closely related to fuse that it shares the same magic + 'fuseblk': ['FUSE_SUPER_MAGIC'], + 'fuse': ['FUSE_SUPER_MAGIC'], + 'fusectl': ['FUSE_CTL_SUPER_MAGIC'], + # gfs is an old version of gfs2 and reuses the magic + 'gfs': ['GFS2_MAGIC'], + 'gfs2': ['GFS2_MAGIC'], + 'hostfs': ['HOSTFS_SUPER_MAGIC'], + 'hpfs': ['HPFS_SUPER_MAGIC'], + 'hugetlbfs': ['HUGETLBFS_MAGIC'], + 'iso9660': ['ISOFS_SUPER_MAGIC'], + 'jffs2': ['JFFS2_SUPER_MAGIC'], + 'minix': ['MINIX_SUPER_MAGIC', + 'MINIX_SUPER_MAGIC2', + 'MINIX2_SUPER_MAGIC', + 'MINIX2_SUPER_MAGIC2', + 'MINIX3_SUPER_MAGIC'], + 'mqueue': ['MQUEUE_MAGIC'], + # msdos is an older legacy version of vfat, shares the magic + 'msdos': ['MSDOS_SUPER_MAGIC'], + # ncp/ncpfs have been removed from the kernel, but ncpfs was the official name + 'ncp': ['NCP_SUPER_MAGIC'], + 'ncpfs': ['NCP_SUPER_MAGIC'], + # nfs is the old version of nfs4, and they share the same magic + 'nfs': ['NFS_SUPER_MAGIC'], + 'nfs4': ['NFS_SUPER_MAGIC'], + 'nilfs2': ['NILFS_SUPER_MAGIC'], + 'nsfs': ['NSFS_MAGIC'], + 'ntfs': ['NTFS_SB_MAGIC'], + 'ntfs3': ['NTFS3_SUPER_MAGIC'], + 'ocfs2': ['OCFS2_SUPER_MAGIC'], + 'openpromfs': ['OPENPROM_SUPER_MAGIC'], + 'orangefs': ['ORANGEFS_DEVREQ_MAGIC'], + 'overlay': ['OVERLAYFS_SUPER_MAGIC'], + 'pidfs': ['PID_FS_MAGIC'], + 'pipefs': ['PIPEFS_MAGIC'], + 'ppc-cmm': ['PPC_CMM_MAGIC'], + 'proc': ['PROC_SUPER_MAGIC'], + 'pstore': ['PSTOREFS_MAGIC'], + # pvfs2 is the old version of orangefs + 'pvfs2': ['ORANGEFS_DEVREQ_MAGIC'], + 'qnx4': ['QNX4_SUPER_MAGIC'], + 'qnx6': ['QNX6_SUPER_MAGIC'], + 'ramfs': ['RAMFS_MAGIC'], + 'resctrl': ['RDTGROUP_SUPER_MAGIC'], + 'reiserfs': ['REISERFS_SUPER_MAGIC'], + 'rpc_pipefs': ['RPC_PIPEFS_SUPER_MAGIC'], + 'secretmem': ['SECRETMEM_MAGIC'], + 'securityfs': ['SECURITYFS_MAGIC'], + 'selinuxfs': ['SELINUX_MAGIC'], + 'shiftfs': ['SHIFTFS_MAGIC'], + 'smackfs': ['SMACK_MAGIC'], + # smb3 is an alias for cifs + 'smb3': ['CIFS_SUPER_MAGIC'], + # smbfs was removed from the kernel in 2010, the magic remains + 'smbfs': ['SMB_SUPER_MAGIC'], + 'sockfs': ['SOCKFS_MAGIC'], + 'squashfs': ['SQUASHFS_MAGIC'], + 'sysfs': ['SYSFS_MAGIC'], + # note that devtmpfs shares the same magic with tmpfs, given it is just a special named instance of it. + 'tmpfs': ['TMPFS_MAGIC'], + 'tracefs': ['TRACEFS_MAGIC'], + 'udf': ['UDF_SUPER_MAGIC'], + 'usbdevfs': ['USBDEVICE_SUPER_MAGIC'], + 'vboxsf': ['VBOXSF_SUPER_MAGIC'], + # note that msdos shares the same magic (and is the older version) + 'vfat': ['MSDOS_SUPER_MAGIC'], + 'v9fs': ['V9FS_MAGIC'], + 'xenfs': ['XENFS_SUPER_MAGIC'], + 'xfs': ['XFS_SUPER_MAGIC'], + 'z3fold': ['Z3FOLD_MAGIC'], + 'zonefs': ['ZONEFS_MAGIC'], + 'zsmalloc': ['ZSMALLOC_MAGIC'], +} + +# System magics are sometimes not unique, because file systems got new +# revisions or got renamed. Let's prefer newer over older here, and thus ignore +# the old names. +OBSOLETE_NAMES = { + 'cpuset', # magic taken over by cgroupfs + 'devtmpfs', # not a file system of its own, but just a "named superblock" of tmpfs + 'ext2', # ext4 is the newest revision of ext2 + ext3 + 'ext3', + 'fuseblk', # closely related to fuse; they share a single magic, but the latter is more common + 'gfs', # magic taken over by gfs2 + 'msdos', # vfat is the newest revision of msdos + 'ncp', # ncpfs (not ncp) was the last name of the netware 'file_system_type' + # name before it was removed in 2018 + 'nfs', # nfs4 is the newest revision of nfs + 'pvfs2', # orangefs is the new name of pvfs2 + 'smb3', # smb3 is an alias for cifs +} + +FILESYSTEM_SETS = [ + ( + "@basic-api", + "Basic filesystem API", + "cgroup", + "cgroup2", + "devpts", + "devtmpfs", + "mqueue", + "proc", + "sysfs", + ), + ( + "@anonymous", + "Anonymous inodes", + "anon_inodefs", + "pipefs", + "sockfs", + ), + ( + "@application", + "Application virtual filesystems", + "autofs", + "fuse", + "overlay", + ), + ( + "@auxiliary-api", + "Auxiliary filesystem API", + "binfmt_misc", + "configfs", + "efivarfs", + "fusectl", + "hugetlbfs", + "rpc_pipefs", + "securityfs", + ), + ( + "@common-block", + "Common block device filesystems", + "btrfs", + "erofs", + "exfat", + "ext4", + "f2fs", + "iso9660", + "ntfs3", + "squashfs", + "udf", + "vfat", + "xfs", + ), + ( + "@historical-block", + "Historical block device filesystems", + "ext2", + "ext3", + "minix", + ), + ( + "@network", + "Well-known network filesystems", + "afs", + "ceph", + "cifs", + "gfs", + "gfs2", + "ncp", + "ncpfs", + "nfs", + "nfs4", + "ocfs2", + "orangefs", + "pvfs2", + "smb3", + "smbfs", + ), + ( + "@privileged-api", + "Privileged filesystem API", + "bpf", + "debugfs", + "pstore", + "tracefs", + ), + ( + "@security", + "Security/MAC API VFS", + "apparmorfs", + "selinuxfs", + "smackfs", + ), + ( + "@temporary", + "Temporary filesystems", + "ramfs", + "tmpfs", + ), + ( + "@known", + "All known filesystems declared in the kernel", + *NAME_TO_MAGIC.keys(), + ), +] + +def generate_gperf(): + print("""\ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +%{ +#if __GNUC__ >= 15 +_Pragma("GCC diagnostic ignored \\"-Wzero-as-null-pointer-constant\\"") +#endif +#include + +#include "filesystems.h" +#include "stat-util.h" + +struct FilesystemMagic { + const char *name; + statfs_f_type_t magic[FILESYSTEM_MAGIC_MAX]; +}; +%} +struct FilesystemMagic; +%language=ANSI-C +%define hash-function-name filesystems_gperf_hash +%define lookup-function-name filesystems_gperf_lookup +%define slot-name name +%readonly-tables +%omit-struct-type +%struct-type +%includes +%%""") + for name, magics in NAME_TO_MAGIC.items(): + print(f"{name + ',':16} {{{', '.join(magics)}}}") + +def generate_fs_type_to_string(): + print("""\ +#include +#include "filesystems.h" + +const char* fs_type_to_string(statfs_f_type_t magic) { + switch (magic) {""") + + for name, magics in NAME_TO_MAGIC.items(): + if name in OBSOLETE_NAMES: + continue + for magic in magics: + print(f' case (statfs_f_type_t) {magic}:') + print(f' return "{name}";') + + print("""\ + } + return NULL; +}""") + +def generate_filesystem_sets(): + print('const FilesystemSet filesystem_sets[_FILESYSTEM_SET_MAX] = {') + + for name, desc, *filesystems in FILESYSTEM_SETS: + enum = 'FILESYSTEM_SET_' + name[1:].upper().replace('-', '_') + + print(f' [{enum}] = {{') + print(f' .name = "{name}",') + print(f' .help = "{desc}",') + print(f' .value =') + for filesystem in filesystems: + print(f' "{filesystem}\\0"') + print(' },') + + print('};') + +def magic_defines(): + cpp = os.environ['CPP'].split() + out = subprocess.check_output( + [*cpp, '-dM', '-include', 'linux/magic.h', '-'], + stdin=subprocess.DEVNULL, + text=True) + for line in out.splitlines(): + _, name, *rest = line.split() + if ('_MAGIC' in name + and rest and rest[0].startswith('0x') + and name not in { + 'STACK_END_MAGIC', + 'MTD_INODE_FS_MAGIC', + 'FUTEXFS_SUPER_MAGIC', + 'CRAMFS_MAGIC_WEND', + }): + yield name + +def check(): + kernel_magics = set(magic_defines()) + our_magics = set(sum(NAME_TO_MAGIC.values(), start=[])) + extra = kernel_magics - our_magics + if extra: + sys.exit(f"kernel knows additional filesystem magics: {', '.join(sorted(extra))}") + +if __name__ == '__main__': + for arg in sys.argv[1:]: + if arg == 'gperf': + generate_gperf() + elif arg == 'fs-type-to-string': + generate_fs_type_to_string() + elif arg == 'filesystem-sets': + generate_filesystem_sets() + elif arg == 'check': + check() + else: + raise ValueError diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf deleted file mode 100644 index 13db51db22..0000000000 --- a/src/basic/filesystems-gperf.gperf +++ /dev/null @@ -1,135 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -%{ -#if __GNUC__ >= 15 -_Pragma("GCC diagnostic ignored \"-Wzero-as-null-pointer-constant\"") -#endif -#include - -#include "filesystems.h" -#include "stat-util.h" - -struct FilesystemMagic { - const char *name; - statfs_f_type_t magic[FILESYSTEM_MAGIC_MAX]; -}; -%} -struct FilesystemMagic; -%language=ANSI-C -%define hash-function-name filesystems_gperf_hash -%define lookup-function-name filesystems_gperf_lookup -%define slot-name name -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -apparmorfs, {AAFS_MAGIC} -adfs, {ADFS_SUPER_MAGIC} -affs, {AFFS_SUPER_MAGIC} -afs, {AFS_FS_MAGIC, AFS_SUPER_MAGIC} -anon_inodefs, {ANON_INODE_FS_MAGIC} -autofs, {AUTOFS_SUPER_MAGIC} -balloon-kvm, {BALLOON_KVM_MAGIC} -bcachefs, {BCACHEFS_SUPER_MAGIC} -bdev, {BDEVFS_MAGIC} -binder, {BINDERFS_SUPER_MAGIC} -binfmt_misc, {BINFMTFS_MAGIC} -bpf, {BPF_FS_MAGIC} -btrfs, {BTRFS_SUPER_MAGIC} -btrfs_test_fs, {BTRFS_TEST_MAGIC} -# cpuset's magic got reassigned to cgroupfs -cpuset, {CGROUP_SUPER_MAGIC} -ceph, {CEPH_SUPER_MAGIC} -cgroup2, {CGROUP2_SUPER_MAGIC} -# note that the cgroupfs magic got reassigned from cpuset -cgroup, {CGROUP_SUPER_MAGIC} -cifs, {CIFS_SUPER_MAGIC, SMB2_SUPER_MAGIC} -coda, {CODA_SUPER_MAGIC} -configfs, {CONFIGFS_MAGIC} -cramfs, {CRAMFS_MAGIC} -dax, {DAXFS_MAGIC} -debugfs, {DEBUGFS_MAGIC} -devmem, {DEVMEM_MAGIC} -devpts, {DEVPTS_SUPER_MAGIC} -# devtmpfs is just a special instance of tmpfs, hence it reports its magic -devtmpfs, {TMPFS_MAGIC} -dmabuf, {DMA_BUF_MAGIC} -ecryptfs, {ECRYPTFS_SUPER_MAGIC} -efivarfs, {EFIVARFS_MAGIC} -efs, {EFS_SUPER_MAGIC} -erofs, {EROFS_SUPER_MAGIC_V1} -# ext2 + ext3 + ext4 use the same magic -ext2, {EXT2_SUPER_MAGIC} -ext3, {EXT3_SUPER_MAGIC} -ext4, {EXT4_SUPER_MAGIC} -exfat, {EXFAT_SUPER_MAGIC} -f2fs, {F2FS_SUPER_MAGIC} -# fuseblk is so closely related to fuse that it shares the same magic -fuseblk, {FUSE_SUPER_MAGIC} -fuse, {FUSE_SUPER_MAGIC} -fusectl, {FUSE_CTL_SUPER_MAGIC} -# gfs is an old version of gfs2 and reuses the magic -gfs, {GFS2_MAGIC} -gfs2, {GFS2_MAGIC} -hostfs, {HOSTFS_SUPER_MAGIC} -hpfs, {HPFS_SUPER_MAGIC} -hugetlbfs, {HUGETLBFS_MAGIC} -iso9660, {ISOFS_SUPER_MAGIC} -jffs2, {JFFS2_SUPER_MAGIC} -minix, {MINIX_SUPER_MAGIC, MINIX_SUPER_MAGIC2, MINIX2_SUPER_MAGIC, MINIX2_SUPER_MAGIC2, MINIX3_SUPER_MAGIC} -mqueue, {MQUEUE_MAGIC} -# msdos is an older legacy version of vfat, shares the magic -msdos, {MSDOS_SUPER_MAGIC} -# ncp/ncpfs have been removed from the kernel, but ncpfs was the official name -ncp, {NCP_SUPER_MAGIC} -ncpfs, {NCP_SUPER_MAGIC} -# nfs is the old version of nfs4, and they share the same magic -nfs, {NFS_SUPER_MAGIC} -nfs4, {NFS_SUPER_MAGIC} -nilfs2, {NILFS_SUPER_MAGIC} -nsfs, {NSFS_MAGIC} -ntfs, {NTFS_SB_MAGIC} -ntfs3, {NTFS3_SUPER_MAGIC} -ocfs2, {OCFS2_SUPER_MAGIC} -openpromfs, {OPENPROM_SUPER_MAGIC} -orangefs, {ORANGEFS_DEVREQ_MAGIC} -overlay, {OVERLAYFS_SUPER_MAGIC} -pidfs, {PID_FS_MAGIC} -pipefs, {PIPEFS_MAGIC} -ppc-cmm, {PPC_CMM_MAGIC} -proc, {PROC_SUPER_MAGIC} -pstore, {PSTOREFS_MAGIC} -# pvfs2 is the old version of orangefs -pvfs2, {ORANGEFS_DEVREQ_MAGIC} -qnx4, {QNX4_SUPER_MAGIC} -qnx6, {QNX6_SUPER_MAGIC} -ramfs, {RAMFS_MAGIC} -resctrl, {RDTGROUP_SUPER_MAGIC} -reiserfs, {REISERFS_SUPER_MAGIC} -rpc_pipefs, {RPC_PIPEFS_SUPER_MAGIC} -secretmem, {SECRETMEM_MAGIC} -securityfs, {SECURITYFS_MAGIC} -selinuxfs, {SELINUX_MAGIC} -shiftfs, {SHIFTFS_MAGIC} -smackfs, {SMACK_MAGIC} -# smb3 is an alias for cifs -smb3, {CIFS_SUPER_MAGIC} -# smbfs was removed from the kernel in 2010, the magic remains -smbfs, {SMB_SUPER_MAGIC} -sockfs, {SOCKFS_MAGIC} -squashfs, {SQUASHFS_MAGIC} -sysfs, {SYSFS_MAGIC} -# note that devtmpfs shares the same magic with tmpfs, given it is just a special named instance of it. -tmpfs, {TMPFS_MAGIC} -tracefs, {TRACEFS_MAGIC} -udf, {UDF_SUPER_MAGIC} -usbdevfs, {USBDEVICE_SUPER_MAGIC} -vboxsf, {VBOXSF_SUPER_MAGIC} -# note that msdos shares the same magic (and is the older version) -vfat, {MSDOS_SUPER_MAGIC} -v9fs, {V9FS_MAGIC} -xenfs, {XENFS_SUPER_MAGIC} -xfs, {XFS_SUPER_MAGIC} -z3fold, {Z3FOLD_MAGIC} -zonefs, {ZONEFS_MAGIC} -zsmalloc, {ZSMALLOC_MAGIC} diff --git a/src/basic/filesystems.c b/src/basic/filesystems.c index c8b407fc7e..2bbab151bb 100644 --- a/src/basic/filesystems.c +++ b/src/basic/filesystems.c @@ -5,15 +5,6 @@ #include "stat-util.h" #include "string-util.h" -const char* fs_type_to_string(statfs_f_type_t magic) { - - switch (magic) { -#include "filesystem-switch-case.inc" - } - - return NULL; -} - int fs_type_from_string(const char *name, const statfs_f_type_t **ret) { const struct FilesystemMagic *fs_magic; @@ -48,123 +39,7 @@ bool fs_in_group(const struct statfs *s, FilesystemGroups fs_group) { return false; } -const FilesystemSet filesystem_sets[_FILESYSTEM_SET_MAX] = { - [FILESYSTEM_SET_BASIC_API] = { - .name = "@basic-api", - .help = "Basic filesystem API", - .value = - "cgroup\0" - "cgroup2\0" - "devpts\0" - "devtmpfs\0" - "mqueue\0" - "proc\0" - "sysfs\0" - }, - [FILESYSTEM_SET_ANONYMOUS] = { - .name = "@anonymous", - .help = "Anonymous inodes", - .value = - "anon_inodefs\0" - "pipefs\0" - "sockfs\0" - }, - [FILESYSTEM_SET_APPLICATION] = { - .name = "@application", - .help = "Application virtual filesystems", - .value = - "autofs\0" - "fuse\0" - "overlay\0" - }, - [FILESYSTEM_SET_AUXILIARY_API] = { - .name = "@auxiliary-api", - .help = "Auxiliary filesystem API", - .value = - "binfmt_misc\0" - "configfs\0" - "efivarfs\0" - "fusectl\0" - "hugetlbfs\0" - "rpc_pipefs\0" - "securityfs\0" - }, - [FILESYSTEM_SET_COMMON_BLOCK] = { - .name = "@common-block", - .help = "Common block device filesystems", - .value = - "btrfs\0" - "erofs\0" - "exfat\0" - "ext4\0" - "f2fs\0" - "iso9660\0" - "ntfs3\0" - "squashfs\0" - "udf\0" - "vfat\0" - "xfs\0" - }, - [FILESYSTEM_SET_HISTORICAL_BLOCK] = { - .name = "@historical-block", - .help = "Historical block device filesystems", - .value = - "ext2\0" - "ext3\0" - "minix\0" - }, - [FILESYSTEM_SET_NETWORK] = { - .name = "@network", - .help = "Well-known network filesystems", - .value = - "afs\0" - "ceph\0" - "cifs\0" - "gfs\0" - "gfs2\0" - "ncp\0" - "ncpfs\0" - "nfs\0" - "nfs4\0" - "ocfs2\0" - "orangefs\0" - "pvfs2\0" - "smb3\0" - "smbfs\0" - }, - [FILESYSTEM_SET_PRIVILEGED_API] = { - .name = "@privileged-api", - .help = "Privileged filesystem API", - .value = - "bpf\0" - "debugfs\0" - "pstore\0" - "tracefs\0" - }, - [FILESYSTEM_SET_SECURITY] = { - .name = "@security", - .help = "Security/MAC API VFS", - .value = - "apparmorfs\0" - "selinuxfs\0" - "smackfs\0" - }, - [FILESYSTEM_SET_TEMPORARY] = { - .name = "@temporary", - .help = "Temporary filesystems", - .value = - "ramfs\0" - "tmpfs\0" - }, - [FILESYSTEM_SET_KNOWN] = { - .name = "@known", - .help = "All known filesystems declared in the kernel", - .value = -#include "filesystem-list.inc" - }, -}; - -const FilesystemSet *filesystem_set_find(const char *name) { +const FilesystemSet* filesystem_set_find(const char *name) { if (isempty(name) || name[0] != '@') return NULL; diff --git a/src/basic/generate-filesystem-list.py b/src/basic/generate-filesystem-list.py deleted file mode 100755 index 52b74f1763..0000000000 --- a/src/basic/generate-filesystem-list.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: LGPL-2.1-or-later - -import sys - -keywords_section = False - -for line in open(sys.argv[1]): - if line[0] == '#': - continue - - if keywords_section: - print('"{}\\0"'.format(line.split(',')[0].strip())) - elif line.startswith('%%'): - keywords_section = True diff --git a/src/basic/generate-filesystem-switch-case.py b/src/basic/generate-filesystem-switch-case.py deleted file mode 100755 index bbd09228e1..0000000000 --- a/src/basic/generate-filesystem-switch-case.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: LGPL-2.1-or-later - -import sys - - -def filter_fsname(name): - # File system magics are sometimes not unique, because file systems got new - # revisions or got renamed. Let's prefer newer over older here, and thus - # ignore the old names. - - return name in { - "cpuset", # magic taken over by cgroupfs - "devtmpfs", # not a file system of its own, but just a "named superblock" of tmpfs - "ext2", # ext4 is the newest revision of ext2 + ext3 - "ext3", - "fuseblk", # closely related to fuse; they share a single magic, but the latter is more common - "gfs", # magic taken over by gfs2 - "msdos", # vfat is the newest revision of msdos - "ncp", # ncpfs (not ncp) was the last name of the netware `file_system_type` - # name before it was removed in 2018 - "nfs", # nfs4 is the newest revision of nfs - "pvfs2", # orangefs is the new name of pvfs2 - "smb3", # smb3 is an alias for cifs - } - - -gperf_file = sys.argv[1] -keywords_section = False - -for line in open(gperf_file): - if line[0] == "#": - continue - - if keywords_section: - name, ids = line.split(",", 1) - - name = name.strip() - if filter_fsname(name): - continue - - ids = ids.strip() - assert ids[0] == "{" - assert ids[-1] == "}" - ids = ids[1:-1] - - for id in ids.split(","): - print(f"case (statfs_f_type_t) {id.strip()}:") - - print(f' return "{name}";') - - if line.startswith("%%"): - keywords_section = True diff --git a/src/basic/meson.build b/src/basic/meson.build index 923f332fcf..6589ce2995 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -168,41 +168,32 @@ basic_sources += generated_gperf_headers ############################################################ -r = run_command( - [ - 'env', '--chdir', meson.project_build_root(), - files('check-filesystems.sh'), - cpp, - files('filesystems-gperf.gperf'), - system_include_args, - ], - check: false, -) -if r.returncode() != 0 - warning('Unknown filesystems defined in kernel headers:\n\n' + r.stdout()) -endif +filesystem_sets_py = files('filesystem-sets.py') +filesystem_sets_c = custom_target( + output : 'filesystem-sets.c', + command : [filesystem_sets_py, + 'fs-type-to-string', + 'filesystem-sets'], + capture : true) + +filesystems_gperf_gperf = custom_target( + output : 'filesystems-gperf.gperf', + command : [filesystem_sets_py, + 'gperf'], + capture : true) filesystems_gperf_h = custom_target( - input : 'filesystems-gperf.gperf', + input : filesystems_gperf_gperf, output : 'filesystems-gperf.h', command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) -filesystem_list_inc = custom_target( - input : 'filesystems-gperf.gperf', - output : 'filesystem-list.inc', - command : [files('generate-filesystem-list.py'), - '@INPUT@'], - capture : true) +generated_sources += [filesystem_sets_c, filesystems_gperf_gperf, filesystems_gperf_h] +basic_sources += [filesystem_sets_c, filesystems_gperf_h] -filesystem_switch_case_inc = custom_target( - input : 'filesystems-gperf.gperf', - output : 'filesystem-switch-case.inc', - command : [files('generate-filesystem-switch-case.py'), - '@INPUT@'], - capture : true) - -generated_sources += [filesystem_list_inc, filesystem_switch_case_inc, filesystems_gperf_h] -basic_sources += [filesystem_list_inc, filesystem_switch_case_inc, filesystems_gperf_h] +test('kernel-filesystem-magics', + filesystem_sets_py, + args : ['check'], + env : ['CPP=@0@'.format(cpp)]) libbasic_static = static_library( 'basic', From aca4353ab2b928e669e1b649a9f6987df1efb084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 25 Jul 2025 17:08:56 +0200 Subject: [PATCH 4/4] Rework file system group lookups We want to check if the magic we got from statfs() is one of the magics listed for one of the file systems in the given group. To do this, we'd iteratate over the file system names, convert each name to an array of magics, and compare those to the one we got. We were using gperf-generated lookup table for this, so the string lookups were quick, but still this seems unnecessarily complex. Let's just generate a simple lookup function, because we can: $ src/basic/filesystem-sets.py fs-in-group bool fs_in_group(const struct statfs *st, FilesystemGroups fs_group) { switch (fs_group) { case FILESYSTEM_SET_BASIC_API: return F_TYPE_EQUAL(st->f_type, CGROUP2_SUPER_MAGIC) || F_TYPE_EQUAL(st->f_type, CGROUP_SUPER_MAGIC) || F_TYPE_EQUAL(st->f_type, DEVPTS_SUPER_MAGIC) || F_TYPE_EQUAL(st->f_type, MQUEUE_MAGIC) || F_TYPE_EQUAL(st->f_type, PROC_SUPER_MAGIC) || F_TYPE_EQUAL(st->f_type, SYSFS_MAGIC) || F_TYPE_EQUAL(st->f_type, TMPFS_MAGIC); case FILESYSTEM_SET_ANONYMOUS: return F_TYPE_EQUAL(st->f_type, ANON_INODE_FS_MAGIC) || F_TYPE_EQUAL(st->f_type, PIPEFS_MAGIC) || F_TYPE_EQUAL(st->f_type, SOCKFS_MAGIC); ... We flatten the nested lookup of group=>fs=>magic into a single level. The compiler can work its magic here to make the lookup quick. --- src/basic/filesystem-sets.py | 19 +++++++++++++++++++ src/basic/filesystems.c | 20 -------------------- src/basic/meson.build | 3 ++- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/basic/filesystem-sets.py b/src/basic/filesystem-sets.py index d374ac41ff..7cf73aaefb 100755 --- a/src/basic/filesystem-sets.py +++ b/src/basic/filesystem-sets.py @@ -297,6 +297,23 @@ const char* fs_type_to_string(statfs_f_type_t magic) { return NULL; }""") +def generate_fs_in_group(): + print('bool fs_in_group(const struct statfs *st, FilesystemGroups fs_group) {') + print(' switch (fs_group) {') + + for name, _, *filesystems in FILESYSTEM_SETS: + magics = sorted(set(sum((NAME_TO_MAGIC[fs] for fs in filesystems), + start=[]))) + enum = 'FILESYSTEM_SET_' + name[1:].upper().replace('-', '_') + print(f' case {enum}:') + opts = '\n || '.join(f'F_TYPE_EQUAL(st->f_type, {magic})' + for magic in magics) + print(f' return {opts};') + + print(' default: assert_not_reached();') + print(' }') + print('}') + def generate_filesystem_sets(): print('const FilesystemSet filesystem_sets[_FILESYSTEM_SET_MAX] = {') @@ -346,6 +363,8 @@ if __name__ == '__main__': generate_fs_type_to_string() elif arg == 'filesystem-sets': generate_filesystem_sets() + elif arg == 'fs-in-group': + generate_fs_in_group() elif arg == 'check': check() else: diff --git a/src/basic/filesystems.c b/src/basic/filesystems.c index 2bbab151bb..cd2789e329 100644 --- a/src/basic/filesystems.c +++ b/src/basic/filesystems.c @@ -19,26 +19,6 @@ int fs_type_from_string(const char *name, const statfs_f_type_t **ret) { return 0; } -bool fs_in_group(const struct statfs *s, FilesystemGroups fs_group) { - int r; - - NULSTR_FOREACH(fs, filesystem_sets[fs_group].value) { - const statfs_f_type_t *magic; - - r = fs_type_from_string(fs, &magic); - if (r >= 0) - for (size_t i = 0; i < FILESYSTEM_MAGIC_MAX; i++) { - if (magic[i] == 0) - break; - - if (is_fs_type(s, magic[i])) - return true; - } - } - - return false; -} - const FilesystemSet* filesystem_set_find(const char *name) { if (isempty(name) || name[0] != '@') return NULL; diff --git a/src/basic/meson.build b/src/basic/meson.build index 6589ce2995..79af468381 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -173,7 +173,8 @@ filesystem_sets_c = custom_target( output : 'filesystem-sets.c', command : [filesystem_sets_py, 'fs-type-to-string', - 'filesystem-sets'], + 'filesystem-sets', + 'fs-in-group'], capture : true) filesystems_gperf_gperf = custom_target(