LCOV - code coverage report
Current view: top level - src/bin/pg_combinebackup - copy_file.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 30 82 36.6 %
Date: 2024-05-09 07:11:19 Functions: 2 5 40.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14