Skip to content

Commit d87caac

Browse files
Jannik2099jwakely
authored andcommitted
libstdc++: Use copy_file_range for filesystem::copy_file
copy_file_range is a recent-ish syscall for copying files. It is similar to sendfile but allows filesystem-specific optimizations. Common are: Reflinks: BTRFS, XFS, ZFS (does not implement the syscall yet) Server-side copy: NFS, SMB, Ceph If copy_file_range is not available for the given files, fall back to sendfile / userspace copy. libstdc++-v3/ChangeLog: * acinclude.m4 (_GLIBCXX_USE_COPY_FILE_RANGE): Define. * config.h.in: Regenerate. * configure: Regenerate. * src/filesystem/ops-common.h (copy_file_copy_file_range): Define new function. (do_copy_file): Use it. Signed-off-by: Jannik Glückert <[email protected]>
1 parent f80a8b4 commit d87caac

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

libstdc++-v3/acinclude.m4

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4954,6 +4954,7 @@ dnl _GLIBCXX_USE_UTIMENSAT
49544954
dnl _GLIBCXX_USE_ST_MTIM
49554955
dnl _GLIBCXX_USE_FCHMOD
49564956
dnl _GLIBCXX_USE_FCHMODAT
4957+
dnl _GLIBCXX_USE_COPY_FILE_RANGE
49574958
dnl _GLIBCXX_USE_SENDFILE
49584959
dnl HAVE_LINK
49594960
dnl HAVE_LSEEK
@@ -5152,6 +5153,25 @@ dnl
51525153
if test $glibcxx_cv_truncate = yes; then
51535154
AC_DEFINE(HAVE_TRUNCATE, 1, [Define if truncate is available in <unistd.h>.])
51545155
fi
5156+
dnl
5157+
AC_CACHE_CHECK([for copy_file_range that can copy files],
5158+
glibcxx_cv_copy_file_range, [dnl
5159+
case "${target_os}" in
5160+
linux*)
5161+
GCC_TRY_COMPILE_OR_LINK(
5162+
[#include <unistd.h>],
5163+
[copy_file_range(1, nullptr, 2, nullptr, 1, 0);],
5164+
[glibcxx_cv_copy_file_range=yes],
5165+
[glibcxx_cv_copy_file_range=no])
5166+
;;
5167+
*)
5168+
glibcxx_cv_copy_file_range=no
5169+
;;
5170+
esac
5171+
])
5172+
if test $glibcxx_cv_copy_file_range = yes; then
5173+
AC_DEFINE(_GLIBCXX_USE_COPY_FILE_RANGE, 1, [Define if copy_file_range is available in <unistd.h>.])
5174+
fi
51555175
dnl
51565176
AC_CACHE_CHECK([for sendfile that can copy files],
51575177
glibcxx_cv_sendfile, [dnl

libstdc++-v3/config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,9 @@
971971
/* Defined if clock_gettime has realtime clock support. */
972972
#undef _GLIBCXX_USE_CLOCK_REALTIME
973973

974+
/* Define if copy_file_range is available in <unistd.h>. */
975+
#undef _GLIBCXX_USE_COPY_FILE_RANGE
976+
974977
/* Define if ISO/IEC TR 24733 decimal floating point types are supported on
975978
this host. */
976979
#undef _GLIBCXX_USE_DECIMAL_FLOAT

libstdc++-v3/configure

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71279,6 +71279,68 @@ $as_echo "$glibcxx_cv_truncate" >&6; }
7127971279

7128071280
$as_echo "#define HAVE_TRUNCATE 1" >>confdefs.h
7128171281

71282+
fi
71283+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for copy_file_range that can copy files" >&5
71284+
$as_echo_n "checking for copy_file_range that can copy files... " >&6; }
71285+
if ${glibcxx_cv_copy_file_range+:} false; then :
71286+
$as_echo_n "(cached) " >&6
71287+
else
71288+
case "${target_os}" in
71289+
linux*)
71290+
if test x$gcc_no_link = xyes; then
71291+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
71292+
/* end confdefs.h. */
71293+
#include <unistd.h>
71294+
int
71295+
main ()
71296+
{
71297+
copy_file_range(1, nullptr, 2, nullptr, 1, 0);
71298+
;
71299+
return 0;
71300+
}
71301+
_ACEOF
71302+
if ac_fn_cxx_try_compile "$LINENO"; then :
71303+
glibcxx_cv_copy_file_range=yes
71304+
else
71305+
glibcxx_cv_copy_file_range=no
71306+
fi
71307+
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
71308+
else
71309+
if test x$gcc_no_link = xyes; then
71310+
as_fn_error $? "Link tests are not allowed after GCC_NO_EXECUTABLES." "$LINENO" 5
71311+
fi
71312+
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
71313+
/* end confdefs.h. */
71314+
#include <unistd.h>
71315+
int
71316+
main ()
71317+
{
71318+
copy_file_range(1, nullptr, 2, nullptr, 1, 0);
71319+
;
71320+
return 0;
71321+
}
71322+
_ACEOF
71323+
if ac_fn_cxx_try_link "$LINENO"; then :
71324+
glibcxx_cv_copy_file_range=yes
71325+
else
71326+
glibcxx_cv_copy_file_range=no
71327+
fi
71328+
rm -f core conftest.err conftest.$ac_objext \
71329+
conftest$ac_exeext conftest.$ac_ext
71330+
fi
71331+
;;
71332+
*)
71333+
glibcxx_cv_copy_file_range=no
71334+
;;
71335+
esac
71336+
71337+
fi
71338+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $glibcxx_cv_copy_file_range" >&5
71339+
$as_echo "$glibcxx_cv_copy_file_range" >&6; }
71340+
if test $glibcxx_cv_copy_file_range = yes; then
71341+
71342+
$as_echo "#define _GLIBCXX_USE_COPY_FILE_RANGE 1" >>confdefs.h
71343+
7128271344
fi
7128371345
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sendfile that can copy files" >&5
7128471346
$as_echo_n "checking for sendfile that can copy files... " >&6; }

libstdc++-v3/src/filesystem/ops-common.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
#ifdef NEED_DO_COPY_FILE
5050
# include <filesystem>
5151
# include <ext/stdio_filebuf.h>
52+
# ifdef _GLIBCXX_USE_COPY_FILE_RANGE
53+
# include <unistd.h> // copy_file_range
54+
# endif
5255
# ifdef _GLIBCXX_USE_SENDFILE
5356
# include <sys/sendfile.h> // sendfile
5457
# include <unistd.h> // lseek
@@ -359,6 +362,32 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
359362
}
360363

361364
#ifdef NEED_DO_COPY_FILE
365+
#ifdef _GLIBCXX_USE_COPY_FILE_RANGE
366+
bool
367+
copy_file_copy_file_range(int fd_in, int fd_out, size_t length) noexcept
368+
{
369+
// a zero-length file is either empty, or not copyable by this syscall
370+
// return early to avoid the syscall cost
371+
if (length == 0)
372+
{
373+
errno = EINVAL;
374+
return false;
375+
}
376+
size_t bytes_left = length;
377+
off64_t off_in = 0, off_out = 0;
378+
ssize_t bytes_copied;
379+
do
380+
{
381+
bytes_copied = ::copy_file_range(fd_in, &off_in, fd_out, &off_out,
382+
bytes_left, 0);
383+
bytes_left -= bytes_copied;
384+
}
385+
while (bytes_left > 0 && bytes_copied > 0);
386+
if (bytes_copied < 0)
387+
return false;
388+
return true;
389+
}
390+
#endif
362391
#if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
363392
bool
364393
copy_file_sendfile(int fd_in, int fd_out, size_t length) noexcept
@@ -529,6 +558,33 @@ _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
529558

530559
bool has_copied = false;
531560

561+
#ifdef _GLIBCXX_USE_COPY_FILE_RANGE
562+
if (!has_copied)
563+
has_copied = copy_file_copy_file_range(in.fd, out.fd, from_st->st_size);
564+
if (!has_copied)
565+
{
566+
// EINVAL: src and dst are the same file (this is not cheaply
567+
// detectable from userspace)
568+
// EINVAL: copy_file_range is unsupported for this file type by the
569+
// underlying filesystem
570+
// ENOTSUP: undocumented, can arise with old kernels and NFS
571+
// EOPNOTSUPP: filesystem does not implement copy_file_range
572+
// ETXTBSY: src or dst is an active swapfile (nonsensical, but allowed
573+
// with normal copying)
574+
// EXDEV: src and dst are on different filesystems that do not support
575+
// cross-fs copy_file_range
576+
// ENOENT: undocumented, can arise with CIFS
577+
// ENOSYS: unsupported by kernel or blocked by seccomp
578+
if (errno != EINVAL && errno != ENOTSUP && errno != EOPNOTSUPP
579+
&& errno != ETXTBSY && errno != EXDEV && errno != ENOENT
580+
&& errno != ENOSYS)
581+
{
582+
ec.assign(errno, std::generic_category());
583+
return false;
584+
}
585+
}
586+
#endif
587+
532588
#if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
533589
if (!has_copied)
534590
has_copied = copy_file_sendfile(in.fd, out.fd, from_st->st_size);

0 commit comments

Comments
 (0)