LCOV - code coverage report
Current view: top level - src/common - controldata_utils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 69.6 % 69 48
Test Date: 2026-03-12 06:14:44 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * controldata_utils.c
       4              :  *      Common code for control data file output.
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *    src/common/controldata_utils.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : 
      17              : #ifndef FRONTEND
      18              : #include "postgres.h"
      19              : #else
      20              : #include "postgres_fe.h"
      21              : #endif
      22              : 
      23              : #include <unistd.h>
      24              : #include <sys/stat.h>
      25              : #include <fcntl.h>
      26              : #include <time.h>
      27              : 
      28              : #include "access/xlog_internal.h"
      29              : #include "catalog/pg_control.h"
      30              : #include "common/controldata_utils.h"
      31              : #ifdef FRONTEND
      32              : #include "common/file_perm.h"
      33              : #include "common/logging.h"
      34              : #endif
      35              : #include "port/pg_crc32c.h"
      36              : 
      37              : #ifndef FRONTEND
      38              : #include "pgstat.h"
      39              : #include "storage/fd.h"
      40              : #include "utils/wait_event.h"
      41              : #endif
      42              : 
      43              : /*
      44              :  * get_controlfile()
      45              :  *
      46              :  * Get controlfile values.  The result is returned as a palloc'd copy of the
      47              :  * control file data.
      48              :  *
      49              :  * crc_ok_p can be used by the caller to see whether the CRC of the control
      50              :  * file data is correct.
      51              :  */
      52              : ControlFileData *
      53          322 : get_controlfile(const char *DataDir, bool *crc_ok_p)
      54              : {
      55              :     char        ControlFilePath[MAXPGPATH];
      56              : 
      57          322 :     snprintf(ControlFilePath, MAXPGPATH, "%s/%s", DataDir, XLOG_CONTROL_FILE);
      58              : 
      59          322 :     return get_controlfile_by_exact_path(ControlFilePath, crc_ok_p);
      60              : }
      61              : 
      62              : /*
      63              :  * get_controlfile_by_exact_path()
      64              :  *
      65              :  * As above, but the caller specifies the path to the control file itself,
      66              :  * rather than the path to the data directory.
      67              :  */
      68              : ControlFileData *
      69          420 : get_controlfile_by_exact_path(const char *ControlFilePath, bool *crc_ok_p)
      70              : {
      71              :     ControlFileData *ControlFile;
      72              :     int         fd;
      73              :     pg_crc32c   crc;
      74              :     int         r;
      75              : #ifdef FRONTEND
      76              :     pg_crc32c   last_crc;
      77          389 :     int         retries = 0;
      78              : #endif
      79              : 
      80              :     Assert(crc_ok_p);
      81              : 
      82          420 :     ControlFile = palloc_object(ControlFileData);
      83              : 
      84              : #ifdef FRONTEND
      85          389 :     INIT_CRC32C(last_crc);
      86              : 
      87          390 : retry:
      88              : #endif
      89              : 
      90              : #ifndef FRONTEND
      91           31 :     if ((fd = OpenTransientFile(ControlFilePath, O_RDONLY | PG_BINARY)) == -1)
      92            0 :         ereport(ERROR,
      93              :                 (errcode_for_file_access(),
      94              :                  errmsg("could not open file \"%s\" for reading: %m",
      95              :                         ControlFilePath)));
      96              : #else
      97          390 :     if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
      98            1 :         pg_fatal("could not open file \"%s\" for reading: %m",
      99              :                  ControlFilePath);
     100              : #endif
     101              : 
     102          420 :     r = read(fd, ControlFile, sizeof(ControlFileData));
     103          420 :     if (r != sizeof(ControlFileData))
     104              :     {
     105            0 :         if (r < 0)
     106              : #ifndef FRONTEND
     107            0 :             ereport(ERROR,
     108              :                     (errcode_for_file_access(),
     109              :                      errmsg("could not read file \"%s\": %m", ControlFilePath)));
     110              : #else
     111            0 :             pg_fatal("could not read file \"%s\": %m", ControlFilePath);
     112              : #endif
     113              :         else
     114              : #ifndef FRONTEND
     115            0 :             ereport(ERROR,
     116              :                     (errcode(ERRCODE_DATA_CORRUPTED),
     117              :                      errmsg("could not read file \"%s\": read %d of %zu",
     118              :                             ControlFilePath, r, sizeof(ControlFileData))));
     119              : #else
     120            0 :             pg_fatal("could not read file \"%s\": read %d of %zu",
     121              :                      ControlFilePath, r, sizeof(ControlFileData));
     122              : #endif
     123              :     }
     124              : 
     125              : #ifndef FRONTEND
     126           31 :     if (CloseTransientFile(fd) != 0)
     127            0 :         ereport(ERROR,
     128              :                 (errcode_for_file_access(),
     129              :                  errmsg("could not close file \"%s\": %m",
     130              :                         ControlFilePath)));
     131              : #else
     132          389 :     if (close(fd) != 0)
     133            0 :         pg_fatal("could not close file \"%s\": %m", ControlFilePath);
     134              : #endif
     135              : 
     136              :     /* Check the CRC. */
     137          420 :     INIT_CRC32C(crc);
     138          420 :     COMP_CRC32C(crc,
     139              :                 ControlFile,
     140              :                 offsetof(ControlFileData, crc));
     141          420 :     FIN_CRC32C(crc);
     142              : 
     143          420 :     *crc_ok_p = EQ_CRC32C(crc, ControlFile->crc);
     144              : 
     145              : #ifdef FRONTEND
     146              : 
     147              :     /*
     148              :      * If the server was writing at the same time, it is possible that we read
     149              :      * partially updated contents on some systems.  If the CRC doesn't match,
     150              :      * retry a limited number of times until we compute the same bad CRC twice
     151              :      * in a row with a short sleep in between.  Then the failure is unlikely
     152              :      * to be due to a concurrent write.
     153              :      */
     154          389 :     if (!*crc_ok_p &&
     155            2 :         (retries == 0 || !EQ_CRC32C(crc, last_crc)) &&
     156              :         retries < 10)
     157              :     {
     158            1 :         retries++;
     159            1 :         last_crc = crc;
     160            1 :         pg_usleep(10000);
     161            1 :         goto retry;
     162              :     }
     163              : #endif
     164              : 
     165              :     /* Make sure the control file is valid byte order. */
     166          419 :     if (ControlFile->pg_control_version % 65536 == 0 &&
     167            0 :         ControlFile->pg_control_version / 65536 != 0)
     168              : #ifndef FRONTEND
     169            0 :         elog(ERROR, _("byte ordering mismatch"));
     170              : #else
     171            0 :         pg_log_warning("possible byte ordering mismatch\n"
     172              :                        "The byte ordering used to store the pg_control file might not match the one\n"
     173              :                        "used by this program.  In that case the results below would be incorrect, and\n"
     174              :                        "the PostgreSQL installation would be incompatible with this data directory.");
     175              : #endif
     176              : 
     177          419 :     return ControlFile;
     178              : }
     179              : 
     180              : /*
     181              :  * update_controlfile()
     182              :  *
     183              :  * Update controlfile values with the contents given by caller.  The
     184              :  * contents to write are included in "ControlFile". "do_sync" can be
     185              :  * optionally used to flush the updated control file.  Note that it is up
     186              :  * to the caller to properly lock ControlFileLock when calling this
     187              :  * routine in the backend.
     188              :  */
     189              : void
     190         9700 : update_controlfile(const char *DataDir,
     191              :                    ControlFileData *ControlFile, bool do_sync)
     192              : {
     193              :     int         fd;
     194              :     char        buffer[PG_CONTROL_FILE_SIZE];
     195              :     char        ControlFilePath[MAXPGPATH];
     196              : 
     197              :     /* Update timestamp  */
     198         9700 :     ControlFile->time = (pg_time_t) time(NULL);
     199              : 
     200              :     /* Recalculate CRC of control file */
     201         9700 :     INIT_CRC32C(ControlFile->crc);
     202         9700 :     COMP_CRC32C(ControlFile->crc,
     203              :                 ControlFile,
     204              :                 offsetof(ControlFileData, crc));
     205         9700 :     FIN_CRC32C(ControlFile->crc);
     206              : 
     207              :     /*
     208              :      * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding
     209              :      * the excess over sizeof(ControlFileData), to avoid premature EOF related
     210              :      * errors when reading it.
     211              :      */
     212         9700 :     memset(buffer, 0, PG_CONTROL_FILE_SIZE);
     213         9700 :     memcpy(buffer, ControlFile, sizeof(ControlFileData));
     214              : 
     215         9700 :     snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s", DataDir, XLOG_CONTROL_FILE);
     216              : 
     217              : #ifndef FRONTEND
     218              : 
     219              :     /*
     220              :      * All errors issue a PANIC, so no need to use OpenTransientFile() and to
     221              :      * worry about file descriptor leaks.
     222              :      */
     223         9611 :     if ((fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY)) < 0)
     224            0 :         ereport(PANIC,
     225              :                 (errcode_for_file_access(),
     226              :                  errmsg("could not open file \"%s\": %m",
     227              :                         ControlFilePath)));
     228              : #else
     229           89 :     if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
     230              :                    pg_file_create_mode)) == -1)
     231            0 :         pg_fatal("could not open file \"%s\": %m", ControlFilePath);
     232              : #endif
     233              : 
     234         9700 :     errno = 0;
     235              : #ifndef FRONTEND
     236         9611 :     pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE);
     237              : #endif
     238         9700 :     if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
     239              :     {
     240              :         /* if write didn't set errno, assume problem is no disk space */
     241            0 :         if (errno == 0)
     242            0 :             errno = ENOSPC;
     243              : 
     244              : #ifndef FRONTEND
     245            0 :         ereport(PANIC,
     246              :                 (errcode_for_file_access(),
     247              :                  errmsg("could not write file \"%s\": %m",
     248              :                         ControlFilePath)));
     249              : #else
     250            0 :         pg_fatal("could not write file \"%s\": %m", ControlFilePath);
     251              : #endif
     252              :     }
     253              : #ifndef FRONTEND
     254         9611 :     pgstat_report_wait_end();
     255              : #endif
     256              : 
     257         9700 :     if (do_sync)
     258              :     {
     259              : #ifndef FRONTEND
     260         9611 :         pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE);
     261         9611 :         if (pg_fsync(fd) != 0)
     262            0 :             ereport(PANIC,
     263              :                     (errcode_for_file_access(),
     264              :                      errmsg("could not fsync file \"%s\": %m",
     265              :                             ControlFilePath)));
     266         9611 :         pgstat_report_wait_end();
     267              : #else
     268           75 :         if (fsync(fd) != 0)
     269            0 :             pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
     270              : #endif
     271              :     }
     272              : 
     273         9700 :     if (close(fd) != 0)
     274              :     {
     275              : #ifndef FRONTEND
     276            0 :         ereport(PANIC,
     277              :                 (errcode_for_file_access(),
     278              :                  errmsg("could not close file \"%s\": %m",
     279              :                         ControlFilePath)));
     280              : #else
     281            0 :         pg_fatal("could not close file \"%s\": %m", ControlFilePath);
     282              : #endif
     283              :     }
     284         9700 : }
        

Generated by: LCOV version 2.0-1