LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - slru_io.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 126 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 9 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * slru_io.c
       3              :  *
       4              :  * Routines for reading and writing SLRU files during upgrade.
       5              :  *
       6              :  * Copyright (c) 2025-2026, PostgreSQL Global Development Group
       7              :  * src/bin/pg_upgrade/slru_io.c
       8              :  */
       9              : 
      10              : #include "postgres_fe.h"
      11              : 
      12              : #include <fcntl.h>
      13              : 
      14              : #include "common/fe_memutils.h"
      15              : #include "common/file_perm.h"
      16              : #include "common/file_utils.h"
      17              : #include "pg_upgrade.h"
      18              : #include "port/pg_iovec.h"
      19              : #include "slru_io.h"
      20              : 
      21              : static SlruSegState *AllocSlruSegState(const char *dir);
      22              : static char *SlruFileName(SlruSegState *state, int64 segno);
      23              : static void SlruFlush(SlruSegState *state);
      24              : 
      25              : /* common parts of AllocSlruRead and AllocSlruWrite */
      26              : static SlruSegState *
      27            0 : AllocSlruSegState(const char *dir)
      28              : {
      29            0 :         SlruSegState *state = pg_malloc(sizeof(*state));
      30              : 
      31            0 :         state->dir = pstrdup(dir);
      32            0 :         state->fn = NULL;
      33            0 :         state->fd = -1;
      34            0 :         state->segno = -1;
      35            0 :         state->pageno = 0;
      36              : 
      37              :         /* state->writing and state->long_segment_names must be set by caller! */
      38              : 
      39            0 :         return state;
      40            0 : }
      41              : 
      42              : /* similar to the backend function with the same name */
      43              : static char *
      44            0 : SlruFileName(SlruSegState *state, int64 segno)
      45              : {
      46            0 :         if (state->long_segment_names)
      47              :         {
      48            0 :                 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFFFFFFFFFFF));
      49            0 :                 return psprintf("%s/%015" PRIX64, state->dir, segno);
      50              :         }
      51              :         else
      52              :         {
      53            0 :                 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFF));
      54            0 :                 return psprintf("%s/%04X", state->dir, (unsigned int) segno);
      55              :         }
      56            0 : }
      57              : 
      58              : /*
      59              :  * Create SLRU reader for dir.
      60              :  */
      61              : SlruSegState *
      62            0 : AllocSlruRead(const char *dir, bool long_segment_names)
      63              : {
      64            0 :         SlruSegState *state = AllocSlruSegState(dir);
      65              : 
      66            0 :         state->writing = false;
      67            0 :         state->long_segment_names = long_segment_names;
      68              : 
      69            0 :         return state;
      70            0 : }
      71              : 
      72              : /*
      73              :  * Read the given page into memory buffer.
      74              :  *
      75              :  * Reading can be done in random order.
      76              :  *
      77              :  * If the file containing 'pageno' does not exist, a fatal error is raised.
      78              :  * If the file exists but is shorter than expected, the missing part is read
      79              :  * as zeros and a warning is logged.  That is reasonable behavior for current
      80              :  * callers.
      81              :  *
      82              :  * This is the slow path of the inlineable SlruReadSwitchPage() function.
      83              :  */
      84              : char *
      85            0 : SlruReadSwitchPageSlow(SlruSegState *state, uint64 pageno)
      86              : {
      87            0 :         int64           segno;
      88            0 :         off_t           offset;
      89            0 :         ssize_t         bytes_read;
      90              : 
      91            0 :         Assert(!state->writing);     /* read only mode */
      92              : 
      93            0 :         if (state->segno != -1 && pageno == state->pageno)
      94            0 :                 return state->buf.data;
      95              : 
      96              :         /* If the new page is on a different SLRU segment, open the new segment */
      97            0 :         segno = pageno / SLRU_PAGES_PER_SEGMENT;
      98            0 :         if (segno != state->segno)
      99              :         {
     100            0 :                 if (state->segno != -1)
     101              :                 {
     102            0 :                         close(state->fd);
     103            0 :                         state->fd = -1;
     104              : 
     105            0 :                         pg_free(state->fn);
     106            0 :                         state->fn = NULL;
     107              : 
     108            0 :                         state->segno = -1;
     109            0 :                 }
     110              : 
     111            0 :                 state->fn = SlruFileName(state, segno);
     112            0 :                 if ((state->fd = open(state->fn, O_RDONLY | PG_BINARY, 0)) < 0)
     113            0 :                         pg_fatal("could not open file \"%s\": %m", state->fn);
     114            0 :                 state->segno = segno;
     115            0 :         }
     116              : 
     117            0 :         offset = (pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
     118            0 :         bytes_read = 0;
     119            0 :         while (bytes_read < BLCKSZ)
     120              :         {
     121            0 :                 ssize_t         rc;
     122              : 
     123            0 :                 rc = pg_pread(state->fd,
     124            0 :                                           &state->buf.data[bytes_read],
     125            0 :                                           BLCKSZ - bytes_read,
     126            0 :                                           offset);
     127            0 :                 if (rc < 0)
     128              :                 {
     129            0 :                         if (errno == EINTR)
     130            0 :                                 continue;
     131            0 :                         pg_fatal("could not read file \"%s\": %m", state->fn);
     132              :                 }
     133            0 :                 if (rc == 0)
     134              :                 {
     135              :                         /* unexpected EOF */
     136            0 :                         pg_log(PG_WARNING, "unexpected EOF reading file \"%s\" at offset %u, reading as zeros",
     137            0 :                                    state->fn, (unsigned int) offset);
     138            0 :                         memset(&state->buf.data[bytes_read], 0, BLCKSZ - bytes_read);
     139            0 :                         break;
     140              :                 }
     141            0 :                 bytes_read += rc;
     142            0 :                 offset += rc;
     143            0 :         }
     144            0 :         state->pageno = pageno;
     145              : 
     146            0 :         return state->buf.data;
     147            0 : }
     148              : 
     149              : /*
     150              :  * Free the reader.
     151              :  */
     152              : void
     153            0 : FreeSlruRead(SlruSegState *state)
     154              : {
     155            0 :         Assert(!state->writing);     /* read only mode */
     156              : 
     157            0 :         if (state->fd != -1)
     158            0 :                 close(state->fd);
     159            0 :         pg_free(state);
     160            0 : }
     161              : 
     162              : /*
     163              :  * Create SLRU writer for dir.
     164              :  */
     165              : SlruSegState *
     166            0 : AllocSlruWrite(const char *dir, bool long_segment_names)
     167              : {
     168            0 :         SlruSegState *state = AllocSlruSegState(dir);
     169              : 
     170            0 :         state->writing = true;
     171            0 :         state->long_segment_names = long_segment_names;
     172              : 
     173            0 :         return state;
     174            0 : }
     175              : 
     176              : /*
     177              :  * Open the given page for writing.
     178              :  *
     179              :  * NOTE: This uses O_EXCL when stepping to a new segment, so this assumes that
     180              :  * each segment is written in full before moving on to the next one.  This
     181              :  * limitation would be easy to lift if needed, but it fits the usage pattern
     182              :  * of current callers.
     183              :  *
     184              :  * This is the slow path of the inlineable SlruWriteSwitchPage() function.
     185              :  */
     186              : char *
     187            0 : SlruWriteSwitchPageSlow(SlruSegState *state, uint64 pageno)
     188              : {
     189            0 :         int64           segno;
     190            0 :         off_t           offset;
     191              : 
     192            0 :         Assert(state->writing);
     193              : 
     194            0 :         if (state->segno != -1 && pageno == state->pageno)
     195            0 :                 return state->buf.data;
     196              : 
     197            0 :         segno = pageno / SLRU_PAGES_PER_SEGMENT;
     198            0 :         offset = (pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
     199              : 
     200            0 :         SlruFlush(state);
     201            0 :         memset(state->buf.data, 0, BLCKSZ);
     202              : 
     203            0 :         if (segno != state->segno)
     204              :         {
     205            0 :                 if (state->segno != -1)
     206              :                 {
     207            0 :                         close(state->fd);
     208            0 :                         state->fd = -1;
     209              : 
     210            0 :                         pg_free(state->fn);
     211            0 :                         state->fn = NULL;
     212              : 
     213            0 :                         state->segno = -1;
     214            0 :                 }
     215              : 
     216              :                 /* Create the segment */
     217            0 :                 state->fn = SlruFileName(state, segno);
     218            0 :                 if ((state->fd = open(state->fn, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     219            0 :                                                           pg_file_create_mode)) < 0)
     220              :                 {
     221            0 :                         pg_fatal("could not create file \"%s\": %m", state->fn);
     222              :                 }
     223              : 
     224            0 :                 state->segno = segno;
     225              : 
     226            0 :                 if (offset > 0)
     227              :                 {
     228            0 :                         if (pg_pwrite_zeros(state->fd, offset, 0) < 0)
     229            0 :                                 pg_fatal("could not write file \"%s\": %m", state->fn);
     230            0 :                 }
     231            0 :         }
     232              : 
     233            0 :         state->pageno = pageno;
     234              : 
     235            0 :         return state->buf.data;
     236            0 : }
     237              : 
     238              : static void
     239            0 : SlruFlush(SlruSegState *state)
     240              : {
     241            0 :         struct iovec iovec = {
     242            0 :                 .iov_base = &state->buf,
     243              :                 .iov_len = BLCKSZ,
     244              :         };
     245            0 :         off_t           offset;
     246              : 
     247            0 :         if (state->segno == -1)
     248            0 :                 return;
     249              : 
     250            0 :         offset = (state->pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
     251              : 
     252            0 :         if (pg_pwritev_with_retry(state->fd, &iovec, 1, offset) < 0)
     253            0 :                 pg_fatal("could not write file \"%s\": %m", state->fn);
     254            0 : }
     255              : 
     256              : /*
     257              :  * Free the writer.
     258              :  */
     259              : void
     260            0 : FreeSlruWrite(SlruSegState *state)
     261              : {
     262            0 :         Assert(state->writing);
     263              : 
     264            0 :         SlruFlush(state);
     265              : 
     266            0 :         if (state->fd != -1)
     267            0 :                 close(state->fd);
     268            0 :         pg_free(state);
     269            0 : }
        

Generated by: LCOV version 2.3.2-1