From 355f25203fd8cb27b33a645a15560ba1ec281849 Mon Sep 17 00:00:00 2001 From: Meta11ic0 Date: Sun, 23 Aug 2026 19:38:46 +0800 Subject: [PATCH] Disable copy_file_range on Linux before 5.3. glibc exposes the symbol on Linux 4.19, but the kernel implementation is incomplete and a second copy_file_range(..., SSIZE_MAX) fails with EINVAL. Do not define HAVE_COPY_FILE_RANGE when the build-time is older than 5.3. Non-Linux still uses a plain function check. Discussion: https://postgr.es/m/CAJh1VjasvNHhLoJu7WX55f2eW2i424MPRKMUF9hVt3YxDdMNAw@mail.gmail.com --- configure | 52 +++++++++++++++++++++++++- configure.ac | 23 +++++++++++- doc/src/sgml/config.sgml | 2 +- doc/src/sgml/ref/pg_combinebackup.sgml | 2 +- doc/src/sgml/ref/pgupgrade.sgml | 2 +- meson.build | 11 +++++- 6 files changed, 86 insertions(+), 6 deletions(-) diff --git a/configure b/configure index d42a7a794ff..38fcde36f25 100755 --- a/configure +++ b/configure @@ -15858,7 +15858,7 @@ fi LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` -for ac_func in backtrace_symbols copyfile copy_file_range elf_aux_info explicit_memset getauxval getifaddrs getpeerucred inet_pton kqueue localeconv_l mbstowcs_l memset_explicit posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l +for ac_func in backtrace_symbols copyfile elf_aux_info explicit_memset getauxval getifaddrs getpeerucred inet_pton kqueue localeconv_l mbstowcs_l memset_explicit posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -15871,6 +15871,56 @@ fi done +# copy_file_range() can fail with EINVAL on Linux before 5.3. +# Other platforms only require the function to be available. +pgac_check_copy_file_range=yes +case $host_os in + linux*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether is 5.3 or later" >&5 +$as_echo_n "checking whether is 5.3 or later... " >&6; } +if ${pgac_cv_linux_headers_5_3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) +#error "Linux kernel headers older than 5.3" +#endif +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + pgac_cv_linux_headers_5_3=yes +else + pgac_cv_linux_headers_5_3=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_linux_headers_5_3" >&5 +$as_echo "$pgac_cv_linux_headers_5_3" >&6; } + test "$pgac_cv_linux_headers_5_3" = yes || pgac_check_copy_file_range=no + ;; +esac +if test "$pgac_check_copy_file_range" = yes; then : + for ac_func in copy_file_range +do : + ac_fn_c_check_func "$LINENO" "copy_file_range" "ac_cv_func_copy_file_range" +if test "x$ac_cv_func_copy_file_range" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_COPY_FILE_RANGE 1 +_ACEOF + +fi +done + +fi + # These typically are compiler builtins, for which AC_CHECK_FUNCS fails. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_bswap16" >&5 $as_echo_n "checking for __builtin_bswap16... " >&6; } diff --git a/configure.ac b/configure.ac index a331749fcb5..b9653f7f1b1 100644 --- a/configure.ac +++ b/configure.ac @@ -1851,7 +1851,6 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` AC_CHECK_FUNCS(m4_normalize([ backtrace_symbols copyfile - copy_file_range elf_aux_info explicit_memset getauxval @@ -1874,6 +1873,28 @@ AC_CHECK_FUNCS(m4_normalize([ wcstombs_l ])) +# copy_file_range() can fail with EINVAL on Linux before 5.3. +# Other platforms only require the function to be available. +pgac_check_copy_file_range=yes +case $host_os in + linux*) + AC_CACHE_CHECK([whether is 5.3 or later], + [pgac_cv_linux_headers_5_3], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) +#error "Linux kernel headers older than 5.3" +#endif]], + [[]])], + [pgac_cv_linux_headers_5_3=yes], + [pgac_cv_linux_headers_5_3=no])]) + test "$pgac_cv_linux_headers_5_3" = yes || pgac_check_copy_file_range=no + ;; +esac +AS_IF([test "$pgac_check_copy_file_range" = yes], + [AC_CHECK_FUNCS([copy_file_range])]) + # These typically are compiler builtins, for which AC_CHECK_FUNCS fails. PGAC_CHECK_BUILTIN_FUNC([__builtin_bswap16], [int x]) PGAC_CHECK_BUILTIN_FUNC([__builtin_bswap32], [int x]) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 0165eb9ec02..c1d15f86bed 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2488,7 +2488,7 @@ include_dir 'conf.d' CLONE uses the copy_file_range() - (Linux, FreeBSD) or copyfile + (Linux kernel 5.3 and later, FreeBSD) or copyfile (macOS) system calls, giving the kernel the opportunity to share disk blocks or push work down to lower layers on some file systems. diff --git a/doc/src/sgml/ref/pg_combinebackup.sgml b/doc/src/sgml/ref/pg_combinebackup.sgml index 9a6d201e0b8..beaa7256615 100644 --- a/doc/src/sgml/ref/pg_combinebackup.sgml +++ b/doc/src/sgml/ref/pg_combinebackup.sgml @@ -210,7 +210,7 @@ PostgreSQL documentation copying. On some file systems this gives results similar to , sharing physical disk blocks, while on others it may still copy blocks, but do so via an optimized path. At present, - it is supported on Linux and FreeBSD. + it is supported on Linux (kernel 5.3 and later) and FreeBSD. diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index e4e8c02e6d6..22545328c1f 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -257,7 +257,7 @@ PostgreSQL documentation copying. On some file systems this gives results similar to , sharing physical disk blocks, while on others it may still copy blocks, but do so via an optimized path. At present, - it is supported on Linux and FreeBSD. + it is supported on Linux (kernel 5.3 and later) and FreeBSD. diff --git a/meson.build b/meson.build index f4cde249242..068dc6cd8d0 100644 --- a/meson.build +++ b/meson.build @@ -3185,13 +3185,22 @@ else socket_dep = not_found_dep endif +# copy_file_range() is incomplete on Linux before 5.3 (EINVAL). +# Non-Linux skips this compile probe and keeps a plain function check. +have_linux_5_3_headers = host_system != 'linux' or cc.compiles(''' +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) +#error "Linux kernel headers older than 5.3" +#endif +''', name: ' is 5.3 or later', args: test_c_args) + # XXX: Might be worth conditioning some checks on the OS, to avoid doing # unnecessary checks over and over, particularly on windows. func_checks = [ ['backtrace_symbols', {'dependencies': [execinfo_dep]}], ['clock_gettime', {'dependencies': [rt_dep], 'define': false}], ['copyfile'], - ['copy_file_range'], + ['copy_file_range', {'skip': not have_linux_5_3_headers}], # gcc/clang's sanitizer helper library provides dlopen but not dlsym, thus # when enabling asan the dlopen check doesn't notice that -ldl is actually # required. Just checking for dlsym() ought to suffice. -- 2.34.1