LCOV - code coverage report
Current view: top level - src/fe_utils - astreamer_tar.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 59.2 % 157 93
Test Date: 2026-07-03 19:57:34 Functions: 38.5 % 13 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 55.8 % 52 29

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * astreamer_tar.c
       4                 :             :  *
       5                 :             :  * This module implements three types of tar processing. A tar parser
       6                 :             :  * expects unlabelled chunks of data (e.g. ASTREAMER_UNKNOWN) and splits
       7                 :             :  * it into labelled chunks (any other value of astreamer_archive_context).
       8                 :             :  * A tar archiver does the reverse: it takes a bunch of labelled chunks
       9                 :             :  * and produces a tarfile, optionally replacing member headers and trailers
      10                 :             :  * so that upstream astreamer objects can perform surgery on the tarfile
      11                 :             :  * contents without knowing the details of the tar format. A tar terminator
      12                 :             :  * just adds two blocks of NUL bytes to the end of the file, since older
      13                 :             :  * server versions produce files with this terminator omitted.
      14                 :             :  *
      15                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      16                 :             :  *
      17                 :             :  * IDENTIFICATION
      18                 :             :  *        src/fe_utils/astreamer_tar.c
      19                 :             :  *-------------------------------------------------------------------------
      20                 :             :  */
      21                 :             : 
      22                 :             : #include "postgres_fe.h"
      23                 :             : 
      24                 :             : #include <time.h>
      25                 :             : 
      26                 :             : #include "common/logging.h"
      27                 :             : #include "fe_utils/astreamer.h"
      28                 :             : #include "pgtar.h"
      29                 :             : 
      30                 :             : typedef struct astreamer_tar_parser
      31                 :             : {
      32                 :             :     astreamer   base;
      33                 :             :     astreamer_archive_context next_context;
      34                 :             :     astreamer_member member;
      35                 :             :     size_t      file_bytes_sent;
      36                 :             :     size_t      pad_bytes_expected;
      37                 :             : } astreamer_tar_parser;
      38                 :             : 
      39                 :             : typedef struct astreamer_tar_archiver
      40                 :             : {
      41                 :             :     astreamer   base;
      42                 :             :     bool        rearchive_member;
      43                 :             : } astreamer_tar_archiver;
      44                 :             : 
      45                 :             : static void astreamer_tar_parser_content(astreamer *streamer,
      46                 :             :                                          astreamer_member *member,
      47                 :             :                                          const char *data, int len,
      48                 :             :                                          astreamer_archive_context context);
      49                 :             : static void astreamer_tar_parser_finalize(astreamer *streamer);
      50                 :             : static void astreamer_tar_parser_free(astreamer *streamer);
      51                 :             : static bool astreamer_tar_header(astreamer_tar_parser *mystreamer);
      52                 :             : 
      53                 :             : static const astreamer_ops astreamer_tar_parser_ops = {
      54                 :             :     .content = astreamer_tar_parser_content,
      55                 :             :     .finalize = astreamer_tar_parser_finalize,
      56                 :             :     .free = astreamer_tar_parser_free
      57                 :             : };
      58                 :             : 
      59                 :             : static void astreamer_tar_archiver_content(astreamer *streamer,
      60                 :             :                                            astreamer_member *member,
      61                 :             :                                            const char *data, int len,
      62                 :             :                                            astreamer_archive_context context);
      63                 :             : static void astreamer_tar_archiver_finalize(astreamer *streamer);
      64                 :             : static void astreamer_tar_archiver_free(astreamer *streamer);
      65                 :             : 
      66                 :             : static const astreamer_ops astreamer_tar_archiver_ops = {
      67                 :             :     .content = astreamer_tar_archiver_content,
      68                 :             :     .finalize = astreamer_tar_archiver_finalize,
      69                 :             :     .free = astreamer_tar_archiver_free
      70                 :             : };
      71                 :             : 
      72                 :             : static void astreamer_tar_terminator_content(astreamer *streamer,
      73                 :             :                                              astreamer_member *member,
      74                 :             :                                              const char *data, int len,
      75                 :             :                                              astreamer_archive_context context);
      76                 :             : static void astreamer_tar_terminator_finalize(astreamer *streamer);
      77                 :             : static void astreamer_tar_terminator_free(astreamer *streamer);
      78                 :             : 
      79                 :             : static const astreamer_ops astreamer_tar_terminator_ops = {
      80                 :             :     .content = astreamer_tar_terminator_content,
      81                 :             :     .finalize = astreamer_tar_terminator_finalize,
      82                 :             :     .free = astreamer_tar_terminator_free
      83                 :             : };
      84                 :             : 
      85                 :             : /*
      86                 :             :  * Create a astreamer that can parse a stream of content as tar data.
      87                 :             :  *
      88                 :             :  * The input should be a series of ASTREAMER_UNKNOWN chunks; the astreamer
      89                 :             :  * specified by 'next' will receive a series of typed chunks, as per the
      90                 :             :  * conventions described in astreamer.h.
      91                 :             :  */
      92                 :             : astreamer *
      93                 :         265 : astreamer_tar_parser_new(astreamer *next)
      94                 :             : {
      95                 :             :     astreamer_tar_parser *streamer;
      96                 :             : 
      97                 :         265 :     streamer = palloc0_object(astreamer_tar_parser);
      98                 :         265 :     *((const astreamer_ops **) &streamer->base.bbs_ops) =
      99                 :             :         &astreamer_tar_parser_ops;
     100                 :         265 :     streamer->base.bbs_next = next;
     101                 :         265 :     initStringInfo(&streamer->base.bbs_buffer);
     102                 :         265 :     streamer->next_context = ASTREAMER_MEMBER_HEADER;
     103                 :             : 
     104                 :         265 :     return &streamer->base;
     105                 :             : }
     106                 :             : 
     107                 :             : /*
     108                 :             :  * Parse unknown content as tar data.
     109                 :             :  */
     110                 :             : static void
     111                 :      369886 : astreamer_tar_parser_content(astreamer *streamer, astreamer_member *member,
     112                 :             :                              const char *data, int len,
     113                 :             :                              astreamer_archive_context context)
     114                 :             : {
     115                 :      369886 :     astreamer_tar_parser *mystreamer = (astreamer_tar_parser *) streamer;
     116                 :             :     size_t      nbytes;
     117                 :             : 
     118                 :             :     /* Expect unparsed input. */
     119                 :             :     Assert(member == NULL);
     120                 :             :     Assert(context == ASTREAMER_UNKNOWN);
     121                 :             : 
     122         [ +  + ]:      801225 :     while (len > 0)
     123                 :             :     {
     124   [ +  +  +  +  :      431578 :         switch (mystreamer->next_context)
                      - ]
     125                 :             :         {
     126                 :      194721 :             case ASTREAMER_MEMBER_HEADER:
     127                 :             : 
     128                 :             :                 /*
     129                 :             :                  * If we're expecting an archive member header, accumulate a
     130                 :             :                  * full block of data before doing anything further.
     131                 :             :                  */
     132         [ -  + ]:      194721 :                 if (!astreamer_buffer_until(streamer, &data, &len,
     133                 :             :                                             TAR_BLOCK_SIZE))
     134                 :           0 :                     return;
     135                 :             : 
     136                 :             :                 /*
     137                 :             :                  * Now we can process the header and get ready to process the
     138                 :             :                  * file contents; however, we might find out that what we
     139                 :             :                  * thought was the next file header is actually the start of
     140                 :             :                  * the archive trailer. Switch modes accordingly.
     141                 :             :                  */
     142         [ +  + ]:      194721 :                 if (astreamer_tar_header(mystreamer))
     143                 :             :                 {
     144         [ +  + ]:      194483 :                     if (mystreamer->member.size == 0)
     145                 :             :                     {
     146                 :             :                         /* No content; trailer is zero-length. */
     147                 :       39934 :                         astreamer_content(mystreamer->base.bbs_next,
     148                 :             :                                           &mystreamer->member,
     149                 :             :                                           NULL, 0,
     150                 :             :                                           ASTREAMER_MEMBER_TRAILER);
     151                 :             : 
     152                 :             :                         /* Expect next header. */
     153                 :       39934 :                         mystreamer->next_context = ASTREAMER_MEMBER_HEADER;
     154                 :             :                     }
     155                 :             :                     else
     156                 :             :                     {
     157                 :             :                         /* Expect contents. */
     158                 :      154549 :                         mystreamer->next_context = ASTREAMER_MEMBER_CONTENTS;
     159                 :             :                     }
     160                 :      194483 :                     mystreamer->base.bbs_buffer.len = 0;
     161                 :      194483 :                     mystreamer->file_bytes_sent = 0;
     162                 :             :                 }
     163                 :             :                 else
     164                 :         238 :                     mystreamer->next_context = ASTREAMER_ARCHIVE_TRAILER;
     165                 :      194721 :                 break;
     166                 :             : 
     167                 :      226100 :             case ASTREAMER_MEMBER_CONTENTS:
     168                 :             : 
     169                 :             :                 /*
     170                 :             :                  * Send as much content as we have, but not more than the
     171                 :             :                  * remaining file length.
     172                 :             :                  */
     173                 :             :                 Assert(mystreamer->file_bytes_sent < mystreamer->member.size);
     174                 :      226100 :                 nbytes = mystreamer->member.size - mystreamer->file_bytes_sent;
     175                 :      226100 :                 nbytes = Min(nbytes, len);
     176                 :             :                 Assert(nbytes > 0);
     177                 :      226100 :                 astreamer_content(mystreamer->base.bbs_next,
     178                 :             :                                   &mystreamer->member,
     179                 :             :                                   data, nbytes,
     180                 :             :                                   ASTREAMER_MEMBER_CONTENTS);
     181                 :      226100 :                 mystreamer->file_bytes_sent += nbytes;
     182                 :      226100 :                 data += nbytes;
     183                 :      226100 :                 len -= nbytes;
     184                 :             : 
     185                 :             :                 /*
     186                 :             :                  * If we've not yet sent the whole file, then there's more
     187                 :             :                  * content to come; otherwise, it's time to expect the file
     188                 :             :                  * trailer.
     189                 :             :                  */
     190                 :             :                 Assert(mystreamer->file_bytes_sent <= mystreamer->member.size);
     191         [ +  + ]:      226100 :                 if (mystreamer->file_bytes_sent == mystreamer->member.size)
     192                 :             :                 {
     193         [ +  + ]:      154523 :                     if (mystreamer->pad_bytes_expected == 0)
     194                 :             :                     {
     195                 :             :                         /* Trailer is zero-length. */
     196                 :      144004 :                         astreamer_content(mystreamer->base.bbs_next,
     197                 :             :                                           &mystreamer->member,
     198                 :             :                                           NULL, 0,
     199                 :             :                                           ASTREAMER_MEMBER_TRAILER);
     200                 :             : 
     201                 :             :                         /* Expect next header. */
     202                 :      144003 :                         mystreamer->next_context = ASTREAMER_MEMBER_HEADER;
     203                 :             :                     }
     204                 :             :                     else
     205                 :             :                     {
     206                 :             :                         /* Trailer is not zero-length. */
     207                 :       10519 :                         mystreamer->next_context = ASTREAMER_MEMBER_TRAILER;
     208                 :             :                     }
     209                 :      154522 :                     mystreamer->base.bbs_buffer.len = 0;
     210                 :             :                 }
     211                 :      226099 :                 break;
     212                 :             : 
     213                 :       10519 :             case ASTREAMER_MEMBER_TRAILER:
     214                 :             : 
     215                 :             :                 /*
     216                 :             :                  * If we're expecting an archive member trailer, accumulate
     217                 :             :                  * the expected number of padding bytes before sending
     218                 :             :                  * anything onward.
     219                 :             :                  */
     220         [ -  + ]:       10519 :                 if (!astreamer_buffer_until(streamer, &data, &len,
     221                 :       10519 :                                             mystreamer->pad_bytes_expected))
     222                 :           0 :                     return;
     223                 :             : 
     224                 :             :                 /* OK, now we can send it. */
     225                 :       10519 :                 astreamer_content(mystreamer->base.bbs_next,
     226                 :             :                                   &mystreamer->member,
     227                 :       10519 :                                   mystreamer->base.bbs_buffer.data,
     228                 :       10519 :                                   mystreamer->pad_bytes_expected,
     229                 :             :                                   ASTREAMER_MEMBER_TRAILER);
     230                 :             : 
     231                 :             :                 /* Expect next file header. */
     232                 :       10519 :                 mystreamer->next_context = ASTREAMER_MEMBER_HEADER;
     233                 :       10519 :                 mystreamer->base.bbs_buffer.len = 0;
     234                 :       10519 :                 break;
     235                 :             : 
     236                 :         238 :             case ASTREAMER_ARCHIVE_TRAILER:
     237                 :             : 
     238                 :             :                 /*
     239                 :             :                  * We've seen an end-of-archive indicator, so anything more is
     240                 :             :                  * buffered and sent as part of the archive trailer.
     241                 :             :                  *
     242                 :             :                  * Per POSIX, the last physical block of a tar archive is
     243                 :             :                  * always full-sized, so there may be undefined data after the
     244                 :             :                  * two zero blocks that mark end-of-archive.  GNU tar, for
     245                 :             :                  * example, zero-pads to a 10kB boundary by default.  We just
     246                 :             :                  * buffer whatever we receive and pass it along at finalize
     247                 :             :                  * time.
     248                 :             :                  */
     249                 :         238 :                 astreamer_buffer_bytes(streamer, &data, &len, len);
     250                 :         238 :                 return;
     251                 :             : 
     252                 :           0 :             default:
     253                 :             :                 /* Shouldn't happen. */
     254                 :           0 :                 pg_fatal("unexpected state while parsing tar archive");
     255                 :             :         }
     256                 :             :     }
     257                 :             : }
     258                 :             : 
     259                 :             : /*
     260                 :             :  * Parse a file header within a tar stream.
     261                 :             :  *
     262                 :             :  * The return value is true if we found a file header and passed it on to the
     263                 :             :  * next astreamer; it is false if we have found the archive trailer.
     264                 :             :  * We throw error if we see invalid data.
     265                 :             :  */
     266                 :             : static bool
     267                 :      194721 : astreamer_tar_header(astreamer_tar_parser *mystreamer)
     268                 :             : {
     269                 :      194721 :     bool        has_nonzero_byte = false;
     270                 :             :     int         i;
     271                 :      194721 :     astreamer_member *member = &mystreamer->member;
     272                 :      194721 :     char       *buffer = mystreamer->base.bbs_buffer.data;
     273                 :             : 
     274                 :             :     Assert(mystreamer->base.bbs_buffer.len == TAR_BLOCK_SIZE);
     275                 :             : 
     276                 :             :     /* Zero out fields of *member, just for consistency. */
     277                 :      194721 :     memset(member, 0, sizeof(astreamer_member));
     278                 :             : 
     279                 :             :     /* Check whether we've got a block of all zero bytes. */
     280         [ +  + ]:      316577 :     for (i = 0; i < TAR_BLOCK_SIZE; ++i)
     281                 :             :     {
     282         [ +  + ]:      316339 :         if (buffer[i] != '\0')
     283                 :             :         {
     284                 :      194483 :             has_nonzero_byte = true;
     285                 :      194483 :             break;
     286                 :             :         }
     287                 :             :     }
     288                 :             : 
     289                 :             :     /*
     290                 :             :      * If the entire block was zeros, this is the end of the archive, not the
     291                 :             :      * start of the next file.
     292                 :             :      */
     293         [ +  + ]:      194721 :     if (!has_nonzero_byte)
     294                 :         238 :         return false;
     295                 :             : 
     296                 :             :     /*
     297                 :             :      * Verify that we have a reasonable-looking header.
     298                 :             :      */
     299         [ -  + ]:      194483 :     if (!isValidTarHeader(buffer))
     300                 :           0 :         pg_fatal("input file does not appear to be a valid tar archive");
     301                 :             : 
     302                 :             :     /*
     303                 :             :      * Parse key fields out of the header.
     304                 :             :      */
     305                 :      194483 :     strlcpy(member->pathname, &buffer[TAR_OFFSET_NAME], MAXPGPATH);
     306         [ -  + ]:      194483 :     if (member->pathname[0] == '\0')
     307                 :           0 :         pg_fatal("tar member has empty name");
     308         [ -  + ]:      194483 :     if (!path_is_safe_for_extraction(member->pathname))
     309                 :           0 :         pg_fatal("tar member has unsafe path name: \"%s\"",
     310                 :             :                  member->pathname);
     311                 :             : 
     312                 :      194483 :     member->size = read_tar_number(&buffer[TAR_OFFSET_SIZE], 12);
     313                 :      194483 :     member->mode = read_tar_number(&buffer[TAR_OFFSET_MODE], 8);
     314                 :      194483 :     member->uid = read_tar_number(&buffer[TAR_OFFSET_UID], 8);
     315                 :      194483 :     member->gid = read_tar_number(&buffer[TAR_OFFSET_GID], 8);
     316                 :             : 
     317   [ +  +  +  -  :      194483 :     switch (buffer[TAR_OFFSET_TYPEFLAG])
                      - ]
     318                 :             :     {
     319                 :      189625 :         case TAR_FILETYPE_PLAIN:
     320                 :             :         case TAR_FILETYPE_PLAIN_OLD:
     321                 :      189625 :             member->is_regular = true;
     322                 :      189625 :             break;
     323                 :        4842 :         case TAR_FILETYPE_DIRECTORY:
     324                 :        4842 :             member->is_directory = true;
     325                 :        4842 :             break;
     326                 :          16 :         case TAR_FILETYPE_SYMLINK:
     327                 :          16 :             member->is_symlink = true;
     328                 :          16 :             strlcpy(member->linktarget, &buffer[TAR_OFFSET_LINKNAME], 100);
     329                 :          16 :             break;
     330                 :           0 :         case TAR_FILETYPE_PAX_EXTENDED:
     331                 :             :         case TAR_FILETYPE_PAX_EXTENDED_GLOBAL:
     332                 :           0 :             pg_fatal("pax extensions to tar format are not supported");
     333                 :             :             break;
     334                 :           0 :         default:
     335                 :             :             /* For special filetypes, set none of the three is_xxx flags */
     336                 :           0 :             break;
     337                 :             :     }
     338                 :             : 
     339                 :             :     /* Compute number of padding bytes. */
     340                 :      194483 :     mystreamer->pad_bytes_expected = tarPaddingBytesRequired(member->size);
     341                 :             : 
     342                 :             :     /* Forward the entire header to the next astreamer. */
     343                 :      194483 :     astreamer_content(mystreamer->base.bbs_next, member,
     344                 :             :                       buffer, TAR_BLOCK_SIZE,
     345                 :             :                       ASTREAMER_MEMBER_HEADER);
     346                 :             : 
     347                 :      194483 :     return true;
     348                 :             : }
     349                 :             : 
     350                 :             : /*
     351                 :             :  * End-of-stream processing for a tar parser.
     352                 :             :  */
     353                 :             : static void
     354                 :         224 : astreamer_tar_parser_finalize(astreamer *streamer)
     355                 :             : {
     356                 :         224 :     astreamer_tar_parser *mystreamer = (astreamer_tar_parser *) streamer;
     357                 :             : 
     358         [ -  + ]:         224 :     if (mystreamer->next_context != ASTREAMER_ARCHIVE_TRAILER &&
     359         [ #  # ]:           0 :         (mystreamer->next_context != ASTREAMER_MEMBER_HEADER ||
     360         [ #  # ]:           0 :          mystreamer->base.bbs_buffer.len > 0))
     361                 :           0 :         pg_fatal("COPY stream ended before last file was finished");
     362                 :             : 
     363                 :             :     /* Send the archive trailer, even if empty. */
     364                 :         224 :     astreamer_content(streamer->bbs_next, NULL,
     365                 :         224 :                       streamer->bbs_buffer.data, streamer->bbs_buffer.len,
     366                 :             :                       ASTREAMER_ARCHIVE_TRAILER);
     367                 :             : 
     368                 :             :     /* Now finalize successor. */
     369                 :         224 :     astreamer_finalize(streamer->bbs_next);
     370                 :         224 : }
     371                 :             : 
     372                 :             : /*
     373                 :             :  * Free memory associated with a tar parser.
     374                 :             :  */
     375                 :             : static void
     376                 :         258 : astreamer_tar_parser_free(astreamer *streamer)
     377                 :             : {
     378                 :         258 :     pfree(streamer->bbs_buffer.data);
     379                 :         258 :     astreamer_free(streamer->bbs_next);
     380                 :         258 :     pfree(streamer);
     381                 :         258 : }
     382                 :             : 
     383                 :             : /*
     384                 :             :  * Create a astreamer that can generate a tar archive.
     385                 :             :  *
     386                 :             :  * This is intended to be usable either for generating a brand-new tar archive
     387                 :             :  * or for modifying one on the fly. The input should be a series of typed
     388                 :             :  * chunks (i.e. not ASTREAMER_UNKNOWN). See also the comments for
     389                 :             :  * astreamer_tar_parser_content.
     390                 :             :  */
     391                 :             : astreamer *
     392                 :           0 : astreamer_tar_archiver_new(astreamer *next)
     393                 :             : {
     394                 :             :     astreamer_tar_archiver *streamer;
     395                 :             : 
     396                 :           0 :     streamer = palloc0_object(astreamer_tar_archiver);
     397                 :           0 :     *((const astreamer_ops **) &streamer->base.bbs_ops) =
     398                 :             :         &astreamer_tar_archiver_ops;
     399                 :           0 :     streamer->base.bbs_next = next;
     400                 :             : 
     401                 :           0 :     return &streamer->base;
     402                 :             : }
     403                 :             : 
     404                 :             : /*
     405                 :             :  * Fix up the stream of input chunks to create a valid tar file.
     406                 :             :  *
     407                 :             :  * If a ASTREAMER_MEMBER_HEADER chunk is of size 0, it is replaced with a
     408                 :             :  * newly-constructed tar header. If it is of size TAR_BLOCK_SIZE, it is
     409                 :             :  * passed through without change. Any other size is a fatal error (and
     410                 :             :  * indicates a bug).
     411                 :             :  *
     412                 :             :  * Whenever a new ASTREAMER_MEMBER_HEADER chunk is constructed, the
     413                 :             :  * corresponding ASTREAMER_MEMBER_TRAILER chunk is also constructed from
     414                 :             :  * scratch. Specifically, we construct a block of zero bytes sufficient to
     415                 :             :  * pad out to a block boundary, as required by the tar format. Other
     416                 :             :  * ASTREAMER_MEMBER_TRAILER chunks are passed through without change.
     417                 :             :  *
     418                 :             :  * Any ASTREAMER_MEMBER_CONTENTS chunks are passed through without change.
     419                 :             :  *
     420                 :             :  * The ASTREAMER_ARCHIVE_TRAILER chunk is replaced with two
     421                 :             :  * blocks of zero bytes. Not all tar programs require this, but apparently
     422                 :             :  * some do. The server does not supply this trailer. If no archive trailer is
     423                 :             :  * present, one will be added by astreamer_tar_parser_finalize.
     424                 :             :  */
     425                 :             : static void
     426                 :           0 : astreamer_tar_archiver_content(astreamer *streamer,
     427                 :             :                                astreamer_member *member,
     428                 :             :                                const char *data, int len,
     429                 :             :                                astreamer_archive_context context)
     430                 :             : {
     431                 :           0 :     astreamer_tar_archiver *mystreamer = (astreamer_tar_archiver *) streamer;
     432                 :             :     char        buffer[2 * TAR_BLOCK_SIZE];
     433                 :             : 
     434                 :             :     Assert(context != ASTREAMER_UNKNOWN);
     435                 :             : 
     436   [ #  #  #  # ]:           0 :     if (context == ASTREAMER_MEMBER_HEADER && len != TAR_BLOCK_SIZE)
     437                 :             :     {
     438                 :             :         Assert(len == 0);
     439                 :             : 
     440                 :             :         /* Replace zero-length tar header with a newly constructed one. */
     441                 :           0 :         tarCreateHeader(buffer, member->pathname, NULL,
     442                 :             :                         member->size, member->mode, member->uid, member->gid,
     443                 :             :                         time(NULL));
     444                 :           0 :         data = buffer;
     445                 :           0 :         len = TAR_BLOCK_SIZE;
     446                 :             : 
     447                 :             :         /* Also make a note to replace padding, in case size changed. */
     448                 :           0 :         mystreamer->rearchive_member = true;
     449                 :             :     }
     450         [ #  # ]:           0 :     else if (context == ASTREAMER_MEMBER_TRAILER &&
     451         [ #  # ]:           0 :              mystreamer->rearchive_member)
     452                 :           0 :     {
     453                 :           0 :         int         pad_bytes = tarPaddingBytesRequired(member->size);
     454                 :             : 
     455                 :             :         /* Also replace padding, if we regenerated the header. */
     456                 :           0 :         memset(buffer, 0, pad_bytes);
     457                 :           0 :         data = buffer;
     458                 :           0 :         len = pad_bytes;
     459                 :             : 
     460                 :             :         /* Don't do this again unless we replace another header. */
     461                 :           0 :         mystreamer->rearchive_member = false;
     462                 :             :     }
     463         [ #  # ]:           0 :     else if (context == ASTREAMER_ARCHIVE_TRAILER)
     464                 :             :     {
     465                 :             :         /* Trailer should always be two blocks of zero bytes. */
     466                 :           0 :         memset(buffer, 0, 2 * TAR_BLOCK_SIZE);
     467                 :           0 :         data = buffer;
     468                 :           0 :         len = 2 * TAR_BLOCK_SIZE;
     469                 :             :     }
     470                 :             : 
     471                 :           0 :     astreamer_content(streamer->bbs_next, member, data, len, context);
     472                 :           0 : }
     473                 :             : 
     474                 :             : /*
     475                 :             :  * End-of-stream processing for a tar archiver.
     476                 :             :  */
     477                 :             : static void
     478                 :           0 : astreamer_tar_archiver_finalize(astreamer *streamer)
     479                 :             : {
     480                 :           0 :     astreamer_finalize(streamer->bbs_next);
     481                 :           0 : }
     482                 :             : 
     483                 :             : /*
     484                 :             :  * Free memory associated with a tar archiver.
     485                 :             :  */
     486                 :             : static void
     487                 :           0 : astreamer_tar_archiver_free(astreamer *streamer)
     488                 :             : {
     489                 :           0 :     astreamer_free(streamer->bbs_next);
     490                 :           0 :     pfree(streamer);
     491                 :           0 : }
     492                 :             : 
     493                 :             : /*
     494                 :             :  * Create a astreamer that blindly adds two blocks of NUL bytes to the
     495                 :             :  * end of an incomplete tarfile that the server might send us.
     496                 :             :  */
     497                 :             : astreamer *
     498                 :           0 : astreamer_tar_terminator_new(astreamer *next)
     499                 :             : {
     500                 :             :     astreamer  *streamer;
     501                 :             : 
     502                 :           0 :     streamer = palloc0_object(astreamer);
     503                 :           0 :     *((const astreamer_ops **) &streamer->bbs_ops) =
     504                 :             :         &astreamer_tar_terminator_ops;
     505                 :           0 :     streamer->bbs_next = next;
     506                 :             : 
     507                 :           0 :     return streamer;
     508                 :             : }
     509                 :             : 
     510                 :             : /*
     511                 :             :  * Pass all the content through without change.
     512                 :             :  */
     513                 :             : static void
     514                 :           0 : astreamer_tar_terminator_content(astreamer *streamer,
     515                 :             :                                  astreamer_member *member,
     516                 :             :                                  const char *data, int len,
     517                 :             :                                  astreamer_archive_context context)
     518                 :             : {
     519                 :             :     /* Expect unparsed input. */
     520                 :             :     Assert(member == NULL);
     521                 :             :     Assert(context == ASTREAMER_UNKNOWN);
     522                 :             : 
     523                 :             :     /* Just forward it. */
     524                 :           0 :     astreamer_content(streamer->bbs_next, member, data, len, context);
     525                 :           0 : }
     526                 :             : 
     527                 :             : /*
     528                 :             :  * At the end, blindly add the two blocks of NUL bytes which the server fails
     529                 :             :  * to supply.
     530                 :             :  */
     531                 :             : static void
     532                 :           0 : astreamer_tar_terminator_finalize(astreamer *streamer)
     533                 :             : {
     534                 :             :     char        buffer[2 * TAR_BLOCK_SIZE];
     535                 :             : 
     536                 :           0 :     memset(buffer, 0, 2 * TAR_BLOCK_SIZE);
     537                 :           0 :     astreamer_content(streamer->bbs_next, NULL, buffer,
     538                 :             :                       2 * TAR_BLOCK_SIZE, ASTREAMER_UNKNOWN);
     539                 :           0 :     astreamer_finalize(streamer->bbs_next);
     540                 :           0 : }
     541                 :             : 
     542                 :             : /*
     543                 :             :  * Free memory associated with a tar terminator.
     544                 :             :  */
     545                 :             : static void
     546                 :           0 : astreamer_tar_terminator_free(astreamer *streamer)
     547                 :             : {
     548                 :           0 :     astreamer_free(streamer->bbs_next);
     549                 :           0 :     pfree(streamer);
     550                 :           0 : }
        

Generated by: LCOV version 2.0-1