LCOV - code coverage report
Current view: top level - src/bin/pg_basebackup - bbstreamer_inject.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 61 74 82.4 %
Date: 2024-04-13 08:11:04 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * bbstreamer_inject.c
       4             :  *
       5             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       6             :  *
       7             :  * IDENTIFICATION
       8             :  *        src/bin/pg_basebackup/bbstreamer_inject.c
       9             :  *-------------------------------------------------------------------------
      10             :  */
      11             : 
      12             : #include "postgres_fe.h"
      13             : 
      14             : #include "bbstreamer.h"
      15             : #include "common/file_perm.h"
      16             : #include "common/logging.h"
      17             : 
      18             : typedef struct bbstreamer_recovery_injector
      19             : {
      20             :     bbstreamer  base;
      21             :     bool        skip_file;
      22             :     bool        is_recovery_guc_supported;
      23             :     bool        is_postgresql_auto_conf;
      24             :     bool        found_postgresql_auto_conf;
      25             :     PQExpBuffer recoveryconfcontents;
      26             :     bbstreamer_member member;
      27             : } bbstreamer_recovery_injector;
      28             : 
      29             : static void bbstreamer_recovery_injector_content(bbstreamer *streamer,
      30             :                                                  bbstreamer_member *member,
      31             :                                                  const char *data, int len,
      32             :                                                  bbstreamer_archive_context context);
      33             : static void bbstreamer_recovery_injector_finalize(bbstreamer *streamer);
      34             : static void bbstreamer_recovery_injector_free(bbstreamer *streamer);
      35             : 
      36             : const bbstreamer_ops bbstreamer_recovery_injector_ops = {
      37             :     .content = bbstreamer_recovery_injector_content,
      38             :     .finalize = bbstreamer_recovery_injector_finalize,
      39             :     .free = bbstreamer_recovery_injector_free
      40             : };
      41             : 
      42             : /*
      43             :  * Create a bbstreamer that can edit recoverydata into an archive stream.
      44             :  *
      45             :  * The input should be a series of typed chunks (not BBSTREAMER_UNKNOWN) as
      46             :  * per the conventions described in bbstreamer.h; the chunks forwarded to
      47             :  * the next bbstreamer will be similarly typed, but the
      48             :  * BBSTREAMER_MEMBER_HEADER chunks may be zero-length in cases where we've
      49             :  * edited the archive stream.
      50             :  *
      51             :  * Our goal is to do one of the following three things with the content passed
      52             :  * via recoveryconfcontents: (1) if is_recovery_guc_supported is false, then
      53             :  * put the content into recovery.conf, replacing any existing archive member
      54             :  * by that name; (2) if is_recovery_guc_supported is true and
      55             :  * postgresql.auto.conf exists in the archive, then append the content
      56             :  * provided to the existing file; and (3) if is_recovery_guc_supported is
      57             :  * true but postgresql.auto.conf does not exist in the archive, then create
      58             :  * it with the specified content.
      59             :  *
      60             :  * In addition, if is_recovery_guc_supported is true, then we create a
      61             :  * zero-length standby.signal file, dropping any file with that name from
      62             :  * the archive.
      63             :  */
      64             : extern bbstreamer *
      65           6 : bbstreamer_recovery_injector_new(bbstreamer *next,
      66             :                                  bool is_recovery_guc_supported,
      67             :                                  PQExpBuffer recoveryconfcontents)
      68             : {
      69             :     bbstreamer_recovery_injector *streamer;
      70             : 
      71           6 :     streamer = palloc0(sizeof(bbstreamer_recovery_injector));
      72           6 :     *((const bbstreamer_ops **) &streamer->base.bbs_ops) =
      73             :         &bbstreamer_recovery_injector_ops;
      74           6 :     streamer->base.bbs_next = next;
      75           6 :     streamer->is_recovery_guc_supported = is_recovery_guc_supported;
      76           6 :     streamer->recoveryconfcontents = recoveryconfcontents;
      77             : 
      78           6 :     return &streamer->base;
      79             : }
      80             : 
      81             : /*
      82             :  * Handle each chunk of tar content while injecting recovery configuration.
      83             :  */
      84             : static void
      85       18906 : bbstreamer_recovery_injector_content(bbstreamer *streamer,
      86             :                                      bbstreamer_member *member,
      87             :                                      const char *data, int len,
      88             :                                      bbstreamer_archive_context context)
      89             : {
      90             :     bbstreamer_recovery_injector *mystreamer;
      91             : 
      92       18906 :     mystreamer = (bbstreamer_recovery_injector *) streamer;
      93             :     Assert(member != NULL || context == BBSTREAMER_ARCHIVE_TRAILER);
      94             : 
      95       18906 :     switch (context)
      96             :     {
      97        5964 :         case BBSTREAMER_MEMBER_HEADER:
      98             :             /* Must copy provided data so we have the option to modify it. */
      99        5964 :             memcpy(&mystreamer->member, member, sizeof(bbstreamer_member));
     100             : 
     101             :             /*
     102             :              * On v12+, skip standby.signal and edit postgresql.auto.conf; on
     103             :              * older versions, skip recovery.conf.
     104             :              */
     105        5964 :             if (mystreamer->is_recovery_guc_supported)
     106             :             {
     107        5964 :                 mystreamer->skip_file =
     108        5964 :                     (strcmp(member->pathname, "standby.signal") == 0);
     109        5964 :                 mystreamer->is_postgresql_auto_conf =
     110        5964 :                     (strcmp(member->pathname, "postgresql.auto.conf") == 0);
     111        5964 :                 if (mystreamer->is_postgresql_auto_conf)
     112             :                 {
     113             :                     /* Remember we saw it so we don't add it again. */
     114           6 :                     mystreamer->found_postgresql_auto_conf = true;
     115             : 
     116             :                     /* Increment length by data to be injected. */
     117           6 :                     mystreamer->member.size +=
     118           6 :                         mystreamer->recoveryconfcontents->len;
     119             : 
     120             :                     /*
     121             :                      * Zap data and len because the archive header is no
     122             :                      * longer valid; some subsequent bbstreamer must
     123             :                      * regenerate it if it's necessary.
     124             :                      */
     125           6 :                     data = NULL;
     126           6 :                     len = 0;
     127             :                 }
     128             :             }
     129             :             else
     130           0 :                 mystreamer->skip_file =
     131           0 :                     (strcmp(member->pathname, "recovery.conf") == 0);
     132             : 
     133             :             /* Do not forward if the file is to be skipped. */
     134        5964 :             if (mystreamer->skip_file)
     135           0 :                 return;
     136        5964 :             break;
     137             : 
     138        6972 :         case BBSTREAMER_MEMBER_CONTENTS:
     139             :             /* Do not forward if the file is to be skipped. */
     140        6972 :             if (mystreamer->skip_file)
     141           0 :                 return;
     142        6972 :             break;
     143             : 
     144        5964 :         case BBSTREAMER_MEMBER_TRAILER:
     145             :             /* Do not forward it the file is to be skipped. */
     146        5964 :             if (mystreamer->skip_file)
     147           0 :                 return;
     148             : 
     149             :             /* Append provided content to whatever we already sent. */
     150        5964 :             if (mystreamer->is_postgresql_auto_conf)
     151           6 :                 bbstreamer_content(mystreamer->base.bbs_next, member,
     152           6 :                                    mystreamer->recoveryconfcontents->data,
     153           6 :                                    mystreamer->recoveryconfcontents->len,
     154             :                                    BBSTREAMER_MEMBER_CONTENTS);
     155        5964 :             break;
     156             : 
     157           6 :         case BBSTREAMER_ARCHIVE_TRAILER:
     158           6 :             if (mystreamer->is_recovery_guc_supported)
     159             :             {
     160             :                 /*
     161             :                  * If we didn't already find (and thus modify)
     162             :                  * postgresql.auto.conf, inject it as an additional archive
     163             :                  * member now.
     164             :                  */
     165           6 :                 if (!mystreamer->found_postgresql_auto_conf)
     166           0 :                     bbstreamer_inject_file(mystreamer->base.bbs_next,
     167             :                                            "postgresql.auto.conf",
     168           0 :                                            mystreamer->recoveryconfcontents->data,
     169           0 :                                            mystreamer->recoveryconfcontents->len);
     170             : 
     171             :                 /* Inject empty standby.signal file. */
     172           6 :                 bbstreamer_inject_file(mystreamer->base.bbs_next,
     173             :                                        "standby.signal", "", 0);
     174             :             }
     175             :             else
     176             :             {
     177             :                 /* Inject recovery.conf file with specified contents. */
     178           0 :                 bbstreamer_inject_file(mystreamer->base.bbs_next,
     179             :                                        "recovery.conf",
     180           0 :                                        mystreamer->recoveryconfcontents->data,
     181           0 :                                        mystreamer->recoveryconfcontents->len);
     182             :             }
     183             : 
     184             :             /* Nothing to do here. */
     185           6 :             break;
     186             : 
     187           0 :         default:
     188             :             /* Shouldn't happen. */
     189           0 :             pg_fatal("unexpected state while injecting recovery settings");
     190             :     }
     191             : 
     192       18906 :     bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
     193             :                        data, len, context);
     194             : }
     195             : 
     196             : /*
     197             :  * End-of-stream processing for this bbstreamer.
     198             :  */
     199             : static void
     200           6 : bbstreamer_recovery_injector_finalize(bbstreamer *streamer)
     201             : {
     202           6 :     bbstreamer_finalize(streamer->bbs_next);
     203           6 : }
     204             : 
     205             : /*
     206             :  * Free memory associated with this bbstreamer.
     207             :  */
     208             : static void
     209           6 : bbstreamer_recovery_injector_free(bbstreamer *streamer)
     210             : {
     211           6 :     bbstreamer_free(streamer->bbs_next);
     212           6 :     pfree(streamer);
     213           6 : }
     214             : 
     215             : /*
     216             :  * Inject a member into the archive with specified contents.
     217             :  */
     218             : void
     219           6 : bbstreamer_inject_file(bbstreamer *streamer, char *pathname, char *data,
     220             :                        int len)
     221             : {
     222             :     bbstreamer_member member;
     223             : 
     224           6 :     strlcpy(member.pathname, pathname, MAXPGPATH);
     225           6 :     member.size = len;
     226           6 :     member.mode = pg_file_create_mode;
     227           6 :     member.is_directory = false;
     228           6 :     member.is_link = false;
     229           6 :     member.linktarget[0] = '\0';
     230             : 
     231             :     /*
     232             :      * There seems to be no principled argument for these values, but they are
     233             :      * what PostgreSQL has historically used.
     234             :      */
     235           6 :     member.uid = 04000;
     236           6 :     member.gid = 02000;
     237             : 
     238             :     /*
     239             :      * We don't know here how to generate valid member headers and trailers
     240             :      * for the archiving format in use, so if those are needed, some successor
     241             :      * bbstreamer will have to generate them using the data from 'member'.
     242             :      */
     243           6 :     bbstreamer_content(streamer, &member, NULL, 0,
     244             :                        BBSTREAMER_MEMBER_HEADER);
     245           6 :     bbstreamer_content(streamer, &member, data, len,
     246             :                        BBSTREAMER_MEMBER_CONTENTS);
     247           6 :     bbstreamer_content(streamer, &member, NULL, 0,
     248             :                        BBSTREAMER_MEMBER_TRAILER);
     249           6 : }

Generated by: LCOV version 1.14