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

Generated by: LCOV version 1.14