LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - file.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 59.6 % 89 53
Test Date: 2026-07-16 11:17:59 Functions: 85.7 % 7 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 41.3 % 46 19

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  *  file.c
       3                 :             :  *
       4                 :             :  *  file system operations
       5                 :             :  *
       6                 :             :  *  Copyright (c) 2010-2026, PostgreSQL Global Development Group
       7                 :             :  *  src/bin/pg_upgrade/file.c
       8                 :             :  */
       9                 :             : 
      10                 :             : #include "postgres_fe.h"
      11                 :             : 
      12                 :             : #include <sys/stat.h>
      13                 :             : #include <limits.h>
      14                 :             : #include <fcntl.h>
      15                 :             : #ifdef HAVE_COPYFILE_H
      16                 :             : #include <copyfile.h>
      17                 :             : #endif
      18                 :             : #ifdef __linux__
      19                 :             : #include <sys/ioctl.h>
      20                 :             : #include <linux/fs.h>
      21                 :             : #endif
      22                 :             : 
      23                 :             : #include "common/file_perm.h"
      24                 :             : #include "pg_upgrade.h"
      25                 :             : 
      26                 :             : 
      27                 :             : /*
      28                 :             :  * cloneFile()
      29                 :             :  *
      30                 :             :  * Clones/reflinks a relation file from src to dst.
      31                 :             :  *
      32                 :             :  * schemaName/relName are relation's SQL name (used for error messages only).
      33                 :             :  */
      34                 :             : void
      35                 :           0 : cloneFile(const char *src, const char *dst,
      36                 :             :           const char *schemaName, const char *relName)
      37                 :             : {
      38                 :             : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
      39                 :             :     if (copyfile(src, dst, NULL, COPYFILE_CLONE_FORCE) < 0)
      40                 :             :         pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
      41                 :             :                  schemaName, relName, src, dst);
      42                 :             : #elif defined(__linux__) && defined(FICLONE)
      43                 :             :     int         src_fd;
      44                 :             :     int         dest_fd;
      45                 :             : 
      46         [ #  # ]:           0 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
      47                 :           0 :         pg_fatal("error while cloning relation \"%s.%s\": could not open file \"%s\": %m",
      48                 :             :                  schemaName, relName, src);
      49                 :             : 
      50         [ #  # ]:           0 :     if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
      51                 :             :                         pg_file_create_mode)) < 0)
      52                 :           0 :         pg_fatal("error while cloning relation \"%s.%s\": could not create file \"%s\": %m",
      53                 :             :                  schemaName, relName, dst);
      54                 :             : 
      55         [ #  # ]:           0 :     if (ioctl(dest_fd, FICLONE, src_fd) < 0)
      56                 :             :     {
      57                 :           0 :         int         save_errno = errno;
      58                 :             : 
      59                 :           0 :         unlink(dst);
      60                 :             : 
      61                 :           0 :         pg_fatal("error while cloning relation \"%s.%s\" (\"%s\" to \"%s\"): %s",
      62                 :             :                  schemaName, relName, src, dst, strerror(save_errno));
      63                 :             :     }
      64                 :             : 
      65                 :           0 :     close(src_fd);
      66                 :           0 :     close(dest_fd);
      67                 :             : #endif
      68                 :           0 : }
      69                 :             : 
      70                 :             : 
      71                 :             : /*
      72                 :             :  * copyFile()
      73                 :             :  *
      74                 :             :  * Copies a relation file from src to dst.
      75                 :             :  * schemaName/relName are relation's SQL name (used for error messages only).
      76                 :             :  */
      77                 :             : void
      78                 :        2010 : copyFile(const char *src, const char *dst,
      79                 :             :          const char *schemaName, const char *relName)
      80                 :             : {
      81                 :             : #ifndef WIN32
      82                 :             :     int         src_fd;
      83                 :             :     int         dest_fd;
      84                 :             :     char       *buffer;
      85                 :             : 
      86         [ -  + ]:        2010 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
      87                 :           0 :         pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
      88                 :             :                  schemaName, relName, src);
      89                 :             : 
      90         [ -  + ]:        2010 :     if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
      91                 :             :                         pg_file_create_mode)) < 0)
      92                 :           0 :         pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
      93                 :             :                  schemaName, relName, dst);
      94                 :             : 
      95                 :             :     /* copy in fairly large chunks for best efficiency */
      96                 :             : #define COPY_BUF_SIZE (50 * BLCKSZ)
      97                 :             : 
      98                 :        2010 :     buffer = (char *) pg_malloc(COPY_BUF_SIZE);
      99                 :             : 
     100                 :             :     /* perform data copying i.e read src source, write to destination */
     101                 :             :     while (true)
     102                 :        1585 :     {
     103                 :        3595 :         ssize_t     nbytes = read(src_fd, buffer, COPY_BUF_SIZE);
     104                 :             : 
     105         [ -  + ]:        3595 :         if (nbytes < 0)
     106                 :           0 :             pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
     107                 :             :                      schemaName, relName, src);
     108                 :             : 
     109         [ +  + ]:        3595 :         if (nbytes == 0)
     110                 :        2010 :             break;
     111                 :             : 
     112                 :        1585 :         errno = 0;
     113         [ -  + ]:        1585 :         if (write(dest_fd, buffer, nbytes) != nbytes)
     114                 :             :         {
     115                 :             :             /* if write didn't set errno, assume problem is no disk space */
     116         [ #  # ]:           0 :             if (errno == 0)
     117                 :           0 :                 errno = ENOSPC;
     118                 :           0 :             pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
     119                 :             :                      schemaName, relName, dst);
     120                 :             :         }
     121                 :             :     }
     122                 :             : 
     123                 :        2010 :     pg_free(buffer);
     124                 :        2010 :     close(src_fd);
     125                 :        2010 :     close(dest_fd);
     126                 :             : 
     127                 :             : #else                           /* WIN32 */
     128                 :             : 
     129                 :             :     if (CopyFile(src, dst, true) == 0)
     130                 :             :     {
     131                 :             :         _dosmaperr(GetLastError());
     132                 :             :         pg_fatal("error while copying relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
     133                 :             :                  schemaName, relName, src, dst);
     134                 :             :     }
     135                 :             : 
     136                 :             : #endif                          /* WIN32 */
     137                 :        2010 : }
     138                 :             : 
     139                 :             : 
     140                 :             : /*
     141                 :             :  * copyFileByRange()
     142                 :             :  *
     143                 :             :  * Copies a relation file from src to dst.
     144                 :             :  * schemaName/relName are relation's SQL name (used for error messages only).
     145                 :             :  */
     146                 :             : void
     147                 :          20 : copyFileByRange(const char *src, const char *dst,
     148                 :             :                 const char *schemaName, const char *relName)
     149                 :             : {
     150                 :             : #ifdef HAVE_COPY_FILE_RANGE
     151                 :             :     int         src_fd;
     152                 :             :     int         dest_fd;
     153                 :             :     ssize_t     nbytes;
     154                 :             : 
     155         [ -  + ]:          20 :     if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
     156                 :           0 :         pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
     157                 :             :                  schemaName, relName, src);
     158                 :             : 
     159         [ -  + ]:          20 :     if ((dest_fd = open(dst, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     160                 :             :                         pg_file_create_mode)) < 0)
     161                 :           0 :         pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
     162                 :             :                  schemaName, relName, dst);
     163                 :             : 
     164                 :             :     do
     165                 :             :     {
     166                 :          34 :         nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
     167         [ -  + ]:          34 :         if (nbytes < 0)
     168                 :           0 :             pg_fatal("error while copying relation \"%s.%s\": could not copy file range from \"%s\" to \"%s\": %m",
     169                 :             :                      schemaName, relName, src, dst);
     170                 :             :     }
     171         [ +  + ]:          34 :     while (nbytes > 0);
     172                 :             : 
     173                 :          20 :     close(src_fd);
     174                 :          20 :     close(dest_fd);
     175                 :             : #endif
     176                 :          20 : }
     177                 :             : 
     178                 :             : 
     179                 :             : /*
     180                 :             :  * linkFile()
     181                 :             :  *
     182                 :             :  * Hard-links a relation file from src to dst.
     183                 :             :  * schemaName/relName are relation's SQL name (used for error messages only).
     184                 :             :  */
     185                 :             : void
     186                 :          20 : linkFile(const char *src, const char *dst,
     187                 :             :          const char *schemaName, const char *relName)
     188                 :             : {
     189         [ -  + ]:          20 :     if (link(src, dst) < 0)
     190                 :           0 :         pg_fatal("error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %m",
     191                 :             :                  schemaName, relName, src, dst);
     192                 :          20 : }
     193                 :             : 
     194                 :             : 
     195                 :             : void
     196                 :           1 : check_file_clone(void)
     197                 :             : {
     198                 :             :     char        existing_file[MAXPGPATH];
     199                 :             :     char        new_link_file[MAXPGPATH];
     200                 :             : 
     201                 :           1 :     snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
     202                 :           1 :     snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.clonetest", new_cluster.pgdata);
     203                 :           1 :     unlink(new_link_file);      /* might fail */
     204                 :             : 
     205                 :             : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
     206                 :             :     if (copyfile(existing_file, new_link_file, NULL, COPYFILE_CLONE_FORCE) < 0)
     207                 :             :         pg_fatal("could not clone file between old and new data directories: %m");
     208                 :             : #elif defined(__linux__) && defined(FICLONE)
     209                 :             :     {
     210                 :             :         int         src_fd;
     211                 :             :         int         dest_fd;
     212                 :             : 
     213         [ -  + ]:           1 :         if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
     214                 :           0 :             pg_fatal("could not open file \"%s\": %m",
     215                 :             :                      existing_file);
     216                 :             : 
     217         [ -  + ]:           1 :         if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     218                 :             :                             pg_file_create_mode)) < 0)
     219                 :           0 :             pg_fatal("could not create file \"%s\": %m",
     220                 :             :                      new_link_file);
     221                 :             : 
     222         [ +  - ]:           1 :         if (ioctl(dest_fd, FICLONE, src_fd) < 0)
     223                 :           1 :             pg_fatal("could not clone file between old and new data directories: %m");
     224                 :             : 
     225                 :           0 :         close(src_fd);
     226                 :           0 :         close(dest_fd);
     227                 :             :     }
     228                 :             : #else
     229                 :             :     pg_fatal("file cloning not supported on this platform");
     230                 :             : #endif
     231                 :             : 
     232                 :           0 :     unlink(new_link_file);
     233                 :           0 : }
     234                 :             : 
     235                 :             : void
     236                 :           1 : check_copy_file_range(void)
     237                 :             : {
     238                 :             :     char        existing_file[MAXPGPATH];
     239                 :             :     char        new_link_file[MAXPGPATH];
     240                 :             : 
     241                 :           1 :     snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
     242                 :           1 :     snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.copy_file_range_test", new_cluster.pgdata);
     243                 :           1 :     unlink(new_link_file);      /* might fail */
     244                 :             : 
     245                 :             : #if defined(HAVE_COPY_FILE_RANGE)
     246                 :             :     {
     247                 :             :         int         src_fd;
     248                 :             :         int         dest_fd;
     249                 :             : 
     250         [ -  + ]:           1 :         if ((src_fd = open(existing_file, O_RDONLY | PG_BINARY, 0)) < 0)
     251                 :           0 :             pg_fatal("could not open file \"%s\": %m",
     252                 :             :                      existing_file);
     253                 :             : 
     254         [ -  + ]:           1 :         if ((dest_fd = open(new_link_file, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     255                 :             :                             pg_file_create_mode)) < 0)
     256                 :           0 :             pg_fatal("could not create file \"%s\": %m",
     257                 :             :                      new_link_file);
     258                 :             : 
     259         [ -  + ]:           1 :         if (copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0) < 0)
     260                 :           0 :             pg_fatal("could not copy file range between old and new data directories: %m");
     261                 :             : 
     262                 :           1 :         close(src_fd);
     263                 :           1 :         close(dest_fd);
     264                 :             :     }
     265                 :             : #else
     266                 :             :     pg_fatal("copy_file_range not supported on this platform");
     267                 :             : #endif
     268                 :             : 
     269                 :           1 :     unlink(new_link_file);
     270                 :           1 : }
     271                 :             : 
     272                 :             : void
     273                 :           2 : check_hard_link(transferMode transfer_mode)
     274                 :             : {
     275                 :             :     char        existing_file[MAXPGPATH];
     276                 :             :     char        new_link_file[MAXPGPATH];
     277                 :             : 
     278                 :           2 :     snprintf(existing_file, sizeof(existing_file), "%s/PG_VERSION", old_cluster.pgdata);
     279                 :           2 :     snprintf(new_link_file, sizeof(new_link_file), "%s/PG_VERSION.linktest", new_cluster.pgdata);
     280                 :           2 :     unlink(new_link_file);      /* might fail */
     281                 :             : 
     282         [ -  + ]:           2 :     if (link(existing_file, new_link_file) < 0)
     283                 :             :     {
     284         [ #  # ]:           0 :         if (transfer_mode == TRANSFER_MODE_LINK)
     285                 :           0 :             pg_fatal("could not create hard link between old and new data directories: %m\n"
     286                 :             :                      "In link mode the old and new data directories must be on the same file system.");
     287         [ #  # ]:           0 :         else if (transfer_mode == TRANSFER_MODE_SWAP)
     288                 :           0 :             pg_fatal("could not create hard link between old and new data directories: %m\n"
     289                 :             :                      "In swap mode the old and new data directories must be on the same file system.");
     290                 :             :         else
     291                 :           0 :             pg_fatal("unrecognized transfer mode");
     292                 :             :     }
     293                 :             : 
     294                 :           2 :     unlink(new_link_file);
     295                 :           2 : }
        

Generated by: LCOV version 2.0-1