LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 43 102 42.2 %
Date: 2025-04-01 16:15:31 Functions: 4 6 66.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14