LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_backup_directory.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 237 267 88.8 %
Date: 2025-08-31 01:17:28 Functions: 27 27 100.0 %
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-2025, 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          44 : InitArchiveFmt_Directory(ArchiveHandle *AH)
     111             : {
     112             :     lclContext *ctx;
     113             : 
     114             :     /* Assuming static functions, this can be copied for each format. */
     115          44 :     AH->ArchiveEntryPtr = _ArchiveEntry;
     116          44 :     AH->StartDataPtr = _StartData;
     117          44 :     AH->WriteDataPtr = _WriteData;
     118          44 :     AH->EndDataPtr = _EndData;
     119          44 :     AH->WriteBytePtr = _WriteByte;
     120          44 :     AH->ReadBytePtr = _ReadByte;
     121          44 :     AH->WriteBufPtr = _WriteBuf;
     122          44 :     AH->ReadBufPtr = _ReadBuf;
     123          44 :     AH->ClosePtr = _CloseArchive;
     124          44 :     AH->ReopenPtr = _ReopenArchive;
     125          44 :     AH->PrintTocDataPtr = _PrintTocData;
     126          44 :     AH->ReadExtraTocPtr = _ReadExtraToc;
     127          44 :     AH->WriteExtraTocPtr = _WriteExtraToc;
     128          44 :     AH->PrintExtraTocPtr = _PrintExtraToc;
     129             : 
     130          44 :     AH->StartLOsPtr = _StartLOs;
     131          44 :     AH->StartLOPtr = _StartLO;
     132          44 :     AH->EndLOPtr = _EndLO;
     133          44 :     AH->EndLOsPtr = _EndLOs;
     134             : 
     135          44 :     AH->PrepParallelRestorePtr = _PrepParallelRestore;
     136          44 :     AH->ClonePtr = _Clone;
     137          44 :     AH->DeClonePtr = _DeClone;
     138             : 
     139          44 :     AH->WorkerJobRestorePtr = _WorkerJobRestoreDirectory;
     140          44 :     AH->WorkerJobDumpPtr = _WorkerJobDumpDirectory;
     141             : 
     142             :     /* Set up our private context */
     143          44 :     ctx = (lclContext *) pg_malloc0(sizeof(lclContext));
     144          44 :     AH->formatData = ctx;
     145             : 
     146          44 :     ctx->dataFH = NULL;
     147          44 :     ctx->LOsTocFH = NULL;
     148             : 
     149             :     /*
     150             :      * Now open the TOC file
     151             :      */
     152             : 
     153          44 :     if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
     154           0 :         pg_fatal("no output directory specified");
     155             : 
     156          44 :     ctx->directory = AH->fSpec;
     157             : 
     158          44 :     if (AH->mode == archModeWrite)
     159             :     {
     160             :         /* we accept an empty existing directory */
     161          20 :         create_or_open_dir(ctx->directory);
     162             :     }
     163             :     else
     164             :     {                           /* Read Mode */
     165             :         char        fname[MAXPGPATH];
     166             :         CompressFileHandle *tocFH;
     167             : 
     168          24 :         setFilePath(AH, fname, "toc.dat");
     169             : 
     170          24 :         tocFH = InitDiscoverCompressFileHandle(fname, PG_BINARY_R);
     171          24 :         if (tocFH == NULL)
     172           0 :             pg_fatal("could not open input file \"%s\": %m", fname);
     173             : 
     174          24 :         ctx->dataFH = tocFH;
     175             : 
     176             :         /*
     177             :          * The TOC of a directory format dump shares the format code of the
     178             :          * tar format.
     179             :          */
     180          24 :         AH->format = archTar;
     181          24 :         ReadHead(AH);
     182          24 :         AH->format = archDirectory;
     183          24 :         ReadToc(AH);
     184             : 
     185             :         /* Nothing else in the file, so close it again... */
     186          24 :         if (!EndCompressFileHandle(tocFH))
     187           0 :             pg_fatal("could not close TOC file: %m");
     188          24 :         ctx->dataFH = NULL;
     189             :     }
     190          44 : }
     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        3450 : _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
     199             : {
     200             :     lclTocEntry *tctx;
     201             :     char        fn[MAXPGPATH];
     202             : 
     203        3450 :     tctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
     204        3450 :     if (strcmp(te->desc, "BLOBS") == 0)
     205             :     {
     206          16 :         snprintf(fn, MAXPGPATH, "blobs_%d.toc", te->dumpId);
     207          16 :         tctx->filename = pg_strdup(fn);
     208             :     }
     209        3434 :     else if (te->dataDumper)
     210             :     {
     211         354 :         snprintf(fn, MAXPGPATH, "%d.dat", te->dumpId);
     212         354 :         tctx->filename = pg_strdup(fn);
     213             :     }
     214             :     else
     215        3080 :         tctx->filename = NULL;
     216             : 
     217        3450 :     te->formatData = tctx;
     218        3450 : }
     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        3450 : _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
     229             : {
     230        3450 :     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        3450 :     if (tctx->filename)
     237         370 :         WriteStr(AH, tctx->filename);
     238             :     else
     239        3080 :         WriteStr(AH, "");
     240        3450 : }
     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        4230 : _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
     250             : {
     251        4230 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     252             : 
     253        4230 :     if (tctx == NULL)
     254             :     {
     255        4230 :         tctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
     256        4230 :         te->formatData = tctx;
     257             :     }
     258             : 
     259        4230 :     tctx->filename = ReadStr(AH);
     260        4230 :     if (strlen(tctx->filename) == 0)
     261             :     {
     262        3778 :         free(tctx->filename);
     263        3778 :         tctx->filename = NULL;
     264             :     }
     265        4230 : }
     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        3200 : _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
     273             : {
     274        3200 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     275             : 
     276        3200 :     if (AH->public.verbose && tctx->filename)
     277           0 :         ahprintf(AH, "-- File: %s\n", tctx->filename);
     278        3200 : }
     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         354 : _StartData(ArchiveHandle *AH, TocEntry *te)
     291             : {
     292         354 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     293         354 :     lclContext *ctx = (lclContext *) AH->formatData;
     294             :     char        fname[MAXPGPATH];
     295             : 
     296         354 :     setFilePath(AH, fname, tctx->filename);
     297             : 
     298         354 :     ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
     299             : 
     300         354 :     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         354 : }
     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       49420 : _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
     315             : {
     316       49420 :     lclContext *ctx = (lclContext *) AH->formatData;
     317       49420 :     CompressFileHandle *CFH = ctx->dataFH;
     318             : 
     319       49420 :     if (dLen <= 0)
     320          16 :         return;
     321       49404 :     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         354 : _EndData(ArchiveHandle *AH, TocEntry *te)
     332             : {
     333         354 :     lclContext *ctx = (lclContext *) AH->formatData;
     334             : 
     335             :     /* Close the file */
     336         354 :     if (!EndCompressFileHandle(ctx->dataFH))
     337           0 :         pg_fatal("could not close data file: %m");
     338             : 
     339         354 :     ctx->dataFH = NULL;
     340         354 : }
     341             : 
     342             : /*
     343             :  * Print data for a given file (can be a LO as well)
     344             :  */
     345             : static void
     346         372 : _PrintFileData(ArchiveHandle *AH, char *filename)
     347             : {
     348             :     size_t      cnt;
     349             :     char       *buf;
     350             :     size_t      buflen;
     351             :     CompressFileHandle *CFH;
     352             : 
     353         372 :     if (!filename)
     354           0 :         return;
     355             : 
     356         372 :     CFH = InitDiscoverCompressFileHandle(filename, PG_BINARY_R);
     357         372 :     if (!CFH)
     358           0 :         pg_fatal("could not open input file \"%s\": %m", filename);
     359             : 
     360         372 :     buflen = DEFAULT_IO_BUFFER_SIZE;
     361         372 :     buf = pg_malloc(buflen);
     362             : 
     363         822 :     while ((cnt = CFH->read_func(buf, buflen, CFH)) > 0)
     364             :     {
     365         450 :         ahwrite(buf, 1, cnt, AH);
     366             :     }
     367             : 
     368         372 :     free(buf);
     369         372 :     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         372 : _PrintTocData(ArchiveHandle *AH, TocEntry *te)
     378             : {
     379         372 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     380             : 
     381         372 :     if (!tctx->filename)
     382           0 :         return;
     383             : 
     384         372 :     if (strcmp(te->desc, "BLOBS") == 0)
     385          16 :         _LoadLOs(AH, te);
     386             :     else
     387             :     {
     388             :         char        fname[MAXPGPATH];
     389             : 
     390         356 :         setFilePath(AH, fname, tctx->filename);
     391         356 :         _PrintFileData(AH, fname);
     392             :     }
     393             : }
     394             : 
     395             : static void
     396          16 : _LoadLOs(ArchiveHandle *AH, TocEntry *te)
     397             : {
     398             :     Oid         oid;
     399          16 :     lclContext *ctx = (lclContext *) AH->formatData;
     400          16 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     401             :     CompressFileHandle *CFH;
     402             :     char        tocfname[MAXPGPATH];
     403             :     char        line[MAXPGPATH];
     404             : 
     405          16 :     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          16 :     if (AH->version < K_VERS_1_16)
     415           0 :         setFilePath(AH, tocfname, "blobs.toc");
     416             :     else
     417          16 :         setFilePath(AH, tocfname, tctx->filename);
     418             : 
     419          16 :     CFH = ctx->LOsTocFH = InitDiscoverCompressFileHandle(tocfname, PG_BINARY_R);
     420             : 
     421          16 :     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          32 :     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          16 :         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          16 :         StartRestoreLO(AH, oid, AH->public.ropt->dropSchema);
     437          16 :         snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, lofname);
     438          16 :         _PrintFileData(AH, path);
     439          16 :         EndRestoreLO(AH, oid);
     440             :     }
     441          16 :     if (!CFH->eof_func(CFH))
     442           0 :         pg_fatal("error reading large object TOC file \"%s\"",
     443             :                  tocfname);
     444             : 
     445          16 :     if (!EndCompressFileHandle(ctx->LOsTocFH))
     446           0 :         pg_fatal("could not close large object TOC file \"%s\": %m",
     447             :                  tocfname);
     448             : 
     449          16 :     ctx->LOsTocFH = NULL;
     450             : 
     451          16 :     EndRestoreLOs(AH);
     452          16 : }
     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      341610 : _WriteByte(ArchiveHandle *AH, const int i)
     462             : {
     463      341610 :     unsigned char c = (unsigned char) i;
     464      341610 :     lclContext *ctx = (lclContext *) AH->formatData;
     465      341610 :     CompressFileHandle *CFH = ctx->dataFH;
     466             : 
     467      341610 :     CFH->write_func(&c, 1, CFH);
     468      341610 :     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      418718 : _ReadByte(ArchiveHandle *AH)
     479             : {
     480      418718 :     lclContext *ctx = (lclContext *) AH->formatData;
     481      418718 :     CompressFileHandle *CFH = ctx->dataFH;
     482             : 
     483      418718 :     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       38452 : _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
     492             : {
     493       38452 :     lclContext *ctx = (lclContext *) AH->formatData;
     494       38452 :     CompressFileHandle *CFH = ctx->dataFH;
     495             : 
     496       38452 :     CFH->write_func(buf, len, CFH);
     497       38452 : }
     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       47110 : _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
     506             : {
     507       47110 :     lclContext *ctx = (lclContext *) AH->formatData;
     508       47110 :     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       47110 :     if (CFH->read_func(buf, len, CFH) != len)
     515           0 :         pg_fatal("could not read from input file: end of file");
     516       47110 : }
     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          44 : _CloseArchive(ArchiveHandle *AH)
     532             : {
     533          44 :     lclContext *ctx = (lclContext *) AH->formatData;
     534             : 
     535          44 :     if (AH->mode == archModeWrite)
     536             :     {
     537             :         CompressFileHandle *tocFH;
     538          20 :         pg_compress_specification compression_spec = {0};
     539             :         char        fname[MAXPGPATH];
     540             : 
     541          20 :         setFilePath(AH, fname, "toc.dat");
     542             : 
     543             :         /* this will actually fork the processes for a parallel backup */
     544          20 :         ctx->pstate = ParallelBackupStart(AH);
     545             : 
     546             :         /* The TOC is always created uncompressed */
     547          20 :         compression_spec.algorithm = PG_COMPRESSION_NONE;
     548          20 :         tocFH = InitCompressFileHandle(compression_spec);
     549          20 :         if (!tocFH->open_write_func(fname, PG_BINARY_W, tocFH))
     550           0 :             pg_fatal("could not open output file \"%s\": %m", fname);
     551          20 :         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          20 :         AH->format = archTar;
     559          20 :         WriteHead(AH);
     560          20 :         AH->format = archDirectory;
     561          20 :         WriteToc(AH);
     562          20 :         if (!EndCompressFileHandle(tocFH))
     563           0 :             pg_fatal("could not close TOC file: %m");
     564          20 :         WriteDataChunks(AH, ctx->pstate);
     565             : 
     566          20 :         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          20 :         if (AH->dosync)
     573           8 :             sync_dir_recurse(ctx->directory, AH->sync_method);
     574             :     }
     575          44 :     AH->FH = NULL;
     576          44 : }
     577             : 
     578             : /*
     579             :  * Reopen the archive's file handle.
     580             :  */
     581             : static void
     582          28 : _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          28 : }
     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          16 : _StartLOs(ArchiveHandle *AH, TocEntry *te)
     604             : {
     605          16 :     lclContext *ctx = (lclContext *) AH->formatData;
     606          16 :     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
     607          16 :     pg_compress_specification compression_spec = {0};
     608             :     char        fname[MAXPGPATH];
     609             : 
     610          16 :     setFilePath(AH, fname, tctx->filename);
     611             : 
     612             :     /* The LO TOC file is never compressed */
     613          16 :     compression_spec.algorithm = PG_COMPRESSION_NONE;
     614          16 :     ctx->LOsTocFH = InitCompressFileHandle(compression_spec);
     615          16 :     if (!ctx->LOsTocFH->open_write_func(fname, "ab", ctx->LOsTocFH))
     616           0 :         pg_fatal("could not open output file \"%s\": %m", fname);
     617          16 : }
     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          16 : _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
     626             : {
     627          16 :     lclContext *ctx = (lclContext *) AH->formatData;
     628             :     char        fname[MAXPGPATH];
     629             : 
     630          16 :     snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
     631             : 
     632          16 :     ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
     633          16 :     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          16 : }
     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          16 : _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
     644             : {
     645          16 :     lclContext *ctx = (lclContext *) AH->formatData;
     646          16 :     CompressFileHandle *CFH = ctx->LOsTocFH;
     647             :     char        buf[50];
     648             :     int         len;
     649             : 
     650             :     /* Close the BLOB data file itself */
     651          16 :     if (!EndCompressFileHandle(ctx->dataFH))
     652           0 :         pg_fatal("could not close LO data file: %m");
     653          16 :     ctx->dataFH = NULL;
     654             : 
     655             :     /* register the LO in blobs_NNN.toc */
     656          16 :     len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
     657          16 :     CFH->write_func(buf, len, CFH);
     658          16 : }
     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          16 : _EndLOs(ArchiveHandle *AH, TocEntry *te)
     667             : {
     668          16 :     lclContext *ctx = (lclContext *) AH->formatData;
     669             : 
     670          16 :     if (!EndCompressFileHandle(ctx->LOsTocFH))
     671           0 :         pg_fatal("could not close LOs TOC file: %m");
     672          16 :     ctx->LOsTocFH = NULL;
     673          16 : }
     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         818 : setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
     683             : {
     684         818 :     lclContext *ctx = (lclContext *) AH->formatData;
     685             :     char       *dname;
     686             : 
     687         818 :     dname = ctx->directory;
     688             : 
     689         818 :     if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
     690           0 :         pg_fatal("file name too long: \"%s\"", dname);
     691             : 
     692         818 :     strcpy(buf, dname);
     693         818 :     strcat(buf, "/");
     694         818 :     strcat(buf, relativeFilename);
     695         818 : }
     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           8 : _PrepParallelRestore(ArchiveHandle *AH)
     709             : {
     710             :     TocEntry   *te;
     711             : 
     712         200 :     for (te = AH->toc->next; te != AH->toc; te = te->next)
     713             :     {
     714         192 :         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         192 :         if (tctx->filename == NULL)
     723         160 :             continue;
     724             : 
     725             :         /* We may ignore items not due to be restored */
     726          32 :         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          32 :         setFilePath(AH, fname, tctx->filename);
     736             : 
     737          32 :         if (stat(fname, &st) == 0)
     738           0 :             te->dataLength = st.st_size;
     739          32 :         else if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
     740             :         {
     741          32 :             if (AH->compression_spec.algorithm == PG_COMPRESSION_GZIP)
     742          32 :                 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          32 :             if (stat(fname, &st) == 0)
     749          32 :                 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          32 :         if (strcmp(te->desc, "BLOBS") == 0)
     760           0 :             te->dataLength *= 1024;
     761             :     }
     762           8 : }
     763             : 
     764             : /*
     765             :  * Clone format-specific fields during parallel restoration.
     766             :  */
     767             : static void
     768          52 : _Clone(ArchiveHandle *AH)
     769             : {
     770          52 :     lclContext *ctx = (lclContext *) AH->formatData;
     771             : 
     772          52 :     AH->formatData = (lclContext *) pg_malloc(sizeof(lclContext));
     773          52 :     memcpy(AH->formatData, ctx, sizeof(lclContext));
     774          52 :     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          52 : }
     786             : 
     787             : static void
     788          52 : _DeClone(ArchiveHandle *AH)
     789             : {
     790          52 :     lclContext *ctx = (lclContext *) AH->formatData;
     791             : 
     792          52 :     free(ctx);
     793          52 : }
     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         284 : _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         284 :     WriteDataChunksForTocEntry(AH, te);
     808             : 
     809         284 :     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          92 : _WorkerJobRestoreDirectory(ArchiveHandle *AH, TocEntry *te)
     818             : {
     819          92 :     return parallel_restore(AH, te);
     820             : }

Generated by: LCOV version 1.16