LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 42.2 % 102 43
Test Date: 2026-07-03 19:57:34 Functions: 66.7 % 6 4
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 30.4 % 69 21

             Branch data     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                 :       14754 : copy_file(const char *src, const char *dst,
      53                 :             :           pg_checksum_context *checksum_ctx,
      54                 :             :           CopyMethod copy_method, bool dry_run)
      55                 :             : {
      56                 :       14754 :     char       *strategy_name = NULL;
      57                 :       14754 :     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         [ -  + ]:       14754 :     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   [ -  +  -  +  :       14754 :     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                 :       13721 :         case COPY_METHOD_COPY:
      93                 :             :             /* leave NULL for simple block-by-block copy */
      94                 :       13721 :             strategy_implementation = copy_file_blocks;
      95                 :       13721 :             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                 :        1033 :         case COPY_METHOD_LINK:
     107                 :        1033 :             strategy_name = "link";
     108                 :        1033 :             strategy_implementation = copy_file_link;
     109                 :        1033 :             break;
     110                 :             :     }
     111                 :             : 
     112         [ -  + ]:       14754 :     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         [ +  + ]:       14754 :         if (strategy_name)
     124         [ +  - ]:        1033 :             pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
     125                 :             :                          src, dst, strategy_name);
     126         [ +  + ]:       13721 :         else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     127         [ +  + ]:       12693 :             pg_log_debug("copying \"%s\" to \"%s\"",
     128                 :             :                          src, dst);
     129                 :             :         else
     130         [ -  + ]:        1028 :             pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
     131                 :             :                          src, dst, pg_checksum_type_name(checksum_ctx->type));
     132                 :             : 
     133                 :       14754 :         strategy_implementation(src, dst, checksum_ctx);
     134                 :             :     }
     135                 :       14754 : }
     136                 :             : 
     137                 :             : /*
     138                 :             :  * Calculate checksum for the src file.
     139                 :             :  */
     140                 :             : static void
     141                 :        1033 : checksum_file(const char *src, pg_checksum_context *checksum_ctx)
     142                 :             : {
     143                 :             :     int         src_fd;
     144                 :             :     uint8      *buffer;
     145                 :        1033 :     const int   buffer_size = 50 * BLCKSZ;
     146                 :             :     ssize_t     rb;
     147                 :             : 
     148                 :             :     /* bail out if no checksum needed */
     149         [ +  - ]:        1033 :     if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
     150                 :        1033 :         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                 :       13721 : 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                 :       13721 :     const int   buffer_size = 50 * BLCKSZ;
     181                 :             :     ssize_t     rb;
     182                 :       13721 :     unsigned    offset = 0;
     183                 :             : 
     184         [ -  + ]:       13721 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     185                 :           0 :         pg_fatal("could not open file \"%s\": %m", src);
     186                 :             : 
     187         [ -  + ]:       13721 :     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                 :       13721 :     buffer = pg_malloc(buffer_size);
     192                 :             : 
     193         [ +  + ]:       25575 :     while ((rb = read(src_fd, buffer, buffer_size)) > 0)
     194                 :             :     {
     195                 :             :         ssize_t     wb;
     196                 :             : 
     197         [ -  + ]:       11854 :         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         [ -  + ]:       11854 :         if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
     207                 :           0 :             pg_fatal("could not update checksum of file \"%s\"", dst);
     208                 :             : 
     209                 :       11854 :         offset += rb;
     210                 :             :     }
     211                 :             : 
     212         [ -  + ]:       13721 :     if (rb < 0)
     213                 :           0 :         pg_fatal("could not read from file \"%s\": %m", src);
     214                 :             : 
     215                 :       13721 :     pg_free(buffer);
     216                 :       13721 :     close(src_fd);
     217                 :       13721 :     close(dest_fd);
     218                 :       13721 : }
     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                 :        1033 : copy_file_link(const char *src, const char *dest,
     330                 :             :                pg_checksum_context *checksum_ctx)
     331                 :             : {
     332         [ -  + ]:        1033 :     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                 :        1033 :     checksum_file(src, checksum_ctx);
     338                 :        1033 : }
        

Generated by: LCOV version 2.0-1