Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_backup_tar.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_backup_tar.c
00004  *
00005  *  This file is copied from the 'files' format file, but dumps data into
00006  *  one temp file then sends it to the output TAR archive.
00007  *
00008  *  The tar format also includes a 'restore.sql' script which is there for
00009  *  the benefit of humans. This script is never used by pg_restore.
00010  *
00011  *  NOTE: If you untar the created 'tar' file, the resulting files are
00012  *  compatible with the 'directory' format. Please keep the two formats in
00013  *  sync.
00014  *
00015  *  See the headers to pg_backup_directory & pg_restore for more details.
00016  *
00017  * Copyright (c) 2000, Philip Warner
00018  *      Rights are granted to use this software in any way so long
00019  *      as this notice is not removed.
00020  *
00021  *  The author is not responsible for loss or damages that may
00022  *  result from it's use.
00023  *
00024  *
00025  * IDENTIFICATION
00026  *      src/bin/pg_dump/pg_backup_tar.c
00027  *
00028  *-------------------------------------------------------------------------
00029  */
00030 
00031 #include "pg_backup.h"
00032 #include "pg_backup_archiver.h"
00033 #include "pg_backup_tar.h"
00034 #include "pg_backup_utils.h"
00035 #include "parallel.h"
00036 #include "pgtar.h"
00037 
00038 #include <sys/stat.h>
00039 #include <ctype.h>
00040 #include <limits.h>
00041 #include <unistd.h>
00042 
00043 static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
00044 static void _StartData(ArchiveHandle *AH, TocEntry *te);
00045 static size_t _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
00046 static void _EndData(ArchiveHandle *AH, TocEntry *te);
00047 static int  _WriteByte(ArchiveHandle *AH, const int i);
00048 static int  _ReadByte(ArchiveHandle *);
00049 static size_t _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
00050 static size_t _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
00051 static void _CloseArchive(ArchiveHandle *AH);
00052 static void _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
00053 static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
00054 static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
00055 static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
00056 
00057 static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
00058 static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
00059 static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
00060 static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
00061 
00062 #define K_STD_BUF_SIZE 1024
00063 
00064 
00065 typedef struct
00066 {
00067 #ifdef HAVE_LIBZ
00068     gzFile      zFH;
00069 #else
00070     FILE       *zFH;
00071 #endif
00072     FILE       *nFH;
00073     FILE       *tarFH;
00074     FILE       *tmpFH;
00075     char       *targetFile;
00076     char        mode;
00077     pgoff_t     pos;
00078     pgoff_t     fileLen;
00079     ArchiveHandle *AH;
00080 } TAR_MEMBER;
00081 
00082 /*
00083  * Maximum file size for a tar member: The limit inherent in the
00084  * format is 2^33-1 bytes (nearly 8 GB).  But we don't want to exceed
00085  * what we can represent in pgoff_t.
00086  */
00087 #define MAX_TAR_MEMBER_FILELEN (((int64) 1 << Min(33, sizeof(pgoff_t)*8 - 1)) - 1)
00088 
00089 typedef struct
00090 {
00091     int         hasSeek;
00092     pgoff_t     filePos;
00093     TAR_MEMBER *blobToc;
00094     FILE       *tarFH;
00095     pgoff_t     tarFHpos;
00096     pgoff_t     tarNextMember;
00097     TAR_MEMBER *FH;
00098     int         isSpecialScript;
00099     TAR_MEMBER *scriptTH;
00100 } lclContext;
00101 
00102 typedef struct
00103 {
00104     TAR_MEMBER *TH;
00105     char       *filename;
00106 } lclTocEntry;
00107 
00108 /* translator: this is a module name */
00109 static const char *modulename = gettext_noop("tar archiver");
00110 
00111 static void _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt);
00112 
00113 static TAR_MEMBER *tarOpen(ArchiveHandle *AH, const char *filename, char mode);
00114 static void tarClose(ArchiveHandle *AH, TAR_MEMBER *TH);
00115 
00116 #ifdef __NOT_USED__
00117 static char *tarGets(char *buf, size_t len, TAR_MEMBER *th);
00118 #endif
00119 static int  tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 3, 4)));
00120 
00121 static void _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th);
00122 static TAR_MEMBER *_tarPositionTo(ArchiveHandle *AH, const char *filename);
00123 static size_t tarRead(void *buf, size_t len, TAR_MEMBER *th);
00124 static size_t tarWrite(const void *buf, size_t len, TAR_MEMBER *th);
00125 static void _tarWriteHeader(TAR_MEMBER *th);
00126 static int  _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th);
00127 static size_t _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh);
00128 
00129 static size_t _scriptOut(ArchiveHandle *AH, const void *buf, size_t len);
00130 
00131 /*
00132  *  Initializer
00133  */
00134 void
00135 InitArchiveFmt_Tar(ArchiveHandle *AH)
00136 {
00137     lclContext *ctx;
00138 
00139     /* Assuming static functions, this can be copied for each format. */
00140     AH->ArchiveEntryPtr = _ArchiveEntry;
00141     AH->StartDataPtr = _StartData;
00142     AH->WriteDataPtr = _WriteData;
00143     AH->EndDataPtr = _EndData;
00144     AH->WriteBytePtr = _WriteByte;
00145     AH->ReadBytePtr = _ReadByte;
00146     AH->WriteBufPtr = _WriteBuf;
00147     AH->ReadBufPtr = _ReadBuf;
00148     AH->ClosePtr = _CloseArchive;
00149     AH->ReopenPtr = NULL;
00150     AH->PrintTocDataPtr = _PrintTocData;
00151     AH->ReadExtraTocPtr = _ReadExtraToc;
00152     AH->WriteExtraTocPtr = _WriteExtraToc;
00153     AH->PrintExtraTocPtr = _PrintExtraToc;
00154 
00155     AH->StartBlobsPtr = _StartBlobs;
00156     AH->StartBlobPtr = _StartBlob;
00157     AH->EndBlobPtr = _EndBlob;
00158     AH->EndBlobsPtr = _EndBlobs;
00159     AH->ClonePtr = NULL;
00160     AH->DeClonePtr = NULL;
00161 
00162     AH->MasterStartParallelItemPtr = NULL;
00163     AH->MasterEndParallelItemPtr = NULL;
00164 
00165     AH->WorkerJobDumpPtr = NULL;
00166     AH->WorkerJobRestorePtr = NULL;
00167 
00168     /*
00169      * Set up some special context used in compressing data.
00170      */
00171     ctx = (lclContext *) pg_malloc0(sizeof(lclContext));
00172     AH->formatData = (void *) ctx;
00173     ctx->filePos = 0;
00174     ctx->isSpecialScript = 0;
00175 
00176     /* Initialize LO buffering */
00177     AH->lo_buf_size = LOBBUFSIZE;
00178     AH->lo_buf = (void *) pg_malloc(LOBBUFSIZE);
00179 
00180     /*
00181      * Now open the tar file, and load the TOC if we're in read mode.
00182      */
00183     if (AH->mode == archModeWrite)
00184     {
00185         if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
00186         {
00187             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
00188             if (ctx->tarFH == NULL)
00189                 exit_horribly(modulename,
00190                            "could not open TOC file \"%s\" for output: %s\n",
00191                               AH->fSpec, strerror(errno));
00192         }
00193         else
00194         {
00195             ctx->tarFH = stdout;
00196             if (ctx->tarFH == NULL)
00197                 exit_horribly(modulename,
00198                               "could not open TOC file for output: %s\n",
00199                               strerror(errno));
00200         }
00201 
00202         ctx->tarFHpos = 0;
00203 
00204         /*
00205          * Make unbuffered since we will dup() it, and the buffers screw each
00206          * other
00207          */
00208         /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
00209 
00210         ctx->hasSeek = checkSeek(ctx->tarFH);
00211 
00212         if (AH->compression < 0 || AH->compression > 9)
00213             AH->compression = Z_DEFAULT_COMPRESSION;
00214 
00215         /* Don't compress into tar files unless asked to do so */
00216         if (AH->compression == Z_DEFAULT_COMPRESSION)
00217             AH->compression = 0;
00218 
00219         /*
00220          * We don't support compression because reading the files back is not
00221          * possible since gzdopen uses buffered IO which totally screws file
00222          * positioning.
00223          */
00224         if (AH->compression != 0)
00225             exit_horribly(modulename,
00226                      "compression is not supported by tar archive format\n");
00227     }
00228     else
00229     {                           /* Read Mode */
00230         if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
00231         {
00232             ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
00233             if (ctx->tarFH == NULL)
00234                 exit_horribly(modulename, "could not open TOC file \"%s\" for input: %s\n",
00235                               AH->fSpec, strerror(errno));
00236         }
00237         else
00238         {
00239             ctx->tarFH = stdin;
00240             if (ctx->tarFH == NULL)
00241                 exit_horribly(modulename, "could not open TOC file for input: %s\n",
00242                               strerror(errno));
00243         }
00244 
00245         /*
00246          * Make unbuffered since we will dup() it, and the buffers screw each
00247          * other
00248          */
00249         /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
00250 
00251         ctx->tarFHpos = 0;
00252 
00253         ctx->hasSeek = checkSeek(ctx->tarFH);
00254 
00255         /*
00256          * Forcibly unmark the header as read since we use the lookahead
00257          * buffer
00258          */
00259         AH->readHeader = 0;
00260 
00261         ctx->FH = (void *) tarOpen(AH, "toc.dat", 'r');
00262         ReadHead(AH);
00263         ReadToc(AH);
00264         tarClose(AH, ctx->FH);  /* Nothing else in the file... */
00265     }
00266 }
00267 
00268 /*
00269  * - Start a new TOC entry
00270  *   Setup the output file name.
00271  */
00272 static void
00273 _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
00274 {
00275     lclTocEntry *ctx;
00276     char        fn[K_STD_BUF_SIZE];
00277 
00278     ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
00279     if (te->dataDumper != NULL)
00280     {
00281 #ifdef HAVE_LIBZ
00282         if (AH->compression == 0)
00283             sprintf(fn, "%d.dat", te->dumpId);
00284         else
00285             sprintf(fn, "%d.dat.gz", te->dumpId);
00286 #else
00287         sprintf(fn, "%d.dat", te->dumpId);
00288 #endif
00289         ctx->filename = pg_strdup(fn);
00290     }
00291     else
00292     {
00293         ctx->filename = NULL;
00294         ctx->TH = NULL;
00295     }
00296     te->formatData = (void *) ctx;
00297 }
00298 
00299 static void
00300 _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
00301 {
00302     lclTocEntry *ctx = (lclTocEntry *) te->formatData;
00303 
00304     if (ctx->filename)
00305         WriteStr(AH, ctx->filename);
00306     else
00307         WriteStr(AH, "");
00308 }
00309 
00310 static void
00311 _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
00312 {
00313     lclTocEntry *ctx = (lclTocEntry *) te->formatData;
00314 
00315     if (ctx == NULL)
00316     {
00317         ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
00318         te->formatData = (void *) ctx;
00319     }
00320 
00321     ctx->filename = ReadStr(AH);
00322     if (strlen(ctx->filename) == 0)
00323     {
00324         free(ctx->filename);
00325         ctx->filename = NULL;
00326     }
00327     ctx->TH = NULL;
00328 }
00329 
00330 static void
00331 _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
00332 {
00333     lclTocEntry *ctx = (lclTocEntry *) te->formatData;
00334 
00335     if (AH->public.verbose && ctx->filename != NULL)
00336         ahprintf(AH, "-- File: %s\n", ctx->filename);
00337 }
00338 
00339 static void
00340 _StartData(ArchiveHandle *AH, TocEntry *te)
00341 {
00342     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
00343 
00344     tctx->TH = tarOpen(AH, tctx->filename, 'w');
00345 }
00346 
00347 static TAR_MEMBER *
00348 tarOpen(ArchiveHandle *AH, const char *filename, char mode)
00349 {
00350     lclContext *ctx = (lclContext *) AH->formatData;
00351     TAR_MEMBER *tm;
00352 
00353 #ifdef HAVE_LIBZ
00354     char        fmode[10];
00355 #endif
00356 
00357     if (mode == 'r')
00358     {
00359         tm = _tarPositionTo(AH, filename);
00360         if (!tm)                /* Not found */
00361         {
00362             if (filename)
00363             {
00364                 /*
00365                  * Couldn't find the requested file. Future: do SEEK(0) and
00366                  * retry.
00367                  */
00368                 exit_horribly(modulename, "could not find file \"%s\" in archive\n", filename);
00369             }
00370             else
00371             {
00372                 /* Any file OK, none left, so return NULL */
00373                 return NULL;
00374             }
00375         }
00376 
00377 #ifdef HAVE_LIBZ
00378 
00379         if (AH->compression == 0)
00380             tm->nFH = ctx->tarFH;
00381         else
00382             exit_horribly(modulename, "compression is not supported by tar archive format\n");
00383         /* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
00384 #else
00385         tm->nFH = ctx->tarFH;
00386 #endif
00387     }
00388     else
00389     {
00390         tm = pg_malloc0(sizeof(TAR_MEMBER));
00391 
00392 #ifndef WIN32
00393         tm->tmpFH = tmpfile();
00394 #else
00395 
00396         /*
00397          * On WIN32, tmpfile() generates a filename in the root directory,
00398          * which requires administrative permissions on certain systems. Loop
00399          * until we find a unique file name we can create.
00400          */
00401         while (1)
00402         {
00403             char       *name;
00404             int         fd;
00405 
00406             name = _tempnam(NULL, "pg_temp_");
00407             if (name == NULL)
00408                 break;
00409             fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_BINARY |
00410                       O_TEMPORARY, S_IRUSR | S_IWUSR);
00411             free(name);
00412 
00413             if (fd != -1)       /* created a file */
00414             {
00415                 tm->tmpFH = fdopen(fd, "w+b");
00416                 break;
00417             }
00418             else if (errno != EEXIST)   /* failure other than file exists */
00419                 break;
00420         }
00421 #endif
00422 
00423         if (tm->tmpFH == NULL)
00424             exit_horribly(modulename, "could not generate temporary file name: %s\n", strerror(errno));
00425 
00426 #ifdef HAVE_LIBZ
00427 
00428         if (AH->compression != 0)
00429         {
00430             sprintf(fmode, "wb%d", AH->compression);
00431             tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
00432             if (tm->zFH == NULL)
00433                 exit_horribly(modulename, "could not open temporary file\n");
00434         }
00435         else
00436             tm->nFH = tm->tmpFH;
00437 #else
00438 
00439         tm->nFH = tm->tmpFH;
00440 #endif
00441 
00442         tm->AH = AH;
00443         tm->targetFile = pg_strdup(filename);
00444     }
00445 
00446     tm->mode = mode;
00447     tm->tarFH = ctx->tarFH;
00448 
00449     return tm;
00450 }
00451 
00452 static void
00453 tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
00454 {
00455     /*
00456      * Close the GZ file since we dup'd. This will flush the buffers.
00457      */
00458     if (AH->compression != 0)
00459         if (GZCLOSE(th->zFH) != 0)
00460             exit_horribly(modulename, "could not close tar member\n");
00461 
00462     if (th->mode == 'w')
00463         _tarAddFile(AH, th);    /* This will close the temp file */
00464 
00465     /*
00466      * else Nothing to do for normal read since we don't dup() normal file
00467      * handle, and we don't use temp files.
00468      */
00469 
00470     if (th->targetFile)
00471         free(th->targetFile);
00472 
00473     th->nFH = NULL;
00474     th->zFH = NULL;
00475 }
00476 
00477 #ifdef __NOT_USED__
00478 static char *
00479 tarGets(char *buf, size_t len, TAR_MEMBER *th)
00480 {
00481     char       *s;
00482     size_t      cnt = 0;
00483     char        c = ' ';
00484     int         eof = 0;
00485 
00486     /* Can't read past logical EOF */
00487     if (len > (th->fileLen - th->pos))
00488         len = th->fileLen - th->pos;
00489 
00490     while (cnt < len && c != '\n')
00491     {
00492         if (_tarReadRaw(th->AH, &c, 1, th, NULL) <= 0)
00493         {
00494             eof = 1;
00495             break;
00496         }
00497         buf[cnt++] = c;
00498     }
00499 
00500     if (eof && cnt == 0)
00501         s = NULL;
00502     else
00503     {
00504         buf[cnt++] = '\0';
00505         s = buf;
00506     }
00507 
00508     if (s)
00509     {
00510         len = strlen(s);
00511         th->pos += len;
00512     }
00513 
00514     return s;
00515 }
00516 #endif
00517 
00518 /*
00519  * Just read bytes from the archive. This is the low level read routine
00520  * that is used for ALL reads on a tar file.
00521  */
00522 static size_t
00523 _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
00524 {
00525     lclContext *ctx = (lclContext *) AH->formatData;
00526     size_t      avail;
00527     size_t      used = 0;
00528     size_t      res = 0;
00529 
00530     avail = AH->lookaheadLen - AH->lookaheadPos;
00531     if (avail > 0)
00532     {
00533         /* We have some lookahead bytes to use */
00534         if (avail >= len)       /* Just use the lookahead buffer */
00535             used = len;
00536         else
00537             used = avail;
00538 
00539         /* Copy, and adjust buffer pos */
00540         memcpy(buf, AH->lookahead + AH->lookaheadPos, used);
00541         AH->lookaheadPos += used;
00542 
00543         /* Adjust required length */
00544         len -= used;
00545     }
00546 
00547     /* Read the file if len > 0 */
00548     if (len > 0)
00549     {
00550         if (fh)
00551             res = fread(&((char *) buf)[used], 1, len, fh);
00552         else if (th)
00553         {
00554             if (th->zFH)
00555                 res = GZREAD(&((char *) buf)[used], 1, len, th->zFH);
00556             else
00557                 res = fread(&((char *) buf)[used], 1, len, th->nFH);
00558         }
00559         else
00560             exit_horribly(modulename, "internal error -- neither th nor fh specified in tarReadRaw()\n");
00561     }
00562 
00563     ctx->tarFHpos += res + used;
00564 
00565     return (res + used);
00566 }
00567 
00568 static size_t
00569 tarRead(void *buf, size_t len, TAR_MEMBER *th)
00570 {
00571     size_t      res;
00572 
00573     if (th->pos + len > th->fileLen)
00574         len = th->fileLen - th->pos;
00575 
00576     if (len <= 0)
00577         return 0;
00578 
00579     res = _tarReadRaw(th->AH, buf, len, th, NULL);
00580 
00581     th->pos += res;
00582 
00583     return res;
00584 }
00585 
00586 static size_t
00587 tarWrite(const void *buf, size_t len, TAR_MEMBER *th)
00588 {
00589     size_t      res;
00590 
00591     if (th->zFH != NULL)
00592         res = GZWRITE(buf, 1, len, th->zFH);
00593     else
00594         res = fwrite(buf, 1, len, th->nFH);
00595 
00596     if (res != len)
00597         exit_horribly(modulename,
00598                     "could not write to output file: %s\n", strerror(errno));
00599 
00600     th->pos += res;
00601     return res;
00602 }
00603 
00604 static size_t
00605 _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
00606 {
00607     lclTocEntry *tctx = (lclTocEntry *) AH->currToc->formatData;
00608 
00609     dLen = tarWrite(data, dLen, tctx->TH);
00610 
00611     return dLen;
00612 }
00613 
00614 static void
00615 _EndData(ArchiveHandle *AH, TocEntry *te)
00616 {
00617     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
00618 
00619     /* Close the file */
00620     tarClose(AH, tctx->TH);
00621     tctx->TH = NULL;
00622 }
00623 
00624 /*
00625  * Print data for a given file
00626  */
00627 static void
00628 _PrintFileData(ArchiveHandle *AH, char *filename, RestoreOptions *ropt)
00629 {
00630     lclContext *ctx = (lclContext *) AH->formatData;
00631     char        buf[4096];
00632     size_t      cnt;
00633     TAR_MEMBER *th;
00634 
00635     if (!filename)
00636         return;
00637 
00638     th = tarOpen(AH, filename, 'r');
00639     ctx->FH = th;
00640 
00641     while ((cnt = tarRead(buf, 4095, th)) > 0)
00642     {
00643         buf[cnt] = '\0';
00644         ahwrite(buf, 1, cnt, AH);
00645     }
00646 
00647     tarClose(AH, th);
00648 }
00649 
00650 
00651 /*
00652  * Print data for a given TOC entry
00653 */
00654 static void
00655 _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
00656 {
00657     lclContext *ctx = (lclContext *) AH->formatData;
00658     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
00659     int         pos1;
00660 
00661     if (!tctx->filename)
00662         return;
00663 
00664     /*
00665      * If we're writing the special restore.sql script, emit a suitable
00666      * command to include each table's data from the corresponding file.
00667      *
00668      * In the COPY case this is a bit klugy because the regular COPY command
00669      * was already printed before we get control.
00670      */
00671     if (ctx->isSpecialScript)
00672     {
00673         if (te->copyStmt)
00674         {
00675             /* Abort the COPY FROM stdin */
00676             ahprintf(AH, "\\.\n");
00677 
00678             /*
00679              * The COPY statement should look like "COPY ... FROM stdin;\n",
00680              * see dumpTableData().
00681              */
00682             pos1 = (int) strlen(te->copyStmt) - 13;
00683             if (pos1 < 6 || strncmp(te->copyStmt, "COPY ", 5) != 0 ||
00684                 strcmp(te->copyStmt + pos1, " FROM stdin;\n") != 0)
00685                 exit_horribly(modulename,
00686                               "unexpected COPY statement syntax: \"%s\"\n",
00687                               te->copyStmt);
00688 
00689             /* Emit all but the FROM part ... */
00690             ahwrite(te->copyStmt, 1, pos1, AH);
00691             /* ... and insert modified FROM */
00692             ahprintf(AH, " FROM '$$PATH$$/%s';\n\n", tctx->filename);
00693         }
00694         else
00695         {
00696             /* --inserts mode, no worries, just include the data file */
00697             ahprintf(AH, "\\i $$PATH$$/%s\n\n", tctx->filename);
00698         }
00699 
00700         return;
00701     }
00702 
00703     if (strcmp(te->desc, "BLOBS") == 0)
00704         _LoadBlobs(AH, ropt);
00705     else
00706         _PrintFileData(AH, tctx->filename, ropt);
00707 }
00708 
00709 static void
00710 _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
00711 {
00712     Oid         oid;
00713     lclContext *ctx = (lclContext *) AH->formatData;
00714     TAR_MEMBER *th;
00715     size_t      cnt;
00716     bool        foundBlob = false;
00717     char        buf[4096];
00718 
00719     StartRestoreBlobs(AH);
00720 
00721     th = tarOpen(AH, NULL, 'r');    /* Open next file */
00722     while (th != NULL)
00723     {
00724         ctx->FH = th;
00725 
00726         if (strncmp(th->targetFile, "blob_", 5) == 0)
00727         {
00728             oid = atooid(&th->targetFile[5]);
00729             if (oid != 0)
00730             {
00731                 ahlog(AH, 1, "restoring large object with OID %u\n", oid);
00732 
00733                 StartRestoreBlob(AH, oid, ropt->dropSchema);
00734 
00735                 while ((cnt = tarRead(buf, 4095, th)) > 0)
00736                 {
00737                     buf[cnt] = '\0';
00738                     ahwrite(buf, 1, cnt, AH);
00739                 }
00740                 EndRestoreBlob(AH, oid);
00741                 foundBlob = true;
00742             }
00743             tarClose(AH, th);
00744         }
00745         else
00746         {
00747             tarClose(AH, th);
00748 
00749             /*
00750              * Once we have found the first blob, stop at the first non-blob
00751              * entry (which will be 'blobs.toc').  This coding would eat all
00752              * the rest of the archive if there are no blobs ... but this
00753              * function shouldn't be called at all in that case.
00754              */
00755             if (foundBlob)
00756                 break;
00757         }
00758 
00759         th = tarOpen(AH, NULL, 'r');
00760     }
00761     EndRestoreBlobs(AH);
00762 }
00763 
00764 
00765 static int
00766 _WriteByte(ArchiveHandle *AH, const int i)
00767 {
00768     lclContext *ctx = (lclContext *) AH->formatData;
00769     int         res;
00770     char        b = i;          /* Avoid endian problems */
00771 
00772     res = tarWrite(&b, 1, ctx->FH);
00773     if (res != EOF)
00774         ctx->filePos += res;
00775     return res;
00776 }
00777 
00778 static int
00779 _ReadByte(ArchiveHandle *AH)
00780 {
00781     lclContext *ctx = (lclContext *) AH->formatData;
00782     size_t      res;
00783     unsigned char c;
00784 
00785     res = tarRead(&c, 1, ctx->FH);
00786     if (res != 1)
00787         exit_horribly(modulename, "unexpected end of file\n");
00788     ctx->filePos += 1;
00789     return c;
00790 }
00791 
00792 static size_t
00793 _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
00794 {
00795     lclContext *ctx = (lclContext *) AH->formatData;
00796     size_t      res;
00797 
00798     res = tarWrite(buf, len, ctx->FH);
00799     ctx->filePos += res;
00800     return res;
00801 }
00802 
00803 static size_t
00804 _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
00805 {
00806     lclContext *ctx = (lclContext *) AH->formatData;
00807     size_t      res;
00808 
00809     res = tarRead(buf, len, ctx->FH);
00810     ctx->filePos += res;
00811     return res;
00812 }
00813 
00814 static void
00815 _CloseArchive(ArchiveHandle *AH)
00816 {
00817     lclContext *ctx = (lclContext *) AH->formatData;
00818     TAR_MEMBER *th;
00819     RestoreOptions *ropt;
00820     RestoreOptions *savRopt;
00821     int         savVerbose,
00822                 i;
00823 
00824     if (AH->mode == archModeWrite)
00825     {
00826         /*
00827          * Write the Header & TOC to the archive FIRST
00828          */
00829         th = tarOpen(AH, "toc.dat", 'w');
00830         ctx->FH = th;
00831         WriteHead(AH);
00832         WriteToc(AH);
00833         tarClose(AH, th);       /* Not needed any more */
00834 
00835         /*
00836          * Now send the data (tables & blobs)
00837          */
00838         WriteDataChunks(AH, NULL);
00839 
00840         /*
00841          * Now this format wants to append a script which does a full restore
00842          * if the files have been extracted.
00843          */
00844         th = tarOpen(AH, "restore.sql", 'w');
00845 
00846         tarPrintf(AH, th, "--\n"
00847                   "-- NOTE:\n"
00848                   "--\n"
00849                   "-- File paths need to be edited. Search for $$PATH$$ and\n"
00850                   "-- replace it with the path to the directory containing\n"
00851                   "-- the extracted data files.\n"
00852                   "--\n");
00853 
00854         AH->CustomOutPtr = _scriptOut;
00855 
00856         ctx->isSpecialScript = 1;
00857         ctx->scriptTH = th;
00858 
00859         ropt = NewRestoreOptions();
00860         memcpy(ropt, AH->ropt, sizeof(RestoreOptions));
00861         ropt->filename = NULL;
00862         ropt->dropSchema = 1;
00863         ropt->compression = 0;
00864         ropt->superuser = NULL;
00865         ropt->suppressDumpWarnings = true;
00866 
00867         savRopt = AH->ropt;
00868         AH->ropt = ropt;
00869 
00870         savVerbose = AH->public.verbose;
00871         AH->public.verbose = 0;
00872 
00873         RestoreArchive((Archive *) AH);
00874 
00875         AH->ropt = savRopt;
00876         AH->public.verbose = savVerbose;
00877 
00878         tarClose(AH, th);
00879 
00880         ctx->isSpecialScript = 0;
00881 
00882         /*
00883          * EOF marker for tar files is two blocks of NULLs.
00884          */
00885         for (i = 0; i < 512 * 2; i++)
00886         {
00887             if (fputc(0, ctx->tarFH) == EOF)
00888                 exit_horribly(modulename,
00889                        "could not write null block at end of tar archive\n");
00890         }
00891     }
00892 
00893     AH->FH = NULL;
00894 }
00895 
00896 static size_t
00897 _scriptOut(ArchiveHandle *AH, const void *buf, size_t len)
00898 {
00899     lclContext *ctx = (lclContext *) AH->formatData;
00900 
00901     return tarWrite(buf, len, ctx->scriptTH);
00902 }
00903 
00904 /*
00905  * BLOB support
00906  */
00907 
00908 /*
00909  * Called by the archiver when starting to save all BLOB DATA (not schema).
00910  * This routine should save whatever format-specific information is needed
00911  * to read the BLOBs back into memory.
00912  *
00913  * It is called just prior to the dumper's DataDumper routine.
00914  *
00915  * Optional, but strongly recommended.
00916  *
00917  */
00918 static void
00919 _StartBlobs(ArchiveHandle *AH, TocEntry *te)
00920 {
00921     lclContext *ctx = (lclContext *) AH->formatData;
00922     char        fname[K_STD_BUF_SIZE];
00923 
00924     sprintf(fname, "blobs.toc");
00925     ctx->blobToc = tarOpen(AH, fname, 'w');
00926 }
00927 
00928 /*
00929  * Called by the archiver when the dumper calls StartBlob.
00930  *
00931  * Mandatory.
00932  *
00933  * Must save the passed OID for retrieval at restore-time.
00934  */
00935 static void
00936 _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
00937 {
00938     lclContext *ctx = (lclContext *) AH->formatData;
00939     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
00940     char        fname[255];
00941     char       *sfx;
00942 
00943     if (oid == 0)
00944         exit_horribly(modulename, "invalid OID for large object (%u)\n", oid);
00945 
00946     if (AH->compression != 0)
00947         sfx = ".gz";
00948     else
00949         sfx = "";
00950 
00951     sprintf(fname, "blob_%u.dat%s", oid, sfx);
00952 
00953     tarPrintf(AH, ctx->blobToc, "%u %s\n", oid, fname);
00954 
00955     tctx->TH = tarOpen(AH, fname, 'w');
00956 }
00957 
00958 /*
00959  * Called by the archiver when the dumper calls EndBlob.
00960  *
00961  * Optional.
00962  *
00963  */
00964 static void
00965 _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
00966 {
00967     lclTocEntry *tctx = (lclTocEntry *) te->formatData;
00968 
00969     tarClose(AH, tctx->TH);
00970 }
00971 
00972 /*
00973  * Called by the archiver when finishing saving all BLOB DATA.
00974  *
00975  * Optional.
00976  *
00977  */
00978 static void
00979 _EndBlobs(ArchiveHandle *AH, TocEntry *te)
00980 {
00981     lclContext *ctx = (lclContext *) AH->formatData;
00982 
00983     /* Write out a fake zero OID to mark end-of-blobs. */
00984     /* WriteInt(AH, 0); */
00985 
00986     tarClose(AH, ctx->blobToc);
00987 }
00988 
00989 
00990 
00991 /*------------
00992  * TAR Support
00993  *------------
00994  */
00995 
00996 static int
00997 tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
00998 {
00999     char       *p = NULL;
01000     va_list     ap;
01001     size_t      bSize = strlen(fmt) + 256;      /* Should be enough */
01002     int         cnt = -1;
01003 
01004     /*
01005      * This is paranoid: deal with the possibility that vsnprintf is willing
01006      * to ignore trailing null
01007      */
01008 
01009     /*
01010      * or returns > 0 even if string does not fit. It may be the case that it
01011      * returns cnt = bufsize
01012      */
01013     while (cnt < 0 || cnt >= (bSize - 1))
01014     {
01015         if (p != NULL)
01016             free(p);
01017         bSize *= 2;
01018         p = (char *) pg_malloc(bSize);
01019         va_start(ap, fmt);
01020         cnt = vsnprintf(p, bSize, fmt, ap);
01021         va_end(ap);
01022     }
01023     cnt = tarWrite(p, cnt, th);
01024     free(p);
01025     return cnt;
01026 }
01027 
01028 bool
01029 isValidTarHeader(char *header)
01030 {
01031     int         sum;
01032     int         chk = tarChecksum(header);
01033 
01034     sscanf(&header[148], "%8o", &sum);
01035 
01036     if (sum != chk)
01037         return false;
01038 
01039     /* POSIX tar format */
01040     if (memcmp(&header[257], "ustar\0", 6) == 0 &&
01041         memcmp(&header[263], "00", 2) == 0)
01042         return true;
01043     /* GNU tar format */
01044     if (memcmp(&header[257], "ustar  \0", 8) == 0)
01045         return true;
01046     /* not-quite-POSIX format written by pre-9.3 pg_dump */
01047     if (memcmp(&header[257], "ustar00\0", 8) == 0)
01048         return true;
01049 
01050     return false;
01051 }
01052 
01053 /* Given the member, write the TAR header & copy the file */
01054 static void
01055 _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
01056 {
01057     lclContext *ctx = (lclContext *) AH->formatData;
01058     FILE       *tmp = th->tmpFH;    /* Grab it for convenience */
01059     char        buf[32768];
01060     size_t      cnt;
01061     pgoff_t     len = 0;
01062     size_t      res;
01063     size_t      i,
01064                 pad;
01065 
01066     /*
01067      * Find file len & go back to start.
01068      */
01069     fseeko(tmp, 0, SEEK_END);
01070     th->fileLen = ftello(tmp);
01071     fseeko(tmp, 0, SEEK_SET);
01072 
01073     /*
01074      * Some compilers will throw a warning knowing this test can never be true
01075      * because pgoff_t can't exceed the compared maximum on their platform.
01076      */
01077     if (th->fileLen > MAX_TAR_MEMBER_FILELEN)
01078         exit_horribly(modulename, "archive member too large for tar format\n");
01079 
01080     _tarWriteHeader(th);
01081 
01082     while ((cnt = fread(buf, 1, sizeof(buf), tmp)) > 0)
01083     {
01084         res = fwrite(buf, 1, cnt, th->tarFH);
01085         if (res != cnt)
01086             exit_horribly(modulename,
01087                           "could not write to output file: %s\n",
01088                           strerror(errno));
01089         len += res;
01090     }
01091 
01092     if (fclose(tmp) != 0)       /* This *should* delete it... */
01093         exit_horribly(modulename, "could not close temporary file: %s\n",
01094                       strerror(errno));
01095 
01096     if (len != th->fileLen)
01097     {
01098         char        buf1[32],
01099                     buf2[32];
01100 
01101         snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) len);
01102         snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) th->fileLen);
01103         exit_horribly(modulename, "actual file length (%s) does not match expected (%s)\n",
01104                       buf1, buf2);
01105     }
01106 
01107     pad = ((len + 511) & ~511) - len;
01108     for (i = 0; i < pad; i++)
01109     {
01110         if (fputc('\0', th->tarFH) == EOF)
01111             exit_horribly(modulename, "could not output padding at end of tar member\n");
01112     }
01113 
01114     ctx->tarFHpos += len + pad;
01115 }
01116 
01117 /* Locate the file in the archive, read header and position to data */
01118 static TAR_MEMBER *
01119 _tarPositionTo(ArchiveHandle *AH, const char *filename)
01120 {
01121     lclContext *ctx = (lclContext *) AH->formatData;
01122     TAR_MEMBER *th = pg_malloc0(sizeof(TAR_MEMBER));
01123     char        c;
01124     char        header[512];
01125     size_t      i,
01126                 len,
01127                 blks;
01128     int         id;
01129 
01130     th->AH = AH;
01131 
01132     /* Go to end of current file, if any */
01133     if (ctx->tarFHpos != 0)
01134     {
01135         char        buf1[100],
01136                     buf2[100];
01137 
01138         snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ctx->tarFHpos);
01139         snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ctx->tarNextMember);
01140         ahlog(AH, 4, "moving from position %s to next member at file position %s\n",
01141               buf1, buf2);
01142 
01143         while (ctx->tarFHpos < ctx->tarNextMember)
01144             _tarReadRaw(AH, &c, 1, NULL, ctx->tarFH);
01145     }
01146 
01147     {
01148         char        buf[100];
01149 
01150         snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ctx->tarFHpos);
01151         ahlog(AH, 4, "now at file position %s\n", buf);
01152     }
01153 
01154     /* We are at the start of the file, or at the next member */
01155 
01156     /* Get the header */
01157     if (!_tarGetHeader(AH, th))
01158     {
01159         if (filename)
01160             exit_horribly(modulename, "could not find header for file \"%s\" in tar archive\n", filename);
01161         else
01162         {
01163             /*
01164              * We're just scanning the archive for the next file, so return
01165              * null
01166              */
01167             free(th);
01168             return NULL;
01169         }
01170     }
01171 
01172     while (filename != NULL && strcmp(th->targetFile, filename) != 0)
01173     {
01174         ahlog(AH, 4, "skipping tar member %s\n", th->targetFile);
01175 
01176         id = atoi(th->targetFile);
01177         if ((TocIDRequired(AH, id) & REQ_DATA) != 0)
01178             exit_horribly(modulename, "restoring data out of order is not supported in this archive format: "
01179                           "\"%s\" is required, but comes before \"%s\" in the archive file.\n",
01180                           th->targetFile, filename);
01181 
01182         /* Header doesn't match, so read to next header */
01183         len = ((th->fileLen + 511) & ~511);     /* Padded length */
01184         blks = len >> 9;        /* # of 512 byte blocks */
01185 
01186         for (i = 0; i < blks; i++)
01187             _tarReadRaw(AH, &header[0], 512, NULL, ctx->tarFH);
01188 
01189         if (!_tarGetHeader(AH, th))
01190             exit_horribly(modulename, "could not find header for file \"%s\" in tar archive\n", filename);
01191     }
01192 
01193     ctx->tarNextMember = ctx->tarFHpos + ((th->fileLen + 511) & ~511);
01194     th->pos = 0;
01195 
01196     return th;
01197 }
01198 
01199 /* Read & verify a header */
01200 static int
01201 _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
01202 {
01203     lclContext *ctx = (lclContext *) AH->formatData;
01204     char        h[512];
01205     char        tag[100];
01206     int         sum,
01207                 chk;
01208     size_t      len;
01209     unsigned long ullen;
01210     pgoff_t     hPos;
01211     bool        gotBlock = false;
01212 
01213     while (!gotBlock)
01214     {
01215 #if 0
01216         if (ftello(ctx->tarFH) != ctx->tarFHpos)
01217         {
01218             char        buf1[100],
01219                         buf2[100];
01220 
01221             snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ftello(ctx->tarFH));
01222             snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ftello(ctx->tarFHpos));
01223             exit_horribly(modulename,
01224               "mismatch in actual vs. predicted file position (%s vs. %s)\n",
01225                           buf1, buf2);
01226         }
01227 #endif
01228 
01229         /* Save the pos for reporting purposes */
01230         hPos = ctx->tarFHpos;
01231 
01232         /* Read a 512 byte block, return EOF, exit if short */
01233         len = _tarReadRaw(AH, h, 512, NULL, ctx->tarFH);
01234         if (len == 0)           /* EOF */
01235             return 0;
01236 
01237         if (len != 512)
01238             exit_horribly(modulename,
01239                           ngettext("incomplete tar header found (%lu byte)\n",
01240                                  "incomplete tar header found (%lu bytes)\n",
01241                                    len),
01242                           (unsigned long) len);
01243 
01244         /* Calc checksum */
01245         chk = tarChecksum(h);
01246         sscanf(&h[148], "%8o", &sum);
01247 
01248         /*
01249          * If the checksum failed, see if it is a null block. If so, silently
01250          * continue to the next block.
01251          */
01252         if (chk == sum)
01253             gotBlock = true;
01254         else
01255         {
01256             int         i;
01257 
01258             for (i = 0; i < 512; i++)
01259             {
01260                 if (h[i] != 0)
01261                 {
01262                     gotBlock = true;
01263                     break;
01264                 }
01265             }
01266         }
01267     }
01268 
01269     sscanf(&h[0], "%99s", tag);
01270     sscanf(&h[124], "%12lo", &ullen);
01271     len = (size_t) ullen;
01272 
01273     {
01274         char        buf[100];
01275 
01276         snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) hPos);
01277         ahlog(AH, 3, "TOC Entry %s at %s (length %lu, checksum %d)\n",
01278               tag, buf, (unsigned long) len, sum);
01279     }
01280 
01281     if (chk != sum)
01282     {
01283         char        buf[100];
01284 
01285         snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ftello(ctx->tarFH));
01286         exit_horribly(modulename,
01287                       "corrupt tar header found in %s "
01288                       "(expected %d, computed %d) file position %s\n",
01289                       tag, sum, chk, buf);
01290     }
01291 
01292     th->targetFile = pg_strdup(tag);
01293     th->fileLen = len;
01294 
01295     return 1;
01296 }
01297 
01298 
01299 static void
01300 _tarWriteHeader(TAR_MEMBER *th)
01301 {
01302     char        h[512];
01303 
01304     tarCreateHeader(h, th->targetFile, NULL, th->fileLen, 0600, 04000, 02000, time(NULL));
01305 
01306     /* Now write the completed header. */
01307     if (fwrite(h, 1, 512, th->tarFH) != 512)
01308         exit_horribly(modulename, "could not write to output file: %s\n", strerror(errno));
01309 }