v3-0002-Confine-various-OS-copy-on-write-and-other-copy-a.patch

application/octet-stream

Filename: v3-0002-Confine-various-OS-copy-on-write-and-other-copy-a.patch
Type: application/octet-stream
Part: 1
Message: Re: pg_upgrade --copy-file-range

Patch

Format: format-patch
Series: patch v3-0002
Subject: Confine various OS copy-on-write (and other copy acceleration) methods via new internal pg_copyfile_* APIs in libpqcommon. Later refactor pg_upgrade to use those APIs for ioctl(FICLONE).
File+
src/bin/pg_upgrade/file.c 81 171
src/bin/pg_upgrade/relfilenumber.c 39 42
src/common/file_utils.c 233 55
src/include/common/file_utils.h 27 10
From 2f58f6af27c94d782925b7f38ea8845cdfb3e0d2 Mon Sep 17 00:00:00 2001
From: Jakub Wartak <jakub.wartak@enterprisedb.com>
Date: Thu, 4 Jan 2024 19:45:30 +0100
Subject: [PATCH v3 2/4] Confine various OS copy-on-write (and other copy
 acceleration) methods via new internal pg_copyfile_* APIs in libpqcommon.
 Later refactor pg_upgrade to use those APIs for ioctl(FICLONE).

Co-authored-by: Thomas Munro <thomas.munro@gmail.com>

Discussion: https://www.postgresql.org/message-id/flat/CA%2BhUKGJvLLNQtzb%3DZWcTsYF8kv8cR_%3DH17CX-eL8qNixeC4DAw%40mail.gmail.com#ce606227e39df74c6b2abf80b8eab04a
---
 src/bin/pg_upgrade/file.c          | 252 ++++++++-----------------
 src/bin/pg_upgrade/relfilenumber.c |  81 ++++----
 src/common/file_utils.c            | 288 +++++++++++++++++++++++------
 src/include/common/file_utils.h    |  37 +++-
 4 files changed, 380 insertions(+), 278 deletions(-)

diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index d173602882..f91cc548ce 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -1,31 +1,23 @@
 /*
- *	file.c
+ * file.c
  *
- *	file system operations
+ * file system operations
  *
- *	Copyright (c) 2010-2023, PostgreSQL Global Development Group
- *	src/bin/pg_upgrade/file.c
+ * Copyright (c) 2010-2023, PostgreSQL Global Development Group
+ * src/bin/pg_upgrade/file.c
  */
 
 #include "postgres_fe.h"
 
-#include <sys/stat.h>
-#include <fcntl.h>
-#ifdef HAVE_COPYFILE_H
-#include <copyfile.h>
-#endif
-#ifdef __linux__
-#include <sys/ioctl.h>
-#include <linux/fs.h>
-#endif
-
 #include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
+#include "common/file_utils.h"
 #include "pg_upgrade.h"
 #include "storage/bufpage.h"
 #include "storage/checksum.h"
 #include "storage/checksum_impl.h"
-
+#include <fcntl.h>
+#include <sys/stat.h>
 
 /*
  * cloneFile()
@@ -35,127 +27,49 @@
  * schemaName/relName are relation's SQL name (used for error messages only).
  */
 void
-cloneFile(const char *src, const char *dst,
-		  const char *schemaName, const char *relName)
+cloneFile(const char *src, const char *dst, const char *schemaName,
+		  const char *relName)
 {
-#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
-	if (copyfile(src, dst, NULL, COPYFILE_CLONE_FORCE) < 0)
-		pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
-				 schemaName, relName, src, dst, strerror(errno));
-#elif defined(__linux__) && defined(FICLONE)
-	int			src_fd;
-	int			dest_fd;
-
-	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while cloning relation \"%s.%s\": could not open file \"%s\": %s",
-				 schemaName, relName, src, strerror(errno));
-
-	if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-						pg_file_create_mode)) < 0)
-		pg_fatal("error while cloning relation \"%s.%s\": could not create file \"%s\": %s",
-				 schemaName, relName, dst, strerror(errno));
-
-	if (ioctl(dest_fd, FICLONE, src_fd) < 0)
-	{
-		int			save_errno = errno;
-
-		unlink(dst);
+	char		action[1024];
 
-		pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
-				 schemaName, relName, src, dst, strerror(save_errno));
-	}
-
-	close(src_fd);
-	close(dest_fd);
-#endif
+	snprintf(action, sizeof(action) - 1, "relation \"%s.%s\"", schemaName,
+			 relName);
+	pg_copyfile_offload(src, dst, action, PG_COPYFILE_IOCTL_FICLONE);
 }
 
-
 /*
  * copyFile()
  *
- * Copies a relation file from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
+ * Copies a relation file from src to dst. schemaName/relName are relation's
+ * SQL name (used for error messages only).
  */
 void
-copyFile(const char *src, const char *dst,
-		 const char *schemaName, const char *relName)
+copyFile(const char *src, const char *dst, const char *schemaName,
+		 const char *relName)
 {
-#ifndef WIN32
-	int			src_fd;
-	int			dest_fd;
-	char	   *buffer;
-
-	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
-				 schemaName, relName, src, strerror(errno));
-
-	if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-						pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %s",
-				 schemaName, relName, dst, strerror(errno));
-
-	/* copy in fairly large chunks for best efficiency */
-#define COPY_BUF_SIZE (50 * BLCKSZ)
-
-	buffer = (char *) pg_malloc(COPY_BUF_SIZE);
-
-	/* perform data copying i.e read src source, write to destination */
-	while (true)
-	{
-		ssize_t		nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
+	char		action[128];
 
-		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)
-			break;
-
-		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));
-		}
-	}
-
-	pg_free(buffer);
-	close(src_fd);
-	close(dest_fd);
-
-#else							/* WIN32 */
-
-	if (CopyFile(src, dst, true) == 0)
-	{
-		_dosmaperr(GetLastError());
-		pg_fatal("error while copying relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
-				 schemaName, relName, src, dst, strerror(errno));
-	}
-
-#endif							/* WIN32 */
+	snprintf(action, sizeof(action) - 1, "relation \"%s.%s\"", schemaName,
+			 relName);
+	pg_copyfile(src, dst, action, NULL);
 }
 
-
 /*
  * linkFile()
  *
- * Hard-links a relation file from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
+ * Hard-links a relation file from src to dst. schemaName/relName are
+ * relation's SQL name (used for error messages only).
  */
 void
-linkFile(const char *src, const char *dst,
-		 const char *schemaName, const char *relName)
+linkFile(const char *src, const char *dst, const char *schemaName,
+		 const char *relName)
 {
 	if (link(src, dst) < 0)
-		pg_fatal("error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
+		pg_fatal("error while creating link for relation \"%s.%s\" (\"%s\" to "
+				 "\"%s\"): %s",
 				 schemaName, relName, src, dst, strerror(errno));
 }
 
-
 /*
  * rewriteVisibilityMap()
  *
@@ -163,14 +77,14 @@ linkFile(const char *src, const char *dst,
  * schemaName/relName are relation's SQL name (used for error messages only).
  *
  * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
+ * visibility map included one bit per heap page; it now includes two. When
+ * upgrading a cluster from before that time to a current PostgreSQL version,
+ * we could refuse to copy visibility maps from the old cluster to the new
+ * cluster; the next VACUUM would recreate them, but at the price of scanning
+ * the entire table.  So, instead, we rewrite the old visibility maps in the
+ * new format.  That way, the all-visible bits remain set for the pages for
+ * which they were set previously.  The all-frozen bits are never set by this
+ * conversion; we leave that to VACUUM.
  */
 void
 rewriteVisibilityMap(const char *fromfile, const char *tofile,
@@ -190,16 +104,19 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
 
 	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s",
+		pg_fatal("error while copying relation \"%s.%s\": could not open file "
+				 "\"%s\": %s",
 				 schemaName, relName, fromfile, strerror(errno));
 
 	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %s",
+		pg_fatal("error while copying relation \"%s.%s\": could not stat file "
+				 "\"%s\": %s",
 				 schemaName, relName, fromfile, strerror(errno));
 
 	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
 					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %s",
+		pg_fatal("error while copying relation \"%s.%s\": could not create file "
+				 "\"%s\": %s",
 				 schemaName, relName, tofile, strerror(errno));
 
 	/* Save old file size */
@@ -223,10 +140,12 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
 		{
 			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s",
+				pg_fatal("error while copying relation \"%s.%s\": could not read file "
+						 "\"%s\": %s",
 						 schemaName, relName, fromfile, strerror(errno));
 			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
+				pg_fatal("error while copying relation \"%s.%s\": partial page found "
+						 "in file \"%s\"",
 						 schemaName, relName, fromfile);
 		}
 
@@ -260,25 +179,30 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 
 			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
 
-			/* Process old page bytes one by one, and turn it into new page. */
+			/*
+			 * Process old page bytes one by one, and turn it into new page.
+			 */
 			while (old_cur < old_break)
 			{
 				uint8		byte = *(uint8 *) old_cur;
 				uint16		new_vmbits = 0;
 				int			i;
 
-				/* Generate new format bits while keeping old information */
+				/*
+				 * Generate new format bits while keeping old information
+				 */
 				for (i = 0; i < BITS_PER_BYTE; i++)
 				{
 					if (byte & (1 << i))
 					{
 						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
+						new_vmbits |= VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
 					}
 				}
 
-				/* Copy new visibility map bytes to new-format page */
+				/*
+				 * Copy new visibility map bytes to new-format page
+				 */
 				new_cur[0] = (char) (new_vmbits & 0xFF);
 				new_cur[1] = (char) (new_vmbits >> 8);
 
@@ -286,11 +210,15 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 				new_cur += BITS_PER_HEAPBLOCK;
 			}
 
-			/* If the last part of the last page is empty, skip writing it */
+			/*
+			 * If the last part of the last page is empty, skip writing it
+			 */
 			if (old_lastpart && empty)
 				break;
 
-			/* Set new checksum for visibility map page, if enabled */
+			/*
+			 * Set new checksum for visibility map page, if enabled
+			 */
 			if (new_cluster.controldata.data_checksum_version != 0)
 				((PageHeader) new_vmbuf.data)->pd_checksum =
 					pg_checksum_page(new_vmbuf.data, new_blkno);
@@ -298,10 +226,13 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
 			errno = 0;
 			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
 			{
-				/* if write didn't set errno, assume problem is no disk space */
+				/*
+				 * 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",
+				pg_fatal("error while copying relation \"%s.%s\": could not write file "
+						 "\"%s\": %s",
 						 schemaName, relName, tofile, strerror(errno));
 			}
 
@@ -322,40 +253,15 @@ check_file_clone(void)
 	char		existing_file[MAXPGPATH];
 	char		new_link_file[MAXPGPATH];
 
-	snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
-	snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.clonetest", new_cluster.pgdata);
+	snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION",
+			 old_cluster.pgdata);
+	snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.clonetest",
+			 new_cluster.pgdata);
 	unlink(new_link_file);		/* might fail */
 
-#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
-	if (copyfile(existing_file, new_link_file, NULL, COPYFILE_CLONE_FORCE) < 0)
-		pg_fatal("could not clone file between old and new data directories: %s",
-				 strerror(errno));
-#elif defined(__linux__) && defined(FICLONE)
-	{
-		int			src_fd;
-		int			dest_fd;
-
-		if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
-			pg_fatal("could not open file \"%s\": %s",
-					 existing_file, strerror(errno));
-
-		if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-							pg_file_create_mode)) < 0)
-			pg_fatal("could not create file \"%s\": %s",
-					 new_link_file, strerror(errno));
-
-		if (ioctl(dest_fd, FICLONE, src_fd) < 0)
-			pg_fatal("could not clone file between old and new data directories: %s",
-					 strerror(errno));
-
-		close(src_fd);
-		close(dest_fd);
-	}
-#else
-	pg_fatal("file cloning not supported on this platform");
-#endif
-
-	unlink(new_link_file);
+	/* will throw error in case it is not supported */
+	pg_copyfile_offload_supported(existing_file, new_link_file, NULL,
+								  PG_COPYFILE_IOCTL_FICLONE);
 }
 
 void
@@ -364,13 +270,17 @@ check_hard_link(void)
 	char		existing_file[MAXPGPATH];
 	char		new_link_file[MAXPGPATH];
 
-	snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
-	snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
+	snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION",
+			 old_cluster.pgdata);
+	snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest",
+			 new_cluster.pgdata);
 	unlink(new_link_file);		/* might fail */
 
 	if (link(existing_file, new_link_file) < 0)
-		pg_fatal("could not create hard link between old and new data directories: %s\n"
-				 "In link mode the old and new data directories must be on the same file system.",
+		pg_fatal(
+				 "could not create hard link between old and new data directories: %s\n"
+				 "In link mode the old and new data directories must be on the same "
+				 "file system.",
 				 strerror(errno));
 
 	unlink(new_link_file);
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index 34bc9c1504..d61fb77bdf 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -15,15 +15,16 @@
 #include "catalog/pg_class_d.h"
 #include "pg_upgrade.h"
 
-static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
-
+static void transfer_single_new_db(FileNameMap *maps, int size,
+								   char *old_tablespace);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix,
+							 bool vm_must_add_frozenbit);
 
 /*
  * transfer_all_new_tablespaces()
  *
- * Responsible for upgrading all database. invokes routines to generate mappings and then
- * physically link the databases.
+ * Responsible for upgrading all database. invokes routines to generate mappings
+ * and then physically link the databases.
  */
 void
 transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
@@ -40,6 +41,9 @@ transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
 		case TRANSFER_MODE_LINK:
 			prep_status_progress("Linking user relation files");
 			break;
+		case TRANSFER_MODE_COPY_FILE_RANGE:
+			prep_status_progress("Copying user relation files with copy_file_range");
+			break;
 	}
 
 	/*
@@ -61,9 +65,7 @@ transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
 									  new_pgdata, old_pgdata);
 
 		for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
-			parallel_transfer_all_new_dbs(old_db_arr,
-										  new_db_arr,
-										  old_pgdata,
+			parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
 										  new_pgdata,
 										  os_info.old_tablespaces[tblnum]);
 		/* reap all children */
@@ -75,23 +77,22 @@ transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
 	check_ok();
 }
 
-
 /*
  * transfer_all_new_dbs()
  *
- * Responsible for upgrading all database. invokes routines to generate mappings and then
- * physically link the databases.
+ * Responsible for upgrading all database. invokes routines to generate mappings
+ * and then physically link the databases.
  */
 void
 transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
-					 char *old_pgdata, char *new_pgdata, char *old_tablespace)
+					 char *old_pgdata, char *new_pgdata,
+					 char *old_tablespace)
 {
 	int			old_dbnum,
 				new_dbnum;
 
 	/* Scan the old cluster databases and transfer their files */
-	for (old_dbnum = new_dbnum = 0;
-		 old_dbnum < old_db_arr->ndbs;
+	for (old_dbnum = new_dbnum = 0; old_dbnum < old_db_arr->ndbs;
 		 old_dbnum++, new_dbnum++)
 	{
 		DbInfo	   *old_db = &old_db_arr->dbs[old_dbnum],
@@ -115,8 +116,8 @@ transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
 			pg_fatal("old database \"%s\" not found in the new cluster",
 					 old_db->db_name);
 
-		mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata,
-									new_pgdata);
+		mappings =
+			gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata, new_pgdata);
 		if (n_maps)
 		{
 			transfer_single_new_db(mappings, n_maps, old_tablespace);
@@ -132,7 +133,8 @@ transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
  * create links for mappings stored in "maps" array.
  */
 static void
-transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace)
+transfer_single_new_db(FileNameMap *maps, int size,
+					   char *old_tablespace)
 {
 	int			mapnum;
 	bool		vm_must_add_frozenbit = false;
@@ -161,7 +163,6 @@ transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace)
 	}
 }
 
-
 /*
  * transfer_relfile()
  *
@@ -170,7 +171,8 @@ transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace)
  * mode.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix,
+				 bool vm_must_add_frozenbit)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -190,20 +192,12 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		else
 			snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
 
-		snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s",
-				 map->old_tablespace,
-				 map->old_tablespace_suffix,
-				 map->db_oid,
-				 map->relfilenumber,
-				 type_suffix,
-				 extent_suffix);
-		snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s",
-				 map->new_tablespace,
-				 map->new_tablespace_suffix,
-				 map->db_oid,
-				 map->relfilenumber,
-				 type_suffix,
-				 extent_suffix);
+		snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s", map->old_tablespace,
+				 map->old_tablespace_suffix, map->db_oid, map->relfilenumber,
+				 type_suffix, extent_suffix);
+		snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s", map->new_tablespace,
+				 map->new_tablespace_suffix, map->db_oid, map->relfilenumber,
+				 type_suffix, extent_suffix);
 
 		/* Is it an extent, fsm, or vm file? */
 		if (type_suffix[0] != '\0' || segno != 0)
@@ -215,7 +209,8 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 				if (errno == ENOENT)
 					return;
 				else
-					pg_fatal("error while checking for file existence \"%s.%s\" (\"%s\" to \"%s\"): %s",
+					pg_fatal("error while checking for file existence \"%s.%s\" (\"%s\" "
+							 "to \"%s\"): %s",
 							 map->nspname, map->relname, old_file, new_file,
 							 strerror(errno));
 			}
@@ -233,27 +228,29 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
 		{
 			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
+			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"", old_file, new_file);
 			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
 		}
 		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
-					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
-						   old_file, new_file);
+					pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"", old_file, new_file);
 					cloneFile(old_file, new_file, map->nspname, map->relname);
 					break;
 				case TRANSFER_MODE_COPY:
-					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
-						   old_file, new_file);
+					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"", old_file, new_file);
 					copyFile(old_file, new_file, map->nspname, map->relname);
 					break;
 				case TRANSFER_MODE_LINK:
-					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
-						   old_file, new_file);
+					pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"", old_file, new_file);
 					linkFile(old_file, new_file, map->nspname, map->relname);
+					break;
+				case TRANSFER_MODE_COPY_FILE_RANGE:
+					pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
+						   old_file, new_file);
+					copyFileByRange(old_file, new_file, map->nspname, map->relname);
+					break;
 			}
 	}
 }
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 5380299f35..25f9a48b21 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -24,10 +24,19 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "common/file_perm.h"
 #include "common/file_utils.h"
 #ifdef FRONTEND
+#include "common/checksum_helper.h"
 #include "common/logging.h"
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
 #endif
+#ifdef __linux__
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#endif
+#endif							/* FRONTEND */
 #include "port/pg_iovec.h"
 
 #ifdef FRONTEND
@@ -42,7 +51,7 @@
 /*
  * pg_xlog has been renamed to pg_wal in version 10.
  */
-#define MINIMUM_VERSION_FOR_PG_WAL	100000
+#define MINIMUM_VERSION_FOR_PG_WAL 100000
 
 #ifdef PG_FLUSH_DATA_WORKS
 static int	pre_sync_fname(const char *fname, bool isdir);
@@ -94,8 +103,7 @@ do_syncfs(const char *path)
  * serverVersion indicates the version of the server to be sync'd.
  */
 void
-sync_pgdata(const char *pg_data,
-			int serverVersion,
+sync_pgdata(const char *pg_data, int serverVersion,
 			DataDirSyncMethod sync_method)
 {
 	bool		xlog_is_symlink;
@@ -127,8 +135,7 @@ sync_pgdata(const char *pg_data,
 		case DATA_DIR_SYNC_METHOD_SYNCFS:
 			{
 #ifndef HAVE_SYNCFS
-				pg_log_error("this build does not support sync method \"%s\"",
-							 "syncfs");
+				pg_log_error("this build does not support sync method \"%s\"", "syncfs");
 				exit(EXIT_FAILURE);
 #else
 				DIR		   *dir;
@@ -145,29 +152,27 @@ sync_pgdata(const char *pg_data,
 				/* Sync the top level pgdata directory. */
 				do_syncfs(pg_data);
 
-				/* If any tablespaces are configured, sync each of those. */
+				/*
+				 * If any tablespaces are configured, sync each of those.
+				 */
 				dir = opendir(pg_tblspc);
 				if (dir == NULL)
-					pg_log_error("could not open directory \"%s\": %m",
-								 pg_tblspc);
+					pg_log_error("could not open directory \"%s\": %m", pg_tblspc);
 				else
 				{
 					while (errno = 0, (de = readdir(dir)) != NULL)
 					{
 						char		subpath[MAXPGPATH * 2];
 
-						if (strcmp(de->d_name, ".") == 0 ||
-							strcmp(de->d_name, "..") == 0)
+						if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
 							continue;
 
-						snprintf(subpath, sizeof(subpath), "%s/%s",
-								 pg_tblspc, de->d_name);
+						snprintf(subpath, sizeof(subpath), "%s/%s", pg_tblspc, de->d_name);
 						do_syncfs(subpath);
 					}
 
 					if (errno)
-						pg_log_error("could not read directory \"%s\": %m",
-									 pg_tblspc);
+						pg_log_error("could not read directory \"%s\": %m", pg_tblspc);
 
 					(void) closedir(dir);
 				}
@@ -176,8 +181,7 @@ sync_pgdata(const char *pg_data,
 				if (xlog_is_symlink)
 					do_syncfs(pg_wal);
 #endif							/* HAVE_SYNCFS */
-			}
-			break;
+			} break;
 
 		case DATA_DIR_SYNC_METHOD_FSYNC:
 			{
@@ -206,8 +210,7 @@ sync_pgdata(const char *pg_data,
 				if (xlog_is_symlink)
 					walkdir(pg_wal, fsync_fname, false);
 				walkdir(pg_tblspc, fsync_fname, true);
-			}
-			break;
+			} break;
 	}
 }
 
@@ -224,8 +227,7 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
 		case DATA_DIR_SYNC_METHOD_SYNCFS:
 			{
 #ifndef HAVE_SYNCFS
-				pg_log_error("this build does not support sync method \"%s\"",
-							 "syncfs");
+				pg_log_error("this build does not support sync method \"%s\"", "syncfs");
 				exit(EXIT_FAILURE);
 #else
 				/*
@@ -234,8 +236,7 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
 				 */
 				do_syncfs(dir);
 #endif							/* HAVE_SYNCFS */
-			}
-			break;
+			} break;
 
 		case DATA_DIR_SYNC_METHOD_FSYNC:
 			{
@@ -248,20 +249,19 @@ sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method)
 #endif
 
 				walkdir(dir, fsync_fname, false);
-			}
-			break;
+			} break;
 	}
 }
 
 /*
- * walkdir: recursively walk a directory, applying the action to each
- * regular file and directory (including the named directory itself).
+ * walkdir: recursively walk a directory, applying the action to each regular
+ * file and directory (including the named directory itself).
  *
- * If process_symlinks is true, the action and recursion are also applied
- * to regular files and directories that are pointed to by symlinks in the
- * given directory; otherwise symlinks are ignored.  Symlinks are always
- * ignored in subdirectories, ie we intentionally don't pass down the
- * process_symlinks flag to recursive calls.
+ * If process_symlinks is true, the action and recursion are also applied to
+ * regular files and directories that are pointed to by symlinks in the given
+ * directory; otherwise symlinks are ignored.  Symlinks are always ignored in
+ * subdirectories, ie we intentionally don't pass down the process_symlinks
+ * flag to recursive calls.
  *
  * Errors are reported but not considered fatal.
  *
@@ -286,8 +286,7 @@ walkdir(const char *path,
 	{
 		char		subpath[MAXPGPATH * 2];
 
-		if (strcmp(de->d_name, ".") == 0 ||
-			strcmp(de->d_name, "..") == 0)
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
 			continue;
 
 		snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
@@ -371,8 +370,8 @@ pre_sync_fname(const char *fname, bool isdir)
  * fsync_fname -- Try to fsync a file or directory
  *
  * Ignores errors trying to open unreadable files, or trying to fsync
- * directories on systems where that isn't allowed/required.  All other errors
- * are fatal.
+ * directories on systems where that isn't allowed/required.  All other
+ * errors are fatal.
  */
 int
 fsync_fname(const char *fname, bool isdir)
@@ -427,8 +426,8 @@ fsync_fname(const char *fname, bool isdir)
 /*
  * fsync_parent_path -- fsync the parent path of a file or directory
  *
- * This is aimed at making file operations persistent on disk in case of
- * an OS crash or power failure.
+ * This is aimed at making file operations persistent on disk in case of an
+ * OS crash or power failure.
  */
 int
 fsync_parent_path(const char *fname)
@@ -453,7 +452,8 @@ fsync_parent_path(const char *fname)
 }
 
 /*
- * durable_rename -- rename(2) wrapper, issuing fsyncs required for durability
+ * durable_rename -- rename(2) wrapper, issuing fsyncs required for
+ * durability
  *
  * Wrapper around rename, similar to the backend version.
  */
@@ -495,8 +495,8 @@ durable_rename(const char *oldfile, const char *newfile)
 	/* Time to do the real deal... */
 	if (rename(oldfile, newfile) != 0)
 	{
-		pg_log_error("could not rename file \"%s\" to \"%s\": %m",
-					 oldfile, newfile);
+		pg_log_error("could not rename file \"%s\" to \"%s\": %m", oldfile,
+					 newfile);
 		return -1;
 	}
 
@@ -513,6 +513,186 @@ durable_rename(const char *oldfile, const char *newfile)
 	return 0;
 }
 
+/* Helper function to optionally prepend error string */
+static inline char *
+opt_errinfo(const char *addon_errmsg)
+{
+	char		buf[128];
+
+	if (addon_errmsg == NULL)
+		return "";
+
+	strcpy(buf, " ");
+	return strncat(buf, addon_errmsg, sizeof(buf) - 2);
+}
+
+/*
+ * Copies a relation file from src to dest. addon_errmsg is an optional
+ * addon error message (can be NULL or include schema/relName)
+ */
+void
+pg_copyfile(const char *src, const char *dest, const char *addon_errmsg,
+			pg_checksum_context *ctx)
+{
+#ifndef WIN32
+	int			src_fd;
+	int			dest_fd;
+	uint8	   *buffer;
+
+	/* copy in fairly large chunks for best efficiency */
+	const int	buffer_size = 50 * BLCKSZ;
+
+	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
+		pg_fatal("error while copying%s: could not open file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), src, strerror(errno));
+
+	if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+						pg_file_create_mode)) < 0)
+		pg_fatal("error while copying%s: could not create file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), dest, strerror(errno));
+
+	buffer = pg_malloc(buffer_size);
+
+	/* perform data copying i.e read src source, write to destination */
+	while (true)
+	{
+		ssize_t		nbytes = read(src_fd, buffer, buffer_size);
+
+		if (nbytes < 0)
+			pg_fatal("error while copying%s: could not read file "
+					 "\"%s\": %s",
+					 opt_errinfo(addon_errmsg), src, strerror(errno));
+
+		if (nbytes == 0)
+			break;
+
+		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%s: could not write file \"%s\": %s",
+					 opt_errinfo(addon_errmsg), dest, strerror(errno));
+		}
+
+		if (pg_checksum_update(ctx, buffer, nbytes) < 0)
+			pg_fatal("could not calculate checksum of file \"%s\"", dest);
+	}
+
+	pg_free(buffer);
+	close(src_fd);
+	close(dest_fd);
+
+#else							/* WIN32 */
+	if (CopyFile(src, dest, true) == 0)
+	{
+		_dosmaperr(GetLastError());
+		pg_fatal("error while copying%s (\"%s\" to \"%s\"): %s", addon_errmsg,
+				 opt_errinfo(addon_errmsg), src, dest, strerror(errno));
+	}
+#endif							/* WIN32 */
+}
+
+/*
+ * pg_copyfile_offload()
+ *
+ * Clones/reflinks a relation file from src to dest using variety of methods
+ *
+ * addon_errmsg can be used to pass additional information in case of errors.
+ * flags, see PG_COPYFILE_* enum in file_utils.h
+ */
+void
+pg_copyfile_offload(const char *src, const char *dest,
+					const char *addon_errmsg, CopyFileMethod flags)
+{
+
+#ifdef WIN32
+	/* on WIN32 we ignore flags, we have no other choice */
+	if (CopyFile(src, dest, true) == 0)
+	{
+		_dosmaperr(GetLastError());
+		pg_fatal("error while copying%s (\"%s\" to \"%s\"): %s", addon_errmsg,
+				 opt_errinfo(addon_errmsg), src, dest, strerror(errno));
+	}
+#elif defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
+	/* on MacOS we ignore flags, we have no other choice */
+	if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0)
+		pg_fatal("error while cloning%s: (\"%s\" to \"%s\"): %s",
+				 opt_errinfo(addon_errmsg), src, dest, strerror(errno));
+
+#elif defined(HAVE_COPY_FILE_RANGE) || defined(FICLONE)
+	int			src_fd;
+	int			dest_fd;
+	ssize_t		nbytes;
+
+	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
+		pg_fatal("error while copying%s: could not open file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), src, strerror(errno));
+
+	if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+						pg_file_create_mode)) < 0)
+		pg_fatal("error while copying%s: could not create file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), dest, strerror(errno));
+
+	if (flags & PG_COPYFILE_COPY_FILE_RANGE)
+	{
+#ifdef HAVE_COPY_FILE_RANGE
+		do
+		{
+			nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
+			if (nbytes < 0 && errno != EINTR)
+				pg_fatal("error while copying%s: could not copy_file_range()"
+						 "from \"%s\" to \"%s\": %s",
+						 opt_errinfo(addon_errmsg), src, dest, strerror(errno));
+		} while (nbytes > 0);
+#else
+		pg_fatal("copy file accelaration via copy_file_range() is not supported on "
+				 "this platform");
+#endif
+	}
+	else if (flags & PG_COPYFILE_IOCTL_FICLONE)
+	{
+#if defined(__linux__) && defined(FICLONE)
+		if (ioctl(dest_fd, FICLONE, src_fd) < 0)
+		{
+			int			save_errno = errno;
+
+			unlink(dest);
+
+			pg_fatal("error while cloning%s: (\"%s\" to \"%s\"): %s",
+					 opt_errinfo(addon_errmsg), src, dest, strerror(save_errno));
+		}
+#else
+		pg_fatal("clone file accelaration via ioctl(FICLONE) is not supported on "
+				 "this platform");
+#endif
+	}
+
+	close(src_fd);
+	close(dest_fd);
+
+#else
+	if (flags & PG_COPYFILE_FALLBACK)
+		pg_copyfile(src, dest, addon_errmsg);
+	else
+		pg_fatal("none of the copy file acceleration methods are supported on this "
+				 "platform");
+#endif
+}
+
+/*  FIXME */
+bool
+pg_copyfile_offload_supported(const char *src, const char *dst,
+							  const char *addon_errmsg,
+							  CopyFileMethod flags)
+{
+	pg_copyfile_offload(src, dst, addon_errmsg, flags);
+	return true;
+}
+
 #endif							/* FRONTEND */
 
 /*
@@ -522,10 +702,8 @@ durable_rename(const char *oldfile, const char *newfile)
  * it should be a level from elog.h.
  */
 PGFileType
-get_dirent_type(const char *path,
-				const struct dirent *de,
-				bool look_through_symlinks,
-				int elevel)
+get_dirent_type(const char *path, const struct dirent *de,
+				bool look_through_symlinks, int elevel)
 {
 	PGFileType	result;
 
@@ -553,7 +731,6 @@ get_dirent_type(const char *path,
 		struct stat fst;
 		int			sret;
 
-
 		if (look_through_symlinks)
 			sret = stat(path, &fst);
 		else
@@ -563,11 +740,11 @@ get_dirent_type(const char *path,
 		{
 			result = PGFILETYPE_ERROR;
 #ifdef FRONTEND
-			pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
+			pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m",
+						   path);
 #else
-			ereport(elevel,
-					(errcode_for_file_access(),
-					 errmsg("could not stat file \"%s\": %m", path)));
+			ereport(elevel, (errcode_for_file_access(),
+							 errmsg("could not stat file \"%s\": %m", path)));
 #endif
 		}
 		else if (S_ISREG(fst.st_mode))
@@ -586,12 +763,12 @@ get_dirent_type(const char *path,
  * write.  The part of 'source' beginning after 'transferred' bytes is copied
  * to 'destination', and its length is returned.  'source' and 'destination'
  * may point to the same array, for in-place adjustment.  A return value of
- * zero indicates completion (for callers without a cheaper way to know that).
+ * zero indicates completion (for callers without a cheaper way to know
+ * that).
  */
 int
 compute_remaining_iovec(struct iovec *destination,
-						const struct iovec *source,
-						int iovcnt,
+						const struct iovec *source, int iovcnt,
 						size_t transferred)
 {
 	Assert(iovcnt > 0);
@@ -634,7 +811,8 @@ compute_remaining_iovec(struct iovec *destination,
  * error is returned, it is unspecified how much has been written.
  */
 ssize_t
-pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt,
+					  off_t offset)
 {
 	struct iovec iov_copy[PG_IOV_MAX];
 	ssize_t		sum = 0;
@@ -680,8 +858,8 @@ pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
  * Writes zeros to file worth "size" bytes at "offset" (from the start of the
  * file), using vectored I/O.
  *
- * Returns the total amount of data written.  On failure, a negative value
- * is returned with errno set.
+ * Returns the total amount of data written.  On failure, a negative value is
+ * returned with errno set.
  */
 ssize_t
 pg_pwrite_zeros(int fd, size_t size, off_t offset)
diff --git a/src/include/common/file_utils.h b/src/include/common/file_utils.h
index 02a940e310..0747109217 100644
--- a/src/include/common/file_utils.h
+++ b/src/include/common/file_utils.h
@@ -30,31 +30,48 @@ typedef enum DataDirSyncMethod
 	DATA_DIR_SYNC_METHOD_SYNCFS,
 } DataDirSyncMethod;
 
+typedef enum CopyFileMethod
+{
+	PG_COPYFILE_FALLBACK = 0x1,
+	PG_COPYFILE_IOCTL_FICLONE = 0x2,	/* Linux */
+	PG_COPYFILE_COPY_FILE_RANGE = 0x4,	/* FreeBSD & Linux >= 4.5 */
+	PG_COPYFILE_COPYFILE_CLONE_FORCE = 0x8	/* MacOS */
+} CopyFileMethod;
+#define PG_COPYFILE_ANY_WITH_FALLBACK (2 << 4) - 1
+
 struct iovec;					/* avoid including port/pg_iovec.h here */
 
 #ifdef FRONTEND
+#include "c.h"
+#include "common/checksum_helper.h"
 extern int	fsync_fname(const char *fname, bool isdir);
 extern void sync_pgdata(const char *pg_data, int serverVersion,
 						DataDirSyncMethod sync_method);
 extern void sync_dir_recurse(const char *dir, DataDirSyncMethod sync_method);
 extern int	durable_rename(const char *oldfile, const char *newfile);
 extern int	fsync_parent_path(const char *fname);
+
+extern void pg_copyfile(const char *src, const char *dest,
+						const char *addon_errmsg, pg_checksum_context *ctx);
+
+extern void pg_copyfile_offload(const char *src, const char *dest,
+								const char *addon_errmsg, CopyFileMethod flags);
+
+extern bool pg_copyfile_offload_supported(const char *src, const char *dst,
+										  const char *addon_errmsg,
+										  CopyFileMethod flags);
+
 #endif
 
-extern PGFileType get_dirent_type(const char *path,
-								  const struct dirent *de,
-								  bool look_through_symlinks,
-								  int elevel);
+extern PGFileType get_dirent_type(const char *path, const struct dirent *de,
+								  bool look_through_symlinks, int elevel);
 
 extern int	compute_remaining_iovec(struct iovec *destination,
-									const struct iovec *source,
-									int iovcnt,
+									const struct iovec *source, int iovcnt,
 									size_t transferred);
 
-extern ssize_t pg_pwritev_with_retry(int fd,
-									 const struct iovec *iov,
-									 int iovcnt,
-									 off_t offset);
+extern ssize_t pg_pwritev_with_retry(int fd, const struct iovec *iov,
+									 int iovcnt, off_t offset);
 
 extern ssize_t pg_pwrite_zeros(int fd, size_t size, off_t offset);
 
-- 
2.30.2