From b435220922d7cd916f1b7acce313c8174738991c Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 30 Apr 2023 14:45:45 +1200
Subject: [PATCH 09/11] Use copy_file_range() in pg_upgrade.

This gives the kernel the opportunity to copy or clone efficiently.
We watch out for EXDEV and fall back to read/write for old Linux
kernels.

XXX Should we also let the user opt out?
---
 src/bin/pg_upgrade/file.c | 65 ++++++++++++++++++++++++++++++---------
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index d173602882..836b2bbbd0 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -9,6 +9,7 @@
 
 #include "postgres_fe.h"
 
+#include <limits.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #ifdef HAVE_COPYFILE_H
@@ -98,32 +99,68 @@ copyFile(const char *src, const char *dst,
 	/* copy in fairly large chunks for best efficiency */
 #define COPY_BUF_SIZE (50 * BLCKSZ)
 
+#ifdef HAVE_COPY_FILE_RANGE
+	buffer = NULL;
+#else
 	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
+#endif
 
 	/* perform data copying i.e read src source, write to destination */
 	while (true)
 	{
-		ssize_t		nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
+		ssize_t		nbytes = 0;
 
-		if (nbytes < 0)
-			pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
-					 schemaName, relName, src, strerror(errno));
+#ifdef HAVE_COPY_FILE_RANGE
+		if (buffer == NULL)
+		{
+			nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
+			if (nbytes < 0)
+			{
+				if (errno == EXDEV)
+				{
+					/* Linux < 5.3 might fail.  Fall back to read/write. */
+					buffer = (char *) pg_malloc(COPY_BUF_SIZE);
+				}
+				else
+				{
+					pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
 
-		if (nbytes == 0)
-			break;
+							 schemaName, relName, src, strerror(errno));
+				}
+			}
+		}
+#endif
 
-		errno = 0;
-		if (write(dest_fd, buffer, nbytes) != nbytes)
+		if (buffer)
 		{
-			/* if write didn't set errno, assume problem is no disk space */
-			if (errno == 0)
-				errno = ENOSPC;
-			pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s",
-					 schemaName, relName, dst, strerror(errno));
+			nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
+
+			if (nbytes < 0)
+				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
+						 schemaName, relName, src, strerror(errno));
+			if (nbytes > 0)
+			{
+				errno = 0;
+				if (write(dest_fd, buffer, nbytes) != nbytes)
+				{
+					/*
+					 * If write didn't set errno, assume problem is no disk
+					 * space.
+					 */
+					if (errno == 0)
+						errno = ENOSPC;
+					pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s",
+							 schemaName, relName, dst, strerror(errno));
+				}
+			}
 		}
+
+		if (nbytes == 0)
+			break;
 	}
 
-	pg_free(buffer);
+	if (buffer)
+		pg_free(buffer);
 	close(src_fd);
 	close(dest_fd);
 
-- 
2.40.1

