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

Generated by: LCOV version 1.14