LCOV - differential code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Coverage Total Hit UBC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 42.2 % 102 43 59 43
Current Date: 2025-09-06 07:49:51 +0900 Functions: 66.7 % 6 4 2 4
Baseline: lcov-20250906-005545-baseline Branches: 30.4 % 69 21 48 21
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 88.9 % 9 8 1 8
(360..) days: 37.6 % 93 35 58 35
Function coverage date bins:
(30,360] days: 100.0 % 1 1 1
(360..) days: 60.0 % 5 3 2 3
Branch coverage date bins:
(30,360] days: 50.0 % 2 1 1 1
(360..) days: 29.9 % 67 20 47 20

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*
                                  2                 :                :  * Copy entire files.
                                  3                 :                :  *
                                  4                 :                :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
                                  5                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  6                 :                :  *
                                  7                 :                :  * src/bin/pg_combinebackup/copy_file.h
                                  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
  626 rhaas@postgresql.org       52                 :CBC       12905 : copy_file(const char *src, const char *dst,
                                 53                 :                :           pg_checksum_context *checksum_ctx,
                                 54                 :                :           CopyMethod copy_method, bool dry_run)
                                 55                 :                : {
  519 tomas.vondra@postgre       56                 :          12905 :     char       *strategy_name = NULL;
                                 57                 :          12905 :     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                 :                :      */
  626 rhaas@postgresql.org       64         [ -  + ]:          12905 :     if (dry_run)
                                 65                 :                :     {
                                 66                 :                :         int         fd;
                                 67                 :                : 
  626 rhaas@postgresql.org       68         [ #  # ]:UBC           0 :         if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
  442 peter@eisentraut.org       69                 :              0 :             pg_fatal("could not open file \"%s\": %m", src);
  626 rhaas@postgresql.org       70         [ #  # ]:              0 :         if (close(fd) < 0)
  442 peter@eisentraut.org       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. */
  519 tomas.vondra@postgre       86   [ -  +  -  +  :CBC       12905 :     switch (copy_method)
                                                 - ]
                                 87                 :                :     {
  519 tomas.vondra@postgre       88                 :UBC           0 :         case COPY_METHOD_CLONE:
                                 89                 :              0 :             strategy_name = "clone";
                                 90                 :              0 :             strategy_implementation = copy_file_clone;
                                 91                 :              0 :             break;
  519 tomas.vondra@postgre       92                 :CBC       11933 :         case COPY_METHOD_COPY:
                                 93                 :                :             /* leave NULL for simple block-by-block copy */
                                 94                 :          11933 :             strategy_implementation = copy_file_blocks;
                                 95                 :          11933 :             break;
  519 tomas.vondra@postgre       96                 :UBC           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
  173 rhaas@postgresql.org      106                 :CBC         972 :         case COPY_METHOD_LINK:
                                107                 :            972 :             strategy_name = "link";
                                108                 :            972 :             strategy_implementation = copy_file_link;
                                109                 :            972 :             break;
                                110                 :                :     }
                                111                 :                : 
  626                           112         [ -  + ]:          12905 :     if (dry_run)
                                113                 :                :     {
  519 tomas.vondra@postgre      114         [ #  # ]:UBC           0 :         if (strategy_name)
                                115         [ #  # ]:              0 :             pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
                                116                 :                :                          src, dst, strategy_name);
                                117                 :                :         else
  626 rhaas@postgresql.org      118         [ #  # ]:              0 :             pg_log_debug("would copy \"%s\" to \"%s\"",
                                119                 :                :                          src, dst);
                                120                 :                :     }
                                121                 :                :     else
                                122                 :                :     {
  519 tomas.vondra@postgre      123         [ +  + ]:CBC       12905 :         if (strategy_name)
                                124         [ +  - ]:            972 :             pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
                                125                 :                :                          src, dst, strategy_name);
                                126         [ +  + ]:          11933 :         else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
  626 rhaas@postgresql.org      127         [ +  + ]:          10966 :             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                 :                : 
  519 tomas.vondra@postgre      133                 :          12905 :         strategy_implementation(src, dst, checksum_ctx);
                                134                 :                :     }
  626 rhaas@postgresql.org      135                 :          12905 : }
                                136                 :                : 
                                137                 :                : /*
                                138                 :                :  * Calculate checksum for the src file.
                                139                 :                :  */
                                140                 :                : static void
  519 tomas.vondra@postgre      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                 :                : 
  519 tomas.vondra@postgre      152         [ #  # ]:UBC           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
  626 rhaas@postgresql.org      174                 :CBC       11933 : 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                 :          11933 :     const int   buffer_size = 50 * BLCKSZ;
                                181                 :                :     ssize_t     rb;
                                182                 :          11933 :     unsigned    offset = 0;
                                183                 :                : 
                                184         [ -  + ]:          11933 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
  626 rhaas@postgresql.org      185                 :UBC           0 :         pg_fatal("could not open file \"%s\": %m", src);
                                186                 :                : 
  626 rhaas@postgresql.org      187         [ -  + ]:CBC       11933 :     if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
                                188                 :                :                         pg_file_create_mode)) < 0)
  626 rhaas@postgresql.org      189                 :UBC           0 :         pg_fatal("could not open file \"%s\": %m", dst);
                                190                 :                : 
  626 rhaas@postgresql.org      191                 :CBC       11933 :     buffer = pg_malloc(buffer_size);
                                192                 :                : 
                                193         [ +  + ]:          22354 :     while ((rb = read(src_fd, buffer, buffer_size)) > 0)
                                194                 :                :     {
                                195                 :                :         ssize_t     wb;
                                196                 :                : 
                                197         [ -  + ]:          10421 :         if ((wb = write(dest_fd, buffer, rb)) != rb)
                                198                 :                :         {
  626 rhaas@postgresql.org      199         [ #  # ]:UBC           0 :             if (wb < 0)
  442 peter@eisentraut.org      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                 :                : 
  626 rhaas@postgresql.org      206         [ -  + ]:CBC       10421 :         if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
  626 rhaas@postgresql.org      207                 :UBC           0 :             pg_fatal("could not update checksum of file \"%s\"", dst);
                                208                 :                : 
  626 rhaas@postgresql.org      209                 :CBC       10421 :         offset += rb;
                                210                 :                :     }
                                211                 :                : 
                                212         [ -  + ]:          11933 :     if (rb < 0)
  442 peter@eisentraut.org      213                 :UBC           0 :         pg_fatal("could not read from file \"%s\": %m", dst);
                                214                 :                : 
  626 rhaas@postgresql.org      215                 :CBC       11933 :     pg_free(buffer);
                                216                 :          11933 :     close(src_fd);
                                217                 :          11933 :     close(dest_fd);
                                218                 :          11933 : }
                                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
  519 tomas.vondra@postgre      227                 :UBC           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                 :                : 
  433                           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 */
  519                           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
  173 rhaas@postgresql.org      329                 :CBC         972 : copy_file_link(const char *src, const char *dest,
                                330                 :                :                pg_checksum_context *checksum_ctx)
                                331                 :                : {
                                332         [ -  + ]:            972 :     if (link(src, dest) < 0)
  131 peter@eisentraut.org      333                 :UBC           0 :         pg_fatal("could not create link from \"%s\" to \"%s\": %m",
                                334                 :                :                  src, dest);
                                335                 :                : 
                                336                 :                :     /* if needed, calculate checksum of the file */
  173 rhaas@postgresql.org      337                 :CBC         972 :     checksum_file(src, checksum_ctx);
                                338                 :            972 : }
        

Generated by: LCOV version 2.4-beta