From 6ff2551f405978ca3403187f55089a31fff8bee1 Mon Sep 17 00:00:00 2001
From: Paul Guo <paulguo@gmail.com>
Date: Mon, 25 Jan 2021 15:23:33 +0800
Subject: [PATCH 2/2] Use copy_file_range() for file copying in pg_rewind.

---
 configure                        |  2 +-
 configure.ac                     |  1 +
 src/bin/pg_rewind/file_ops.c     |  8 +++++---
 src/bin/pg_rewind/file_ops.h     |  2 +-
 src/bin/pg_rewind/local_source.c | 22 ++++++++++++++++++++++
 src/include/pg_config.h.in       |  3 +++
 6 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/configure b/configure
index e202697bbf..64aec3b9af 100755
--- a/configure
+++ b/configure
@@ -15150,7 +15150,7 @@ fi
 LIBS_including_readline="$LIBS"
 LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
-for ac_func in backtrace_symbols clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit kqueue mbstowcs_l memset_s poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink readv setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale wcstombs_l writev
+for ac_func in backtrace_symbols clock_gettime copyfile copy_file_range fdatasync getifaddrs getpeerucred getrlimit kqueue mbstowcs_l memset_s poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink readv setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale wcstombs_l writev
 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"
diff --git a/configure.ac b/configure.ac
index a5ad072ee4..7ff3072410 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1647,6 +1647,7 @@ AC_CHECK_FUNCS(m4_normalize([
 	backtrace_symbols
 	clock_gettime
 	copyfile
+	copy_file_range
 	fdatasync
 	getifaddrs
 	getpeerucred
diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c
index 0dd9c6c95e..9ecac5788b 100644
--- a/src/bin/pg_rewind/file_ops.c
+++ b/src/bin/pg_rewind/file_ops.c
@@ -43,17 +43,17 @@ static void recurse_dir(const char *datadir, const char *parentpath,
  * Open a target file for writing. If 'trunc' is true and the file already
  * exists, it will be truncated.
  */
-void
+int
 open_target_file(const char *path, bool trunc)
 {
 	int			mode;
 
 	if (dry_run)
-		return;
+		return -1;
 
 	if (dstfd != -1 && !trunc &&
 		strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
-		return;					/* already open */
+		return dstfd;					/* already open */
 
 	close_target_file();
 
@@ -66,6 +66,8 @@ open_target_file(const char *path, bool trunc)
 	if (dstfd < 0)
 		pg_fatal("could not open target file \"%s\": %m",
 				 dstpath);
+
+	return dstfd;
 }
 
 /*
diff --git a/src/bin/pg_rewind/file_ops.h b/src/bin/pg_rewind/file_ops.h
index b6b8b319d5..d1cde6e705 100644
--- a/src/bin/pg_rewind/file_ops.h
+++ b/src/bin/pg_rewind/file_ops.h
@@ -12,7 +12,7 @@
 
 #include "filemap.h"
 
-extern void open_target_file(const char *path, bool trunc);
+extern int open_target_file(const char *path, bool trunc);
 extern void write_target_range(char *buf, off_t begin, size_t size);
 extern void close_target_file(void);
 extern void remove_target_file(const char *path, bool missing_ok);
diff --git a/src/bin/pg_rewind/local_source.c b/src/bin/pg_rewind/local_source.c
index 9c3491c3fb..1d17cc8304 100644
--- a/src/bin/pg_rewind/local_source.c
+++ b/src/bin/pg_rewind/local_source.c
@@ -91,6 +91,27 @@ local_fetch_file_range(rewind_source *source, const char *path, off_t off,
 
 	open_target_file(path, false);
 
+#ifdef HAVE_COPY_FILE_RANGE
+	ssize_t		readlen;
+
+	if (!dry_run)
+	{
+		if (lseek(dstfd, begin, SEEK_SET) == -1)
+			pg_fatal("could not seek in target file \"%s\": %m", path);
+
+		while (end - begin > 0)
+		{
+			readlen = copy_file_range(srcfd, NULL, dstfd, NULL, end - begin, 0);
+
+			if (readlen < 0)
+				pg_fatal("could not copy file from \"%s\" to \"%s\": %m", srcpath, path);
+			else if (readlen == 0)
+				pg_fatal("unexpected EOF while copying file \"%s\" to \"%s\"", srcpath, path);
+
+			begin += readlen;
+		}
+	}
+#else
 	while (end - begin > 0)
 	{
 		ssize_t		readlen;
@@ -111,6 +132,7 @@ local_fetch_file_range(rewind_source *source, const char *path, off_t off,
 		write_target_range(buf.data, begin, readlen);
 		begin += readlen;
 	}
+#endif
 
 	if (close(srcfd) != 0)
 		pg_fatal("could not close file \"%s\": %m", srcpath);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index f4d9f3b408..8706b9879f 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -104,6 +104,9 @@
 /* Define to 1 if you have the <copyfile.h> header file. */
 #undef HAVE_COPYFILE_H
 
+/* Define to 1 if you have the `copy_file_range' function. */
+#undef HAVE_COPY_FILE_RANGE
+
 /* Define to 1 if you have the <crtdefs.h> header file. */
 #undef HAVE_CRTDEFS_H
 
-- 
2.14.3

