LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_backup_directory.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 88.8 % 267 237
Test Date: 2026-03-11 23:15:12 Functions: 100.0 % 27 27
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1