LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 96 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 6 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copy entire files.
       3              :  *
       4              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       5              :  * Portions Copyright (c) 1994, Regents of the University of California
       6              :  *
       7              :  * src/bin/pg_combinebackup/copy_file.c
       8              :  *
       9              :  *-------------------------------------------------------------------------
      10              :  */
      11              : #include "postgres_fe.h"
      12              : 
      13              : #ifdef HAVE_COPYFILE_H
      14              : #include <copyfile.h>
      15              : #endif
      16              : #ifdef __linux__
      17              : #include <sys/ioctl.h>
      18              : #include <linux/fs.h>
      19              : #endif
      20              : #include <fcntl.h>
      21              : #include <limits.h>
      22              : #include <sys/stat.h>
      23              : #include <unistd.h>
      24              : 
      25              : #include "common/file_perm.h"
      26              : #include "common/logging.h"
      27              : #include "copy_file.h"
      28              : 
      29              : static void copy_file_blocks(const char *src, const char *dst,
      30              :                                                          pg_checksum_context *checksum_ctx);
      31              : 
      32              : static void copy_file_clone(const char *src, const char *dest,
      33              :                                                         pg_checksum_context *checksum_ctx);
      34              : 
      35              : static void copy_file_by_range(const char *src, const char *dest,
      36              :                                                            pg_checksum_context *checksum_ctx);
      37              : 
      38              : #ifdef WIN32
      39              : static void copy_file_copyfile(const char *src, const char *dst,
      40              :                                                            pg_checksum_context *checksum_ctx);
      41              : #endif
      42              : 
      43              : static void copy_file_link(const char *src, const char *dest,
      44              :                                                    pg_checksum_context *checksum_ctx);
      45              : 
      46              : /*
      47              :  * Copy a regular file, optionally computing a checksum, and emitting
      48              :  * appropriate debug messages. But if we're in dry-run mode, then just emit
      49              :  * the messages and don't copy anything.
      50              :  */
      51              : void
      52            0 : copy_file(const char *src, const char *dst,
      53              :                   pg_checksum_context *checksum_ctx,
      54              :                   CopyMethod copy_method, bool dry_run)
      55              : {
      56            0 :         char       *strategy_name = NULL;
      57            0 :         void            (*strategy_implementation) (const char *, const char *,
      58              :                                                                                         pg_checksum_context *checksum_ctx) = NULL;
      59              : 
      60              :         /*
      61              :          * In dry-run mode, we don't actually copy anything, nor do we read any
      62              :          * data from the source file, but we do verify that we can open it.
      63              :          */
      64            0 :         if (dry_run)
      65              :         {
      66            0 :                 int                     fd;
      67              : 
      68            0 :                 if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
      69            0 :                         pg_fatal("could not open file \"%s\": %m", src);
      70            0 :                 if (close(fd) < 0)
      71            0 :                         pg_fatal("could not close file \"%s\": %m", src);
      72            0 :         }
      73              : 
      74              : #ifdef WIN32
      75              : 
      76              :         /*
      77              :          * We have no specific switch to enable CopyFile on Windows, because it's
      78              :          * supported (as far as we know) on all Windows machines. So,
      79              :          * automatically enable it unless some other strategy was selected.
      80              :          */
      81              :         if (copy_method == COPY_METHOD_COPY)
      82              :                 copy_method = COPY_METHOD_COPYFILE;
      83              : #endif
      84              : 
      85              :         /* Determine the name of the copy strategy for use in log messages. */
      86            0 :         switch (copy_method)
      87              :         {
      88              :                 case COPY_METHOD_CLONE:
      89            0 :                         strategy_name = "clone";
      90            0 :                         strategy_implementation = copy_file_clone;
      91            0 :                         break;
      92              :                 case COPY_METHOD_COPY:
      93              :                         /* leave NULL for simple block-by-block copy */
      94            0 :                         strategy_implementation = copy_file_blocks;
      95            0 :                         break;
      96              :                 case COPY_METHOD_COPY_FILE_RANGE:
      97            0 :                         strategy_name = "copy_file_range";
      98            0 :                         strategy_implementation = copy_file_by_range;
      99            0 :                         break;
     100              : #ifdef WIN32
     101              :                 case COPY_METHOD_COPYFILE:
     102              :                         strategy_name = "CopyFile";
     103              :                         strategy_implementation = copy_file_copyfile;
     104              :                         break;
     105              : #endif
     106              :                 case COPY_METHOD_LINK:
     107            0 :                         strategy_name = "link";
     108            0 :                         strategy_implementation = copy_file_link;
     109            0 :                         break;
     110              :         }
     111              : 
     112            0 :         if (dry_run)
     113              :         {
     114            0 :                 if (strategy_name)
     115            0 :                         pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
     116              :                                                  src, dst, strategy_name);
     117              :                 else
     118            0 :                         pg_log_debug("would copy \"%s\" to \"%s\"",
     119              :                                                  src, dst);
     120            0 :         }
     121              :         else
     122              :         {
     123            0 :                 if (strategy_name)
     124            0 :                         pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
     125              :                                                  src, dst, strategy_name);
     126            0 :                 else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     127            0 :                         pg_log_debug("copying \"%s\" to \"%s\"",
     128              :                                                  src, dst);
     129              :                 else
     130            0 :                         pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
     131              :                                                  src, dst, pg_checksum_type_name(checksum_ctx->type));
     132              : 
     133            0 :                 strategy_implementation(src, dst, checksum_ctx);
     134              :         }
     135            0 : }
     136              : 
     137              : /*
     138              :  * Calculate checksum for the src file.
     139              :  */
     140              : static void
     141            0 : checksum_file(const char *src, pg_checksum_context *checksum_ctx)
     142              : {
     143            0 :         int                     src_fd;
     144            0 :         uint8      *buffer;
     145            0 :         const int       buffer_size = 50 * BLCKSZ;
     146            0 :         ssize_t         rb;
     147              : 
     148              :         /* bail out if no checksum needed */
     149            0 :         if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     150            0 :                 return;
     151              : 
     152            0 :         if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     153            0 :                 pg_fatal("could not open file \"%s\": %m", src);
     154              : 
     155            0 :         buffer = pg_malloc(buffer_size);
     156              : 
     157            0 :         while ((rb = read(src_fd, buffer, buffer_size)) > 0)
     158              :         {
     159            0 :                 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
     160            0 :                         pg_fatal("could not update checksum of file \"%s\"", src);
     161              :         }
     162              : 
     163            0 :         if (rb < 0)
     164            0 :                 pg_fatal("could not read file \"%s\": %m", src);
     165              : 
     166            0 :         pg_free(buffer);
     167            0 :         close(src_fd);
     168            0 : }
     169              : 
     170              : /*
     171              :  * Copy a file block by block, and optionally compute a checksum as we go.
     172              :  */
     173              : static void
     174            0 : copy_file_blocks(const char *src, const char *dst,
     175              :                                  pg_checksum_context *checksum_ctx)
     176              : {
     177            0 :         int                     src_fd;
     178            0 :         int                     dest_fd;
     179            0 :         uint8      *buffer;
     180            0 :         const int       buffer_size = 50 * BLCKSZ;
     181            0 :         ssize_t         rb;
     182            0 :         unsigned        offset = 0;
     183              : 
     184            0 :         if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     185            0 :                 pg_fatal("could not open file \"%s\": %m", src);
     186              : 
     187            0 :         if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
     188            0 :                                                 pg_file_create_mode)) < 0)
     189            0 :                 pg_fatal("could not open file \"%s\": %m", dst);
     190              : 
     191            0 :         buffer = pg_malloc(buffer_size);
     192              : 
     193            0 :         while ((rb = read(src_fd, buffer, buffer_size)) > 0)
     194              :         {
     195            0 :                 ssize_t         wb;
     196              : 
     197            0 :                 if ((wb = write(dest_fd, buffer, rb)) != rb)
     198              :                 {
     199            0 :                         if (wb < 0)
     200            0 :                                 pg_fatal("could not write to file \"%s\": %m", dst);
     201              :                         else
     202            0 :                                 pg_fatal("could not write to file \"%s\", offset %u: wrote %d of %d",
     203              :                                                  dst, offset, (int) wb, (int) rb);
     204            0 :                 }
     205              : 
     206            0 :                 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
     207            0 :                         pg_fatal("could not update checksum of file \"%s\"", dst);
     208              : 
     209            0 :                 offset += rb;
     210            0 :         }
     211              : 
     212            0 :         if (rb < 0)
     213            0 :                 pg_fatal("could not read from file \"%s\": %m", dst);
     214              : 
     215            0 :         pg_free(buffer);
     216            0 :         close(src_fd);
     217            0 :         close(dest_fd);
     218            0 : }
     219              : 
     220              : /*
     221              :  * copy_file_clone
     222              :  *              Clones/reflinks a file from src to dest.
     223              :  *
     224              :  * If needed, also reads the file and calculates the checksum.
     225              :  */
     226              : static void
     227            0 : copy_file_clone(const char *src, const char *dest,
     228              :                                 pg_checksum_context *checksum_ctx)
     229              : {
     230              : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
     231            0 :         if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0)
     232            0 :                 pg_fatal("error while cloning file \"%s\" to \"%s\": %m", src, dest);
     233              : #elif defined(__linux__) && defined(FICLONE)
     234              :         {
     235              :                 int                     src_fd;
     236              :                 int                     dest_fd;
     237              : 
     238              :                 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     239              :                         pg_fatal("could not open file \"%s\": %m", src);
     240              : 
     241              :                 if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     242              :                                                         pg_file_create_mode)) < 0)
     243              :                         pg_fatal("could not create file \"%s\": %m", dest);
     244              : 
     245              :                 if (ioctl(dest_fd, FICLONE, src_fd) < 0)
     246              :                 {
     247              :                         int                     save_errno = errno;
     248              : 
     249              :                         unlink(dest);
     250              : 
     251              :                         pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
     252              :                                          src, dest, strerror(save_errno));
     253              :                 }
     254              : 
     255              :                 close(src_fd);
     256              :                 close(dest_fd);
     257              :         }
     258              : #else
     259              :         pg_fatal("file cloning not supported on this platform");
     260              : #endif
     261              : 
     262              :         /* if needed, calculate checksum of the file */
     263            0 :         checksum_file(src, checksum_ctx);
     264            0 : }
     265              : 
     266              : /*
     267              :  * copy_file_by_range
     268              :  *              Copies a file from src to dest using copy_file_range system call.
     269              :  *
     270              :  * If needed, also reads the file and calculates the checksum.
     271              :  */
     272              : static void
     273            0 : copy_file_by_range(const char *src, const char *dest,
     274              :                                    pg_checksum_context *checksum_ctx)
     275              : {
     276              : #if defined(HAVE_COPY_FILE_RANGE)
     277              :         int                     src_fd;
     278              :         int                     dest_fd;
     279              :         ssize_t         nbytes;
     280              : 
     281              :         if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     282              :                 pg_fatal("could not open file \"%s\": %m", src);
     283              : 
     284              :         if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     285              :                                                 pg_file_create_mode)) < 0)
     286              :                 pg_fatal("could not create file \"%s\": %m", dest);
     287              : 
     288              :         do
     289              :         {
     290              :                 nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
     291              :                 if (nbytes < 0)
     292              :                         pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
     293              :                                          src, dest);
     294              :         } while (nbytes > 0);
     295              : 
     296              :         close(src_fd);
     297              :         close(dest_fd);
     298              : #else
     299            0 :         pg_fatal("copy_file_range not supported on this platform");
     300              : #endif
     301              : 
     302              :         /* if needed, calculate checksum of the file */
     303            0 :         checksum_file(src, checksum_ctx);
     304            0 : }
     305              : 
     306              : #ifdef WIN32
     307              : static void
     308              : copy_file_copyfile(const char *src, const char *dst,
     309              :                                    pg_checksum_context *checksum_ctx)
     310              : {
     311              :         if (CopyFile(src, dst, true) == 0)
     312              :         {
     313              :                 _dosmaperr(GetLastError());
     314              :                 pg_fatal("could not copy file \"%s\" to \"%s\": %m", src, dst);
     315              :         }
     316              : 
     317              :         /* if needed, calculate checksum of the file */
     318              :         checksum_file(src, checksum_ctx);
     319              : }
     320              : #endif                                                  /* WIN32 */
     321              : 
     322              : /*
     323              :  * copy_file_link
     324              :  *              Hard-links a file from src to dest.
     325              :  *
     326              :  * If needed, also reads the file and calculates the checksum.
     327              :  */
     328              : static void
     329            0 : copy_file_link(const char *src, const char *dest,
     330              :                            pg_checksum_context *checksum_ctx)
     331              : {
     332            0 :         if (link(src, dest) < 0)
     333            0 :                 pg_fatal("could not create link from \"%s\" to \"%s\": %m",
     334              :                                  src, dest);
     335              : 
     336              :         /* if needed, calculate checksum of the file */
     337            0 :         checksum_file(src, checksum_ctx);
     338            0 : }
        

Generated by: LCOV version 2.3.2-1