diff --git a/config/kernel-objtool.m4 b/config/kernel-objtool.m4 index e616ccebcbc0..3020440eb388 100644 --- a/config/kernel-objtool.m4 +++ b/config/kernel-objtool.m4 @@ -1,90 +1,107 @@ dnl # dnl # Detect objtool functionality. dnl # dnl # dnl # Kernel 5.10: linux/frame.h was renamed linux/objtool.h dnl # AC_DEFUN([ZFS_AC_KERNEL_OBJTOOL_HEADER], [ AC_MSG_CHECKING([whether objtool header is available]) ZFS_LINUX_TRY_COMPILE([ #include ],[ ],[ objtool_header=$LINUX/include/linux/objtool.h AC_DEFINE(HAVE_KERNEL_OBJTOOL_HEADER, 1, [kernel has linux/objtool.h]) AC_MSG_RESULT(linux/objtool.h) ],[ objtool_header=$LINUX/include/linux/frame.h AC_MSG_RESULT(linux/frame.h) ]) ]) dnl # dnl # Check for objtool support. dnl # AC_DEFUN([ZFS_AC_KERNEL_SRC_OBJTOOL], [ dnl # 4.6 API for compile-time stack validation ZFS_LINUX_TEST_SRC([objtool], [ #undef __ASSEMBLY__ #include #include ],[ #if !defined(FRAME_BEGIN) #error "FRAME_BEGIN is not defined" #endif ]) dnl # 4.6 API added STACK_FRAME_NON_STANDARD macro ZFS_LINUX_TEST_SRC([stack_frame_non_standard], [ #ifdef HAVE_KERNEL_OBJTOOL_HEADER #include #else #include #endif ],[ #if !defined(STACK_FRAME_NON_STANDARD) #error "STACK_FRAME_NON_STANDARD is not defined." #endif ]) + + dnl # 6.15 made CONFIG_OBJTOOL_WERROR=y the default. We need to handle + dnl # this or our build will fail. + ZFS_LINUX_TEST_SRC([config_objtool_werror], [ + #if !defined(CONFIG_OBJTOOL_WERROR) + #error "CONFIG_OBJTOOL_WERROR is not defined." + #endif + ]) + ]) AC_DEFUN([ZFS_AC_KERNEL_OBJTOOL], [ AC_MSG_CHECKING( [whether compile-time stack validation (objtool) is available]) ZFS_LINUX_TEST_RESULT([objtool], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_KERNEL_OBJTOOL, 1, [kernel does stack verification]) AC_MSG_CHECKING([whether STACK_FRAME_NON_STANDARD is defined]) ZFS_LINUX_TEST_RESULT([stack_frame_non_standard], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_STACK_FRAME_NON_STANDARD, 1, [STACK_FRAME_NON_STANDARD is defined]) dnl # Needed for kernels missing the asm macro. We grep dnl # for it in the header file since there is currently dnl # no test to check the result of assembling a file. AC_MSG_CHECKING( [whether STACK_FRAME_NON_STANDARD asm macro is defined]) dnl # Escape square brackets. sp='@<:@@<:@:space:@:>@@:>@' dotmacro='@<:@.@:>@macro' regexp="^$sp*$dotmacro$sp+STACK_FRAME_NON_STANDARD$sp" AS_IF([$EGREP -s -q "$regexp" $objtool_header],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_STACK_FRAME_NON_STANDARD_ASM, 1, [STACK_FRAME_NON_STANDARD asm macro is defined]) ],[ AC_MSG_RESULT(no) ]) ],[ AC_MSG_RESULT(no) ]) + + AC_MSG_CHECKING([whether CONFIG_OBJTOOL_WERROR is defined]) + ZFS_LINUX_TEST_RESULT([config_objtool_werror],[ + AC_MSG_RESULT(yes) + CONFIG_OBJTOOL_WERROR_DEFINED=yes + ],[ + AC_MSG_RESULT(no) + ]) ],[ AC_MSG_RESULT(no) ]) ]) diff --git a/config/zfs-build.m4 b/config/zfs-build.m4 index 55fc029f0847..7cf1b02d8757 100644 --- a/config/zfs-build.m4 +++ b/config/zfs-build.m4 @@ -1,657 +1,697 @@ AC_DEFUN([ZFS_AC_LICENSE], [ AC_MSG_CHECKING([zfs author]) AC_MSG_RESULT([$ZFS_META_AUTHOR]) AC_MSG_CHECKING([zfs license]) AC_MSG_RESULT([$ZFS_META_LICENSE]) ]) AC_DEFUN([ZFS_AC_DEBUG_ENABLE], [ DEBUG_CFLAGS="-Werror" DEBUG_CPPFLAGS="-DDEBUG -UNDEBUG" DEBUG_LDFLAGS="" DEBUG_ZFS="_with_debug" WITH_DEBUG="true" AC_DEFINE(ZFS_DEBUG, 1, [zfs debugging enabled]) KERNEL_DEBUG_CFLAGS="-Werror" KERNEL_DEBUG_CPPFLAGS="-DDEBUG -UNDEBUG" ]) AC_DEFUN([ZFS_AC_DEBUG_DISABLE], [ DEBUG_CFLAGS="" DEBUG_CPPFLAGS="-UDEBUG -DNDEBUG" DEBUG_LDFLAGS="" DEBUG_ZFS="_without_debug" WITH_DEBUG="" KERNEL_DEBUG_CFLAGS="" KERNEL_DEBUG_CPPFLAGS="-UDEBUG -DNDEBUG" ]) dnl # dnl # When debugging is enabled: dnl # - Enable all ASSERTs (-DDEBUG) dnl # - Promote all compiler warnings to errors (-Werror) dnl # dnl # (If INVARIANTS is detected, we need to force DEBUG, or strange panics dnl # can ensue.) dnl # AC_DEFUN([ZFS_AC_DEBUG], [ AC_MSG_CHECKING([whether assertion support will be enabled]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Enable compiler and code assertions @<:@default=no@:>@])], [], [enable_debug=no]) AS_CASE(["x$enable_debug"], ["xyes"], [ZFS_AC_DEBUG_ENABLE], ["xno"], [ZFS_AC_DEBUG_DISABLE], [AC_MSG_ERROR([Unknown option $enable_debug])]) AS_CASE(["x$enable_invariants"], ["xyes"], [], ["xno"], [], [ZFS_AC_DEBUG_INVARIANTS_DETECT]) AS_CASE(["x$enable_invariants"], ["xyes"], [ZFS_AC_DEBUG_ENABLE], ["xno"], [], [AC_MSG_ERROR([Unknown option $enable_invariants])]) AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUG_CPPFLAGS) AC_SUBST(DEBUG_LDFLAGS) AC_SUBST(DEBUG_ZFS) AC_SUBST(WITH_DEBUG) AC_SUBST(KERNEL_DEBUG_CFLAGS) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_MSG_RESULT([$enable_debug]) ]) AC_DEFUN([ZFS_AC_DEBUGINFO_ENABLE], [ DEBUG_CFLAGS="$DEBUG_CFLAGS -g -fno-inline $NO_IPA_SRA" KERNEL_DEBUG_CFLAGS="$KERNEL_DEBUG_CFLAGS -fno-inline $KERNEL_NO_IPA_SRA" KERNEL_MAKE="$KERNEL_MAKE CONFIG_DEBUG_INFO=y" DEBUGINFO_ZFS="_with_debuginfo" ]) AC_DEFUN([ZFS_AC_DEBUGINFO_DISABLE], [ DEBUGINFO_ZFS="_without_debuginfo" ]) AC_DEFUN([ZFS_AC_DEBUGINFO], [ AC_MSG_CHECKING([whether debuginfo support will be forced]) AC_ARG_ENABLE([debuginfo], [AS_HELP_STRING([--enable-debuginfo], [Force generation of debuginfo @<:@default=no@:>@])], [], [enable_debuginfo=no]) AS_CASE(["x$enable_debuginfo"], ["xyes"], [ZFS_AC_DEBUGINFO_ENABLE], ["xno"], [ZFS_AC_DEBUGINFO_DISABLE], [AC_MSG_ERROR([Unknown option $enable_debuginfo])]) AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUGINFO_ZFS) AC_SUBST(KERNEL_DEBUG_CFLAGS) AC_SUBST(KERNEL_MAKE) AC_MSG_RESULT([$enable_debuginfo]) ]) dnl # dnl # Disabled by default, provides basic memory tracking. Track the total dnl # number of bytes allocated with kmem_alloc() and freed with kmem_free(). dnl # Then at module unload time if any bytes were leaked it will be reported dnl # on the console. dnl # AC_DEFUN([ZFS_AC_DEBUG_KMEM], [ AC_MSG_CHECKING([whether basic kmem accounting is enabled]) AC_ARG_ENABLE([debug-kmem], [AS_HELP_STRING([--enable-debug-kmem], [Enable basic kmem accounting @<:@default=no@:>@])], [], [enable_debug_kmem=no]) AS_IF([test "x$enable_debug_kmem" = xyes], [ KERNEL_DEBUG_CPPFLAGS="${KERNEL_DEBUG_CPPFLAGS} -DDEBUG_KMEM" DEBUG_KMEM_ZFS="_with_debug_kmem" ], [ DEBUG_KMEM_ZFS="_without_debug_kmem" ]) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_SUBST(DEBUG_KMEM_ZFS) AC_MSG_RESULT([$enable_debug_kmem]) ]) dnl # dnl # Disabled by default, provides detailed memory tracking. This feature dnl # also requires --enable-debug-kmem to be set. When enabled not only will dnl # total bytes be tracked but also the location of every kmem_alloc() and dnl # kmem_free(). When the module is unloaded a list of all leaked addresses dnl # and where they were allocated will be dumped to the console. Enabling dnl # this feature has a significant impact on performance but it makes finding dnl # memory leaks straight forward. dnl # AC_DEFUN([ZFS_AC_DEBUG_KMEM_TRACKING], [ AC_MSG_CHECKING([whether detailed kmem tracking is enabled]) AC_ARG_ENABLE([debug-kmem-tracking], [AS_HELP_STRING([--enable-debug-kmem-tracking], [Enable detailed kmem tracking @<:@default=no@:>@])], [], [enable_debug_kmem_tracking=no]) AS_IF([test "x$enable_debug_kmem_tracking" = xyes], [ KERNEL_DEBUG_CPPFLAGS="${KERNEL_DEBUG_CPPFLAGS} -DDEBUG_KMEM_TRACKING" DEBUG_KMEM_TRACKING_ZFS="_with_debug_kmem_tracking" ], [ DEBUG_KMEM_TRACKING_ZFS="_without_debug_kmem_tracking" ]) AC_SUBST(KERNEL_DEBUG_CPPFLAGS) AC_SUBST(DEBUG_KMEM_TRACKING_ZFS) AC_MSG_RESULT([$enable_debug_kmem_tracking]) ]) AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS_DETECT_FREEBSD], [ AS_IF([sysctl -n kern.conftxt | grep -Fqx $'options\tINVARIANTS'], [enable_invariants="yes"], [enable_invariants="no"]) ]) AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS_DETECT], [ AM_COND_IF([BUILD_FREEBSD], [ZFS_AC_DEBUG_INVARIANTS_DETECT_FREEBSD], [enable_invariants="no"]) ]) dnl # dnl # Detected for the running kernel by default, enables INVARIANTS features dnl # in the FreeBSD kernel module. This feature must be used when building dnl # for a FreeBSD kernel with "options INVARIANTS" in the KERNCONF and must dnl # not be used when the INVARIANTS option is absent. dnl # AC_DEFUN([ZFS_AC_DEBUG_INVARIANTS], [ AC_MSG_CHECKING([whether FreeBSD kernel INVARIANTS checks are enabled]) AC_ARG_ENABLE([invariants], [AS_HELP_STRING([--enable-invariants], [Enable FreeBSD kernel INVARIANTS checks [[default: detect]]])], [], [ZFS_AC_DEBUG_INVARIANTS_DETECT]) AS_IF([test "x$enable_invariants" = xyes], [WITH_INVARIANTS="true"], [WITH_INVARIANTS=""]) AC_SUBST(WITH_INVARIANTS) AC_MSG_RESULT([$enable_invariants]) ]) +dnl # Disabled by default. If enabled allows a configured "turn objtools +dnl # warnings into errors" (CONFIG_OBJTOOL_WERROR) behavior to take effect. +dnl # If disabled, objtool warnings are never turned into errors. It can't +dnl # be enabled if the kernel wasn't compiled with CONFIG_OBJTOOL_WERROR=y. +dnl # +AC_DEFUN([ZFS_AC_OBJTOOL_WERROR], [ + AC_MSG_CHECKING([whether objtool error on warning behavior is enabled]) + AC_ARG_ENABLE([objtool-werror], + [AS_HELP_STRING([--enable-objtool-werror], + [Enable objtool's error on warning behaviour if present @<:@default=no@:>@])], + [enable_objtool_werror=$enableval], + [enable_objtool_werror=no]) + AC_MSG_RESULT([$enable_objtool_werror]) + + AS_IF([test x$CONFIG_OBJTOOL_WERROR_DEFINED = xyes],[ + AS_IF([test x$enable_objtool_werror = xyes],[ + AC_MSG_NOTICE([enable-objtool-werror defined, keeping -Werror ]) + ],[ + AC_MSG_NOTICE([enable-objtool-werror undefined, disabling -Werror ]) + OBJTOOL_DISABLE_WERROR=y + abs_objtool_binary=$kernelsrc/tools/objtool/objtool + AS_IF([test -x $abs_objtool_binary],[],[ + AC_MSG_ERROR([*** objtool binary $abs_objtool_binary not found]) + ]) + dnl # The path to the wrapper is defined in modules/Makefile.in. + ]) + ],[ + dnl # We can't enable --Werror if it's not there. + AS_IF([test x$enable_objtool_werror = xyes],[ + AC_MSG_ERROR([ + *** Cannot enable objtool-werror, + *** a kernel built with CONFIG_OBJTOOL_WERROR=y is required. + ]) + ],[]) + ]) + + AC_SUBST(OBJTOOL_DISABLE_WERROR) + AC_SUBST(abs_objtool_binary) +]) + AC_DEFUN([ZFS_AC_CONFIG_ALWAYS], [ AX_COUNT_CPUS([]) AC_SUBST(CPU_COUNT) ZFS_AC_CONFIG_ALWAYS_CC_NO_CLOBBERED ZFS_AC_CONFIG_ALWAYS_CC_INFINITE_RECURSION ZFS_AC_CONFIG_ALWAYS_KERNEL_CC_INFINITE_RECURSION ZFS_AC_CONFIG_ALWAYS_CC_IMPLICIT_FALLTHROUGH ZFS_AC_CONFIG_ALWAYS_CC_FRAME_LARGER_THAN ZFS_AC_CONFIG_ALWAYS_CC_NO_FORMAT_TRUNCATION ZFS_AC_CONFIG_ALWAYS_CC_NO_FORMAT_ZERO_LENGTH ZFS_AC_CONFIG_ALWAYS_CC_FORMAT_OVERFLOW ZFS_AC_CONFIG_ALWAYS_CC_NO_OMIT_FRAME_POINTER ZFS_AC_CONFIG_ALWAYS_CC_NO_IPA_SRA ZFS_AC_CONFIG_ALWAYS_KERNEL_CC_NO_IPA_SRA ZFS_AC_CONFIG_ALWAYS_CC_ASAN ZFS_AC_CONFIG_ALWAYS_CC_UBSAN ZFS_AC_CONFIG_ALWAYS_TOOLCHAIN_SIMD ZFS_AC_CONFIG_ALWAYS_SYSTEM ZFS_AC_CONFIG_ALWAYS_ARCH ZFS_AC_CONFIG_ALWAYS_PYTHON ZFS_AC_CONFIG_ALWAYS_PYZFS ZFS_AC_CONFIG_ALWAYS_SED ZFS_AC_CONFIG_ALWAYS_CPPCHECK ZFS_AC_CONFIG_ALWAYS_SHELLCHECK ZFS_AC_CONFIG_ALWAYS_PARALLEL ]) AC_DEFUN([ZFS_AC_CONFIG], [ dnl # Remove the previous build test directory. rm -Rf build ZFS_CONFIG=all AC_ARG_WITH([config], AS_HELP_STRING([--with-config=CONFIG], [Config file 'kernel|user|all|srpm']), [ZFS_CONFIG="$withval"]) AC_ARG_ENABLE([linux-builtin], [AS_HELP_STRING([--enable-linux-builtin], [Configure for builtin in-tree kernel modules @<:@default=no@:>@])], [], [enable_linux_builtin=no]) AC_MSG_CHECKING([zfs config]) AC_MSG_RESULT([$ZFS_CONFIG]); AC_SUBST(ZFS_CONFIG) ZFS_AC_CONFIG_ALWAYS AM_COND_IF([BUILD_LINUX], [ AC_ARG_VAR([TEST_JOBS], [simultaneous jobs during configure]) if test "x$ac_cv_env_TEST_JOBS_set" != "xset"; then TEST_JOBS=$CPU_COUNT fi AC_SUBST(TEST_JOBS) ]) ZFS_INIT_SYSV= ZFS_INIT_SYSTEMD= ZFS_WANT_MODULES_LOAD_D= case "$ZFS_CONFIG" in kernel) ZFS_AC_CONFIG_KERNEL ;; user) ZFS_AC_CONFIG_USER ;; all) ZFS_AC_CONFIG_USER ZFS_AC_CONFIG_KERNEL ;; dist) ;; srpm) ;; *) AC_MSG_RESULT([Error!]) AC_MSG_ERROR([Bad value "$ZFS_CONFIG" for --with-config, user kernel|user|all|srpm]) ;; esac AM_CONDITIONAL([INIT_SYSV], [test "x$ZFS_INIT_SYSV" = "xyes"]) AM_CONDITIONAL([INIT_SYSTEMD], [test "x$ZFS_INIT_SYSTEMD" = "xyes"]) AM_CONDITIONAL([WANT_MODULES_LOAD_D], [test "x$ZFS_WANT_MODULES_LOAD_D" = "xyes"]) AM_CONDITIONAL([CONFIG_USER], [test "$ZFS_CONFIG" = user -o "$ZFS_CONFIG" = all]) AM_CONDITIONAL([CONFIG_KERNEL], [test "$ZFS_CONFIG" = kernel -o "$ZFS_CONFIG" = all] && [test "x$enable_linux_builtin" != xyes ]) AM_CONDITIONAL([CONFIG_QAT], [test "$ZFS_CONFIG" = kernel -o "$ZFS_CONFIG" = all] && [test "x$qatsrc" != x ]) AM_CONDITIONAL([WANT_DEVNAME2DEVID], [test "x$user_libudev" = xyes ]) AM_CONDITIONAL([WANT_MMAP_LIBAIO], [test "x$user_libaio" = xyes ]) AM_CONDITIONAL([PAM_ZFS_ENABLED], [test "x$enable_pam" = xyes]) ]) dnl # dnl # Check for rpm+rpmbuild to build RPM packages. If these tools dnl # are missing it is non-fatal but you will not be able to build dnl # RPM packages and will be warned if you try too. dnl # dnl # By default the generic spec file will be used because it requires dnl # minimal dependencies. Distribution specific spec files can be dnl # placed under the 'rpm/' directory and enabled using dnl # the --with-spec= configure option. dnl # AC_DEFUN([ZFS_AC_RPM], [ RPM=rpm RPMBUILD=rpmbuild AC_MSG_CHECKING([whether $RPM is available]) AS_IF([tmp=$($RPM --version 2>/dev/null)], [ RPM_VERSION=$(echo $tmp | $AWK '/RPM/ { print $[3] }') HAVE_RPM=yes AC_MSG_RESULT([$HAVE_RPM ($RPM_VERSION)]) ],[ HAVE_RPM=no AC_MSG_RESULT([$HAVE_RPM]) ]) AC_MSG_CHECKING([whether $RPMBUILD is available]) AS_IF([tmp=$($RPMBUILD --version 2>/dev/null)], [ RPMBUILD_VERSION=$(echo $tmp | $AWK '/RPM/ { print $[3] }') HAVE_RPMBUILD=yes AC_MSG_RESULT([$HAVE_RPMBUILD ($RPMBUILD_VERSION)]) ],[ HAVE_RPMBUILD=no AC_MSG_RESULT([$HAVE_RPMBUILD]) ]) RPM_DEFINE_COMMON='--define "$(DEBUG_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUGINFO_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(DEBUG_KMEM_TRACKING_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(ASAN_ZFS) 1"' RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "$(UBSAN_ZFS) 1"' AS_IF([test "x$enable_debuginfo" = xyes], [ RPM_DEFINE_COMMON=${RPM_DEFINE_COMMON}' --define "__strip /bin/true"' ]) RPM_DEFINE_UTIL=' --define "_initconfdir $(initconfdir)"' dnl # Make the next three RPM_DEFINE_UTIL additions conditional, since dnl # their values may not be set when running: dnl # dnl # ./configure --with-config=srpm dnl # AS_IF([test -n "$dracutdir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_dracutdir $(dracutdir)"' ]) AS_IF([test -n "$udevdir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_udevdir $(udevdir)"' ]) AS_IF([test -n "$udevruledir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_udevruledir $(udevruledir)"' ]) AS_IF([test -n "$bashcompletiondir" ], [ RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_bashcompletiondir $(bashcompletiondir)"' ]) RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_SYSTEMD)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYZFS)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PAM)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYTHON_VERSION)' RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' $(DEFINE_PYTHON_PKG_VERSION)' dnl # Override default lib directory on Debian/Ubuntu systems. The dnl # provided /usr/lib/rpm/platform//macros files do not dnl # specify the correct path for multiarch systems as described dnl # by the packaging guidelines. dnl # dnl # https://wiki.ubuntu.com/MultiarchSpec dnl # https://wiki.debian.org/Multiarch/Implementation dnl # AS_IF([test "$DEFAULT_PACKAGE" = "deb"], [ MULTIARCH_LIBDIR="lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)" RPM_DEFINE_UTIL=${RPM_DEFINE_UTIL}' --define "_lib $(MULTIARCH_LIBDIR)"' AC_SUBST(MULTIARCH_LIBDIR) ]) dnl # Make RPM_DEFINE_KMOD additions conditional on CONFIG_KERNEL, dnl # since the values will not be set otherwise. The spec files dnl # provide defaults for them. dnl # RPM_DEFINE_KMOD='--define "_wrong_version_format_terminate_build 0"' AM_COND_IF([CONFIG_KERNEL], [ RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernels $(LINUX_VERSION)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "ksrc $(LINUX)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kobj $(LINUX_OBJ)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernel_cc KERNEL_CC=$(KERNEL_CC)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernel_ld KERNEL_LD=$(KERNEL_LD)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernel_llvm KERNEL_LLVM=$(KERNEL_LLVM)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernel_cross_compile KERNEL_CROSS_COMPILE=$(KERNEL_CROSS_COMPILE)"' RPM_DEFINE_KMOD=${RPM_DEFINE_KMOD}' --define "kernel_arch KERNEL_ARCH=$(KERNEL_ARCH)"' ]) RPM_DEFINE_DKMS='' SRPM_DEFINE_COMMON='--define "build_src_rpm 1"' SRPM_DEFINE_UTIL= SRPM_DEFINE_KMOD= SRPM_DEFINE_DKMS= RPM_SPEC_DIR="rpm/generic" AC_ARG_WITH([spec], AS_HELP_STRING([--with-spec=SPEC], [Spec files 'generic|redhat']), [RPM_SPEC_DIR="rpm/$withval"]) AC_MSG_CHECKING([whether spec files are available]) AC_MSG_RESULT([yes ($RPM_SPEC_DIR/*.spec.in)]) AC_SUBST(HAVE_RPM) AC_SUBST(RPM) AC_SUBST(RPM_VERSION) AC_SUBST(HAVE_RPMBUILD) AC_SUBST(RPMBUILD) AC_SUBST(RPMBUILD_VERSION) AC_SUBST(RPM_SPEC_DIR) AC_SUBST(RPM_DEFINE_UTIL) AC_SUBST(RPM_DEFINE_KMOD) AC_SUBST(RPM_DEFINE_DKMS) AC_SUBST(RPM_DEFINE_COMMON) AC_SUBST(SRPM_DEFINE_UTIL) AC_SUBST(SRPM_DEFINE_KMOD) AC_SUBST(SRPM_DEFINE_DKMS) AC_SUBST(SRPM_DEFINE_COMMON) ]) dnl # dnl # Check for dpkg+dpkg-buildpackage to build DEB packages. If these dnl # tools are missing it is non-fatal but you will not be able to build dnl # DEB packages and will be warned if you try too. dnl # AC_DEFUN([ZFS_AC_DPKG], [ DPKG=dpkg DPKGBUILD=dpkg-buildpackage AC_MSG_CHECKING([whether $DPKG is available]) AS_IF([tmp=$($DPKG --version 2>/dev/null)], [ DPKG_VERSION=$(echo $tmp | $AWK '/Debian/ { print $[7] }') HAVE_DPKG=yes AC_MSG_RESULT([$HAVE_DPKG ($DPKG_VERSION)]) ],[ HAVE_DPKG=no AC_MSG_RESULT([$HAVE_DPKG]) ]) AC_MSG_CHECKING([whether $DPKGBUILD is available]) AS_IF([tmp=$($DPKGBUILD --version 2>/dev/null)], [ DPKGBUILD_VERSION=$(echo $tmp | \ $AWK '/Debian/ { print $[4] }' | cut -f-4 -d'.') HAVE_DPKGBUILD=yes AC_MSG_RESULT([$HAVE_DPKGBUILD ($DPKGBUILD_VERSION)]) ],[ HAVE_DPKGBUILD=no AC_MSG_RESULT([$HAVE_DPKGBUILD]) ]) AC_SUBST(HAVE_DPKG) AC_SUBST(DPKG) AC_SUBST(DPKG_VERSION) AC_SUBST(HAVE_DPKGBUILD) AC_SUBST(DPKGBUILD) AC_SUBST(DPKGBUILD_VERSION) AC_SUBST([CFGOPTS], ["$CFGOPTS"]) ]) dnl # dnl # Until native packaging for various different packing systems dnl # can be added the least we can do is attempt to use alien to dnl # convert the RPM packages to the needed package type. This is dnl # a hack but so far it has worked reasonable well. dnl # AC_DEFUN([ZFS_AC_ALIEN], [ ALIEN=alien AC_MSG_CHECKING([whether $ALIEN is available]) AS_IF([tmp=$($ALIEN --version 2>/dev/null)], [ ALIEN_VERSION=$(echo $tmp | $AWK '{ print $[3] }') ALIEN_MAJOR=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[1] }') ALIEN_MINOR=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[2] }') ALIEN_POINT=$(echo ${ALIEN_VERSION} | $AWK -F'.' '{ print $[3] }') HAVE_ALIEN=yes AC_MSG_RESULT([$HAVE_ALIEN ($ALIEN_VERSION)]) ],[ HAVE_ALIEN=no AC_MSG_RESULT([$HAVE_ALIEN]) ]) AC_SUBST(HAVE_ALIEN) AC_SUBST(ALIEN) AC_SUBST(ALIEN_VERSION) AC_SUBST(ALIEN_MAJOR) AC_SUBST(ALIEN_MINOR) AC_SUBST(ALIEN_POINT) ]) dnl # dnl # Using the VENDOR tag from config.guess set the default dnl # package type for 'make pkg': (rpm | deb | tgz) dnl # AC_DEFUN([ZFS_AC_DEFAULT_PACKAGE], [ AC_MSG_CHECKING([os distribution]) AC_ARG_WITH([vendor], [AS_HELP_STRING([--with-vendor], [Distribution vendor @<:@default=check@:>@])], [with_vendor=$withval], [with_vendor=check]) AS_IF([test "x$with_vendor" = "xcheck"],[ if test -f /etc/alpine-release ; then VENDOR=alpine ; elif test -f /etc/arch-release ; then VENDOR=arch ; elif test -f /etc/artix-release ; then VENDOR=artix ; elif test -f /etc/fedora-release ; then VENDOR=fedora ; elif test -f /bin/freebsd-version ; then VENDOR=freebsd ; elif test -f /etc/gentoo-release ; then VENDOR=gentoo ; elif test -f /etc/lunar.release ; then VENDOR=lunar ; elif test -f /etc/openEuler-release ; then VENDOR=openeuler ; elif test -f /etc/SuSE-release ; then VENDOR=sles ; elif test -f /etc/slackware-version ; then VENDOR=slackware ; elif test -f /etc/toss-release ; then VENDOR=toss ; elif test -f /etc/lsb-release ; then VENDOR=ubuntu ; # put debian and redhat last as derivatives may have also their file elif test -f /etc/debian_version ; then VENDOR=debian ; elif test -f /etc/redhat-release ; then VENDOR=redhat ; else VENDOR= ; fi], [ test "x${with_vendor}" != x],[ VENDOR="$with_vendor" ], [ VENDOR= ; ] ) AC_MSG_RESULT([$VENDOR]) AC_SUBST(VENDOR) AC_MSG_CHECKING([default package type]) case "$VENDOR" in alpine|arch|artix|gentoo|lunar|slackware) DEFAULT_PACKAGE=tgz ;; debian|ubuntu) DEFAULT_PACKAGE=deb ;; freebsd) DEFAULT_PACKAGE=pkg ;; *) # fedora|openeuler|redhat|sles|toss DEFAULT_PACKAGE=rpm ;; esac AC_MSG_RESULT([$DEFAULT_PACKAGE]) AC_SUBST(DEFAULT_PACKAGE) AC_MSG_CHECKING([default init directory]) case "$VENDOR" in freebsd) initdir=$sysconfdir/rc.d ;; *) initdir=$sysconfdir/init.d;; esac AC_MSG_RESULT([$initdir]) AC_SUBST(initdir) AC_MSG_CHECKING([default shell]) case "$VENDOR" in alpine|gentoo) DEFAULT_INIT_SHELL=/sbin/openrc-run IS_SYSV_RC=false ;; artix) DEFAULT_INIT_SHELL=/usr/bin/openrc-run IS_SYSV_RC=false ;; *) DEFAULT_INIT_SHELL=/bin/sh IS_SYSV_RC=true ;; esac AC_MSG_RESULT([$DEFAULT_INIT_SHELL]) AC_SUBST(DEFAULT_INIT_SHELL) AC_SUBST(IS_SYSV_RC) AC_MSG_CHECKING([default nfs server init script]) AS_IF([test "$VENDOR" = "debian"], [DEFAULT_INIT_NFS_SERVER="nfs-kernel-server"], [DEFAULT_INIT_NFS_SERVER="nfs"] ) AC_MSG_RESULT([$DEFAULT_INIT_NFS_SERVER]) AC_SUBST(DEFAULT_INIT_NFS_SERVER) AC_MSG_CHECKING([default init config directory]) case "$VENDOR" in alpine|artix|gentoo) initconfdir=/etc/conf.d ;; fedora|openeuler|redhat|sles|toss) initconfdir=/etc/sysconfig ;; freebsd) initconfdir=$sysconfdir/rc.conf.d ;; *) # debian|ubuntu initconfdir=/etc/default ;; esac AC_MSG_RESULT([$initconfdir]) AC_SUBST(initconfdir) AC_MSG_CHECKING([whether initramfs-tools is available]) if test -d /usr/share/initramfs-tools ; then RPM_DEFINE_INITRAMFS='--define "_initramfs 1"' AC_MSG_RESULT([yes]) else RPM_DEFINE_INITRAMFS='' AC_MSG_RESULT([no]) fi AC_SUBST(RPM_DEFINE_INITRAMFS) AC_MSG_CHECKING([default bash completion directory]) case "$VENDOR" in alpine|arch|artix|debian|gentoo|ubuntu) bashcompletiondir=/usr/share/bash-completion/completions ;; freebsd) bashcompletiondir=$sysconfdir/bash_completion.d ;; *) bashcompletiondir=/etc/bash_completion.d ;; esac AC_MSG_RESULT([$bashcompletiondir]) AC_SUBST(bashcompletiondir) ]) dnl # dnl # Default ZFS package configuration dnl # AC_DEFUN([ZFS_AC_PACKAGE], [ ZFS_AC_DEFAULT_PACKAGE AS_IF([test x$VENDOR != xfreebsd], [ ZFS_AC_RPM ZFS_AC_DPKG ZFS_AC_ALIEN ]) ]) diff --git a/configure.ac b/configure.ac index c05c874affc9..f4b52e1f7abc 100644 --- a/configure.ac +++ b/configure.ac @@ -1,92 +1,94 @@ // SPDX-License-Identifier: CDDL-1.0 /* * This file is part of OpenZFS. * * Copyright (c) 2009 Lawrence Livermore National Security, LLC. * Produced at Lawrence Livermore National Laboratory * Written by: * Brian Behlendorf , * Herb Wartens , * Jim Garlick * LLNL-CODE-403049 * * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or https://opensource.org/licenses/CDDL-1.0. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ AC_INIT(m4_esyscmd(awk '/^Name:/ {printf $2}' META), m4_esyscmd(awk '/^Version:/ {printf $2}' META)) CFGOPTS="$*" AC_LANG(C) ZFS_AC_META AC_CONFIG_AUX_DIR([config]) AC_CONFIG_MACRO_DIR([config]) AC_CANONICAL_TARGET AM_MAINTAINER_MODE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_INIT_AUTOMAKE([subdir-objects foreign]) # Remove default macros from config.h: # PACKAGE, PACKAGE_{BUGREPORT,NAME,STRING,TARNAME,VERSION}, STDC_HEADERS, VERSION AC_CONFIG_HEADERS([zfs_config.h], [ $SED -nri~ -e '/^$/be' -e 'N;N;/#define (PACKAGE|VERSION|STDC_HEADERS)/d' -e ':e' -e 'p' zfs_config.h && rm zfs_config.h~ || exit]) LT_INIT AC_PROG_INSTALL AC_PROG_CC AC_PROG_LN_S PKG_PROG_PKG_CONFIG AM_PROG_AS AM_PROG_CC_C_O AX_CODE_COVERAGE _AM_PROG_TAR(pax) ZFS_AC_LICENSE ZFS_AC_CONFIG ZFS_AC_PACKAGE ZFS_AC_DEBUG ZFS_AC_DEBUGINFO ZFS_AC_DEBUG_KMEM ZFS_AC_DEBUG_KMEM_TRACKING ZFS_AC_DEBUG_INVARIANTS +ZFS_AC_OBJTOOL_WERROR AC_CONFIG_FILES([ contrib/debian/rules contrib/debian/changelog Makefile include/Makefile lib/libzfs/libzfs.pc lib/libzfs_core/libzfs_core.pc lib/libzfsbootenv/libzfsbootenv.pc module/Kbuild module/Makefile rpm/generic/zfs-dkms.spec rpm/generic/zfs-kmod.spec rpm/generic/zfs.spec rpm/redhat/zfs-dkms.spec rpm/redhat/zfs-kmod.spec rpm/redhat/zfs.spec tests/zfs-tests/tests/Makefile zfs.release ]) +AC_CONFIG_FILES([scripts/objtool-wrapper], [chmod +x scripts/objtool-wrapper]) AC_OUTPUT ZFS_AC_KERNEL_VERSION_WARNING diff --git a/module/Makefile.in b/module/Makefile.in index a65cbfce1a90..e9a268121762 100644 --- a/module/Makefile.in +++ b/module/Makefile.in @@ -1,174 +1,175 @@ include Kbuild INSTALL_MOD_DIR ?= extra INSTALL_MOD_PATH ?= $(DESTDIR) all: modules distclean maintainer-clean: clean install: modules_install data_install uninstall: modules_uninstall data_uninstall check: .PHONY: all distclean maintainer-clean install uninstall check distdir \ modules modules-Linux modules-FreeBSD modules-unknown \ clean clean-Linux clean-FreeBSD \ modules_install modules_install-Linux modules_install-FreeBSD \ data_install data_install-Linux data_install-FreeBSD \ modules_uninstall modules_uninstall-Linux modules_uninstall-FreeBSD \ data_uninstall data_uninstall-Linux data_uninstall-FreeBSD \ cppcheck cppcheck-Linux cppcheck-FreeBSD # For FreeBSD, use debug options from ./configure if not overridden. export WITH_DEBUG ?= @WITH_DEBUG@ export WITH_INVARIANTS ?= @WITH_INVARIANTS@ # Filter out options that FreeBSD make doesn't understand getflags = ( \ set -- \ $(filter-out --%,$(firstword $(MFLAGS))) \ $(filter -I%,$(MFLAGS)) \ $(filter -j%,$(MFLAGS)); \ fmakeflags=""; \ while getopts :deiI:j:knqrstw flag; do \ case $$flag in \ \?) :;; \ :) if [ $$OPTARG = "j" ]; then \ ncpus=$$(sysctl -n kern.smp.cpus 2>/dev/null || :); \ if [ -n "$$ncpus" ]; then fmakeflags="$$fmakeflags -j$$ncpus"; fi; \ fi;; \ d) fmakeflags="$$fmakeflags -dA";; \ *) fmakeflags="$$fmakeflags -$$flag$$OPTARG";; \ esac; \ done; \ echo $$fmakeflags \ ) FMAKEFLAGS = -C @abs_srcdir@ -f Makefile.bsd $(shell $(getflags)) ifneq (@abs_srcdir@,@abs_builddir@) FMAKEFLAGS += MAKEOBJDIR=@abs_builddir@ endif FMAKE = env -u MAKEFLAGS make $(FMAKEFLAGS) modules-Linux: mkdir -p $(sort $(dir $(spl-objs) $(spl-))) mkdir -p $(sort $(dir $(zfs-objs) $(zfs-))) $(MAKE) -C @LINUX_OBJ@ $(if @KERNEL_CC@,CC=@KERNEL_CC@) \ $(if @KERNEL_LD@,LD=@KERNEL_LD@) $(if @KERNEL_LLVM@,LLVM=@KERNEL_LLVM@) \ $(if @KERNEL_CROSS_COMPILE@,CROSS_COMPILE=@KERNEL_CROSS_COMPILE@) \ $(if @KERNEL_ARCH@,ARCH=@KERNEL_ARCH@) \ + $(if @OBJTOOL_DISABLE_WERROR@,objtool=@top_builddir@/scripts/objtool-wrapper) \ M="$$PWD" @KERNEL_MAKE@ CONFIG_ZFS=m modules modules-FreeBSD: +$(FMAKE) modules-unknown: @true modules: modules-@ac_system@ clean-Linux: @# Only cleanup the kernel build directories when CONFIG_KERNEL @# is defined. This indicates that kernel modules should be built. @CONFIG_KERNEL_TRUE@ $(MAKE) -C @LINUX_OBJ@ M="$$PWD" @KERNEL_MAKE@ clean $(RM) @LINUX_SYMBOLS@ Module.markers find . -name '*.ur-safe' -type f -delete clean-FreeBSD: +$(FMAKE) clean clean: clean-@ac_system@ .PHONY: modules_uninstall-Linux-legacy modules_uninstall-Linux-legacy: $(RM) -r $(addprefix $(KMODDIR)/$(INSTALL_MOD_DIR)/,spl/ avl/ icp/ lua/ nvpair/ unicode/ zcommon/ zfs/ zstd/) KMODDIR := $(INSTALL_MOD_PATH)/lib/modules/@LINUX_VERSION@ modules_install-Linux: modules_uninstall-Linux-legacy @# Install the kernel modules $(MAKE) -C @LINUX_OBJ@ M="$$PWD" modules_install \ INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) \ INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) \ $(if @KERNEL_ARCH@,ARCH=@KERNEL_ARCH@) \ KERNELRELEASE=@LINUX_VERSION@ @# Remove extraneous build products when packaging if [ -n "$(DESTDIR)" ] && [ "$(DONT_DELETE_MODULES_FILES)" != "1" ]; then \ find $(KMODDIR) -name 'modules.*' -delete; \ fi @# Debian ships tiny fake System.map files that are @# syntactically valid but just say @# "if you want system.map go install this package" @# Naturally, depmod is less than amused by this. @# So if we find it missing or with one of these present, @# we check for the alternate path for the System.map sysmap=$(INSTALL_MOD_PATH)/boot/System.map-@LINUX_VERSION@; \ { [ -f "$$sysmap" ] && [ $$(wc -l < "$$sysmap") -ge 100 ]; } || \ sysmap=$(INSTALL_MOD_PATH)/usr/lib/debug/boot/System.map-@LINUX_VERSION@; \ if [ -f $$sysmap ]; then \ depmod -ae -F $$sysmap @LINUX_VERSION@ -b $(INSTALL_MOD_PATH)/; \ fi modules_install-FreeBSD: @# Install the kernel modules +$(FMAKE) install modules_install: modules_install-@ac_system@ data_install-Linux: @mkdir -p $(DESTDIR)/@prefix@/src/zfs-@VERSION@/@LINUX_VERSION@ cp ../zfs.release ../zfs_config.h @LINUX_SYMBOLS@ $(DESTDIR)/@prefix@/src/zfs-@VERSION@/@LINUX_VERSION@ data_install-FreeBSD: @ data_install: data_install-@ac_system@ modules_uninstall-Linux: modules_uninstall-Linux-legacy @# Uninstall the kernel modules $(RM) $(addprefix $(KMODDIR)/$(INSTALL_MOD_DIR)/,zfs.ko spl.ko) modules_uninstall-FreeBSD: @false modules_uninstall: modules_uninstall-@ac_system@ data_uninstall-Linux: $(RM) $(addprefix $(DESTDIR)/@prefix@/src/zfs-@VERSION@/@LINUX_VERSION@/,zfs.release zfs_config.h @LINUX_SYMBOLS@) data_uninstall-FreeBSD: @ data_uninstall: data_uninstall-@ac_system@ cppcheck-Linux: @CPPCHECK@ -j@CPU_COUNT@ --std=c99 --quiet --force --error-exitcode=2 \ --inline-suppr \ --suppress=unmatchedSuppression \ --suppress=noValidConfiguration \ --enable=warning,information -D_KERNEL \ --include=@LINUX_OBJ@/include/generated/autoconf.h \ --include=@top_builddir@/zfs_config.h \ --config-exclude=@LINUX_OBJ@/include \ -i zstd/lib \ -I @LINUX_OBJ@/include \ -I @top_srcdir@/include/os/linux/kernel \ -I @top_srcdir@/include/os/linux/spl \ -I @top_srcdir@/include/os/linux/zfs \ -I @top_srcdir@/include \ avl icp lua nvpair unicode zcommon zfs zstd os/linux cppcheck-FreeBSD: @true cppcheck: cppcheck-@ac_system@ distdir: cd @srcdir@ && find . -name '*.[chS]' -exec sh -c 'for f; do mkdir -p $$distdir/$${f%/*}; cp @srcdir@/$$f $$distdir/$$f; done' _ {} + cp @srcdir@/Makefile.bsd $$distdir/Makefile.bsd gen-zstd-symbols: for obj in $(addprefix zstd/,$(ZSTD_UPSTREAM_OBJS)); do echo; echo "/* $${obj#zstd/}: */"; @OBJDUMP@ -t $$obj | awk '$$2 == "g" && !/ zfs_/ {print "#define\t" $$6 " zfs_" $$6}' | sort; done >> zstd/include/zstd_compat_wrapper.h check-zstd-symbols: @OBJDUMP@ -t $(addprefix zstd/,$(ZSTD_UPSTREAM_OBJS)) | awk '/file format/ {print} $$2 == "g" && (!/ zfs_/ && !/ __pfx_zfs_/) {++ret; print} END {exit ret}' diff --git a/scripts/.gitignore b/scripts/.gitignore index 5621a6e147a0..443cb7b8484e 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1 +1,2 @@ common.sh +objtool-wrapper diff --git a/scripts/objtool-wrapper.in b/scripts/objtool-wrapper.in new file mode 100644 index 000000000000..0451f8718233 --- /dev/null +++ b/scripts/objtool-wrapper.in @@ -0,0 +1,36 @@ +#!/bin/sh + +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 Attila Fülöp +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +# Filter out objtools '--Werror' flag. + +objtool="@abs_objtool_binary@" +args=$(echo "$*" | sed s/--Werror//) + +if [ -z "$objtool" ]; then + echo "$(basename "$0"): No objtool binary configured" 1>&2 + exit 1; +fi + +# shellcheck disable=SC2086 +exec "$objtool" $args diff --git a/scripts/spdxcheck.pl b/scripts/spdxcheck.pl index bddda22334a8..47128402f7bc 100755 --- a/scripts/spdxcheck.pl +++ b/scripts/spdxcheck.pl @@ -1,432 +1,433 @@ #!/usr/bin/env perl # SPDX-License-Identifier: MIT # # Copyright (c) 2025, Rob Norris # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. use 5.010; use warnings; use strict; # All files known to git are either "tagged" or "untagged". Tagged files are # expected to have a license tag, while untagged files are expected to _not_ # have a license tag. There is no "optional" tag; all files are either "tagged" # or "untagged". # # Whether or not a file is tagged or untagged is determined using the patterns # in $tagged_patterns and $untagged_patterns and the following sequence: # # - if the file's full path is explicity listed in $tagged_patterns, then the # file is tagged. # # - if the file's full path is explicitly listed in $untagged_patterns, then # file is untagged. # # - if the filename matches a pattern in $tagged_patterns, and does not match a # pattern in $untagged_patterns, then the file is tagged # # - otherwise, the file is untagged. # # The patterns do a simple glob-like match over the entire path relative to the # root of the git repo (no leading /). '*' matches as anything at that point, # across path fragments. '?' matches a single character. my $tagged_patterns = q( # Compiled source files *.c *.h *.S # Python files, eg test suite drivers, libzfs bindings *.py *.py.in # Various support scripts *.sh *.pl # Test suite *.ksh *.ksh.in *.kshlib *.kshlib.in *.shlib # Test suite data files *.run *.cfg *.cfg.in *.fio *.lua *.zcp # Manpages man/man?/*.? man/man?/*.?.in # Unsuffixed programs (or generated of same) cmd/arcstat.in cmd/arc_summary cmd/dbufstat.in cmd/zilstat.in cmd/zpool/zpool.d/* etc/init.d/zfs-import.in etc/init.d/zfs-load-key.in etc/init.d/zfs-mount.in etc/init.d/zfs-share.in etc/init.d/zfs-zed.in etc/zfs/zfs-functions.in + scripts/objtool-wrapper.in # Misc items that have clear licensing info but aren't easily matched, # or are the first of a class that we aren't ready to match yet. config/ax_code_coverage.m4 configure.ac module/lua/README.zfs scripts/kmodtool tests/zfs-tests/tests/functional/inheritance/README.config tests/zfs-tests/tests/functional/inheritance/README.state cmd/zed/zed.d/statechange-notify.sh ); my $untagged_patterns = q( # Exclude CI tooling as it's not interesting for overall project # licensing. .github/* # Everything below this has unclear licensing. Work is happening to # identify and update them. Once one gains a tag it should be removed # from this list. cmd/zed/zed.d/*.sh cmd/zpool/zpool.d/* contrib/coverity/model.c include/libzdb.h include/os/freebsd/spl/sys/inttypes.h include/os/freebsd/spl/sys/mode.h include/os/freebsd/spl/sys/trace.h include/os/freebsd/spl/sys/trace_zfs.h include/os/freebsd/zfs/sys/zpl.h include/os/linux/kernel/linux/page_compat.h lib/libspl/include/os/freebsd/sys/sysmacros.h lib/libspl/include/sys/string.h lib/libspl/include/sys/trace_spl.h lib/libspl/include/sys/trace_zfs.h lib/libzdb/libzdb.c module/lua/setjmp/setjmp.S module/lua/setjmp/setjmp_ppc.S module/zstd/include/sparc_compat.h module/zstd/zstd_sparc.c tests/zfs-tests/cmd/cp_files.c tests/zfs-tests/cmd/zed_fd_spill-zedlet.c tests/zfs-tests/tests/functional/tmpfile/tmpfile_001_pos.c tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c tests/zfs-tests/tests/functional/tmpfile/tmpfile_003_pos.c tests/zfs-tests/tests/functional/tmpfile/tmpfile_test.c autogen.sh contrib/bpftrace/zfs-trace.sh contrib/pyzfs/docs/source/conf.py contrib/pyzfs/libzfs_core/test/__init__.py contrib/pyzfs/setup.py.in contrib/zcp/autosnap.lua scripts/commitcheck.sh scripts/man-dates.sh scripts/mancheck.sh scripts/paxcheck.sh scripts/zfs-helpers.sh scripts/zfs-tests-color.sh scripts/zfs.sh scripts/zimport.sh tests/zfs-tests/callbacks/zfs_failsafe.ksh tests/zfs-tests/include/commands.cfg tests/zfs-tests/include/tunables.cfg tests/zfs-tests/include/zpool_script.shlib tests/zfs-tests/tests/functional/mv_files/random_creation.ksh ); # For files expected to have a license tags, these are the acceptable tags by # path. A file in one of these paths with a tag not listed here must be in the # override list below. If the file is not in any of these paths, then # $default_license_tags is used. my $default_license_tags = [ 'CDDL-1.0', '0BSD', 'BSD-2-Clause', 'BSD-3-Clause', 'MIT' ]; my @path_license_tags = ( # Conventional wisdom is that the Linux SPL must be GPL2+ for # kernel compatibility. 'module/os/linux/spl' => ['GPL-2.0-or-later'], 'include/os/linux/spl' => ['GPL-2.0-or-later'], # Third-party code should keep it's original license 'module/zstd/lib' => ['BSD-3-Clause OR GPL-2.0-only'], 'module/lua' => ['MIT'], # lua/setjmp is platform-specific code sourced from various places 'module/lua/setjmp' => $default_license_tags, # Some of the fletcher modules are dual-licensed 'module/zcommon/zfs_fletcher' => ['BSD-2-Clause OR GPL-2.0-only', 'CDDL-1.0'], 'module/icp' => ['Apache-2.0', 'CDDL-1.0'], # Python bindings are always Apache-2.0 'contrib/pyzfs' => ['Apache-2.0'], ); # This is a list of "special case" license tags that are in use in the tree, # and the files where they occur. these exist for a variety of reasons, and # generally should not be used for new code. If you need to bring in code that # has a different license from the acceptable ones listed above, then you will # also need to add it here, with rationale provided and approval given in your # PR. my %override_file_license_tags = ( # SPDX have repeatedly rejected the creation of a tag for a public # domain dedication, as not all dedications are clear and unambiguious # in their meaning and not all jurisdictions permit relinquishing a # copyright anyway. # # A reasonably common workaround appears to be to create a local # (project-specific) identifier to convey whatever meaning the project # wishes it to. To cover OpenZFS' use of third-party code with a # public domain dedication, we use this custom tag. # # Further reading: # https://github.com/spdx/old-wiki/blob/main/Pages/Legal%20Team/Decisions/Dealing%20with%20Public%20Domain%20within%20SPDX%20Files.md # https://spdx.github.io/spdx-spec/v2.3/other-licensing-information-detected/ # https://cr.yp.to/spdx.html # 'LicenseRef-OpenZFS-ThirdParty-PublicDomain' => [qw( include/sys/skein.h module/icp/algs/skein/skein_block.c module/icp/algs/skein/skein.c module/icp/algs/skein/skein_impl.h module/icp/algs/skein/skein_iv.c module/icp/algs/skein/skein_port.h module/zfs/vdev_draid_rand.c )], # Legacy inclusions 'Brian-Gladman-3-Clause' => [qw( module/icp/asm-x86_64/aes/aestab.h module/icp/asm-x86_64/aes/aesopt.h module/icp/asm-x86_64/aes/aeskey.c module/icp/asm-x86_64/aes/aes_amd64.S )], 'OpenSSL-standalone' => [qw( module/icp/asm-x86_64/aes/aes_aesni.S )], 'LGPL-2.1-or-later' => [qw( config/ax_code_coverage.m4 )], # Legacy inclusions of BSD-2-Clause files in Linux SPL. 'BSD-2-Clause' => [qw( include/os/linux/spl/sys/debug.h module/os/linux/spl/spl-zone.c )], # Temporary overrides for things that have the wrong license for # their path. Work is underway to understand and resolve these. 'GPL-2.0-or-later' => [qw( include/os/freebsd/spl/sys/kstat.h include/os/freebsd/spl/sys/sunddi.h include/sys/mod.h )], 'CDDL-1.0' => [qw( include/os/linux/spl/sys/errno.h include/os/linux/spl/sys/ia32/asm_linkage.h include/os/linux/spl/sys/misc.h include/os/linux/spl/sys/procfs_list.h include/os/linux/spl/sys/trace.h include/os/linux/spl/sys/trace_spl.h include/os/linux/spl/sys/trace_taskq.h include/os/linux/spl/sys/wmsum.h module/os/linux/spl/spl-procfs-list.c module/os/linux/spl/spl-trace.c module/lua/README.zfs )], ); ########## sub setup_patterns { my ($patterns) = @_; my @re; my @files; for my $pat (split "\n", $patterns) { # remove leading/trailing whitespace and comments $pat =~ s/(:?^\s*|\s*(:?#.*)?$)//g; # skip (now-)empty lines next if $pat eq ''; # if the "pattern" has no metachars, then it's a literal file # path and gets matched a bit more strongly unless ($pat =~ m/[?*]/) { push @files, $pat; next; } # naive pattern to regex conversion # escape simple metachars $pat =~ s/([\.\(\[])/\Q$1\E/g; $pat =~ s/\?/./g; # glob ? -> regex . $pat =~ s/\*/.*/g; # glob * -> regex .* push @re, $pat; } my $re = join '|', @re; return (qr/^(?:$re)$/, { map { $_ => 1 } @files }); }; my ($tagged_re, $tagged_files) = setup_patterns($tagged_patterns); my ($untagged_re, $untagged_files) = setup_patterns($untagged_patterns); sub file_is_tagged { my ($file) = @_; # explicitly tagged if ($tagged_files->{$file}) { delete $tagged_files->{$file}; return 1; } # explicitly untagged if ($untagged_files->{$file}) { delete $untagged_files->{$file}; return 0; } # must match tagged patterns and not match untagged patterns return ($file =~ $tagged_re) && !($file =~ $untagged_re); } my %override_tags = map { my $tag = $_; map { $_ => $tag } @{$override_file_license_tags{$_}}; } keys %override_file_license_tags; ########## my $rc = 0; # Get a list of all files known to git. This is a crude way of avoiding any # build artifacts that have tags embedded in them. my @git_files = sort grep { chomp } qx(git ls-tree --name-only -r HEAD); # Scan all files and work out if their tags are correct. for my $file (@git_files) { # Ignore non-files. git can store other types of objects (submodule # dirs, symlinks, etc) that aren't interesting for licensing. next unless -f $file && ! -l $file; # Open the file, and extract its license tag. We only check the first # 4K of each file because many of these files are large, binary, or # both. For a typical source file that means the tag should be found # within the first ~50 lines. open my $fh, '<', $file or die "$0: couldn't open $file: $!\n"; my $nbytes = read $fh, my $buf, 4096; die "$0: couldn't read $file: $!\n" if !defined $nbytes; my ($tag) = $buf =~ m/\bSPDX-License-Identifier: ([A-Za-z0-9_\-\. ]+)$/smg; close $fh; # Decide if the file should have a tag at all my $tagged = file_is_tagged($file); # If no license tag is wanted, there's not much left to do if (!$tagged) { if (defined $tag) { # untagged file has a tag, pattern change required say "unexpected license tag: $file"; $rc = 1; } next; } # If a tag is required, but doesn't have one, warn and loop. if (!defined $tag) { say "missing license tag: $file"; $rc = 1; next; } # Determine the set of valid license tags for this file. Start with # the defaults. my $tags = $default_license_tags; if ($override_tags{$file}) { # File has an explicit override, use it. $tags = [delete $override_tags{$file}]; } else { # Work through the path tag sets, taking the set with the # most precise match. If no sets match, we fall through and # are left with the default set. my $matchlen = 0; for (my $n = 0; $n < @path_license_tags; $n += 2) { my ($path, $t) = @path_license_tags[$n,$n+1]; if (substr($file, 0, length($path)) eq $path && length($path) > $matchlen) { $tags = $t; $matchlen = length($path); } } } # Confirm the file's tag is in the set, and warn if not. my %tags = map { $_ => 1 } @$tags; unless ($tags{$tag}) { say "invalid license tag: $file"; say " (got $tag; expected: @$tags)"; $rc = 1; next; } } ########## # List any files explicitly listed as tagged or untagged that we didn't see. # Likely the file was removed from the repo but not from our lists. for my $file (sort keys %$tagged_files) { say "explicitly tagged file not on disk: $file"; $rc = 1; } for my $file (sort keys %$untagged_files) { say "explicitly untagged file not on disk: $file"; $rc = 1; } for my $file (sort keys %override_tags) { say "explicitly overridden file not on disk: $file"; $rc = 1; } exit $rc;