/*
* Copy entire files .
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
* src / bin / pg_combinebackup / copy_file . h
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres_fe.h"
#ifdef HAVE_COPYFILE_H
#include <copyfile.h>
#endif
#ifdef __linux__
#include <sys/ioctl.h>
#include <linux/fs.h>
#endif
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common/file_perm.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 copy_file_clone(const char *src, const char *dest,
pg_checksum_context *checksum_ctx);
static void copy_file_by_range(const char *src, const char *dest,
pg_checksum_context *checksum_ctx);
#ifdef WIN32
static void copy_file_copyfile(const char *src, const char *dst,
pg_checksum_context *checksum_ctx);
#endif
static void copy_file_link(const char *src, const char *dest,
pg_checksum_context *checksum_ctx);
/*
* Copy a regular file , optionally computing a checksum , and emitting
* appropriate debug messages . But if we ' re in dry - run mode , then just emit
* the messages and don ' t copy anything .
*/
void
copy_file(const char *src, const char *dst,
pg_checksum_context *checksum_ctx,
CopyMethod copy_method, bool dry_run)
{
char *strategy_name = NULL;
void (*strategy_implementation) (const char *, const char *,
pg_checksum_context *checksum_ctx) = NULL;
/*
* In dry - run mode , we don ' t actually copy anything , nor do we read any
* data from the source file , but we do verify that we can open it .
*/
if (dry_run)
{
int fd;
if ((fd = open(src, O_RDONLY | PG_BINARY, 0 )) < 0 )
pg_fatal("could not open file \" %s\": %m" , src);
if (close(fd) < 0 )
pg_fatal("could not close file \" %s\": %m" , src);
}
#ifdef WIN32
/*
* We have no specific switch to enable CopyFile on Windows , because it ' s
* supported ( as far as we know ) on all Windows machines . So ,
* automatically enable it unless some other strategy was selected .
*/
if (copy_method == COPY_METHOD_COPY)
copy_method = COPY_METHOD_COPYFILE;
#endif
/* Determine the name of the copy strategy for use in log messages. */
switch (copy_method)
{
case COPY_METHOD_CLONE:
strategy_name = "clone" ;
strategy_implementation = copy_file_clone;
break ;
case COPY_METHOD_COPY:
/* leave NULL for simple block-by-block copy */
strategy_implementation = copy_file_blocks;
break ;
case COPY_METHOD_COPY_FILE_RANGE:
strategy_name = "copy_file_range" ;
strategy_implementation = copy_file_by_range;
break ;
#ifdef WIN32
case COPY_METHOD_COPYFILE:
strategy_name = "CopyFile" ;
strategy_implementation = copy_file_copyfile;
break ;
#endif
case COPY_METHOD_LINK:
strategy_name = "link" ;
strategy_implementation = copy_file_link;
break ;
}
if (dry_run)
{
if (strategy_name)
pg_log_debug("would copy \" %s\" to \" %s\" using strategy %s" ,
src, dst, strategy_name);
else
pg_log_debug("would copy \" %s\" to \" %s\"" ,
src, dst);
}
else
{
if (strategy_name)
pg_log_debug("copying \" %s\" to \" %s\" using strategy %s" ,
src, dst, strategy_name);
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));
strategy_implementation(src, dst, checksum_ctx);
}
}
/*
* Calculate checksum for the src file .
*/
static void
checksum_file(const char *src, pg_checksum_context *checksum_ctx)
{
int src_fd;
uint8 *buffer;
const int buffer_size = 50 * BLCKSZ;
ssize_t rb;
/* bail out if no checksum needed */
if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
return ;
if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0 )) < 0 )
pg_fatal("could not open file \" %s\": %m" , src);
buffer = pg_malloc(buffer_size);
while ((rb = read(src_fd, buffer, buffer_size)) > 0 )
{
if (pg_checksum_update(checksum_ctx, buffer, rb) < 0 )
pg_fatal("could not update checksum of file \" %s\"" , src);
}
if (rb < 0 )
pg_fatal("could not read file \" %s\": %m" , src);
pg_free(buffer);
close(src_fd);
}
/*
* Copy a file block by block , and optionally compute a checksum as we go .
*/
static void
copy_file_blocks(const char *src, const char *dst,
pg_checksum_context *checksum_ctx)
{
int src_fd;
int dest_fd;
uint8 *buffer;
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);
if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
pg_file_create_mode)) < 0 )
pg_fatal("could not open file \" %s\": %m" , dst);
buffer = pg_malloc(buffer_size);
while ((rb = read(src_fd, buffer, buffer_size)) > 0 )
{
ssize_t wb;
if ((wb = write(dest_fd, buffer, rb)) != rb)
{
if (wb < 0 )
pg_fatal("could not write to file \" %s\": %m" , dst);
else
pg_fatal("could not write to file \" %s\", offset %u: wrote %d of %d" ,
dst, offset, (int ) wb, (int ) rb);
}
if (pg_checksum_update(checksum_ctx, buffer, rb) < 0 )
pg_fatal("could not update checksum of file \" %s\"" , dst);
offset += rb;
}
if (rb < 0 )
pg_fatal("could not read from file \" %s\": %m" , src);
pg_free(buffer);
close(src_fd);
close(dest_fd);
}
/*
* copy_file_clone
* Clones / reflinks a file from src to dest .
*
* If needed , also reads the file and calculates the checksum .
*/
static void
copy_file_clone(const char *src, const char *dest,
pg_checksum_context *checksum_ctx)
{
#if defined (HAVE_COPYFILE) && defined (COPYFILE_CLONE_FORCE)
if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0 )
pg_fatal("error while cloning file \" %s\" to \" %s\": %m" , src, dest);
#elif defined (__linux__) && defined (FICLONE)
{
int src_fd;
int dest_fd;
if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0 )) < 0 )
pg_fatal("could not open file \" %s\": %m" , src);
if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
pg_file_create_mode)) < 0 )
pg_fatal("could not create file \" %s\": %m" , dest);
if (ioctl(dest_fd, FICLONE, src_fd) < 0 )
{
int save_errno = errno;
unlink(dest);
pg_fatal("error while cloning file \" %s\" to \" %s\": %s" ,
src, dest, strerror(save_errno));
}
close(src_fd);
close(dest_fd);
}
#else
pg_fatal("file cloning not supported on this platform" );
#endif
/* if needed, calculate checksum of the file */
checksum_file(src, checksum_ctx);
}
/*
* copy_file_by_range
* Copies a file from src to dest using copy_file_range system call .
*
* If needed , also reads the file and calculates the checksum .
*/
static void
copy_file_by_range(const char *src, const char *dest,
pg_checksum_context *checksum_ctx)
{
#if defined (HAVE_COPY_FILE_RANGE)
int src_fd;
int dest_fd;
ssize_t nbytes;
if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0 )) < 0 )
pg_fatal("could not open file \" %s\": %m" , src);
if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
pg_file_create_mode)) < 0 )
pg_fatal("could not create file \" %s\": %m" , dest);
do
{
nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0 );
if (nbytes < 0 )
pg_fatal("error while copying file range from \" %s\" to \" %s\": %m" ,
src, dest);
} while (nbytes > 0 );
close(src_fd);
close(dest_fd);
#else
pg_fatal("copy_file_range not supported on this platform" );
#endif
/* if needed, calculate checksum of the file */
checksum_file(src, checksum_ctx);
}
#ifdef WIN32
static void
copy_file_copyfile(const char *src, const char *dst,
pg_checksum_context *checksum_ctx)
{
if (CopyFile(src, dst, true ) == 0 )
{
_dosmaperr(GetLastError());
pg_fatal("could not copy file \" %s\" to \" %s\": %m" , src, dst);
}
/* if needed, calculate checksum of the file */
checksum_file(src, checksum_ctx);
}
#endif /* WIN32 */
/*
* copy_file_link
* Hard - links a file from src to dest .
*
* If needed , also reads the file and calculates the checksum .
*/
static void
copy_file_link(const char *src, const char *dest,
pg_checksum_context *checksum_ctx)
{
if (link(src, dest) < 0 )
pg_fatal("could not create link from \" %s\" to \" %s\": %m" ,
src, dest);
/* if needed, calculate checksum of the file */
checksum_file(src, checksum_ctx);
}
Messung V0.5 in Prozent C=93 H=95 G=93
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland