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

Generated by: LCOV version 1.14