LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 42.2 % 102 43
Test Date: 2026-03-10 01:14:48 Functions: 66.7 % 6 4
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        13879 : copy_file(const char *src, const char *dst,
      53              :           pg_checksum_context *checksum_ctx,
      54              :           CopyMethod copy_method, bool dry_run)
      55              : {
      56        13879 :     char       *strategy_name = NULL;
      57        13879 :     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        13879 :     if (dry_run)
      65              :     {
      66              :         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              :     }
      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        13879 :     switch (copy_method)
      87              :     {
      88            0 :         case COPY_METHOD_CLONE:
      89            0 :             strategy_name = "clone";
      90            0 :             strategy_implementation = copy_file_clone;
      91            0 :             break;
      92        12907 :         case COPY_METHOD_COPY:
      93              :             /* leave NULL for simple block-by-block copy */
      94        12907 :             strategy_implementation = copy_file_blocks;
      95        12907 :             break;
      96            0 :         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          972 :         case COPY_METHOD_LINK:
     107          972 :             strategy_name = "link";
     108          972 :             strategy_implementation = copy_file_link;
     109          972 :             break;
     110              :     }
     111              : 
     112        13879 :     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              :     }
     121              :     else
     122              :     {
     123        13879 :         if (strategy_name)
     124          972 :             pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
     125              :                          src, dst, strategy_name);
     126        12907 :         else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     127        11940 :             pg_log_debug("copying \"%s\" to \"%s\"",
     128              :                          src, dst);
     129              :         else
     130          967 :             pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
     131              :                          src, dst, pg_checksum_type_name(checksum_ctx->type));
     132              : 
     133        13879 :         strategy_implementation(src, dst, checksum_ctx);
     134              :     }
     135        13879 : }
     136              : 
     137              : /*
     138              :  * Calculate checksum for the src file.
     139              :  */
     140              : static void
     141          972 : checksum_file(const char *src, pg_checksum_context *checksum_ctx)
     142              : {
     143              :     int         src_fd;
     144              :     uint8      *buffer;
     145          972 :     const int   buffer_size = 50 * BLCKSZ;
     146              :     ssize_t     rb;
     147              : 
     148              :     /* bail out if no checksum needed */
     149          972 :     if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     150          972 :         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              : }
     169              : 
     170              : /*
     171              :  * Copy a file block by block, and optionally compute a checksum as we go.
     172              :  */
     173              : static void
     174        12907 : copy_file_blocks(const char *src, const char *dst,
     175              :                  pg_checksum_context *checksum_ctx)
     176              : {
     177              :     int         src_fd;
     178              :     int         dest_fd;
     179              :     uint8      *buffer;
     180        12907 :     const int   buffer_size = 50 * BLCKSZ;
     181              :     ssize_t     rb;
     182        12907 :     unsigned    offset = 0;
     183              : 
     184        12907 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     185            0 :         pg_fatal("could not open file \"%s\": %m", src);
     186              : 
     187        12907 :     if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
     188              :                         pg_file_create_mode)) < 0)
     189            0 :         pg_fatal("could not open file \"%s\": %m", dst);
     190              : 
     191        12907 :     buffer = pg_malloc(buffer_size);
     192              : 
     193        24226 :     while ((rb = read(src_fd, buffer, buffer_size)) > 0)
     194              :     {
     195              :         ssize_t     wb;
     196              : 
     197        11319 :         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              :         }
     205              : 
     206        11319 :         if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
     207            0 :             pg_fatal("could not update checksum of file \"%s\"", dst);
     208              : 
     209        11319 :         offset += rb;
     210              :     }
     211              : 
     212        12907 :     if (rb < 0)
     213            0 :         pg_fatal("could not read from file \"%s\": %m", src);
     214              : 
     215        12907 :     pg_free(buffer);
     216        12907 :     close(src_fd);
     217        12907 :     close(dest_fd);
     218        12907 : }
     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              :     if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0)
     232              :         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            0 :         if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     239            0 :             pg_fatal("could not open file \"%s\": %m", src);
     240              : 
     241            0 :         if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     242              :                             pg_file_create_mode)) < 0)
     243            0 :             pg_fatal("could not create file \"%s\": %m", dest);
     244              : 
     245            0 :         if (ioctl(dest_fd, FICLONE, src_fd) < 0)
     246              :         {
     247            0 :             int         save_errno = errno;
     248              : 
     249            0 :             unlink(dest);
     250              : 
     251            0 :             pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
     252              :                      src, dest, strerror(save_errno));
     253              :         }
     254              : 
     255            0 :         close(src_fd);
     256            0 :         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            0 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     282            0 :         pg_fatal("could not open file \"%s\": %m", src);
     283              : 
     284            0 :     if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     285              :                         pg_file_create_mode)) < 0)
     286            0 :         pg_fatal("could not create file \"%s\": %m", dest);
     287              : 
     288              :     do
     289              :     {
     290            0 :         nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
     291            0 :         if (nbytes < 0)
     292            0 :             pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
     293              :                      src, dest);
     294            0 :     } while (nbytes > 0);
     295              : 
     296            0 :     close(src_fd);
     297            0 :     close(dest_fd);
     298              : #else
     299              :     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          972 : copy_file_link(const char *src, const char *dest,
     330              :                pg_checksum_context *checksum_ctx)
     331              : {
     332          972 :     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          972 :     checksum_file(src, checksum_ctx);
     338          972 : }
        

Generated by: LCOV version 2.0-1