rfile **sourcemap,
off_t *offsetmap,
pg_checksum_context *checksum_ctx,
+ CopyMethod copy_method,
bool debug,
bool dry_run);
static void read_bytes(rfile *rf, void *buffer, unsigned length);
+static void write_block(int wfd, char *output_filename,
+ uint8 *buffer,
+ pg_checksum_context *checksum_ctx);
+static void read_block(rfile *s, off_t off, uint8 *buffer);
/*
* Reconstruct a full file from an incremental file and a chain of prior
{
write_reconstructed_file(input_filename, output_filename,
block_length, sourcemap, offsetmap,
- &checksum_ctx, debug, dry_run);
+ &checksum_ctx, copy_method,
+ debug, dry_run);
debug_reconstruction(n_prior_backups + 1, source, dry_run);
}
rfile **sourcemap,
off_t *offsetmap,
pg_checksum_context *checksum_ctx,
+ CopyMethod copy_method,
bool debug,
bool dry_run)
{
{
uint8 buffer[BLCKSZ];
rfile *s = sourcemap[i];
- int wb;
/* Update accounting information. */
if (s == NULL)
* uninitialized block, so just zero-fill it.
*/
memset(buffer, 0, BLCKSZ);
- }
- else
- {
- int rb;
- /* Read the block from the correct source, except if dry-run. */
- rb = pg_pread(s->fd, buffer, BLCKSZ, offsetmap[i]);
- if (rb != BLCKSZ)
- {
- 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]);
- }
+ /* Write out the block, update the checksum if needed. */
+ write_block(wfd, output_filename, buffer, checksum_ctx);
+
+ /* Nothing else to do for zero-filled blocks. */
+ continue;
}
- /* Write out the block. */
- if ((wb = write(wfd, buffer, BLCKSZ)) != BLCKSZ)
+ /* Copy the block using the appropriate copy method. */
+ if (copy_method != COPY_METHOD_COPY_FILE_RANGE)
{
- if (wb < 0)
- pg_fatal("could not write file \"%s\": %m", output_filename);
- else
- pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
- output_filename, wb, BLCKSZ);
+ /*
+ * Read the block from the correct source file, and then write it
+ * out, possibly with a checksum update.
+ */
+ read_block(s, offsetmap[i], buffer);
+ write_block(wfd, output_filename, buffer, checksum_ctx);
}
+ else /* use copy_file_range */
+ {
+ /* copy_file_range modifies the offset, so use a local copy */
+ off_t off = offsetmap[i];
+ size_t nwritten = 0;
+
+ /*
+ * Retry until we've written all the bytes (the offset is updated
+ * by copy_file_range, and so is the wfd file offset).
+ */
+ do
+ {
+ int wb;
+
+ wb = copy_file_range(s->fd, &off, wfd, NULL, BLCKSZ - nwritten, 0);
+
+ if (wb < 0)
+ pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
+ input_filename, output_filename);
+
+ nwritten += wb;
+
+ } while (BLCKSZ > nwritten);
+
+ /*
+ * When checksum calculation not needed, we're done, otherwise
+ * read the block and pass it to the checksum calculation.
+ */
+ if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
+ continue;
+
+ read_block(s, offsetmap[i], buffer);
- /* Update the checksum computation. */
- if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
- pg_fatal("could not update checksum of file \"%s\"",
- output_filename);
+ if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
+ pg_fatal("could not update checksum of file \"%s\"",
+ output_filename);
+ }
}
/* Debugging output. */
if (wfd >= 0 && close(wfd) != 0)
pg_fatal("could not close \"%s\": %m", output_filename);
}
+
+/*
+ * Write the block into the file (using the file descriptor), and
+ * if needed update the checksum calculation.
+ *
+ * The buffer is expected to contain BLCKSZ bytes. The filename is
+ * provided only for the error message.
+ */
+static void
+write_block(int fd, char *output_filename,
+ uint8 *buffer, pg_checksum_context *checksum_ctx)
+{
+ int wb;
+
+ if ((wb = write(fd, buffer, BLCKSZ)) != BLCKSZ)
+ {
+ if (wb < 0)
+ pg_fatal("could not write file \"%s\": %m", output_filename);
+ else
+ pg_fatal("could not write file \"%s\": wrote only %d of %d bytes",
+ output_filename, wb, BLCKSZ);
+ }
+
+ /* Update the checksum computation. */
+ if (pg_checksum_update(checksum_ctx, buffer, BLCKSZ) < 0)
+ pg_fatal("could not update checksum of file \"%s\"",
+ output_filename);
+}
+
+/*
+ * Read a block of data (BLCKSZ bytes) into the the buffer.
+ */
+static void
+read_block(rfile *s, off_t off, uint8 *buffer)
+{
+ int rb;
+
+ /* Read the block from the correct source, except if dry-run. */
+ rb = pg_pread(s->fd, buffer, BLCKSZ, off);
+ if (rb != BLCKSZ)
+ {
+ 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) off);
+ }
+}