LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_backup_directory.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 88.2 % 271 239
Test Date: 2026-11-11 13:16:13 Functions: 100.0 % 27 27
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 60.2 % 98 59

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pg_backup_directory.c
       4                 :             :  *
       5                 :             :  *  A directory format dump is a directory, which contains a "toc.dat" file
       6                 :             :  *  for the TOC, and a separate file for each data entry, named "<oid>.dat".
       7                 :             :  *  Large objects are stored in separate files named "blob_<oid>.dat",
       8                 :             :  *  and there's a plain-text TOC file for each BLOBS TOC entry named
       9                 :             :  *  "blobs_<dumpID>.toc" (or just "blobs.toc" in archive versions before 16).
      10                 :             :  *
      11                 :             :  *  If compression is used, each data file is individually compressed and the
      12                 :             :  *  ".gz" suffix is added to the filenames. The TOC files are never
      13                 :             :  *  compressed by pg_dump, however they are accepted with the .gz suffix too,
      14                 :             :  *  in case the user has manually compressed them with 'gzip'.
      15                 :             :  *
      16                 :             :  *  NOTE: This format is identical to the files written in the tar file in
      17                 :             :  *  the 'tar' format, except that we don't write the restore.sql file (TODO),
      18                 :             :  *  and the tar format doesn't support compression. Please keep the formats in
      19                 :             :  *  sync.
      20                 :             :  *
      21                 :             :  *
      22                 :             :  *  Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      23                 :             :  *  Portions Copyright (c) 1994, Regents of the University of California
      24                 :             :  *  Portions Copyright (c) 2000, Philip Warner
      25                 :             :  *
      26                 :             :  *  Rights are granted to use this software in any way so long
      27                 :             :  *  as this notice is not removed.
      28                 :             :  *
      29                 :             :  *  The author is not responsible for loss or damages that may
      30                 :             :  *  result from its use.
      31                 :             :  *
      32                 :             :  * IDENTIFICATION
      33                 :             :  *      src/bin/pg_dump/pg_backup_directory.c
      34                 :             :  *
      35                 :             :  *-------------------------------------------------------------------------
      36                 :             :  */
      37                 :             : #include "postgres_fe.h"
      38                 :             : 
      39                 :             : #include <dirent.h>
      40                 :             : #include <sys/stat.h>
      41                 :             : 
      42                 :             : #include "common/file_utils.h"
      43                 :             : #include "compress_io.h"
      44                 :             : #include "dumputils.h"
      45                 :             : #include "parallel.h"
      46                 :             : #include "pg_backup_utils.h"
      47                 :             : 
      48                 :             : typedef struct
      49                 :             : {
      50                 :             :     /*
      51                 :             :      * Our archive location. This is basically what the user specified as his
      52                 :             :      * backup file but of course here it is a directory.
      53                 :             :      */
      54                 :             :     char       *directory;
      55                 :             : 
      56                 :             :     CompressFileHandle *dataFH; /* currently open data file */
      57                 :             :     CompressFileHandle *LOsTocFH;   /* file handle for blobs_NNN.toc */
      58                 :             :     ParallelState *pstate;      /* for parallel backup / restore */
      59                 :             : } lclContext;
      60                 :             : 
      61                 :             : typedef struct
      62                 :             : {
      63                 :             :     char       *filename;       /* filename excluding the directory (basename) */
      64                 :             : } lclTocEntry;
      65                 :             : 
      66                 :             : /* prototypes for private functions */
      67                 :             : static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
      68                 :             : static void _StartData(ArchiveHandle *AH, TocEntry *te);
      69                 :             : static void _EndData(ArchiveHandle *AH, TocEntry *te);
      70                 :             : static void _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
      71                 :             : static int  _WriteByte(ArchiveHandle *AH, const int i);
      72                 :             : static int  _ReadByte(ArchiveHandle *AH);
      73                 :             : static void _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
      74                 :             : static void _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
      75                 :             : static void _CloseArchive(ArchiveHandle *AH);
      76                 :             : static void _ReopenArchive(ArchiveHandle *AH);
      77                 :             : static void _PrintTocData(ArchiveHandle *AH, TocEntry *te);
      78                 :             : 
      79                 :             : static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
      80                 :             : static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
      81                 :             : static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
      82                 :             : 
      83                 :             : static void _StartLOs(ArchiveHandle *AH, TocEntry *te);
      84                 :             : static void _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
      85                 :             : static void _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
      86                 :             : static void _EndLOs(ArchiveHandle *AH, TocEntry *te);
      87                 :             : static void _LoadLOs(ArchiveHandle *AH, TocEntry *te);
      88                 :             : 
      89                 :             : static void _PrepParallelRestore(ArchiveHandle *AH);
      90                 :             : static void _Clone(ArchiveHandle *AH);
      91                 :             : static void _DeClone(ArchiveHandle *AH);
      92                 :             : 
      93                 :             : static int  _WorkerJobRestoreDirectory(ArchiveHandle *AH, TocEntry *te);
      94                 :             : static int  _WorkerJobDumpDirectory(ArchiveHandle *AH, TocEntry *te);
      95                 :             : 
      96                 :             : static void setFilePath(ArchiveHandle *AH, char *buf,
      97                 :             :                         const char *relativeFilename);
      98                 :             : 
      99                 :             : /*
     100                 :             :  *  Init routine required by ALL formats. This is a global routine
     101                 :             :  *  and should be declared in pg_backup_archiver.h
     102                 :             :  *
     103                 :             :  *  Its task is to create any extra archive context (using AH->formatData),
     104                 :             :  *  and to initialize the supported function pointers.
     105                 :             :  *
     106                 :             :  *  It should also prepare whatever its input source is for reading/writing,
     107                 :             :  *  and in the case of a read mode connection, it should load the Header & TOC.
     108                 :             :  */
     109                 :             : void
     110                 :          28 : InitArchiveFmt_Directory(ArchiveHandle *AH)
     111                 :             : {
     112                 :             :     lclContext *ctx;
     113                 :             : 
     114                 :             :     /* Assuming static functions, this can be copied for each format. */
     115                 :          28 :     AH->ArchiveEntryPtr = _ArchiveEntry;
     116                 :          28 :     AH->StartDataPtr = _StartData;
     117                 :          28 :     AH->WriteDataPtr = _WriteData;
     118                 :          28 :     AH->EndDataPtr = _EndData;
     119                 :          28 :     AH->WriteBytePtr = _WriteByte;
     120                 :          28 :     AH->ReadBytePtr = _ReadByte;
     121                 :          28 :     AH->WriteBufPtr = _WriteBuf;
     122                 :          28 :     AH->ReadBufPtr = _ReadBuf;
     123                 :          28 :     AH->ClosePtr = _CloseArchive;
     124                 :          28 :     AH->ReopenPtr = _ReopenArchive;
     125                 :          28 :     AH->PrintTocDataPtr = _PrintTocData;
     126                 :          28 :     AH->ReadExtraTocPtr = _ReadExtraToc;
     127                 :          28 :     AH->WriteExtraTocPtr = _WriteExtraToc;
     128                 :          28 :     AH->PrintExtraTocPtr = _PrintExtraToc;
     129                 :             : 
     130                 :          28 :     AH->StartLOsPtr = _StartLOs;
     131                 :          28 :     AH->StartLOPtr = _StartLO;
     132                 :          28 :     AH->EndLOPtr = _EndLO;
     133                 :          28 :     AH->EndLOsPtr = _EndLOs;
     134                 :             : 
     135                 :          28 :     AH->PrepParallelRestorePtr = _PrepParallelRestore;
     136                 :          28 :     AH->ClonePtr = _Clone;
     137                 :          28 :     AH->DeClonePtr = _DeClone;
     138                 :             : 
     139                 :          28 :     AH->WorkerJobRestorePtr = _WorkerJobRestoreDirectory;
     140                 :          28 :     AH->WorkerJobDumpPtr = _WorkerJobDumpDirectory;
     141                 :             : 
     142                 :             :     /* Set up our private context */
     143                 :          28 :     ctx = pg_malloc0_object(lclContext);
     144                 :          28 :     AH->formatData = ctx;
     145                 :             : 
     146                 :          28 :     ctx->dataFH = NULL;
     147                 :          28 :     ctx->LOsTocFH = NULL;
     148                 :             : 
     149                 :             :     /*
     150                 :             :      * Now open the TOC file
     151                 :             :      */
     152                 :             : 
     153   [ +  -  -  + ]:          28 :     if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
     154                 :           0 :         pg_fatal("no output directory specified");
     155                 :             : 
     156                 :          28 :     ctx->directory = AH->fSpec;
     157                 :             : 
     158         [ +  + ]:          28 :     if (AH->mode == archModeWrite)
     159                 :             :     {
     160                 :             :         /* we accept an empty existing directory */
     161                 :          12 :         create_or_open_dir(ctx->directory);
     162                 :             :     }
     163                 :             :     else
     164                 :             :     {                           /* Read Mode */
     165                 :             :         char        fname[MAXPGPATH];
     166                 :             :         CompressFileHandle *tocFH;
     167                 :             : 
     168                 :          16 :         setFilePath(AH, fname, "toc.dat");
     169                 :             : 
     170                 :          16 :         tocFH = InitDiscoverCompressFileHandle(fname, PG_BINARY_R);
     171         [ -  + ]:          16 :         if (tocFH == NULL)
     172                 :           0 :             pg_fatal("could not open input file \"%s\": %m", fname);
     173                 :             : 
     174                 :          16 :         ctx->dataFH = tocFH;
     175                 :             : 
     176                 :             :         /*
     177                 :             :          * The TOC of a directory format dump shares the format code of the
     178                 :             :          * tar format.
     179                 :             :          */
     180                 :          16 :         AH->format = archTar;
     181                 :          16 :         ReadHead(AH);
     182                 :          16 :         AH->format = archDirectory;
     183                 :          16 :         ReadToc(AH);
     184                 :             : 
     185                 :             :         /* Nothing else in the file, so close it again... */
     186         [ -  + ]:          16 :         if (!EndCompressFileHandle(tocFH))
     187                 :           0 :             pg_fatal("could not close TOC file: %m");
     188                 :          16 :         ctx->dataFH = NULL;
     189                 :             :     }
     190                 :          28 : }
     191                 :             : 
     192                 :             : /*
     193                 :             :  * Called by the Archiver when the dumper creates a new TOC entry.
     194                 :             :  *
     195                 :             :  * We determine the filename for this entry.
     196                 :             :  */
     197                 :             : static void
     198                 :        1075 : _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
     199                 :             : {
     200                 :             :     lclTocEntry *tctx;
     201                 :             :     char        fn[MAXPGPATH];
     202                 :             : 
     203                 :        1075 :     tctx = pg_malloc0_object(lclTocEntry);
     204         [ +  + ]:        1075 :     if (strcmp(te->desc, "BLOBS") == 0)
     205                 :             :     {
     206                 :           6 :         snprintf(fn, MAXPGPATH, "blobs_%d.toc", te->dumpId);
     207                 :           6 :         tctx->filename = pg_strdup(fn);
     208                 :             :     }
     209         [ +  + ]:        1069 :     else if (te->dataDumper)
     210                 :             :     {
     211                 :         121 :         snprintf(fn, MAXPGPATH, "%d.dat", te->dumpId);
     212                 :         121 :         tctx->filename = pg_strdup(fn);
     213                 :             :     }
     214                 :             :     else
     215                 :         948 :         tctx->filename = NULL;
     216                 :             : 
     217                 :        1075 :     te->formatData = tctx;
     218                 :        1075 : }
     219                 :             : 
     220                 :             : /*
     221                 :             :  * Called by the Archiver to save any extra format-related TOC entry
     222                 :             :  * data.
     223                 :             :  *
     224                 :             :  * Use the Archiver routines to write data - they are non-endian, and
     225                 :             :  * maintain other important file information.
     226                 :             :  */
     227                 :             : static void
     228                 :        1075 : _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
     229                 :             : {
     230                 :        1075 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     231                 :             : 
     232                 :             :     /*
     233                 :             :      * A dumpable object has set tctx->filename, any other object has not.
     234                 :             :      * (see _ArchiveEntry).
     235                 :             :      */
     236         [ +  + ]:        1075 :     if (tctx->filename)
     237                 :         127 :         WriteStr(AH, tctx->filename);
     238                 :             :     else
     239                 :         948 :         WriteStr(AH, "");
     240                 :        1075 : }
     241                 :             : 
     242                 :             : /*
     243                 :             :  * Called by the Archiver to read any extra format-related TOC data.
     244                 :             :  *
     245                 :             :  * Needs to match the order defined in _WriteExtraToc, and should also
     246                 :             :  * use the Archiver input routines.
     247                 :             :  */
     248                 :             : static void
     249                 :        1531 : _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
     250                 :             : {
     251                 :        1531 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     252                 :             : 
     253         [ +  - ]:        1531 :     if (tctx == NULL)
     254                 :             :     {
     255                 :        1531 :         tctx = pg_malloc0_object(lclTocEntry);
     256                 :        1531 :         te->formatData = tctx;
     257                 :             :     }
     258                 :             : 
     259                 :        1531 :     tctx->filename = ReadStr(AH);
     260         [ +  + ]:        1531 :     if (strlen(tctx->filename) == 0)
     261                 :             :     {
     262                 :        1351 :         free(tctx->filename);
     263                 :        1351 :         tctx->filename = NULL;
     264                 :             :     }
     265         [ -  + ]:         180 :     else if (first_dir_separator(tctx->filename) != NULL)
     266                 :           0 :         pg_fatal("invalid file name in archive: \"%s\"", tctx->filename);
     267                 :        1531 : }
     268                 :             : 
     269                 :             : /*
     270                 :             :  * Called by the Archiver when restoring an archive to output a comment
     271                 :             :  * that includes useful information about the TOC entry.
     272                 :             :  */
     273                 :             : static void
     274                 :         935 : _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
     275                 :             : {
     276                 :         935 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     277                 :             : 
     278   [ -  +  -  - ]:         935 :     if (AH->public.verbose && tctx->filename)
     279                 :           0 :         ahprintf(AH, "-- File: %s\n", tctx->filename);
     280                 :         935 : }
     281                 :             : 
     282                 :             : /*
     283                 :             :  * Called by the archiver when saving TABLE DATA (not schema). This routine
     284                 :             :  * should save whatever format-specific information is needed to read
     285                 :             :  * the archive back.
     286                 :             :  *
     287                 :             :  * It is called just prior to the dumper's 'DataDumper' routine being called.
     288                 :             :  *
     289                 :             :  * We create the data file for writing.
     290                 :             :  */
     291                 :             : static void
     292                 :         121 : _StartData(ArchiveHandle *AH, TocEntry *te)
     293                 :             : {
     294                 :         121 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     295                 :         121 :     lclContext *ctx = (lclContext *) AH->formatData;
     296                 :             :     char        fname[MAXPGPATH];
     297                 :             : 
     298                 :         121 :     setFilePath(AH, fname, tctx->filename);
     299                 :             : 
     300                 :         121 :     ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
     301                 :             : 
     302         [ -  + ]:         121 :     if (!ctx->dataFH->open_write_func(fname, PG_BINARY_W, ctx->dataFH))
     303                 :           0 :         pg_fatal("could not open output file \"%s\": %m", fname);
     304                 :         121 : }
     305                 :             : 
     306                 :             : /*
     307                 :             :  * Called by archiver when dumper calls WriteData. This routine is
     308                 :             :  * called for both LO and table data; it is the responsibility of
     309                 :             :  * the format to manage each kind of data using StartLO/StartData.
     310                 :             :  *
     311                 :             :  * It should only be called from within a DataDumper routine.
     312                 :             :  *
     313                 :             :  * We write the data to the open data file.
     314                 :             :  */
     315                 :             : static void
     316                 :       24662 : _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
     317                 :             : {
     318                 :       24662 :     lclContext *ctx = (lclContext *) AH->formatData;
     319                 :       24662 :     CompressFileHandle *CFH = ctx->dataFH;
     320                 :             : 
     321         [ +  + ]:       24662 :     if (dLen <= 0)
     322                 :           6 :         return;
     323                 :       24656 :     CFH->write_func(data, dLen, CFH);
     324                 :             : }
     325                 :             : 
     326                 :             : /*
     327                 :             :  * Called by the archiver when a dumper's 'DataDumper' routine has
     328                 :             :  * finished.
     329                 :             :  *
     330                 :             :  * We close the data file.
     331                 :             :  */
     332                 :             : static void
     333                 :         121 : _EndData(ArchiveHandle *AH, TocEntry *te)
     334                 :             : {
     335                 :         121 :     lclContext *ctx = (lclContext *) AH->formatData;
     336                 :             : 
     337                 :             :     /* Close the file */
     338         [ -  + ]:         121 :     if (!EndCompressFileHandle(ctx->dataFH))
     339                 :           0 :         pg_fatal("could not close data file: %m");
     340                 :             : 
     341                 :         121 :     ctx->dataFH = NULL;
     342                 :         121 : }
     343                 :             : 
     344                 :             : /*
     345                 :             :  * Print data for a given file (can be a LO as well)
     346                 :             :  */
     347                 :             : static void
     348                 :         134 : _PrintFileData(ArchiveHandle *AH, char *filename)
     349                 :             : {
     350                 :             :     size_t      cnt;
     351                 :             :     char       *buf;
     352                 :             :     size_t      buflen;
     353                 :             :     CompressFileHandle *CFH;
     354                 :             : 
     355         [ -  + ]:         134 :     if (!filename)
     356                 :           0 :         return;
     357                 :             : 
     358                 :         134 :     CFH = InitDiscoverCompressFileHandle(filename, PG_BINARY_R);
     359         [ -  + ]:         134 :     if (!CFH)
     360                 :           0 :         pg_fatal("could not open input file \"%s\": %m", filename);
     361                 :             : 
     362                 :         134 :     buflen = DEFAULT_IO_BUFFER_SIZE;
     363                 :         134 :     buf = pg_malloc(buflen);
     364                 :             : 
     365         [ +  + ]:         267 :     while ((cnt = CFH->read_func(buf, buflen, CFH)) > 0)
     366                 :             :     {
     367                 :         133 :         ahwrite(buf, 1, cnt, AH);
     368                 :             :     }
     369                 :             : 
     370                 :         133 :     pg_free(buf);
     371         [ -  + ]:         133 :     if (!EndCompressFileHandle(CFH))
     372                 :           0 :         pg_fatal("could not close data file \"%s\": %m", filename);
     373                 :             : }
     374                 :             : 
     375                 :             : /*
     376                 :             :  * Print data for a given TOC entry
     377                 :             :  */
     378                 :             : static void
     379                 :         134 : _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     380                 :             : {
     381                 :         134 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     382                 :             : 
     383         [ -  + ]:         134 :     if (!tctx->filename)
     384                 :           0 :         return;
     385                 :             : 
     386         [ +  + ]:         134 :     if (strcmp(te->desc, "BLOBS") == 0)
     387                 :           5 :         _LoadLOs(AH, te);
     388                 :             :     else
     389                 :             :     {
     390                 :             :         char        fname[MAXPGPATH];
     391                 :             : 
     392                 :         129 :         setFilePath(AH, fname, tctx->filename);
     393                 :         129 :         _PrintFileData(AH, fname);
     394                 :             :     }
     395                 :             : }
     396                 :             : 
     397                 :             : static void
     398                 :           5 : _LoadLOs(ArchiveHandle *AH, TocEntry *te)
     399                 :             : {
     400                 :             :     Oid         oid;
     401                 :           5 :     lclContext *ctx = (lclContext *) AH->formatData;
     402                 :           5 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     403                 :             :     CompressFileHandle *CFH;
     404                 :             :     char        tocfname[MAXPGPATH];
     405                 :             :     char        line[MAXPGPATH];
     406                 :             : 
     407                 :           5 :     StartRestoreLOs(AH);
     408                 :             : 
     409                 :             :     /*
     410                 :             :      * Note: before archive v16, there was always only one BLOBS TOC entry,
     411                 :             :      * now there can be multiple.  Furthermore, although the actual filename
     412                 :             :      * was always "blobs.toc" before v16, the value of tctx->filename did not
     413                 :             :      * match that before commit 548e50976 fixed it.  For simplicity we assume
     414                 :             :      * it must be "blobs.toc" in all archives before v16.
     415                 :             :      */
     416         [ -  + ]:           5 :     if (AH->version < K_VERS_1_16)
     417                 :           0 :         setFilePath(AH, tocfname, "blobs.toc");
     418                 :             :     else
     419                 :           5 :         setFilePath(AH, tocfname, tctx->filename);
     420                 :             : 
     421                 :           5 :     CFH = ctx->LOsTocFH = InitDiscoverCompressFileHandle(tocfname, PG_BINARY_R);
     422                 :             : 
     423         [ -  + ]:           5 :     if (ctx->LOsTocFH == NULL)
     424                 :           0 :         pg_fatal("could not open large object TOC file \"%s\" for input: %m",
     425                 :             :                  tocfname);
     426                 :             : 
     427                 :             :     /* Read the LOs TOC file line-by-line, and process each LO */
     428         [ +  + ]:          10 :     while ((CFH->gets_func(line, MAXPGPATH, CFH)) != NULL)
     429                 :             :     {
     430                 :             :         char        lofname[MAXPGPATH + 1];
     431                 :             :         char        path[MAXPGPATH];
     432                 :             : 
     433                 :             :         /* Can't overflow because line and lofname are the same length */
     434         [ -  + ]:           5 :         if (sscanf(line, "%u %" CppAsString2(MAXPGPATH) "s\n", &oid, lofname) != 2)
     435                 :           0 :             pg_fatal("invalid line in large object TOC file \"%s\": \"%s\"",
     436                 :             :                      tocfname, line);
     437         [ -  + ]:           5 :         if (first_dir_separator(lofname) != NULL)
     438                 :           0 :             pg_fatal("invalid file name in archive: \"%s\"", lofname);
     439                 :             : 
     440                 :           5 :         StartRestoreLO(AH, oid, AH->public.ropt->dropSchema);
     441                 :           5 :         snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, lofname);
     442                 :           5 :         _PrintFileData(AH, path);
     443                 :           5 :         EndRestoreLO(AH, oid);
     444                 :             :     }
     445         [ -  + ]:           5 :     if (!CFH->eof_func(CFH))
     446                 :           0 :         pg_fatal("error reading large object TOC file \"%s\"",
     447                 :             :                  tocfname);
     448                 :             : 
     449         [ -  + ]:           5 :     if (!EndCompressFileHandle(ctx->LOsTocFH))
     450                 :           0 :         pg_fatal("could not close large object TOC file \"%s\": %m",
     451                 :             :                  tocfname);
     452                 :             : 
     453                 :           5 :     ctx->LOsTocFH = NULL;
     454                 :             : 
     455                 :           5 :     EndRestoreLOs(AH);
     456                 :           5 : }
     457                 :             : 
     458                 :             : 
     459                 :             : /*
     460                 :             :  * Write a byte of data to the archive.
     461                 :             :  * Called by the archiver to do integer & byte output to the archive.
     462                 :             :  * These routines are only used to read & write the headers & TOC.
     463                 :             :  */
     464                 :             : static int
     465                 :      106399 : _WriteByte(ArchiveHandle *AH, const int i)
     466                 :             : {
     467                 :      106399 :     unsigned char c = (unsigned char) i;
     468                 :      106399 :     lclContext *ctx = (lclContext *) AH->formatData;
     469                 :      106399 :     CompressFileHandle *CFH = ctx->dataFH;
     470                 :             : 
     471                 :      106399 :     CFH->write_func(&c, 1, CFH);
     472                 :      106399 :     return 1;
     473                 :             : }
     474                 :             : 
     475                 :             : /*
     476                 :             :  * Read a byte of data from the archive.
     477                 :             :  * Called by the archiver to read bytes & integers from the archive.
     478                 :             :  * These routines are only used to read & write headers & TOC.
     479                 :             :  * EOF should be treated as a fatal error.
     480                 :             :  */
     481                 :             : static int
     482                 :      151397 : _ReadByte(ArchiveHandle *AH)
     483                 :             : {
     484                 :      151397 :     lclContext *ctx = (lclContext *) AH->formatData;
     485                 :      151397 :     CompressFileHandle *CFH = ctx->dataFH;
     486                 :             : 
     487                 :      151397 :     return CFH->getc_func(CFH);
     488                 :             : }
     489                 :             : 
     490                 :             : /*
     491                 :             :  * Write a buffer of data to the archive.
     492                 :             :  * Called by the archiver to write a block of bytes to the TOC or a data file.
     493                 :             :  */
     494                 :             : static void
     495                 :       11920 : _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
     496                 :             : {
     497                 :       11920 :     lclContext *ctx = (lclContext *) AH->formatData;
     498                 :       11920 :     CompressFileHandle *CFH = ctx->dataFH;
     499                 :             : 
     500                 :       11920 :     CFH->write_func(buf, len, CFH);
     501                 :       11920 : }
     502                 :             : 
     503                 :             : /*
     504                 :             :  * Read a block of bytes from the archive.
     505                 :             :  *
     506                 :             :  * Called by the archiver to read a block of bytes from the archive
     507                 :             :  */
     508                 :             : static void
     509                 :       16962 : _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
     510                 :             : {
     511                 :       16962 :     lclContext *ctx = (lclContext *) AH->formatData;
     512                 :       16962 :     CompressFileHandle *CFH = ctx->dataFH;
     513                 :             : 
     514                 :             :     /*
     515                 :             :      * We do not expect a short read, so fail if we get one.  The read_func
     516                 :             :      * already dealt with any outright I/O error.
     517                 :             :      */
     518         [ -  + ]:       16962 :     if (CFH->read_func(buf, len, CFH) != len)
     519                 :           0 :         pg_fatal("could not read from input file: end of file");
     520                 :       16962 : }
     521                 :             : 
     522                 :             : /*
     523                 :             :  * Close the archive.
     524                 :             :  *
     525                 :             :  * When writing the archive, this is the routine that actually starts
     526                 :             :  * the process of saving it to files. No data should be written prior
     527                 :             :  * to this point, since the user could sort the TOC after creating it.
     528                 :             :  *
     529                 :             :  * If an archive is to be written, this routine must call:
     530                 :             :  *      WriteHead           to save the archive header
     531                 :             :  *      WriteToc            to save the TOC entries
     532                 :             :  *      WriteDataChunks     to save all data & LOs.
     533                 :             :  */
     534                 :             : static void
     535                 :          27 : _CloseArchive(ArchiveHandle *AH)
     536                 :             : {
     537                 :          27 :     lclContext *ctx = (lclContext *) AH->formatData;
     538                 :             : 
     539         [ +  + ]:          27 :     if (AH->mode == archModeWrite)
     540                 :             :     {
     541                 :             :         CompressFileHandle *tocFH;
     542                 :          12 :         pg_compress_specification compression_spec = {0};
     543                 :             :         char        fname[MAXPGPATH];
     544                 :             : 
     545                 :          12 :         setFilePath(AH, fname, "toc.dat");
     546                 :             : 
     547                 :             :         /* this will actually fork the processes for a parallel backup */
     548                 :          12 :         ctx->pstate = ParallelBackupStart(AH);
     549                 :             : 
     550                 :             :         /* The TOC is always created uncompressed */
     551                 :          12 :         compression_spec.algorithm = PG_COMPRESSION_NONE;
     552                 :          12 :         tocFH = InitCompressFileHandle(compression_spec);
     553         [ -  + ]:          12 :         if (!tocFH->open_write_func(fname, PG_BINARY_W, tocFH))
     554                 :           0 :             pg_fatal("could not open output file \"%s\": %m", fname);
     555                 :          12 :         ctx->dataFH = tocFH;
     556                 :             : 
     557                 :             :         /*
     558                 :             :          * Write 'tar' in the format field of the toc.dat file. The directory
     559                 :             :          * is compatible with 'tar', so there's no point having a different
     560                 :             :          * format code for it.
     561                 :             :          */
     562                 :          12 :         AH->format = archTar;
     563                 :          12 :         WriteHead(AH);
     564                 :          12 :         AH->format = archDirectory;
     565                 :          12 :         WriteToc(AH);
     566         [ -  + ]:          12 :         if (!EndCompressFileHandle(tocFH))
     567                 :           0 :             pg_fatal("could not close TOC file: %m");
     568                 :          12 :         WriteDataChunks(AH, ctx->pstate);
     569                 :             : 
     570                 :          12 :         ParallelBackupEnd(AH, ctx->pstate);
     571                 :             : 
     572                 :             :         /*
     573                 :             :          * In directory mode, there is no need to sync all the entries
     574                 :             :          * individually. Just recurse once through all the files generated.
     575                 :             :          */
     576         [ +  + ]:          12 :         if (AH->dosync)
     577                 :           2 :             sync_dir_recurse(ctx->directory, AH->sync_method);
     578                 :             :     }
     579                 :          27 :     AH->FH = NULL;
     580                 :          27 : }
     581                 :             : 
     582                 :             : /*
     583                 :             :  * Reopen the archive's file handle.
     584                 :             :  */
     585                 :             : static void
     586                 :          17 : _ReopenArchive(ArchiveHandle *AH)
     587                 :             : {
     588                 :             :     /*
     589                 :             :      * Our TOC is in memory, our data files are opened by each child anyway as
     590                 :             :      * they are separate. We support reopening the archive by just doing
     591                 :             :      * nothing.
     592                 :             :      */
     593                 :          17 : }
     594                 :             : 
     595                 :             : /*
     596                 :             :  * LO support
     597                 :             :  */
     598                 :             : 
     599                 :             : /*
     600                 :             :  * Called by the archiver when starting to save BLOB DATA (not schema).
     601                 :             :  * It is called just prior to the dumper's DataDumper routine.
     602                 :             :  *
     603                 :             :  * We open the large object TOC file here, so that we can append a line to
     604                 :             :  * it for each LO.
     605                 :             :  */
     606                 :             : static void
     607                 :           6 : _StartLOs(ArchiveHandle *AH, TocEntry *te)
     608                 :             : {
     609                 :           6 :     lclContext *ctx = (lclContext *) AH->formatData;
     610                 :           6 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     611                 :           6 :     pg_compress_specification compression_spec = {0};
     612                 :             :     char        fname[MAXPGPATH];
     613                 :             : 
     614                 :           6 :     setFilePath(AH, fname, tctx->filename);
     615                 :             : 
     616                 :             :     /* The LO TOC file is never compressed */
     617                 :           6 :     compression_spec.algorithm = PG_COMPRESSION_NONE;
     618                 :           6 :     ctx->LOsTocFH = InitCompressFileHandle(compression_spec);
     619         [ -  + ]:           6 :     if (!ctx->LOsTocFH->open_write_func(fname, "ab", ctx->LOsTocFH))
     620                 :           0 :         pg_fatal("could not open output file \"%s\": %m", fname);
     621                 :           6 : }
     622                 :             : 
     623                 :             : /*
     624                 :             :  * Called by the archiver when we're about to start dumping a LO.
     625                 :             :  *
     626                 :             :  * We create a file to write the LO to.
     627                 :             :  */
     628                 :             : static void
     629                 :           6 : _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
     630                 :             : {
     631                 :           6 :     lclContext *ctx = (lclContext *) AH->formatData;
     632                 :             :     char        fname[MAXPGPATH];
     633                 :             : 
     634                 :           6 :     snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
     635                 :             : 
     636                 :           6 :     ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
     637         [ -  + ]:           6 :     if (!ctx->dataFH->open_write_func(fname, PG_BINARY_W, ctx->dataFH))
     638                 :           0 :         pg_fatal("could not open output file \"%s\": %m", fname);
     639                 :           6 : }
     640                 :             : 
     641                 :             : /*
     642                 :             :  * Called by the archiver when the dumper is finished writing a LO.
     643                 :             :  *
     644                 :             :  * We close the LO file and write an entry to the LO TOC file for it.
     645                 :             :  */
     646                 :             : static void
     647                 :           6 : _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
     648                 :             : {
     649                 :           6 :     lclContext *ctx = (lclContext *) AH->formatData;
     650                 :           6 :     CompressFileHandle *CFH = ctx->LOsTocFH;
     651                 :             :     char        buf[50];
     652                 :             :     int         len;
     653                 :             : 
     654                 :             :     /* Close the BLOB data file itself */
     655         [ -  + ]:           6 :     if (!EndCompressFileHandle(ctx->dataFH))
     656                 :           0 :         pg_fatal("could not close LO data file: %m");
     657                 :           6 :     ctx->dataFH = NULL;
     658                 :             : 
     659                 :             :     /* register the LO in blobs_NNN.toc */
     660                 :           6 :     len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
     661                 :           6 :     CFH->write_func(buf, len, CFH);
     662                 :           6 : }
     663                 :             : 
     664                 :             : /*
     665                 :             :  * Called by the archiver when finishing saving BLOB DATA.
     666                 :             :  *
     667                 :             :  * We close the LOs TOC file.
     668                 :             :  */
     669                 :             : static void
     670                 :           6 : _EndLOs(ArchiveHandle *AH, TocEntry *te)
     671                 :             : {
     672                 :           6 :     lclContext *ctx = (lclContext *) AH->formatData;
     673                 :             : 
     674         [ -  + ]:           6 :     if (!EndCompressFileHandle(ctx->LOsTocFH))
     675                 :           0 :         pg_fatal("could not close LOs TOC file: %m");
     676                 :           6 :     ctx->LOsTocFH = NULL;
     677                 :           6 : }
     678                 :             : 
     679                 :             : /*
     680                 :             :  * Gets a relative file name and prepends the output directory, writing the
     681                 :             :  * result to buf. The caller needs to make sure that buf is MAXPGPATH bytes
     682                 :             :  * big. Can't use a static char[MAXPGPATH] inside the function because we run
     683                 :             :  * multithreaded on Windows.
     684                 :             :  */
     685                 :             : static void
     686                 :         309 : setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
     687                 :             : {
     688                 :         309 :     lclContext *ctx = (lclContext *) AH->formatData;
     689                 :             :     char       *dname;
     690                 :             : 
     691                 :         309 :     dname = ctx->directory;
     692                 :             : 
     693         [ -  + ]:         309 :     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
     694                 :           0 :         pg_fatal("file name too long: \"%s\"", dname);
     695                 :             : 
     696                 :         309 :     strcpy(buf, dname);
     697                 :         309 :     strcat(buf, "/");
     698                 :         309 :     strcat(buf, relativeFilename);
     699                 :         309 : }
     700                 :             : 
     701                 :             : /*
     702                 :             :  * Prepare for parallel restore.
     703                 :             :  *
     704                 :             :  * The main thing that needs to happen here is to fill in TABLE DATA and BLOBS
     705                 :             :  * TOC entries' dataLength fields with appropriate values to guide the
     706                 :             :  * ordering of restore jobs.  The source of said data is format-dependent,
     707                 :             :  * as is the exact meaning of the values.
     708                 :             :  *
     709                 :             :  * A format module might also choose to do other setup here.
     710                 :             :  */
     711                 :             : static void
     712                 :           5 : _PrepParallelRestore(ArchiveHandle *AH)
     713                 :             : {
     714                 :             :     TocEntry   *te;
     715                 :             : 
     716         [ +  + ]:         124 :     for (te = AH->toc->next; te != AH->toc; te = te->next)
     717                 :             :     {
     718                 :         119 :         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     719                 :             :         char        fname[MAXPGPATH];
     720                 :             :         struct stat st;
     721                 :             : 
     722                 :             :         /*
     723                 :             :          * A dumpable object has set tctx->filename, any other object has not.
     724                 :             :          * (see _ArchiveEntry).
     725                 :             :          */
     726         [ +  + ]:         119 :         if (tctx->filename == NULL)
     727                 :          99 :             continue;
     728                 :             : 
     729                 :             :         /* We may ignore items not due to be restored */
     730         [ -  + ]:          20 :         if ((te->reqs & (REQ_DATA | REQ_STATS)) == 0)
     731                 :           0 :             continue;
     732                 :             : 
     733                 :             :         /*
     734                 :             :          * Stat the file and, if successful, put its size in dataLength.  When
     735                 :             :          * using compression, the physical file size might not be a very good
     736                 :             :          * guide to the amount of work involved in restoring the file, but we
     737                 :             :          * only need an approximate indicator of that.
     738                 :             :          */
     739                 :          20 :         setFilePath(AH, fname, tctx->filename);
     740                 :             : 
     741         [ -  + ]:          20 :         if (stat(fname, &st) == 0)
     742                 :           0 :             te->dataLength = st.st_size;
     743         [ +  - ]:          20 :         else if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
     744                 :             :         {
     745         [ +  - ]:          20 :             if (AH->compression_spec.algorithm == PG_COMPRESSION_GZIP)
     746                 :          20 :                 strlcat(fname, ".gz", sizeof(fname));
     747         [ #  # ]:           0 :             else if (AH->compression_spec.algorithm == PG_COMPRESSION_LZ4)
     748                 :           0 :                 strlcat(fname, ".lz4", sizeof(fname));
     749         [ #  # ]:           0 :             else if (AH->compression_spec.algorithm == PG_COMPRESSION_ZSTD)
     750                 :           0 :                 strlcat(fname, ".zst", sizeof(fname));
     751                 :             : 
     752         [ +  - ]:          20 :             if (stat(fname, &st) == 0)
     753                 :          20 :                 te->dataLength = st.st_size;
     754                 :             :         }
     755                 :             : 
     756                 :             :         /*
     757                 :             :          * If this is a BLOBS entry, what we stat'd was blobs_NNN.toc, which
     758                 :             :          * most likely is a lot smaller than the actual blob data.  We don't
     759                 :             :          * have a cheap way to estimate how much smaller, but fortunately it
     760                 :             :          * doesn't matter too much as long as we get the LOs processed
     761                 :             :          * reasonably early.  Arbitrarily scale up by a factor of 1K.
     762                 :             :          */
     763         [ -  + ]:          20 :         if (strcmp(te->desc, "BLOBS") == 0)
     764                 :           0 :             te->dataLength *= 1024;
     765                 :             :     }
     766                 :           5 : }
     767                 :             : 
     768                 :             : /*
     769                 :             :  * Clone format-specific fields during parallel restoration.
     770                 :             :  */
     771                 :             : static void
     772                 :          28 : _Clone(ArchiveHandle *AH)
     773                 :             : {
     774                 :          28 :     lclContext *ctx = (lclContext *) AH->formatData;
     775                 :             : 
     776                 :          28 :     AH->formatData = pg_malloc_object(lclContext);
     777                 :          28 :     memcpy(AH->formatData, ctx, sizeof(lclContext));
     778                 :          28 :     ctx = (lclContext *) AH->formatData;
     779                 :             : 
     780                 :             :     /*
     781                 :             :      * TOC-entry-local state isn't an issue because any one TOC entry is
     782                 :             :      * touched by just one worker child.
     783                 :             :      */
     784                 :             : 
     785                 :             :     /*
     786                 :             :      * We also don't copy the ParallelState pointer (pstate), only the leader
     787                 :             :      * process ever writes to it.
     788                 :             :      */
     789                 :          28 : }
     790                 :             : 
     791                 :             : static void
     792                 :          28 : _DeClone(ArchiveHandle *AH)
     793                 :             : {
     794                 :          28 :     lclContext *ctx = (lclContext *) AH->formatData;
     795                 :             : 
     796                 :          28 :     free(ctx);
     797                 :          28 : }
     798                 :             : 
     799                 :             : /*
     800                 :             :  * This function is executed in the child of a parallel backup for a
     801                 :             :  * directory-format archive and dumps the actual data for one TOC entry.
     802                 :             :  */
     803                 :             : static int
     804                 :          72 : _WorkerJobDumpDirectory(ArchiveHandle *AH, TocEntry *te)
     805                 :             : {
     806                 :             :     /*
     807                 :             :      * This function returns void. We either fail and die horribly or
     808                 :             :      * succeed... A failure will be detected by the parent when the child dies
     809                 :             :      * unexpectedly.
     810                 :             :      */
     811                 :          72 :     WriteDataChunksForTocEntry(AH, te);
     812                 :             : 
     813                 :          72 :     return 0;
     814                 :             : }
     815                 :             : 
     816                 :             : /*
     817                 :             :  * This function is executed in the child of a parallel restore from a
     818                 :             :  * directory-format archive and restores the actual data for one TOC entry.
     819                 :             :  */
     820                 :             : static int
     821                 :          55 : _WorkerJobRestoreDirectory(ArchiveHandle *AH, TocEntry *te)
     822                 :             : {
     823                 :          55 :     return parallel_restore(AH, te);
     824                 :             : }
        

Generated by: LCOV version 2.0-1