Use copy_file_range to copy files
The copy_file_range syscall (introduced in Linux kernel version 4.5) can copy files more efficiently via server-side copy etc. * admin/merge-gnulib (GNULIB_MODULES): Add copy-file-range. * lib/copy-file-range.c, m4/copy-file-range.m4: New files, copied from Gnulib. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * src/fileio.c (Fcopy_file): Try copy_file_range first, falling back on read+write only if copy_file_range failed or if the input is empty and so could be a /proc file.
This commit is contained in:
parent
111408a0e9
commit
486a81f387
6 changed files with 123 additions and 9 deletions
|
@ -27,7 +27,7 @@ GNULIB_URL=git://git.savannah.gnu.org/gnulib.git
|
|||
|
||||
GNULIB_MODULES='
|
||||
alloca-opt binary-io byteswap c-ctype c-strcase
|
||||
careadlinkat close-stream
|
||||
careadlinkat close-stream copy-file-range
|
||||
count-leading-zeros count-one-bits count-trailing-zeros
|
||||
crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer
|
||||
d-type diffseq dosname dtoastr dtotimespec dup2
|
||||
|
|
33
lib/copy-file-range.c
Normal file
33
lib/copy-file-range.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* Stub for copy_file_range
|
||||
Copyright 2019 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
ssize_t
|
||||
copy_file_range (int infd, off_t *pinoff,
|
||||
int outfd, off_t *poutoff,
|
||||
size_t length, unsigned int flags)
|
||||
{
|
||||
/* There is little need to emulate copy_file_range with read+write,
|
||||
since programs that use copy_file_range must fall back on
|
||||
read+write anyway. */
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
|
@ -74,6 +74,7 @@
|
|||
# c-strcase \
|
||||
# careadlinkat \
|
||||
# close-stream \
|
||||
# copy-file-range \
|
||||
# count-leading-zeros \
|
||||
# count-one-bits \
|
||||
# count-trailing-zeros \
|
||||
|
@ -1274,6 +1275,17 @@ EXTRA_DIST += close-stream.h
|
|||
endif
|
||||
## end gnulib module close-stream
|
||||
|
||||
## begin gnulib module copy-file-range
|
||||
ifeq (,$(OMIT_GNULIB_MODULE_copy-file-range))
|
||||
|
||||
|
||||
EXTRA_DIST += copy-file-range.c
|
||||
|
||||
EXTRA_libgnu_a_SOURCES += copy-file-range.c
|
||||
|
||||
endif
|
||||
## end gnulib module copy-file-range
|
||||
|
||||
## begin gnulib module count-leading-zeros
|
||||
ifeq (,$(OMIT_GNULIB_MODULE_count-leading-zeros))
|
||||
|
||||
|
|
36
m4/copy-file-range.m4
Normal file
36
m4/copy-file-range.m4
Normal file
|
@ -0,0 +1,36 @@
|
|||
# copy-file-range.m4
|
||||
dnl Copyright 2019 Free Software Foundation, Inc.
|
||||
dnl This file is free software; the Free Software Foundation
|
||||
dnl gives unlimited permission to copy and/or distribute it,
|
||||
dnl with or without modifications, as long as this notice is preserved.
|
||||
|
||||
AC_DEFUN([gl_FUNC_COPY_FILE_RANGE],
|
||||
[
|
||||
AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
|
||||
|
||||
dnl Persuade glibc <unistd.h> to declare copy_file_range.
|
||||
AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
|
||||
|
||||
dnl Use AC_LINK_IFELSE, rather than AC_CHECK_FUNCS or a variant,
|
||||
dnl since we don't want AC_CHECK_FUNCS's checks for glibc stubs.
|
||||
dnl Programs that use copy_file_range must fall back on read+write
|
||||
dnl anyway, and there's little point to substituting the Gnulib stub
|
||||
dnl for a glibc stub.
|
||||
AC_CACHE_CHECK([for copy_file_range], [gl_cv_func_copy_file_range],
|
||||
[AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <unistd.h>
|
||||
]],
|
||||
[[ssize_t (*func) (int, off_t *, int, off_t, size_t, unsigned)
|
||||
= copy_file_range;
|
||||
return func (0, 0, 0, 0, 0, 0) & 127;
|
||||
]])
|
||||
],
|
||||
[gl_cv_func_copy_file_range=yes],
|
||||
[gl_cv_func_copy_file_range=no])
|
||||
])
|
||||
|
||||
if test "$gl_cv_func_copy_file_range" != yes; then
|
||||
HAVE_COPY_FILE_RANGE=0
|
||||
fi
|
||||
])
|
|
@ -56,6 +56,7 @@ AC_DEFUN([gl_EARLY],
|
|||
# Code from module clock-time:
|
||||
# Code from module cloexec:
|
||||
# Code from module close-stream:
|
||||
# Code from module copy-file-range:
|
||||
# Code from module count-leading-zeros:
|
||||
# Code from module count-one-bits:
|
||||
# Code from module count-trailing-zeros:
|
||||
|
@ -201,6 +202,11 @@ AC_DEFUN([gl_INIT],
|
|||
AC_CHECK_FUNCS_ONCE([readlinkat])
|
||||
gl_CLOCK_TIME
|
||||
gl_MODULE_INDICATOR([close-stream])
|
||||
gl_FUNC_COPY_FILE_RANGE
|
||||
if test $HAVE_COPY_FILE_RANGE = 0; then
|
||||
AC_LIBOBJ([copy-file-range])
|
||||
fi
|
||||
gl_UNISTD_MODULE_INDICATOR([copy-file-range])
|
||||
gl_COUNT_LEADING_ZEROS
|
||||
gl_COUNT_ONE_BITS
|
||||
gl_COUNT_TRAILING_ZEROS
|
||||
|
@ -846,6 +852,7 @@ AC_DEFUN([gl_FILE_LIST], [
|
|||
lib/cloexec.h
|
||||
lib/close-stream.c
|
||||
lib/close-stream.h
|
||||
lib/copy-file-range.c
|
||||
lib/count-leading-zeros.c
|
||||
lib/count-leading-zeros.h
|
||||
lib/count-one-bits.c
|
||||
|
@ -995,6 +1002,7 @@ AC_DEFUN([gl_FILE_LIST], [
|
|||
m4/builtin-expect.m4
|
||||
m4/byteswap.m4
|
||||
m4/clock_time.m4
|
||||
m4/copy-file-range.m4
|
||||
m4/count-leading-zeros.m4
|
||||
m4/count-one-bits.m4
|
||||
m4/count-trailing-zeros.m4
|
||||
|
|
41
src/fileio.c
41
src/fileio.c
|
@ -2117,14 +2117,39 @@ permissions. */)
|
|||
newsize = st.st_size;
|
||||
else
|
||||
{
|
||||
char buf[MAX_ALLOCA];
|
||||
ptrdiff_t n;
|
||||
for (newsize = 0; 0 < (n = emacs_read_quit (ifd, buf, sizeof buf));
|
||||
newsize += n)
|
||||
if (emacs_write_quit (ofd, buf, n) != n)
|
||||
report_file_error ("Write error", newname);
|
||||
if (n < 0)
|
||||
report_file_error ("Read error", file);
|
||||
off_t insize = st.st_size;
|
||||
ssize_t copied;
|
||||
|
||||
for (newsize = 0; newsize < insize; newsize += copied)
|
||||
{
|
||||
/* Copy at most COPY_MAX bytes at a time; this is min
|
||||
(PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
|
||||
surely aligned well. */
|
||||
ptrdiff_t copy_max = min (PTRDIFF_MAX, SIZE_MAX) >> 30 << 30;
|
||||
off_t intail = insize - newsize;
|
||||
ptrdiff_t len = min (intail, copy_max);
|
||||
copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0);
|
||||
if (copied <= 0)
|
||||
break;
|
||||
maybe_quit ();
|
||||
}
|
||||
|
||||
/* Fall back on read+write if copy_file_range failed, or if the
|
||||
input is empty and so could be a /proc file. read+write will
|
||||
either succeed, or report an error more precisely than
|
||||
copy_file_range would. */
|
||||
if (newsize != insize || insize == 0)
|
||||
{
|
||||
char buf[MAX_ALLOCA];
|
||||
for (; (copied = emacs_read_quit (ifd, buf, sizeof buf));
|
||||
newsize += copied)
|
||||
{
|
||||
if (copied < 0)
|
||||
report_file_error ("Read error", file);
|
||||
if (emacs_write_quit (ofd, buf, copied) != copied)
|
||||
report_file_error ("Write error", newname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Truncate any existing output file after writing the data. This
|
||||
|
|
Loading…
Add table
Reference in a new issue