Hi,

I took a quick look at the remaining part adding copy_file_range to
pg_combinebackup. The patch no longer applies, so I had to rebase it.
Most of the issues were trivial, but I had to fix a couple missing
prototypes - I added them to copy_file.h/c, mostly.

0001 is the minimal rebase + those fixes

0002 has a couple review comments in copy_file, and it also undoes a lot
of unnecessary formatting changes (already pointed out by Peter a couple
days ago).

A couple review comments:

1) AFAIK opt_errinfo() returns pointer to the local "buf" variable.

2) I wonder if we even need opt_errinfo(). I'm not sure it actually
makes anything simpler.

3) I think it'd be nice to make CopyFileMethod more consistent with
transferMode in pg_upgrade.h (I mean, it seems wise to make the naming
more consistent, it's probably not worth unifying this somehow).

4) I wonder how we came up with copying the files by 50 blocks, but I
now realize it's been like this before this patch. I only noticed
because the patch adds a comment before buffer_size calculation.

5) I dislike the renaming of copy_file_blocks to pg_copyfile. The new
name is way more generic / less descriptive - it's clear it copies the
file block by block (well, in chunks). pg_copyfile is pretty vague.

6) This leaves behind copy_file_copyfile, which is now unused.

7) The patch reworks how combinebackup deals with alternative copy
implementations - instead of setting strategy_implementation and calling
that, the decisions now happen in pg_copyfile_offload with a lot of
conditions / ifdef / defined ... I find it pretty hard to understand and
reason about. I liked the strategy_implementation approach, as it forces
us to keep each method in a separate function.

Perhaps there's a reason why that doesn't work for copy_file_range? But
in that case this needs much clearer comments.


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
From 39f42eee4c6f50d106672afe108294ee59082500 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Tue, 19 Mar 2024 15:34:18 +0100
Subject: [PATCH v20240319 2/2] review and cleanup

---
 src/bin/pg_combinebackup/copy_file.c        |   3 +
 src/bin/pg_combinebackup/copy_file.h        |   1 +
 src/bin/pg_combinebackup/pg_combinebackup.c | 197 +++++++++++---------
 src/bin/pg_combinebackup/reconstruct.c      | 105 ++++++-----
 src/bin/pg_combinebackup/reconstruct.h      |  19 +-
 5 files changed, 190 insertions(+), 135 deletions(-)

diff --git a/src/bin/pg_combinebackup/copy_file.c b/src/bin/pg_combinebackup/copy_file.c
index 16e26b4f573..f45670dd47c 100644
--- a/src/bin/pg_combinebackup/copy_file.c
+++ b/src/bin/pg_combinebackup/copy_file.c
@@ -77,6 +77,8 @@ opt_errinfo(const char *addon_errmsg)
 		return "";
 
 	strcpy(buf, " ");
+
+	/* XXX isn't this broken? this returns pointer to local variable */
 	return strncat(buf, addon_errmsg, sizeof(buf) - 2);
 }
 
@@ -93,6 +95,7 @@ pg_copyfile(const char *src, const char *dest, const char *addon_errmsg,
 	int			dest_fd;
 	uint8	   *buffer;
 
+	/* XXX where does the 50 blocks come from? larger/smaller? */
 	/* copy in fairly large chunks for best efficiency */
 	const int	buffer_size = 50 * BLCKSZ;
 
diff --git a/src/bin/pg_combinebackup/copy_file.h b/src/bin/pg_combinebackup/copy_file.h
index 2797a340055..f4d0ac47d0e 100644
--- a/src/bin/pg_combinebackup/copy_file.h
+++ b/src/bin/pg_combinebackup/copy_file.h
@@ -15,6 +15,7 @@
 #include "common/checksum_helper.h"
 #include "common/file_utils.h"
 
+/* XXX do we even want this? how does pg_upgrade to this? */
 typedef enum CopyFileMethod
 {
 	PG_COPYFILE_FALLBACK = 0x1,
diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index 1455360d81c..8fa7827c563 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -99,10 +99,15 @@ static void cleanup_directories_atexit(void);
 static void create_output_directory(char *dirname, cb_options *opt);
 static void help(const char *progname);
 static bool parse_oid(char *s, Oid *result);
-static void process_directory_recursively(
-										  Oid tsoid, char *input_directory, char *output_directory,
-										  char *relative_path, int n_prior_backups, char **prior_backup_dirs,
-										  manifest_data **manifests, manifest_writer *mwriter, cb_options *opt);
+static void process_directory_recursively(Oid tsoid,
+										  char *input_directory,
+										  char *output_directory,
+										  char *relative_path,
+										  int n_prior_backups,
+										  char **prior_backup_dirs,
+										  manifest_data **manifests,
+										  manifest_writer *mwriter,
+										  cb_options *opt);
 static int	read_pg_version_file(char *directory);
 static void remember_to_cleanup_directory(char *target_path, bool rmtopdir);
 static void reset_directory_cleanup_list(void);
@@ -156,8 +161,8 @@ main(int argc, char *argv[])
 	opt.copy_method = 0;
 
 	/* process command-line options */
-	while ((c = getopt_long(argc, argv, "dnNPo:T:", long_options, &optindex)) !=
-		   -1)
+	while ((c = getopt_long(argc, argv, "dnNPo:T:",
+							long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -178,8 +183,10 @@ main(int argc, char *argv[])
 				add_tablespace_mapping(&opt, optarg);
 				break;
 			case 1:
-				if (!pg_checksum_parse_type(optarg, &opt.manifest_checksums))
-					pg_fatal("unrecognized checksum algorithm: \"%s\"", optarg);
+				if (!pg_checksum_parse_type(optarg,
+											&opt.manifest_checksums))
+					pg_fatal("unrecognized checksum algorithm: \"%s\"",
+							 optarg);
 				break;
 			case 2:
 				opt.no_manifest = true;
@@ -295,8 +302,7 @@ main(int argc, char *argv[])
 		 * won't have the WAL ranges for the resulting manifest.
 		 */
 		if (manifests[n_prior_backups] == NULL)
-			pg_fatal("can't generate a manifest because no manifest is available for "
-					 "the final input backup");
+			pg_fatal("can't generate a manifest because no manifest is available for the final input backup");
 	}
 	else
 		mwriter = NULL;
@@ -308,15 +314,15 @@ main(int argc, char *argv[])
 	{
 		pg_log_debug("generating \"%s/backup_label\"", opt.output);
 		last_backup_label->cursor = 0;
-		write_backup_label(opt.output, last_backup_label, opt.manifest_checksums,
-						   mwriter);
+		write_backup_label(opt.output, last_backup_label,
+						   opt.manifest_checksums, mwriter);
 	}
 
 	/* Process everything that's not part of a user-defined tablespace. */
 	pg_log_debug("processing backup directory \"%s\"", last_input_dir);
-	process_directory_recursively(InvalidOid, last_input_dir, opt.output, NULL,
-								  n_prior_backups, prior_backup_dirs, manifests,
-								  mwriter, &opt);
+	process_directory_recursively(InvalidOid, last_input_dir, opt.output,
+								  NULL, n_prior_backups, prior_backup_dirs,
+								  manifests, mwriter, &opt);
 
 	/* Process user-defined tablespaces. */
 	for (ts = tablespaces; ts != NULL; ts = ts->next)
@@ -332,15 +338,16 @@ main(int argc, char *argv[])
 		{
 			char		linkpath[MAXPGPATH];
 
-			snprintf(linkpath, MAXPGPATH, "%s/pg_tblspc/%u", opt.output, ts->oid);
+			snprintf(linkpath, MAXPGPATH, "%s/pg_tblspc/%u", opt.output,
+					 ts->oid);
 
 			if (opt.dry_run)
 				pg_log_debug("would create symbolic link from \"%s\" to \"%s\"",
 							 linkpath, ts->new_dir);
 			else
 			{
-				pg_log_debug("creating symbolic link from \"%s\" to \"%s\"", linkpath,
-							 ts->new_dir);
+				pg_log_debug("creating symbolic link from \"%s\" to \"%s\"",
+							 linkpath, ts->new_dir);
 				if (symlink(ts->new_dir, linkpath) != 0)
 					pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
 							 linkpath, ts->new_dir);
@@ -354,19 +361,21 @@ main(int argc, char *argv[])
 			{
 				pg_log_debug("creating directory \"%s\"", ts->new_dir);
 				if (pg_mkdir_p(ts->new_dir, pg_dir_create_mode) == -1)
-					pg_fatal("could not create directory \"%s\": %m", ts->new_dir);
+					pg_fatal("could not create directory \"%s\": %m",
+							 ts->new_dir);
 			}
 		}
 
 		/* OK, now handle the directory contents. */
-		process_directory_recursively(ts->oid, ts->old_dir, ts->new_dir, NULL,
-									  n_prior_backups, prior_backup_dirs, manifests,
-									  mwriter, &opt);
+		process_directory_recursively(ts->oid, ts->old_dir, ts->new_dir,
+									  NULL, n_prior_backups, prior_backup_dirs,
+									  manifests, mwriter, &opt);
 	}
 
 	/* Finalize the backup_manifest, if we're generating one. */
 	if (mwriter != NULL)
-		finalize_manifest(mwriter, manifests[n_prior_backups]->first_wal_range);
+		finalize_manifest(mwriter,
+						  manifests[n_prior_backups]->first_wal_range);
 
 	/* fsync that output directory unless we've been told not to do so */
 	if (!opt.no_sync)
@@ -422,9 +431,7 @@ add_tablespace_mapping(cb_options *opt, char *arg)
 			*dst_ptr++ = *arg_ptr;
 	}
 	if (!tsmap->old_dir[0] || !tsmap->new_dir[0])
-		pg_fatal(
-				 "invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"",
-				 arg);
+		pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
 
 	/*
 	 * All tablespaces are created with absolute directories, so specifying a
@@ -496,8 +503,8 @@ check_backup_label_files(int n_backups, char **backup_dirs)
 			pg_fatal("could not close \"%s\": %m", pathbuf);
 
 		/* Parse the file contents. */
-		parse_backup_label(pathbuf, buf, &start_tli, &start_lsn, &previous_tli,
-						   &previous_lsn);
+		parse_backup_label(pathbuf, buf, &start_tli, &start_lsn,
+						   &previous_tli, &previous_lsn);
 
 		/*
 		 * Sanity checks.
@@ -508,19 +515,18 @@ check_backup_label_files(int n_backups, char **backup_dirs)
 		 * we don't have that information.
 		 */
 		if (i > 0 && previous_tli == 0)
-			pg_fatal("backup at \"%s\" is a full backup, but only the first backup "
-					 "should be a full backup",
+			pg_fatal("backup at \"%s\" is a full backup, but only the first backup should be a full backup",
 					 backup_dirs[i]);
 		if (i == 0 && previous_tli != 0)
-			pg_fatal("backup at \"%s\" is an incremental backup, but the first "
-					 "backup should be a full backup",
+			pg_fatal("backup at \"%s\" is an incremental backup, but the first backup should be a full backup",
 					 backup_dirs[i]);
 		if (i < n_backups - 1 && start_tli != check_tli)
 			pg_fatal("backup at \"%s\" starts on timeline %u, but expected %u",
 					 backup_dirs[i], start_tli, check_tli);
 		if (i < n_backups - 1 && start_lsn != check_lsn)
 			pg_fatal("backup at \"%s\" starts at LSN %X/%X, but expected %X/%X",
-					 backup_dirs[i], LSN_FORMAT_ARGS(start_lsn),
+					 backup_dirs[i],
+					 LSN_FORMAT_ARGS(start_lsn),
 					 LSN_FORMAT_ARGS(check_lsn));
 		check_tli = previous_tli;
 		check_lsn = previous_lsn;
@@ -572,7 +578,8 @@ check_control_files(int n_backups, char **backup_dirs)
 
 		/* Can't interpret control file if not current version. */
 		if (control_file->pg_control_version != PG_CONTROL_VERSION)
-			pg_fatal("%s: unexpected control file version", controlpath);
+			pg_fatal("%s: unexpected control file version",
+					 controlpath);
 
 		/* System identifiers should all match. */
 		if (i == n_backups - 1)
@@ -698,23 +705,16 @@ help(const char *progname)
 	printf(_("\nOptions:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -n, --dry-run             don't actually do anything\n"));
-	printf(_("  -N, --no-sync             do not wait for changes to be written "
-			 "safely to disk\n"));
+	printf(_("  -N, --no-sync             do not wait for changes to be written safely to disk\n"));
 	printf(_("  -o, --output              output directory\n"));
-	printf(_(
-			 "  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
+	printf(_("  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
 			 "                            relocate tablespace in OLDDIR to NEWDIR\n"));
-	printf(
-		   _("      --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
+	printf(_("      --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
 			 "                            use algorithm for manifest checksums\n"));
-	printf(_(
-			 "      --no-manifest         suppress generation of backup manifest\n"));
-	printf(
-		   _("      --sync-method=METHOD  set method for syncing files to disk\n"));
-	printf(_("      --clone               clone (reflink) instead of copying "
-			 "files\n"));
-	printf(
-		   _("      --copy-file-range     copy using copy_file_range() syscall\n"));
+	printf(_("      --no-manifest         suppress generation of backup manifest\n"));
+	printf(_("      --sync-method=METHOD  set method for syncing files to disk\n"));
+	printf(_("      --clone               clone (reflink) instead of copying files\n"));
+	printf(_("      --copy-file-range     copy using copy_file_range() syscall\n"));
 	printf(_("  -?, --help                show this help, then exit\n"));
 
 	printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
@@ -761,10 +761,15 @@ parse_oid(char *s, Oid *result)
  * the locations of those previous backups.
  */
 static void
-process_directory_recursively(
-							  Oid tsoid, char *input_directory, char *output_directory,
-							  char *relative_path, int n_prior_backups, char **prior_backup_dirs,
-							  manifest_data **manifests, manifest_writer *mwriter, cb_options *opt)
+process_directory_recursively(Oid tsoid,
+							  char *input_directory,
+							  char *output_directory,
+							  char *relative_path,
+							  int n_prior_backups,
+							  char **prior_backup_dirs,
+							  manifest_data **manifests,
+							  manifest_writer *mwriter,
+							  cb_options *opt)
 {
 	char		ifulldir[MAXPGPATH];
 	char		ofulldir[MAXPGPATH];
@@ -817,11 +822,13 @@ process_directory_recursively(
 	}
 	else
 	{
-		snprintf(ifulldir, MAXPGPATH, "%s/%s", input_directory, relative_path);
-		snprintf(ofulldir, MAXPGPATH, "%s/%s", output_directory, relative_path);
+		snprintf(ifulldir, MAXPGPATH, "%s/%s", input_directory,
+				 relative_path);
+		snprintf(ofulldir, MAXPGPATH, "%s/%s", output_directory,
+				 relative_path);
 		if (OidIsValid(tsoid))
-			snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/%s/", tsoid,
-					 relative_path);
+			snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/%s/",
+					 tsoid, relative_path);
 		else
 			snprintf(manifest_prefix, MAXPGPATH, "%s/", relative_path);
 	}
@@ -857,7 +864,8 @@ process_directory_recursively(
 		pg_checksum_context checksum_ctx;
 
 		/* Ignore "." and ".." entries. */
-		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+		if (strcmp(de->d_name, ".") == 0 ||
+			strcmp(de->d_name, "..") == 0)
 			continue;
 
 		/* Construct input path. */
@@ -893,9 +901,11 @@ process_directory_recursively(
 						 de->d_name);
 
 			/* And recurse. */
-			process_directory_recursively(tsoid, input_directory, output_directory,
-										  new_relative_path, n_prior_backups,
-										  prior_backup_dirs, manifests, mwriter, opt);
+			process_directory_recursively(tsoid,
+										  input_directory, output_directory,
+										  new_relative_path,
+										  n_prior_backups, prior_backup_dirs,
+										  manifests, mwriter, opt);
 			continue;
 		}
 
@@ -913,37 +923,47 @@ process_directory_recursively(
 		 * Skip the backup_label and backup_manifest files; they require
 		 * special handling and are handled elsewhere.
 		 */
-		if (relative_path == NULL && (strcmp(de->d_name, "backup_label") == 0 ||
-									  strcmp(de->d_name, "backup_manifest") == 0))
+		if (relative_path == NULL &&
+			(strcmp(de->d_name, "backup_label") == 0 ||
+			 strcmp(de->d_name, "backup_manifest") == 0))
 			continue;
 
 		/*
 		 * If it's an incremental file, hand it off to the reconstruction
 		 * code, which will figure out what to do.
 		 */
-		if (strncmp(de->d_name, INCREMENTAL_PREFIX, INCREMENTAL_PREFIX_LENGTH) ==
-			0)
+		if (strncmp(de->d_name, INCREMENTAL_PREFIX,
+					INCREMENTAL_PREFIX_LENGTH) == 0)
 		{
 			/* Output path should not include "INCREMENTAL." prefix. */
 			snprintf(ofullpath, MAXPGPATH, "%s/%s", ofulldir,
 					 de->d_name + INCREMENTAL_PREFIX_LENGTH);
 
+
 			/* Manifest path likewise omits incremental prefix. */
 			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
 					 de->d_name + INCREMENTAL_PREFIX_LENGTH);
 
 			/* Reconstruction logic will do the rest. */
-			reconstruct_from_incremental_file(
-											  ifullpath, ofullpath, relative_path,
-											  de->d_name + INCREMENTAL_PREFIX_LENGTH, n_prior_backups,
-											  prior_backup_dirs, manifests, manifest_path, checksum_type,
-											  &checksum_length, &checksum_payload, opt->debug, opt->dry_run,
+			reconstruct_from_incremental_file(ifullpath, ofullpath,
+											  relative_path,
+											  de->d_name + INCREMENTAL_PREFIX_LENGTH,
+											  n_prior_backups,
+											  prior_backup_dirs,
+											  manifests,
+											  manifest_path,
+											  checksum_type,
+											  &checksum_length,
+											  &checksum_payload,
+											  opt->debug,
+											  opt->dry_run,
 											  opt->copy_method);
 		}
 		else
 		{
 			/* Construct the path that the backup_manifest will use. */
-			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix, de->d_name);
+			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
+					 de->d_name);
 
 			/*
 			 * It's not an incremental file, so we need to copy the entire
@@ -953,11 +973,13 @@ process_directory_recursively(
 			 * backup_manifest for the final input directory, we can save some
 			 * work by reusing that checksum instead of computing a new one.
 			 */
-			if (checksum_type != CHECKSUM_TYPE_NONE && latest_manifest != NULL)
+			if (checksum_type != CHECKSUM_TYPE_NONE &&
+				latest_manifest != NULL)
 			{
 				manifest_file *mfile;
 
-				mfile = manifest_files_lookup(latest_manifest->files, manifest_path);
+				mfile = manifest_files_lookup(latest_manifest->files,
+											  manifest_path);
 				if (mfile == NULL)
 				{
 					char	   *bmpath;
@@ -966,9 +988,10 @@ process_directory_recursively(
 					 * The directory is out of sync with the backup_manifest,
 					 * so emit a warning.
 					 */
-					bmpath = psprintf("%s/%s", input_directory, "backup_manifest");
-					pg_log_warning("\"%s\" contains no entry for \"%s\"", bmpath,
-								   manifest_path);
+					bmpath = psprintf("%s/%s", input_directory,
+									  "backup_manifest");
+					pg_log_warning("\"%s\" contains no entry for \"%s\"",
+								   bmpath, manifest_path);
 					pfree(bmpath);
 				}
 				else if (mfile->checksum_type == checksum_type)
@@ -1001,7 +1024,8 @@ process_directory_recursively(
 			if (checksum_ctx.type != CHECKSUM_TYPE_NONE && !opt->dry_run)
 			{
 				checksum_payload = pg_malloc(PG_CHECKSUM_MAX_LENGTH);
-				checksum_length = pg_checksum_final(&checksum_ctx, checksum_payload);
+				checksum_length = pg_checksum_final(&checksum_ctx,
+													checksum_payload);
 			}
 		}
 
@@ -1027,8 +1051,10 @@ process_directory_recursively(
 				pg_fatal("could not stat file \"%s\": %m", ofullpath);
 
 			/* OK, now do the work. */
-			add_file_to_manifest(mwriter, manifest_path, sb.st_size, sb.st_mtime,
-								 checksum_type, checksum_length, checksum_payload);
+			add_file_to_manifest(mwriter, manifest_path,
+								 sb.st_size, sb.st_mtime,
+								 checksum_type, checksum_length,
+								 checksum_payload);
 		}
 
 		/* Avoid leaking memory. */
@@ -1136,8 +1162,7 @@ reset_directory_cleanup_list(void)
  * final backup in the backup chain.
  */
 static cb_tablespace *
-scan_for_existing_tablespaces(char *pathname,
-							  cb_options *opt)
+scan_for_existing_tablespaces(char *pathname, cb_options *opt)
 {
 	char		pg_tblspc[MAXPGPATH];
 	DIR		   *dir;
@@ -1170,8 +1195,7 @@ scan_for_existing_tablespaces(char *pathname,
 		/* Ignore any file name that doesn't look like a proper OID. */
 		if (!parse_oid(de->d_name, &oid))
 		{
-			pg_log_debug(
-						 "skipping \"%s\" because the filename is not a legal tablespace OID",
+			pg_log_debug("skipping \"%s\" because the filename is not a legal tablespace OID",
 						 tblspcdir);
 			continue;
 		}
@@ -1182,8 +1206,7 @@ scan_for_existing_tablespaces(char *pathname,
 			exit(1);
 		if (type != PGFILETYPE_LNK && type != PGFILETYPE_DIR)
 		{
-			pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor "
-						 "a directory",
+			pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor a directory",
 						 tblspcdir);
 			continue;
 		}
@@ -1203,7 +1226,8 @@ scan_for_existing_tablespaces(char *pathname,
 			/* Read the link target. */
 			link_length = readlink(tblspcdir, link_target, sizeof(link_target));
 			if (link_length < 0)
-				pg_fatal("could not read symbolic link \"%s\": %m", tblspcdir);
+				pg_fatal("could not read symbolic link \"%s\": %m",
+						 tblspcdir);
 			if (link_length >= sizeof(link_target))
 				pg_fatal("symbolic link \"%s\" is too long", tblspcdir);
 			link_target[link_length] = '\0';
@@ -1230,7 +1254,8 @@ scan_for_existing_tablespaces(char *pathname,
 
 			/* Every non-in-place tablespace must be mapped. */
 			if (tsmap == NULL)
-				pg_fatal("tablespace at \"%s\" has no tablespace mapping", link_target);
+				pg_fatal("tablespace at \"%s\" has no tablespace mapping",
+						 link_target);
 		}
 		else
 		{
diff --git a/src/bin/pg_combinebackup/reconstruct.c b/src/bin/pg_combinebackup/reconstruct.c
index 4daff9c77be..c37cceba030 100644
--- a/src/bin/pg_combinebackup/reconstruct.c
+++ b/src/bin/pg_combinebackup/reconstruct.c
@@ -46,16 +46,20 @@ typedef struct rfile
 	off_t		highest_offset_read;
 } rfile;
 
-static void debug_reconstruction(int n_source, rfile **sources, bool dry_run);
+static void debug_reconstruction(int n_source,
+								 rfile **sources,
+								 bool dry_run);
 static unsigned find_reconstructed_block_length(rfile *s);
 static rfile *make_incremental_rfile(char *filename);
 static rfile *make_rfile(char *filename, bool missing_ok);
 static void write_reconstructed_file(char *input_filename,
 									 char *output_filename,
-									 unsigned block_length, rfile **sourcemap,
+									 unsigned block_length,
+									 rfile **sourcemap,
 									 off_t *offsetmap,
 									 pg_checksum_context *checksum_ctx,
-									 bool debug, bool dry_run);
+									 bool debug,
+									 bool dry_run);
 static void read_bytes(rfile *rf, void *buffer, unsigned length);
 
 /*
@@ -74,12 +78,19 @@ static void read_bytes(rfile *rf, void *buffer, unsigned length);
  * an array of pathnames where those backups can be found.
  */
 void
-reconstruct_from_incremental_file(
-								  char *input_filename, char *output_filename, char *relative_path,
-								  char *bare_file_name, int n_prior_backups, char **prior_backup_dirs,
-								  manifest_data **manifests, char *manifest_path,
-								  pg_checksum_type checksum_type, int *checksum_length,
-								  uint8 **checksum_payload, bool debug, bool dry_run,
+reconstruct_from_incremental_file(char *input_filename,
+								  char *output_filename,
+								  char *relative_path,
+								  char *bare_file_name,
+								  int n_prior_backups,
+								  char **prior_backup_dirs,
+								  manifest_data **manifests,
+								  char *manifest_path,
+								  pg_checksum_type checksum_type,
+								  int *checksum_length,
+								  uint8 **checksum_payload,
+								  bool debug,
+								  bool dry_run,
 								  CopyFileMethod copy_method)
 {
 	rfile	  **source;
@@ -157,8 +168,8 @@ reconstruct_from_incremental_file(
 		 * Look for the full file in the previous backup. If not found, then
 		 * look for an incremental file instead.
 		 */
-		snprintf(source_filename, MAXPGPATH, "%s/%s/%s", prior_backup_dirs[sidx],
-				 relative_path, bare_file_name);
+		snprintf(source_filename, MAXPGPATH, "%s/%s/%s",
+				 prior_backup_dirs[sidx], relative_path, bare_file_name);
 		if ((s = make_rfile(source_filename, true)) == NULL)
 		{
 			snprintf(source_filename, MAXPGPATH, "%s/%s/INCREMENTAL.%s",
@@ -221,7 +232,8 @@ reconstruct_from_incremental_file(
 			{
 				uint64		expected_length;
 
-				expected_length = (uint64) latest_source->truncation_block_length;
+				expected_length =
+					(uint64) latest_source->truncation_block_length;
 				expected_length *= BLCKSZ;
 				if (expected_length == sb.st_size)
 				{
@@ -242,7 +254,8 @@ reconstruct_from_incremental_file(
 		{
 			BlockNumber b = s->relative_block_numbers[i];
 
-			if (b < latest_source->truncation_block_length && sourcemap[b] == NULL)
+			if (b < latest_source->truncation_block_length &&
+				sourcemap[b] == NULL)
 			{
 				sourcemap[b] = s;
 				offsetmap[b] = s->header_length + (i * BLCKSZ);
@@ -271,16 +284,16 @@ reconstruct_from_incremental_file(
 									  manifest_path);
 		if (mfile == NULL)
 		{
-			char	   *path =
-				psprintf("%s/backup_manifest", prior_backup_dirs[copy_source_index]);
+			char	   *path = psprintf("%s/backup_manifest",
+										prior_backup_dirs[copy_source_index]);
 
 			/*
 			 * The directory is out of sync with the backup_manifest, so emit
 			 * a warning.
 			 */
-			/*- translator: the first %s is a backup manifest file, the second is a
-	         * file absent therein */
-			pg_log_warning("\"%s\" contains no entry for \"%s\"", path,
+			/*- translator: the first %s is a backup manifest file, the second is a file absent therein */
+			pg_log_warning("\"%s\" contains no entry for \"%s\"",
+						   path,
 						   manifest_path);
 			pfree(path);
 		}
@@ -288,7 +301,8 @@ reconstruct_from_incremental_file(
 		{
 			*checksum_length = mfile->checksum_length;
 			*checksum_payload = pg_malloc(*checksum_length);
-			memcpy(*checksum_payload, mfile->checksum_payload, *checksum_length);
+			memcpy(*checksum_payload, mfile->checksum_payload,
+				   *checksum_length);
 			checksum_type = CHECKSUM_TYPE_NONE;
 		}
 	}
@@ -305,13 +319,13 @@ reconstruct_from_incremental_file(
 	 * Otherwise, reconstruct.
 	 */
 	if (copy_source != NULL)
-		copy_file(copy_source->filename, output_filename, &checksum_ctx, dry_run,
-				  copy_method);
+		copy_file(copy_source->filename, output_filename,
+				  &checksum_ctx, dry_run, copy_method);
 	else
 	{
-		write_reconstructed_file(input_filename, output_filename, block_length,
-								 sourcemap, offsetmap, &checksum_ctx, debug,
-								 dry_run);
+		write_reconstructed_file(input_filename, output_filename,
+								 block_length, sourcemap, offsetmap,
+								 &checksum_ctx, debug, dry_run);
 		debug_reconstruction(n_prior_backups + 1, source, dry_run);
 	}
 
@@ -319,7 +333,8 @@ reconstruct_from_incremental_file(
 	if (checksum_type != CHECKSUM_TYPE_NONE)
 	{
 		*checksum_payload = pg_malloc(PG_CHECKSUM_MAX_LENGTH);
-		*checksum_length = pg_checksum_final(&checksum_ctx, *checksum_payload);
+		*checksum_length = pg_checksum_final(&checksum_ctx,
+											 *checksum_payload);
 	}
 
 	/*
@@ -364,11 +379,11 @@ debug_reconstruction(int n_source, rfile **sources, bool dry_run)
 
 		/* Debug logging. */
 		if (dry_run)
-			pg_log_debug("would have read %u blocks from \"%s\"", s->num_blocks_read,
-						 s->filename);
+			pg_log_debug("would have read %u blocks from \"%s\"",
+						 s->num_blocks_read, s->filename);
 		else
-			pg_log_debug("read %u blocks from \"%s\"", s->num_blocks_read,
-						 s->filename);
+			pg_log_debug("read %u blocks from \"%s\"",
+						 s->num_blocks_read, s->filename);
 
 		/*
 		 * In dry-run mode, we don't actually try to read data from the file,
@@ -387,7 +402,8 @@ debug_reconstruction(int n_source, rfile **sources, bool dry_run)
 				pg_fatal("could not stat \"%s\": %m", s->filename);
 			if (sb.st_size < s->highest_offset_read)
 				pg_fatal("file \"%s\" is too short: expected %llu, found %llu",
-						 s->filename, (unsigned long long) s->highest_offset_read,
+						 s->filename,
+						 (unsigned long long) s->highest_offset_read,
 						 (unsigned long long) sb.st_size);
 		}
 	}
@@ -440,8 +456,7 @@ make_incremental_rfile(char *filename)
 	read_bytes(rf, &rf->truncation_block_length,
 			   sizeof(rf->truncation_block_length));
 	if (rf->truncation_block_length > RELSEG_SIZE)
-		pg_fatal("file \"%s\" has truncation block length %u in excess of segment "
-				 "size %u",
+		pg_fatal("file \"%s\" has truncation block length %u in excess of segment size %u",
 				 filename, rf->truncation_block_length, RELSEG_SIZE);
 
 	/* Read block numbers if there are any. */
@@ -508,10 +523,12 @@ read_bytes(rfile *rf, void *buffer, unsigned length)
 static void
 write_reconstructed_file(char *input_filename,
 						 char *output_filename,
-						 unsigned block_length, rfile **sourcemap,
+						 unsigned block_length,
+						 rfile **sourcemap,
 						 off_t *offsetmap,
 						 pg_checksum_context *checksum_ctx,
-						 bool debug, bool dry_run)
+						 bool debug,
+						 bool dry_run)
 {
 	int			wfd = -1;
 	unsigned	i;
@@ -554,8 +571,8 @@ write_reconstructed_file(char *input_filename,
 				if (current_block == start_of_range)
 					appendStringInfo(&debug_buf, " %u:zero", current_block);
 				else
-					appendStringInfo(&debug_buf, " %u-%u:zero", start_of_range,
-									 current_block);
+					appendStringInfo(&debug_buf, " %u-%u:zero",
+									 start_of_range, current_block);
 			}
 			else
 			{
@@ -587,7 +604,8 @@ write_reconstructed_file(char *input_filename,
 
 	/* Open the output file, except in dry_run mode. */
 	if (!dry_run &&
-		(wfd = open(output_filename, O_RDWR | PG_BINARY | O_CREAT | O_EXCL,
+		(wfd = open(output_filename,
+					O_RDWR | PG_BINARY | O_CREAT | O_EXCL,
 					pg_file_create_mode)) < 0)
 		pg_fatal("could not open file \"%s\": %m", output_filename);
 
@@ -604,8 +622,8 @@ write_reconstructed_file(char *input_filename,
 		else
 		{
 			s->num_blocks_read++;
-			s->highest_offset_read =
-				Max(s->highest_offset_read, offsetmap[i] + BLCKSZ);
+			s->highest_offset_read = Max(s->highest_offset_read,
+										 offsetmap[i] + BLCKSZ);
 		}
 
 		/* Skip the rest of this in dry-run mode. */
@@ -632,9 +650,9 @@ write_reconstructed_file(char *input_filename,
 				if (rb < 0)
 					pg_fatal("could not read file \"%s\": %m", s->filename);
 				else
-					pg_fatal("could not read file \"%s\": read only %d of %d bytes at "
-							 "offset %llu",
-							 s->filename, rb, BLCKSZ, (unsigned long long) offsetmap[i]);
+					pg_fatal("could not read file \"%s\": read only %d of %d bytes at offset %llu",
+							 s->filename, rb, BLCKSZ,
+							 (unsigned long long) offsetmap[i]);
 			}
 		}
 
@@ -650,7 +668,8 @@ write_reconstructed_file(char *input_filename,
 
 		/* Update the checksum computation. */
 		if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
-			pg_fatal("could not update checksum of file \"%s\"", output_filename);
+			pg_fatal("could not update checksum of file \"%s\"",
+					 output_filename);
 	}
 
 	/* Debugging output. */
diff --git a/src/bin/pg_combinebackup/reconstruct.h b/src/bin/pg_combinebackup/reconstruct.h
index 1fa734011bd..8d19dbf7e50 100644
--- a/src/bin/pg_combinebackup/reconstruct.h
+++ b/src/bin/pg_combinebackup/reconstruct.h
@@ -18,12 +18,19 @@
 #include "common/file_utils.h"
 #include "load_manifest.h"
 
-extern void reconstruct_from_incremental_file(
-											  char *input_filename, char *output_filename, char *relative_path,
-											  char *bare_file_name, int n_prior_backups, char **prior_backup_dirs,
-											  manifest_data **manifests, char *manifest_path,
-											  pg_checksum_type checksum_type, int *checksum_length,
-											  uint8 **checksum_payload, bool debug, bool dry_run,
+extern void reconstruct_from_incremental_file(char *input_filename,
+											  char *output_filename,
+											  char *relative_path,
+											  char *bare_file_name,
+											  int n_prior_backups,
+											  char **prior_backup_dirs,
+											  manifest_data **manifests,
+											  char *manifest_path,
+											  pg_checksum_type checksum_type,
+											  int *checksum_length,
+											  uint8 **checksum_payload,
+											  bool debug,
+											  bool dry_run,
 											  CopyFileMethod copy_method);
 
 #endif
-- 
2.44.0

From b1183fbae8ed0123d7385a8501f1a843f0d9aa85 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Tue, 19 Mar 2024 15:16:29 +0100
Subject: [PATCH v20240319 1/2] rebased patch

---
 src/bin/pg_combinebackup/copy_file.c        | 227 +++++++++++++-------
 src/bin/pg_combinebackup/copy_file.h        |  14 +-
 src/bin/pg_combinebackup/pg_combinebackup.c | 220 ++++++++++---------
 src/bin/pg_combinebackup/reconstruct.c      | 106 ++++-----
 src/bin/pg_combinebackup/reconstruct.h      |  22 +-
 5 files changed, 328 insertions(+), 261 deletions(-)

diff --git a/src/bin/pg_combinebackup/copy_file.c b/src/bin/pg_combinebackup/copy_file.c
index e6d2423278a..16e26b4f573 100644
--- a/src/bin/pg_combinebackup/copy_file.c
+++ b/src/bin/pg_combinebackup/copy_file.c
@@ -10,19 +10,21 @@
  */
 #include "postgres_fe.h"
 
-#ifdef HAVE_COPYFILE_H
-#include <copyfile.h>
-#endif
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "common/file_perm.h"
+#include "common/file_utils.h"
 #include "common/logging.h"
 #include "copy_file.h"
 
-static void copy_file_blocks(const char *src, const char *dst,
-							 pg_checksum_context *checksum_ctx);
+static void pg_copyfile(const char *src, const char *dest, const char *addon_errmsg,
+						pg_checksum_context *ctx);
+
+static void pg_copyfile_offload(const char *src, const char *dest,
+								const char *addon_errmsg, CopyFileMethod flags);
 
 #ifdef WIN32
 static void copy_file_copyfile(const char *src, const char *dst);
@@ -35,7 +37,8 @@ static void copy_file_copyfile(const char *src, const char *dst);
  */
 void
 copy_file(const char *src, const char *dst,
-		  pg_checksum_context *checksum_ctx, bool dry_run)
+		  pg_checksum_context *checksum_ctx, bool dry_run,
+		  CopyFileMethod copy_strategy)
 {
 	/*
 	 * In dry-run mode, we don't actually copy anything, nor do we read any
@@ -49,6 +52,8 @@ copy_file(const char *src, const char *dst,
 			pg_fatal("could not open \"%s\": %m", src);
 		if (close(fd) < 0)
 			pg_fatal("could not close \"%s\": %m", src);
+
+		return;
 	}
 
 	/*
@@ -56,104 +61,180 @@ copy_file(const char *src, const char *dst,
 	 * operating system primitives that we know about to copy the file; this
 	 * may be quicker than a naive block copy.
 	 */
-	if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
-	{
-		char	   *strategy_name = NULL;
-		void		(*strategy_implementation) (const char *, const char *) = NULL;
+	if (checksum_ctx->type == CHECKSUM_TYPE_NONE && copy_strategy != 0)
+		pg_copyfile_offload(src, dst, NULL, copy_strategy);
+	else
+		pg_copyfile(src, dst, NULL, checksum_ctx);
+}
 
-#ifdef WIN32
-		strategy_name = "CopyFile";
-		strategy_implementation = copy_file_copyfile;
-#endif
+/* Helper function to optionally prepend error string */
+static inline char *
+opt_errinfo(const char *addon_errmsg)
+{
+	char		buf[128];
 
-		if (strategy_name != NULL)
-		{
-			if (dry_run)
-				pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
-							 src, dst, strategy_name);
-			else
-			{
-				pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
-							 src, dst, strategy_name);
-				(*strategy_implementation) (src, dst);
-			}
-			return;
-		}
-	}
+	if (addon_errmsg == NULL)
+		return "";
 
-	/*
-	 * Fall back to the simple approach of reading and writing all the blocks,
-	 * feeding them into the checksum context as we go.
-	 */
-	if (dry_run)
-	{
-		if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
-			pg_log_debug("would copy \"%s\" to \"%s\"",
-						 src, dst);
-		else
-			pg_log_debug("would copy \"%s\" to \"%s\" and checksum with %s",
-						 src, dst, pg_checksum_type_name(checksum_ctx->type));
-	}
-	else
-	{
-		if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
-			pg_log_debug("copying \"%s\" to \"%s\"",
-						 src, dst);
-		else
-			pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
-						 src, dst, pg_checksum_type_name(checksum_ctx->type));
-		copy_file_blocks(src, dst, checksum_ctx);
-	}
+	strcpy(buf, " ");
+	return strncat(buf, addon_errmsg, sizeof(buf) - 2);
 }
 
 /*
- * Copy a file block by block, and optionally compute a checksum as we go.
+ * Copies a relation file from src to dest. addon_errmsg is an optional
+ * addon error message (can be NULL or include schema/relName)
  */
 static void
-copy_file_blocks(const char *src, const char *dst,
-				 pg_checksum_context *checksum_ctx)
+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;
-	ssize_t		rb;
-	unsigned	offset = 0;
 
 	if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("could not open file \"%s\": %m", src);
+		pg_fatal("error while copying%s: could not open file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), src, strerror(errno));
 
-	if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
+	if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
 						pg_file_create_mode)) < 0)
-		pg_fatal("could not open file \"%s\": %m", dst);
+		pg_fatal("error while copying%s: could not create file \"%s\": %s",
+				 opt_errinfo(addon_errmsg), dest, strerror(errno));
 
 	buffer = pg_malloc(buffer_size);
 
-	while ((rb = read(src_fd, buffer, buffer_size)) > 0)
+	/* perform data copying i.e read src source, write to destination */
+	while (true)
 	{
-		ssize_t		wb;
+		ssize_t		nbytes = read(src_fd, buffer, buffer_size);
 
-		if ((wb = write(dest_fd, buffer, rb)) != rb)
+		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 (wb < 0)
-				pg_fatal("could not write file \"%s\": %m", dst);
-			else
-				pg_fatal("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
-						 dst, (int) wb, (int) rb, offset);
+			/*
+			 * 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(checksum_ctx, buffer, rb) < 0)
-			pg_fatal("could not update checksum of file \"%s\"", dst);
+		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
+ */
+static void
+pg_copyfile_offload(const char *src, const char *dest,
+					const char *addon_errmsg, CopyFileMethod flags)
+{
 
-		offset += rb;
+#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 (rb < 0)
-		pg_fatal("could not read file \"%s\": %m", dst);
+	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
+	}
 
-	pg_free(buffer);
 	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
 }
 
 #ifdef WIN32
diff --git a/src/bin/pg_combinebackup/copy_file.h b/src/bin/pg_combinebackup/copy_file.h
index 0f6bc09403f..2797a340055 100644
--- a/src/bin/pg_combinebackup/copy_file.h
+++ b/src/bin/pg_combinebackup/copy_file.h
@@ -11,9 +11,21 @@
 #ifndef COPY_FILE_H
 #define COPY_FILE_H
 
+#include "c.h"
 #include "common/checksum_helper.h"
+#include "common/file_utils.h"
+
+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
 
 extern void copy_file(const char *src, const char *dst,
-					  pg_checksum_context *checksum_ctx, bool dry_run);
+					  pg_checksum_context *checksum_ctx, bool dry_run,
+					  CopyFileMethod copy_strategy);
 
 #endif							/* COPY_FILE_H */
diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index 74f8be9eeac..1455360d81c 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -69,6 +69,7 @@ typedef struct cb_options
 	pg_checksum_type manifest_checksums;
 	bool		no_manifest;
 	DataDirSyncMethod sync_method;
+	CopyFileMethod copy_method;
 } cb_options;
 
 /*
@@ -98,15 +99,10 @@ static void cleanup_directories_atexit(void);
 static void create_output_directory(char *dirname, cb_options *opt);
 static void help(const char *progname);
 static bool parse_oid(char *s, Oid *result);
-static void process_directory_recursively(Oid tsoid,
-										  char *input_directory,
-										  char *output_directory,
-										  char *relative_path,
-										  int n_prior_backups,
-										  char **prior_backup_dirs,
-										  manifest_data **manifests,
-										  manifest_writer *mwriter,
-										  cb_options *opt);
+static void process_directory_recursively(
+										  Oid tsoid, char *input_directory, char *output_directory,
+										  char *relative_path, int n_prior_backups, char **prior_backup_dirs,
+										  manifest_data **manifests, manifest_writer *mwriter, cb_options *opt);
 static int	read_pg_version_file(char *directory);
 static void remember_to_cleanup_directory(char *target_path, bool rmtopdir);
 static void reset_directory_cleanup_list(void);
@@ -129,8 +125,9 @@ main(int argc, char *argv[])
 		{"manifest-checksums", required_argument, NULL, 1},
 		{"no-manifest", no_argument, NULL, 2},
 		{"sync-method", required_argument, NULL, 3},
-		{NULL, 0, NULL, 0}
-	};
+		{"clone", no_argument, NULL, 4},
+		{"copy-file-range", no_argument, NULL, 5},
+	{NULL, 0, NULL, 0}};
 
 	const char *progname;
 	char	   *last_input_dir;
@@ -156,10 +153,11 @@ main(int argc, char *argv[])
 	memset(&opt, 0, sizeof(opt));
 	opt.manifest_checksums = CHECKSUM_TYPE_CRC32C;
 	opt.sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
+	opt.copy_method = 0;
 
 	/* process command-line options */
-	while ((c = getopt_long(argc, argv, "dnNPo:T:",
-							long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "dnNPo:T:", long_options, &optindex)) !=
+		   -1)
 	{
 		switch (c)
 		{
@@ -180,10 +178,8 @@ main(int argc, char *argv[])
 				add_tablespace_mapping(&opt, optarg);
 				break;
 			case 1:
-				if (!pg_checksum_parse_type(optarg,
-											&opt.manifest_checksums))
-					pg_fatal("unrecognized checksum algorithm: \"%s\"",
-							 optarg);
+				if (!pg_checksum_parse_type(optarg, &opt.manifest_checksums))
+					pg_fatal("unrecognized checksum algorithm: \"%s\"", optarg);
 				break;
 			case 2:
 				opt.no_manifest = true;
@@ -192,6 +188,12 @@ main(int argc, char *argv[])
 				if (!parse_sync_method(optarg, &opt.sync_method))
 					exit(1);
 				break;
+			case 4:
+				opt.copy_method = PG_COPYFILE_IOCTL_FICLONE;
+				break;
+			case 5:
+				opt.copy_method = PG_COPYFILE_COPY_FILE_RANGE;
+				break;
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -213,6 +215,14 @@ main(int argc, char *argv[])
 	if (opt.no_manifest)
 		opt.manifest_checksums = CHECKSUM_TYPE_NONE;
 
+	/*
+	 * We cannot provide file copy/clone offload in case when we need to
+	 * calculate checksums
+	 */
+	if (opt.copy_method != 0 && opt.manifest_checksums != CHECKSUM_TYPE_NONE)
+		pg_fatal("unable to use accelerated copy when manifest checksums "
+				 "are to be calculated. Use --no-manifest");
+
 	/* Read the server version from the final backup. */
 	version = read_pg_version_file(argv[argc - 1]);
 
@@ -285,7 +295,8 @@ main(int argc, char *argv[])
 		 * won't have the WAL ranges for the resulting manifest.
 		 */
 		if (manifests[n_prior_backups] == NULL)
-			pg_fatal("can't generate a manifest because no manifest is available for the final input backup");
+			pg_fatal("can't generate a manifest because no manifest is available for "
+					 "the final input backup");
 	}
 	else
 		mwriter = NULL;
@@ -297,15 +308,15 @@ main(int argc, char *argv[])
 	{
 		pg_log_debug("generating \"%s/backup_label\"", opt.output);
 		last_backup_label->cursor = 0;
-		write_backup_label(opt.output, last_backup_label,
-						   opt.manifest_checksums, mwriter);
+		write_backup_label(opt.output, last_backup_label, opt.manifest_checksums,
+						   mwriter);
 	}
 
 	/* Process everything that's not part of a user-defined tablespace. */
 	pg_log_debug("processing backup directory \"%s\"", last_input_dir);
-	process_directory_recursively(InvalidOid, last_input_dir, opt.output,
-								  NULL, n_prior_backups, prior_backup_dirs,
-								  manifests, mwriter, &opt);
+	process_directory_recursively(InvalidOid, last_input_dir, opt.output, NULL,
+								  n_prior_backups, prior_backup_dirs, manifests,
+								  mwriter, &opt);
 
 	/* Process user-defined tablespaces. */
 	for (ts = tablespaces; ts != NULL; ts = ts->next)
@@ -321,16 +332,15 @@ main(int argc, char *argv[])
 		{
 			char		linkpath[MAXPGPATH];
 
-			snprintf(linkpath, MAXPGPATH, "%s/pg_tblspc/%u", opt.output,
-					 ts->oid);
+			snprintf(linkpath, MAXPGPATH, "%s/pg_tblspc/%u", opt.output, ts->oid);
 
 			if (opt.dry_run)
 				pg_log_debug("would create symbolic link from \"%s\" to \"%s\"",
 							 linkpath, ts->new_dir);
 			else
 			{
-				pg_log_debug("creating symbolic link from \"%s\" to \"%s\"",
-							 linkpath, ts->new_dir);
+				pg_log_debug("creating symbolic link from \"%s\" to \"%s\"", linkpath,
+							 ts->new_dir);
 				if (symlink(ts->new_dir, linkpath) != 0)
 					pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
 							 linkpath, ts->new_dir);
@@ -344,21 +354,19 @@ main(int argc, char *argv[])
 			{
 				pg_log_debug("creating directory \"%s\"", ts->new_dir);
 				if (pg_mkdir_p(ts->new_dir, pg_dir_create_mode) == -1)
-					pg_fatal("could not create directory \"%s\": %m",
-							 ts->new_dir);
+					pg_fatal("could not create directory \"%s\": %m", ts->new_dir);
 			}
 		}
 
 		/* OK, now handle the directory contents. */
-		process_directory_recursively(ts->oid, ts->old_dir, ts->new_dir,
-									  NULL, n_prior_backups, prior_backup_dirs,
-									  manifests, mwriter, &opt);
+		process_directory_recursively(ts->oid, ts->old_dir, ts->new_dir, NULL,
+									  n_prior_backups, prior_backup_dirs, manifests,
+									  mwriter, &opt);
 	}
 
 	/* Finalize the backup_manifest, if we're generating one. */
 	if (mwriter != NULL)
-		finalize_manifest(mwriter,
-						  manifests[n_prior_backups]->first_wal_range);
+		finalize_manifest(mwriter, manifests[n_prior_backups]->first_wal_range);
 
 	/* fsync that output directory unless we've been told not to do so */
 	if (!opt.no_sync)
@@ -414,7 +422,9 @@ add_tablespace_mapping(cb_options *opt, char *arg)
 			*dst_ptr++ = *arg_ptr;
 	}
 	if (!tsmap->old_dir[0] || !tsmap->new_dir[0])
-		pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
+		pg_fatal(
+				 "invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"",
+				 arg);
 
 	/*
 	 * All tablespaces are created with absolute directories, so specifying a
@@ -486,8 +496,8 @@ check_backup_label_files(int n_backups, char **backup_dirs)
 			pg_fatal("could not close \"%s\": %m", pathbuf);
 
 		/* Parse the file contents. */
-		parse_backup_label(pathbuf, buf, &start_tli, &start_lsn,
-						   &previous_tli, &previous_lsn);
+		parse_backup_label(pathbuf, buf, &start_tli, &start_lsn, &previous_tli,
+						   &previous_lsn);
 
 		/*
 		 * Sanity checks.
@@ -498,18 +508,19 @@ check_backup_label_files(int n_backups, char **backup_dirs)
 		 * we don't have that information.
 		 */
 		if (i > 0 && previous_tli == 0)
-			pg_fatal("backup at \"%s\" is a full backup, but only the first backup should be a full backup",
+			pg_fatal("backup at \"%s\" is a full backup, but only the first backup "
+					 "should be a full backup",
 					 backup_dirs[i]);
 		if (i == 0 && previous_tli != 0)
-			pg_fatal("backup at \"%s\" is an incremental backup, but the first backup should be a full backup",
+			pg_fatal("backup at \"%s\" is an incremental backup, but the first "
+					 "backup should be a full backup",
 					 backup_dirs[i]);
 		if (i < n_backups - 1 && start_tli != check_tli)
 			pg_fatal("backup at \"%s\" starts on timeline %u, but expected %u",
 					 backup_dirs[i], start_tli, check_tli);
 		if (i < n_backups - 1 && start_lsn != check_lsn)
 			pg_fatal("backup at \"%s\" starts at LSN %X/%X, but expected %X/%X",
-					 backup_dirs[i],
-					 LSN_FORMAT_ARGS(start_lsn),
+					 backup_dirs[i], LSN_FORMAT_ARGS(start_lsn),
 					 LSN_FORMAT_ARGS(check_lsn));
 		check_tli = previous_tli;
 		check_lsn = previous_lsn;
@@ -561,8 +572,7 @@ check_control_files(int n_backups, char **backup_dirs)
 
 		/* Can't interpret control file if not current version. */
 		if (control_file->pg_control_version != PG_CONTROL_VERSION)
-			pg_fatal("%s: unexpected control file version",
-					 controlpath);
+			pg_fatal("%s: unexpected control file version", controlpath);
 
 		/* System identifiers should all match. */
 		if (i == n_backups - 1)
@@ -688,14 +698,23 @@ help(const char *progname)
 	printf(_("\nOptions:\n"));
 	printf(_("  -d, --debug               generate lots of debugging output\n"));
 	printf(_("  -n, --dry-run             don't actually do anything\n"));
-	printf(_("  -N, --no-sync             do not wait for changes to be written safely to disk\n"));
+	printf(_("  -N, --no-sync             do not wait for changes to be written "
+			 "safely to disk\n"));
 	printf(_("  -o, --output              output directory\n"));
-	printf(_("  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
+	printf(_(
+			 "  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
 			 "                            relocate tablespace in OLDDIR to NEWDIR\n"));
-	printf(_("      --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
+	printf(
+		   _("      --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
 			 "                            use algorithm for manifest checksums\n"));
-	printf(_("      --no-manifest         suppress generation of backup manifest\n"));
-	printf(_("      --sync-method=METHOD  set method for syncing files to disk\n"));
+	printf(_(
+			 "      --no-manifest         suppress generation of backup manifest\n"));
+	printf(
+		   _("      --sync-method=METHOD  set method for syncing files to disk\n"));
+	printf(_("      --clone               clone (reflink) instead of copying "
+			 "files\n"));
+	printf(
+		   _("      --copy-file-range     copy using copy_file_range() syscall\n"));
 	printf(_("  -?, --help                show this help, then exit\n"));
 
 	printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
@@ -742,15 +761,10 @@ parse_oid(char *s, Oid *result)
  * the locations of those previous backups.
  */
 static void
-process_directory_recursively(Oid tsoid,
-							  char *input_directory,
-							  char *output_directory,
-							  char *relative_path,
-							  int n_prior_backups,
-							  char **prior_backup_dirs,
-							  manifest_data **manifests,
-							  manifest_writer *mwriter,
-							  cb_options *opt)
+process_directory_recursively(
+							  Oid tsoid, char *input_directory, char *output_directory,
+							  char *relative_path, int n_prior_backups, char **prior_backup_dirs,
+							  manifest_data **manifests, manifest_writer *mwriter, cb_options *opt)
 {
 	char		ifulldir[MAXPGPATH];
 	char		ofulldir[MAXPGPATH];
@@ -803,13 +817,11 @@ process_directory_recursively(Oid tsoid,
 	}
 	else
 	{
-		snprintf(ifulldir, MAXPGPATH, "%s/%s", input_directory,
-				 relative_path);
-		snprintf(ofulldir, MAXPGPATH, "%s/%s", output_directory,
-				 relative_path);
+		snprintf(ifulldir, MAXPGPATH, "%s/%s", input_directory, relative_path);
+		snprintf(ofulldir, MAXPGPATH, "%s/%s", output_directory, relative_path);
 		if (OidIsValid(tsoid))
-			snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/%s/",
-					 tsoid, relative_path);
+			snprintf(manifest_prefix, MAXPGPATH, "pg_tblspc/%u/%s/", tsoid,
+					 relative_path);
 		else
 			snprintf(manifest_prefix, MAXPGPATH, "%s/", relative_path);
 	}
@@ -845,8 +857,7 @@ process_directory_recursively(Oid tsoid,
 		pg_checksum_context checksum_ctx;
 
 		/* Ignore "." and ".." entries. */
-		if (strcmp(de->d_name, ".") == 0 ||
-			strcmp(de->d_name, "..") == 0)
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
 			continue;
 
 		/* Construct input path. */
@@ -882,11 +893,9 @@ process_directory_recursively(Oid tsoid,
 						 de->d_name);
 
 			/* And recurse. */
-			process_directory_recursively(tsoid,
-										  input_directory, output_directory,
-										  new_relative_path,
-										  n_prior_backups, prior_backup_dirs,
-										  manifests, mwriter, opt);
+			process_directory_recursively(tsoid, input_directory, output_directory,
+										  new_relative_path, n_prior_backups,
+										  prior_backup_dirs, manifests, mwriter, opt);
 			continue;
 		}
 
@@ -904,46 +913,37 @@ process_directory_recursively(Oid tsoid,
 		 * Skip the backup_label and backup_manifest files; they require
 		 * special handling and are handled elsewhere.
 		 */
-		if (relative_path == NULL &&
-			(strcmp(de->d_name, "backup_label") == 0 ||
-			 strcmp(de->d_name, "backup_manifest") == 0))
+		if (relative_path == NULL && (strcmp(de->d_name, "backup_label") == 0 ||
+									  strcmp(de->d_name, "backup_manifest") == 0))
 			continue;
 
 		/*
 		 * If it's an incremental file, hand it off to the reconstruction
 		 * code, which will figure out what to do.
 		 */
-		if (strncmp(de->d_name, INCREMENTAL_PREFIX,
-					INCREMENTAL_PREFIX_LENGTH) == 0)
+		if (strncmp(de->d_name, INCREMENTAL_PREFIX, INCREMENTAL_PREFIX_LENGTH) ==
+			0)
 		{
 			/* Output path should not include "INCREMENTAL." prefix. */
 			snprintf(ofullpath, MAXPGPATH, "%s/%s", ofulldir,
 					 de->d_name + INCREMENTAL_PREFIX_LENGTH);
 
-
 			/* Manifest path likewise omits incremental prefix. */
 			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
 					 de->d_name + INCREMENTAL_PREFIX_LENGTH);
 
 			/* Reconstruction logic will do the rest. */
-			reconstruct_from_incremental_file(ifullpath, ofullpath,
-											  relative_path,
-											  de->d_name + INCREMENTAL_PREFIX_LENGTH,
-											  n_prior_backups,
-											  prior_backup_dirs,
-											  manifests,
-											  manifest_path,
-											  checksum_type,
-											  &checksum_length,
-											  &checksum_payload,
-											  opt->debug,
-											  opt->dry_run);
+			reconstruct_from_incremental_file(
+											  ifullpath, ofullpath, relative_path,
+											  de->d_name + INCREMENTAL_PREFIX_LENGTH, n_prior_backups,
+											  prior_backup_dirs, manifests, manifest_path, checksum_type,
+											  &checksum_length, &checksum_payload, opt->debug, opt->dry_run,
+											  opt->copy_method);
 		}
 		else
 		{
 			/* Construct the path that the backup_manifest will use. */
-			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix,
-					 de->d_name);
+			snprintf(manifest_path, MAXPGPATH, "%s%s", manifest_prefix, de->d_name);
 
 			/*
 			 * It's not an incremental file, so we need to copy the entire
@@ -953,13 +953,11 @@ process_directory_recursively(Oid tsoid,
 			 * backup_manifest for the final input directory, we can save some
 			 * work by reusing that checksum instead of computing a new one.
 			 */
-			if (checksum_type != CHECKSUM_TYPE_NONE &&
-				latest_manifest != NULL)
+			if (checksum_type != CHECKSUM_TYPE_NONE && latest_manifest != NULL)
 			{
 				manifest_file *mfile;
 
-				mfile = manifest_files_lookup(latest_manifest->files,
-											  manifest_path);
+				mfile = manifest_files_lookup(latest_manifest->files, manifest_path);
 				if (mfile == NULL)
 				{
 					char	   *bmpath;
@@ -968,10 +966,9 @@ process_directory_recursively(Oid tsoid,
 					 * The directory is out of sync with the backup_manifest,
 					 * so emit a warning.
 					 */
-					bmpath = psprintf("%s/%s", input_directory,
-									  "backup_manifest");
-					pg_log_warning("\"%s\" contains no entry for \"%s\"",
-								   bmpath, manifest_path);
+					bmpath = psprintf("%s/%s", input_directory, "backup_manifest");
+					pg_log_warning("\"%s\" contains no entry for \"%s\"", bmpath,
+								   manifest_path);
 					pfree(bmpath);
 				}
 				else if (mfile->checksum_type == checksum_type)
@@ -993,7 +990,8 @@ process_directory_recursively(Oid tsoid,
 
 			/* Actually copy the file. */
 			snprintf(ofullpath, MAXPGPATH, "%s/%s", ofulldir, de->d_name);
-			copy_file(ifullpath, ofullpath, &checksum_ctx, opt->dry_run);
+			copy_file(ifullpath, ofullpath, &checksum_ctx, opt->dry_run,
+					  opt->copy_method);
 
 			/*
 			 * If copy_file() performed a checksum calculation for us, then
@@ -1003,8 +1001,7 @@ process_directory_recursively(Oid tsoid,
 			if (checksum_ctx.type != CHECKSUM_TYPE_NONE && !opt->dry_run)
 			{
 				checksum_payload = pg_malloc(PG_CHECKSUM_MAX_LENGTH);
-				checksum_length = pg_checksum_final(&checksum_ctx,
-													checksum_payload);
+				checksum_length = pg_checksum_final(&checksum_ctx, checksum_payload);
 			}
 		}
 
@@ -1030,10 +1027,8 @@ process_directory_recursively(Oid tsoid,
 				pg_fatal("could not stat file \"%s\": %m", ofullpath);
 
 			/* OK, now do the work. */
-			add_file_to_manifest(mwriter, manifest_path,
-								 sb.st_size, sb.st_mtime,
-								 checksum_type, checksum_length,
-								 checksum_payload);
+			add_file_to_manifest(mwriter, manifest_path, sb.st_size, sb.st_mtime,
+								 checksum_type, checksum_length, checksum_payload);
 		}
 
 		/* Avoid leaking memory. */
@@ -1141,7 +1136,8 @@ reset_directory_cleanup_list(void)
  * final backup in the backup chain.
  */
 static cb_tablespace *
-scan_for_existing_tablespaces(char *pathname, cb_options *opt)
+scan_for_existing_tablespaces(char *pathname,
+							  cb_options *opt)
 {
 	char		pg_tblspc[MAXPGPATH];
 	DIR		   *dir;
@@ -1174,7 +1170,8 @@ scan_for_existing_tablespaces(char *pathname, cb_options *opt)
 		/* Ignore any file name that doesn't look like a proper OID. */
 		if (!parse_oid(de->d_name, &oid))
 		{
-			pg_log_debug("skipping \"%s\" because the filename is not a legal tablespace OID",
+			pg_log_debug(
+						 "skipping \"%s\" because the filename is not a legal tablespace OID",
 						 tblspcdir);
 			continue;
 		}
@@ -1185,7 +1182,8 @@ scan_for_existing_tablespaces(char *pathname, cb_options *opt)
 			exit(1);
 		if (type != PGFILETYPE_LNK && type != PGFILETYPE_DIR)
 		{
-			pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor a directory",
+			pg_log_debug("skipping \"%s\" because it is neither a symbolic link nor "
+						 "a directory",
 						 tblspcdir);
 			continue;
 		}
@@ -1205,8 +1203,7 @@ scan_for_existing_tablespaces(char *pathname, cb_options *opt)
 			/* Read the link target. */
 			link_length = readlink(tblspcdir, link_target, sizeof(link_target));
 			if (link_length < 0)
-				pg_fatal("could not read symbolic link \"%s\": %m",
-						 tblspcdir);
+				pg_fatal("could not read symbolic link \"%s\": %m", tblspcdir);
 			if (link_length >= sizeof(link_target))
 				pg_fatal("symbolic link \"%s\" is too long", tblspcdir);
 			link_target[link_length] = '\0';
@@ -1233,8 +1230,7 @@ scan_for_existing_tablespaces(char *pathname, cb_options *opt)
 
 			/* Every non-in-place tablespace must be mapped. */
 			if (tsmap == NULL)
-				pg_fatal("tablespace at \"%s\" has no tablespace mapping",
-						 link_target);
+				pg_fatal("tablespace at \"%s\" has no tablespace mapping", link_target);
 		}
 		else
 		{
diff --git a/src/bin/pg_combinebackup/reconstruct.c b/src/bin/pg_combinebackup/reconstruct.c
index 41f06bb26b5..4daff9c77be 100644
--- a/src/bin/pg_combinebackup/reconstruct.c
+++ b/src/bin/pg_combinebackup/reconstruct.c
@@ -46,20 +46,16 @@ typedef struct rfile
 	off_t		highest_offset_read;
 } rfile;
 
-static void debug_reconstruction(int n_source,
-								 rfile **sources,
-								 bool dry_run);
+static void debug_reconstruction(int n_source, rfile **sources, bool dry_run);
 static unsigned find_reconstructed_block_length(rfile *s);
 static rfile *make_incremental_rfile(char *filename);
 static rfile *make_rfile(char *filename, bool missing_ok);
 static void write_reconstructed_file(char *input_filename,
 									 char *output_filename,
-									 unsigned block_length,
-									 rfile **sourcemap,
+									 unsigned block_length, rfile **sourcemap,
 									 off_t *offsetmap,
 									 pg_checksum_context *checksum_ctx,
-									 bool debug,
-									 bool dry_run);
+									 bool debug, bool dry_run);
 static void read_bytes(rfile *rf, void *buffer, unsigned length);
 
 /*
@@ -78,19 +74,13 @@ static void read_bytes(rfile *rf, void *buffer, unsigned length);
  * an array of pathnames where those backups can be found.
  */
 void
-reconstruct_from_incremental_file(char *input_filename,
-								  char *output_filename,
-								  char *relative_path,
-								  char *bare_file_name,
-								  int n_prior_backups,
-								  char **prior_backup_dirs,
-								  manifest_data **manifests,
-								  char *manifest_path,
-								  pg_checksum_type checksum_type,
-								  int *checksum_length,
-								  uint8 **checksum_payload,
-								  bool debug,
-								  bool dry_run)
+reconstruct_from_incremental_file(
+								  char *input_filename, char *output_filename, char *relative_path,
+								  char *bare_file_name, int n_prior_backups, char **prior_backup_dirs,
+								  manifest_data **manifests, char *manifest_path,
+								  pg_checksum_type checksum_type, int *checksum_length,
+								  uint8 **checksum_payload, bool debug, bool dry_run,
+								  CopyFileMethod copy_method)
 {
 	rfile	  **source;
 	rfile	   *latest_source = NULL;
@@ -167,8 +157,8 @@ reconstruct_from_incremental_file(char *input_filename,
 		 * Look for the full file in the previous backup. If not found, then
 		 * look for an incremental file instead.
 		 */
-		snprintf(source_filename, MAXPGPATH, "%s/%s/%s",
-				 prior_backup_dirs[sidx], relative_path, bare_file_name);
+		snprintf(source_filename, MAXPGPATH, "%s/%s/%s", prior_backup_dirs[sidx],
+				 relative_path, bare_file_name);
 		if ((s = make_rfile(source_filename, true)) == NULL)
 		{
 			snprintf(source_filename, MAXPGPATH, "%s/%s/INCREMENTAL.%s",
@@ -231,8 +221,7 @@ reconstruct_from_incremental_file(char *input_filename,
 			{
 				uint64		expected_length;
 
-				expected_length =
-					(uint64) latest_source->truncation_block_length;
+				expected_length = (uint64) latest_source->truncation_block_length;
 				expected_length *= BLCKSZ;
 				if (expected_length == sb.st_size)
 				{
@@ -253,8 +242,7 @@ reconstruct_from_incremental_file(char *input_filename,
 		{
 			BlockNumber b = s->relative_block_numbers[i];
 
-			if (b < latest_source->truncation_block_length &&
-				sourcemap[b] == NULL)
+			if (b < latest_source->truncation_block_length && sourcemap[b] == NULL)
 			{
 				sourcemap[b] = s;
 				offsetmap[b] = s->header_length + (i * BLCKSZ);
@@ -283,16 +271,16 @@ reconstruct_from_incremental_file(char *input_filename,
 									  manifest_path);
 		if (mfile == NULL)
 		{
-			char	   *path = psprintf("%s/backup_manifest",
-										prior_backup_dirs[copy_source_index]);
+			char	   *path =
+				psprintf("%s/backup_manifest", prior_backup_dirs[copy_source_index]);
 
 			/*
 			 * The directory is out of sync with the backup_manifest, so emit
 			 * a warning.
 			 */
-			/*- translator: the first %s is a backup manifest file, the second is a file absent therein */
-			pg_log_warning("\"%s\" contains no entry for \"%s\"",
-						   path,
+			/*- translator: the first %s is a backup manifest file, the second is a
+	         * file absent therein */
+			pg_log_warning("\"%s\" contains no entry for \"%s\"", path,
 						   manifest_path);
 			pfree(path);
 		}
@@ -300,8 +288,7 @@ reconstruct_from_incremental_file(char *input_filename,
 		{
 			*checksum_length = mfile->checksum_length;
 			*checksum_payload = pg_malloc(*checksum_length);
-			memcpy(*checksum_payload, mfile->checksum_payload,
-				   *checksum_length);
+			memcpy(*checksum_payload, mfile->checksum_payload, *checksum_length);
 			checksum_type = CHECKSUM_TYPE_NONE;
 		}
 	}
@@ -318,13 +305,13 @@ reconstruct_from_incremental_file(char *input_filename,
 	 * Otherwise, reconstruct.
 	 */
 	if (copy_source != NULL)
-		copy_file(copy_source->filename, output_filename,
-				  &checksum_ctx, dry_run);
+		copy_file(copy_source->filename, output_filename, &checksum_ctx, dry_run,
+				  copy_method);
 	else
 	{
-		write_reconstructed_file(input_filename, output_filename,
-								 block_length, sourcemap, offsetmap,
-								 &checksum_ctx, debug, dry_run);
+		write_reconstructed_file(input_filename, output_filename, block_length,
+								 sourcemap, offsetmap, &checksum_ctx, debug,
+								 dry_run);
 		debug_reconstruction(n_prior_backups + 1, source, dry_run);
 	}
 
@@ -332,8 +319,7 @@ reconstruct_from_incremental_file(char *input_filename,
 	if (checksum_type != CHECKSUM_TYPE_NONE)
 	{
 		*checksum_payload = pg_malloc(PG_CHECKSUM_MAX_LENGTH);
-		*checksum_length = pg_checksum_final(&checksum_ctx,
-											 *checksum_payload);
+		*checksum_length = pg_checksum_final(&checksum_ctx, *checksum_payload);
 	}
 
 	/*
@@ -378,11 +364,11 @@ debug_reconstruction(int n_source, rfile **sources, bool dry_run)
 
 		/* Debug logging. */
 		if (dry_run)
-			pg_log_debug("would have read %u blocks from \"%s\"",
-						 s->num_blocks_read, s->filename);
+			pg_log_debug("would have read %u blocks from \"%s\"", s->num_blocks_read,
+						 s->filename);
 		else
-			pg_log_debug("read %u blocks from \"%s\"",
-						 s->num_blocks_read, s->filename);
+			pg_log_debug("read %u blocks from \"%s\"", s->num_blocks_read,
+						 s->filename);
 
 		/*
 		 * In dry-run mode, we don't actually try to read data from the file,
@@ -401,8 +387,7 @@ debug_reconstruction(int n_source, rfile **sources, bool dry_run)
 				pg_fatal("could not stat \"%s\": %m", s->filename);
 			if (sb.st_size < s->highest_offset_read)
 				pg_fatal("file \"%s\" is too short: expected %llu, found %llu",
-						 s->filename,
-						 (unsigned long long) s->highest_offset_read,
+						 s->filename, (unsigned long long) s->highest_offset_read,
 						 (unsigned long long) sb.st_size);
 		}
 	}
@@ -455,7 +440,8 @@ make_incremental_rfile(char *filename)
 	read_bytes(rf, &rf->truncation_block_length,
 			   sizeof(rf->truncation_block_length));
 	if (rf->truncation_block_length > RELSEG_SIZE)
-		pg_fatal("file \"%s\" has truncation block length %u in excess of segment size %u",
+		pg_fatal("file \"%s\" has truncation block length %u in excess of segment "
+				 "size %u",
 				 filename, rf->truncation_block_length, RELSEG_SIZE);
 
 	/* Read block numbers if there are any. */
@@ -522,12 +508,10 @@ read_bytes(rfile *rf, void *buffer, unsigned length)
 static void
 write_reconstructed_file(char *input_filename,
 						 char *output_filename,
-						 unsigned block_length,
-						 rfile **sourcemap,
+						 unsigned block_length, rfile **sourcemap,
 						 off_t *offsetmap,
 						 pg_checksum_context *checksum_ctx,
-						 bool debug,
-						 bool dry_run)
+						 bool debug, bool dry_run)
 {
 	int			wfd = -1;
 	unsigned	i;
@@ -570,8 +554,8 @@ write_reconstructed_file(char *input_filename,
 				if (current_block == start_of_range)
 					appendStringInfo(&debug_buf, " %u:zero", current_block);
 				else
-					appendStringInfo(&debug_buf, " %u-%u:zero",
-									 start_of_range, current_block);
+					appendStringInfo(&debug_buf, " %u-%u:zero", start_of_range,
+									 current_block);
 			}
 			else
 			{
@@ -603,8 +587,7 @@ write_reconstructed_file(char *input_filename,
 
 	/* Open the output file, except in dry_run mode. */
 	if (!dry_run &&
-		(wfd = open(output_filename,
-					O_RDWR | PG_BINARY | O_CREAT | O_EXCL,
+		(wfd = open(output_filename, O_RDWR | PG_BINARY | O_CREAT | O_EXCL,
 					pg_file_create_mode)) < 0)
 		pg_fatal("could not open file \"%s\": %m", output_filename);
 
@@ -621,8 +604,8 @@ write_reconstructed_file(char *input_filename,
 		else
 		{
 			s->num_blocks_read++;
-			s->highest_offset_read = Max(s->highest_offset_read,
-										 offsetmap[i] + BLCKSZ);
+			s->highest_offset_read =
+				Max(s->highest_offset_read, offsetmap[i] + BLCKSZ);
 		}
 
 		/* Skip the rest of this in dry-run mode. */
@@ -649,9 +632,9 @@ write_reconstructed_file(char *input_filename,
 				if (rb < 0)
 					pg_fatal("could not read file \"%s\": %m", s->filename);
 				else
-					pg_fatal("could not read file \"%s\": read only %d of %d bytes at offset %llu",
-							 s->filename, rb, BLCKSZ,
-							 (unsigned long long) offsetmap[i]);
+					pg_fatal("could not read file \"%s\": read only %d of %d bytes at "
+							 "offset %llu",
+							 s->filename, rb, BLCKSZ, (unsigned long long) offsetmap[i]);
 			}
 		}
 
@@ -667,8 +650,7 @@ write_reconstructed_file(char *input_filename,
 
 		/* Update the checksum computation. */
 		if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
-			pg_fatal("could not update checksum of file \"%s\"",
-					 output_filename);
+			pg_fatal("could not update checksum of file \"%s\"", output_filename);
 	}
 
 	/* Debugging output. */
diff --git a/src/bin/pg_combinebackup/reconstruct.h b/src/bin/pg_combinebackup/reconstruct.h
index 8e33a8a95a0..1fa734011bd 100644
--- a/src/bin/pg_combinebackup/reconstruct.h
+++ b/src/bin/pg_combinebackup/reconstruct.h
@@ -13,21 +13,17 @@
 #ifndef RECONSTRUCT_H
 #define RECONSTRUCT_H
 
+#include "c.h"
 #include "common/checksum_helper.h"
+#include "common/file_utils.h"
 #include "load_manifest.h"
 
-extern void reconstruct_from_incremental_file(char *input_filename,
-											  char *output_filename,
-											  char *relative_path,
-											  char *bare_file_name,
-											  int n_prior_backups,
-											  char **prior_backup_dirs,
-											  manifest_data **manifests,
-											  char *manifest_path,
-											  pg_checksum_type checksum_type,
-											  int *checksum_length,
-											  uint8 **checksum_payload,
-											  bool debug,
-											  bool dry_run);
+extern void reconstruct_from_incremental_file(
+											  char *input_filename, char *output_filename, char *relative_path,
+											  char *bare_file_name, int n_prior_backups, char **prior_backup_dirs,
+											  manifest_data **manifests, char *manifest_path,
+											  pg_checksum_type checksum_type, int *checksum_length,
+											  uint8 **checksum_payload, bool debug, bool dry_run,
+											  CopyFileMethod copy_method);
 
 #endif
-- 
2.44.0

Reply via email to