]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/pg_backup_tar.c
Remove all the special-case code for INT64_IS_BUSTED, per decision that
[postgresql] / src / bin / pg_dump / pg_backup_tar.c
1 /*-------------------------------------------------------------------------
2  *
3  * pg_backup_tar.c
4  *
5  *      This file is copied from the 'files' format file, but dumps data into
6  *      one temp file then sends it to the output TAR archive.
7  *
8  *      See the headers to pg_backup_files & pg_restore for more details.
9  *
10  * Copyright (c) 2000, Philip Warner
11  *              Rights are granted to use this software in any way so long
12  *              as this notice is not removed.
13  *
14  *      The author is not responsible for loss or damages that may
15  *      result from it's use.
16  *
17  *
18  * IDENTIFICATION
19  *              $PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_tar.c,v 1.67 2010/01/07 04:53:35 tgl Exp $
20  *
21  *-------------------------------------------------------------------------
22  */
23
24 #include "pg_backup.h"
25 #include "pg_backup_archiver.h"
26 #include "pg_backup_tar.h"
27
28 #include <sys/stat.h>
29 #include <ctype.h>
30 #include <limits.h>
31 #include <unistd.h>
32
33 static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
34 static void _StartData(ArchiveHandle *AH, TocEntry *te);
35 static size_t _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
36 static void _EndData(ArchiveHandle *AH, TocEntry *te);
37 static int      _WriteByte(ArchiveHandle *AH, const int i);
38 static int      _ReadByte(ArchiveHandle *);
39 static size_t _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
40 static size_t _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
41 static void _CloseArchive(ArchiveHandle *AH);
42 static void _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt);
43 static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
44 static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
45 static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
46
47 static void _StartBlobs(ArchiveHandle *AH, TocEntry *te);
48 static void _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
49 static void _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid);
50 static void _EndBlobs(ArchiveHandle *AH, TocEntry *te);
51
52 #define K_STD_BUF_SIZE 1024
53
54
55 #ifdef HAVE_LIBZ
56  /* typedef gzFile       ThingFile; */
57 typedef FILE ThingFile;
58 #else
59 typedef FILE ThingFile;
60 #endif
61
62 typedef struct
63 {
64         ThingFile  *zFH;
65         FILE       *nFH;
66         FILE       *tarFH;
67         FILE       *tmpFH;
68         char       *targetFile;
69         char            mode;
70         pgoff_t         pos;
71         pgoff_t         fileLen;
72         ArchiveHandle *AH;
73 } TAR_MEMBER;
74
75 /*
76  * Maximum file size for a tar member: The limit inherent in the
77  * format is 2^33-1 bytes (nearly 8 GB).  But we don't want to exceed
78  * what we can represent in pgoff_t.
79  */
80 #define MAX_TAR_MEMBER_FILELEN (((int64) 1 << Min(33, sizeof(pgoff_t)*8 - 1)) - 1)
81
82 typedef struct
83 {
84         int                     hasSeek;
85         pgoff_t         filePos;
86         TAR_MEMBER *blobToc;
87         FILE       *tarFH;
88         pgoff_t         tarFHpos;
89         pgoff_t         tarNextMember;
90         TAR_MEMBER *FH;
91         int                     isSpecialScript;
92         TAR_MEMBER *scriptTH;
93 } lclContext;
94
95 typedef struct
96 {
97         TAR_MEMBER *TH;
98         char       *filename;
99 } lclTocEntry;
100
101 static const char *modulename = gettext_noop("tar archiver");
102
103 static void _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt);
104
105 static TAR_MEMBER *tarOpen(ArchiveHandle *AH, const char *filename, char mode);
106 static void tarClose(ArchiveHandle *AH, TAR_MEMBER *TH);
107
108 #ifdef __NOT_USED__
109 static char *tarGets(char *buf, size_t len, TAR_MEMBER *th);
110 #endif
111 static int      tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...);
112
113 static void _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th);
114 static int      _tarChecksum(char *th);
115 static TAR_MEMBER *_tarPositionTo(ArchiveHandle *AH, const char *filename);
116 static size_t tarRead(void *buf, size_t len, TAR_MEMBER *th);
117 static size_t tarWrite(const void *buf, size_t len, TAR_MEMBER *th);
118 static void _tarWriteHeader(TAR_MEMBER *th);
119 static int      _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th);
120 static size_t _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh);
121
122 static size_t _scriptOut(ArchiveHandle *AH, const void *buf, size_t len);
123
124 /*
125  *      Initializer
126  */
127 void
128 InitArchiveFmt_Tar(ArchiveHandle *AH)
129 {
130         lclContext *ctx;
131
132         /* Assuming static functions, this can be copied for each format. */
133         AH->ArchiveEntryPtr = _ArchiveEntry;
134         AH->StartDataPtr = _StartData;
135         AH->WriteDataPtr = _WriteData;
136         AH->EndDataPtr = _EndData;
137         AH->WriteBytePtr = _WriteByte;
138         AH->ReadBytePtr = _ReadByte;
139         AH->WriteBufPtr = _WriteBuf;
140         AH->ReadBufPtr = _ReadBuf;
141         AH->ClosePtr = _CloseArchive;
142         AH->ReopenPtr = NULL;
143         AH->PrintTocDataPtr = _PrintTocData;
144         AH->ReadExtraTocPtr = _ReadExtraToc;
145         AH->WriteExtraTocPtr = _WriteExtraToc;
146         AH->PrintExtraTocPtr = _PrintExtraToc;
147
148         AH->StartBlobsPtr = _StartBlobs;
149         AH->StartBlobPtr = _StartBlob;
150         AH->EndBlobPtr = _EndBlob;
151         AH->EndBlobsPtr = _EndBlobs;
152         AH->ClonePtr = NULL;
153         AH->DeClonePtr = NULL;
154
155         /*
156          * Set up some special context used in compressing data.
157          */
158         ctx = (lclContext *) calloc(1, sizeof(lclContext));
159         AH->formatData = (void *) ctx;
160         ctx->filePos = 0;
161         ctx->isSpecialScript = 0;
162
163         /* Initialize LO buffering */
164         AH->lo_buf_size = LOBBUFSIZE;
165         AH->lo_buf = (void *) malloc(LOBBUFSIZE);
166         if (AH->lo_buf == NULL)
167                 die_horribly(AH, modulename, "out of memory\n");
168
169         /*
170          * Now open the TOC file
171          */
172         if (AH->mode == archModeWrite)
173         {
174                 if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
175                 {
176                         ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
177                         if (ctx->tarFH == NULL)
178                                 die_horribly(NULL, modulename,
179                                                    "could not open TOC file \"%s\" for output: %s\n",
180                                                          AH->fSpec, strerror(errno));
181                 }
182                 else
183                 {
184                         ctx->tarFH = stdout;
185                         if (ctx->tarFH == NULL)
186                                 die_horribly(NULL, modulename,
187                                                          "could not open TOC file for output: %s\n",
188                                                          strerror(errno));
189                 }
190
191                 ctx->tarFHpos = 0;
192
193                 /*
194                  * Make unbuffered since we will dup() it, and the buffers screw each
195                  * other
196                  */
197                 /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
198
199                 ctx->hasSeek = checkSeek(ctx->tarFH);
200
201                 if (AH->compression < 0 || AH->compression > 9)
202                         AH->compression = Z_DEFAULT_COMPRESSION;
203
204                 /* Don't compress into tar files unless asked to do so */
205                 if (AH->compression == Z_DEFAULT_COMPRESSION)
206                         AH->compression = 0;
207
208                 /*
209                  * We don't support compression because reading the files back is not
210                  * possible since gzdopen uses buffered IO which totally screws file
211                  * positioning.
212                  */
213                 if (AH->compression != 0)
214                         die_horribly(NULL, modulename, "compression not supported by tar output format\n");
215
216         }
217         else
218         {                                                       /* Read Mode */
219                 if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
220                 {
221                         ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
222                         if (ctx->tarFH == NULL)
223                                 die_horribly(NULL, modulename, "could not open TOC file \"%s\" for input: %s\n",
224                                                          AH->fSpec, strerror(errno));
225                 }
226                 else
227                 {
228                         ctx->tarFH = stdin;
229                         if (ctx->tarFH == NULL)
230                                 die_horribly(NULL, modulename, "could not open TOC file for input: %s\n",
231                                                          strerror(errno));
232                 }
233
234                 /*
235                  * Make unbuffered since we will dup() it, and the buffers screw each
236                  * other
237                  */
238                 /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
239
240                 ctx->tarFHpos = 0;
241
242                 ctx->hasSeek = checkSeek(ctx->tarFH);
243
244                 /*
245                  * Forcibly unmark the header as read since we use the lookahead
246                  * buffer
247                  */
248                 AH->readHeader = 0;
249
250                 ctx->FH = (void *) tarOpen(AH, "toc.dat", 'r');
251                 ReadHead(AH);
252                 ReadToc(AH);
253                 tarClose(AH, ctx->FH);  /* Nothing else in the file... */
254         }
255 }
256
257 /*
258  * - Start a new TOC entry
259  *       Setup the output file name.
260  */
261 static void
262 _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
263 {
264         lclTocEntry *ctx;
265         char            fn[K_STD_BUF_SIZE];
266
267         ctx = (lclTocEntry *) calloc(1, sizeof(lclTocEntry));
268         if (te->dataDumper != NULL)
269         {
270 #ifdef HAVE_LIBZ
271                 if (AH->compression == 0)
272                         sprintf(fn, "%d.dat", te->dumpId);
273                 else
274                         sprintf(fn, "%d.dat.gz", te->dumpId);
275 #else
276                 sprintf(fn, "%d.dat", te->dumpId);
277 #endif
278                 ctx->filename = strdup(fn);
279         }
280         else
281         {
282                 ctx->filename = NULL;
283                 ctx->TH = NULL;
284         }
285         te->formatData = (void *) ctx;
286 }
287
288 static void
289 _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
290 {
291         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
292
293         if (ctx->filename)
294                 WriteStr(AH, ctx->filename);
295         else
296                 WriteStr(AH, "");
297 }
298
299 static void
300 _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
301 {
302         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
303
304         if (ctx == NULL)
305         {
306                 ctx = (lclTocEntry *) calloc(1, sizeof(lclTocEntry));
307                 te->formatData = (void *) ctx;
308         }
309
310         ctx->filename = ReadStr(AH);
311         if (strlen(ctx->filename) == 0)
312         {
313                 free(ctx->filename);
314                 ctx->filename = NULL;
315         }
316         ctx->TH = NULL;
317 }
318
319 static void
320 _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
321 {
322         lclTocEntry *ctx = (lclTocEntry *) te->formatData;
323
324         if (AH->public.verbose && ctx->filename != NULL)
325                 ahprintf(AH, "-- File: %s\n", ctx->filename);
326 }
327
328 static void
329 _StartData(ArchiveHandle *AH, TocEntry *te)
330 {
331         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
332
333         tctx->TH = tarOpen(AH, tctx->filename, 'w');
334 }
335
336 static TAR_MEMBER *
337 tarOpen(ArchiveHandle *AH, const char *filename, char mode)
338 {
339         lclContext *ctx = (lclContext *) AH->formatData;
340         TAR_MEMBER *tm;
341
342 #ifdef HAVE_LIBZ
343         char            fmode[10];
344 #endif
345
346         if (mode == 'r')
347         {
348                 tm = _tarPositionTo(AH, filename);
349                 if (!tm)                                /* Not found */
350                 {
351                         if (filename)           /* Couldn't find the requested file. Future:
352                                                                  * DO SEEK(0) and retry. */
353                                 die_horribly(AH, modulename, "could not find file %s in archive\n", filename);
354                         else
355                                 /* Any file OK, non left, so return NULL */
356                                 return NULL;
357                 }
358
359 #ifdef HAVE_LIBZ
360
361                 if (AH->compression == 0)
362                         tm->nFH = ctx->tarFH;
363                 else
364                         die_horribly(AH, modulename, "compression support is disabled in this format\n");
365                 /* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
366 #else
367                 tm->nFH = ctx->tarFH;
368 #endif
369
370         }
371         else
372         {
373                 tm = calloc(1, sizeof(TAR_MEMBER));
374
375 #ifndef WIN32
376                 tm->tmpFH = tmpfile();
377 #else
378
379                 /*
380                  * On WIN32, tmpfile() generates a filename in the root directory,
381                  * which requires administrative permissions on certain systems. Loop
382                  * until we find a unique file name we can create.
383                  */
384                 while (1)
385                 {
386                         char       *name;
387                         int                     fd;
388
389                         name = _tempnam(NULL, "pg_temp_");
390                         if (name == NULL)
391                                 break;
392                         fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_BINARY |
393                                           O_TEMPORARY, S_IRUSR | S_IWUSR);
394                         free(name);
395
396                         if (fd != -1)           /* created a file */
397                         {
398                                 tm->tmpFH = fdopen(fd, "w+b");
399                                 break;
400                         }
401                         else if (errno != EEXIST)       /* failure other than file exists */
402                                 break;
403                 }
404 #endif
405
406                 if (tm->tmpFH == NULL)
407                         die_horribly(AH, modulename, "could not generate temporary file name: %s\n", strerror(errno));
408
409 #ifdef HAVE_LIBZ
410
411                 if (AH->compression != 0)
412                 {
413                         sprintf(fmode, "wb%d", AH->compression);
414                         tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
415                         if (tm->zFH == NULL)
416                                 die_horribly(AH, modulename, "could not open temporary file\n");
417
418                 }
419                 else
420                         tm->nFH = tm->tmpFH;
421 #else
422
423                 tm->nFH = tm->tmpFH;
424 #endif
425
426                 tm->AH = AH;
427                 tm->targetFile = strdup(filename);
428         }
429
430         tm->mode = mode;
431         tm->tarFH = ctx->tarFH;
432
433         return tm;
434
435 }
436
437 static void
438 tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
439 {
440         /*
441          * Close the GZ file since we dup'd. This will flush the buffers.
442          */
443         if (AH->compression != 0)
444                 if (GZCLOSE(th->zFH) != 0)
445                         die_horribly(AH, modulename, "could not close tar member\n");
446
447         if (th->mode == 'w')
448                 _tarAddFile(AH, th);    /* This will close the temp file */
449
450         /*
451          * else Nothing to do for normal read since we don't dup() normal file
452          * handle, and we don't use temp files.
453          */
454
455         if (th->targetFile)
456                 free(th->targetFile);
457
458         th->nFH = NULL;
459         th->zFH = NULL;
460 }
461
462 #ifdef __NOT_USED__
463 static char *
464 tarGets(char *buf, size_t len, TAR_MEMBER *th)
465 {
466         char       *s;
467         size_t          cnt = 0;
468         char            c = ' ';
469         int                     eof = 0;
470
471         /* Can't read past logical EOF */
472         if (len > (th->fileLen - th->pos))
473                 len = th->fileLen - th->pos;
474
475         while (cnt < len && c != '\n')
476         {
477                 if (_tarReadRaw(th->AH, &c, 1, th, NULL) <= 0)
478                 {
479                         eof = 1;
480                         break;
481                 }
482                 buf[cnt++] = c;
483         }
484
485         if (eof && cnt == 0)
486                 s = NULL;
487         else
488         {
489                 buf[cnt++] = '\0';
490                 s = buf;
491         }
492
493         if (s)
494         {
495                 len = strlen(s);
496                 th->pos += len;
497         }
498
499         return s;
500 }
501 #endif
502
503 /*
504  * Just read bytes from the archive. This is the low level read routine
505  * that is used for ALL reads on a tar file.
506  */
507 static size_t
508 _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
509 {
510         lclContext *ctx = (lclContext *) AH->formatData;
511         size_t          avail;
512         size_t          used = 0;
513         size_t          res = 0;
514
515         avail = AH->lookaheadLen - AH->lookaheadPos;
516         if (avail > 0)
517         {
518                 /* We have some lookahead bytes to use */
519                 if (avail >= len)               /* Just use the lookahead buffer */
520                         used = len;
521                 else
522                         used = avail;
523
524                 /* Copy, and adjust buffer pos */
525                 memcpy(buf, AH->lookahead + AH->lookaheadPos, used);
526                 AH->lookaheadPos += used;
527
528                 /* Adjust required length */
529                 len -= used;
530         }
531
532         /* Read the file if len > 0 */
533         if (len > 0)
534         {
535                 if (fh)
536                         res = fread(&((char *) buf)[used], 1, len, fh);
537                 else if (th)
538                 {
539                         if (th->zFH)
540                                 res = GZREAD(&((char *) buf)[used], 1, len, th->zFH);
541                         else
542                                 res = fread(&((char *) buf)[used], 1, len, th->nFH);
543                 }
544                 else
545                         die_horribly(AH, modulename, "internal error -- neither th nor fh specified in tarReadRaw()\n");
546         }
547
548         ctx->tarFHpos += res + used;
549
550         return (res + used);
551 }
552
553 static size_t
554 tarRead(void *buf, size_t len, TAR_MEMBER *th)
555 {
556         size_t          res;
557
558         if (th->pos + len > th->fileLen)
559                 len = th->fileLen - th->pos;
560
561         if (len <= 0)
562                 return 0;
563
564         res = _tarReadRaw(th->AH, buf, len, th, NULL);
565
566         th->pos += res;
567
568         return res;
569 }
570
571 static size_t
572 tarWrite(const void *buf, size_t len, TAR_MEMBER *th)
573 {
574         size_t          res;
575
576         if (th->zFH != 0)
577                 res = GZWRITE((void *) buf, 1, len, th->zFH);
578         else
579                 res = fwrite(buf, 1, len, th->nFH);
580
581         if (res != len)
582                 die_horribly(th->AH, modulename,
583                                          "could not write to output file: %s\n", strerror(errno));
584
585         th->pos += res;
586         return res;
587 }
588
589 static size_t
590 _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
591 {
592         lclTocEntry *tctx = (lclTocEntry *) AH->currToc->formatData;
593
594         dLen = tarWrite((void *) data, dLen, tctx->TH);
595
596         return dLen;
597 }
598
599 static void
600 _EndData(ArchiveHandle *AH, TocEntry *te)
601 {
602         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
603
604         /* Close the file */
605         tarClose(AH, tctx->TH);
606         tctx->TH = NULL;
607 }
608
609 /*
610  * Print data for a given file
611  */
612 static void
613 _PrintFileData(ArchiveHandle *AH, char *filename, RestoreOptions *ropt)
614 {
615         lclContext *ctx = (lclContext *) AH->formatData;
616         char            buf[4096];
617         size_t          cnt;
618         TAR_MEMBER *th;
619
620         if (!filename)
621                 return;
622
623         th = tarOpen(AH, filename, 'r');
624         ctx->FH = th;
625
626         while ((cnt = tarRead(buf, 4095, th)) > 0)
627         {
628                 buf[cnt] = '\0';
629                 ahwrite(buf, 1, cnt, AH);
630         }
631
632         tarClose(AH, th);
633 }
634
635
636 /*
637  * Print data for a given TOC entry
638 */
639 static void
640 _PrintTocData(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt)
641 {
642         lclContext *ctx = (lclContext *) AH->formatData;
643         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
644         char       *tmpCopy;
645         size_t          i,
646                                 pos1,
647                                 pos2;
648
649         if (!tctx->filename)
650                 return;
651
652         if (ctx->isSpecialScript)
653         {
654                 if (!te->copyStmt)
655                         return;
656
657                 /* Abort the default COPY */
658                 ahprintf(AH, "\\.\n");
659
660                 /* Get a copy of the COPY statement and clean it up */
661                 tmpCopy = strdup(te->copyStmt);
662                 for (i = 0; i < strlen(tmpCopy); i++)
663                         tmpCopy[i] = pg_tolower((unsigned char) tmpCopy[i]);
664
665                 /*
666                  * This is very nasty; we don't know if the archive used WITH OIDS, so
667                  * we search the string for it in a paranoid sort of way.
668                  */
669                 if (strncmp(tmpCopy, "copy ", 5) != 0)
670                         die_horribly(AH, modulename,
671                                                  "invalid COPY statement -- could not find \"copy\" in string \"%s\"\n", tmpCopy);
672
673                 pos1 = 5;
674                 for (pos1 = 5; pos1 < strlen(tmpCopy); pos1++)
675                         if (tmpCopy[pos1] != ' ')
676                                 break;
677
678                 if (tmpCopy[pos1] == '"')
679                         pos1 += 2;
680
681                 pos1 += strlen(te->tag);
682
683                 for (pos2 = pos1; pos2 < strlen(tmpCopy); pos2++)
684                         if (strncmp(&tmpCopy[pos2], "from stdin", 10) == 0)
685                                 break;
686
687                 if (pos2 >= strlen(tmpCopy))
688                         die_horribly(AH, modulename,
689                                                  "invalid COPY statement -- could not find \"from stdin\" in string \"%s\" starting at position %lu\n",
690                                                  tmpCopy, (unsigned long) pos1);
691
692                 ahwrite(tmpCopy, 1, pos2, AH);  /* 'copy "table" [with oids]' */
693                 ahprintf(AH, " from '$$PATH$$/%s' %s", tctx->filename, &tmpCopy[pos2 + 10]);
694
695                 return;
696         }
697
698         if (strcmp(te->desc, "BLOBS") == 0)
699                 _LoadBlobs(AH, ropt);
700         else
701                 _PrintFileData(AH, tctx->filename, ropt);
702 }
703
704 static void
705 _LoadBlobs(ArchiveHandle *AH, RestoreOptions *ropt)
706 {
707         Oid                     oid;
708         lclContext *ctx = (lclContext *) AH->formatData;
709         TAR_MEMBER *th;
710         size_t          cnt;
711         bool            foundBlob = false;
712         char            buf[4096];
713
714         StartRestoreBlobs(AH);
715
716         th = tarOpen(AH, NULL, 'r');    /* Open next file */
717         while (th != NULL)
718         {
719                 ctx->FH = th;
720
721                 if (strncmp(th->targetFile, "blob_", 5) == 0)
722                 {
723                         oid = atooid(&th->targetFile[5]);
724                         if (oid != 0)
725                         {
726                                 ahlog(AH, 1, "restoring large object OID %u\n", oid);
727
728                                 StartRestoreBlob(AH, oid, ropt->dropSchema);
729
730                                 while ((cnt = tarRead(buf, 4095, th)) > 0)
731                                 {
732                                         buf[cnt] = '\0';
733                                         ahwrite(buf, 1, cnt, AH);
734                                 }
735                                 EndRestoreBlob(AH, oid);
736                                 foundBlob = true;
737                         }
738                         tarClose(AH, th);
739                 }
740                 else
741                 {
742                         tarClose(AH, th);
743
744                         /*
745                          * Once we have found the first blob, stop at the first non-blob
746                          * entry (which will be 'blobs.toc').  This coding would eat all
747                          * the rest of the archive if there are no blobs ... but this
748                          * function shouldn't be called at all in that case.
749                          */
750                         if (foundBlob)
751                                 break;
752                 }
753
754                 th = tarOpen(AH, NULL, 'r');
755         }
756         EndRestoreBlobs(AH);
757 }
758
759
760 static int
761 _WriteByte(ArchiveHandle *AH, const int i)
762 {
763         lclContext *ctx = (lclContext *) AH->formatData;
764         int                     res;
765         char            b = i;                  /* Avoid endian problems */
766
767         res = tarWrite(&b, 1, ctx->FH);
768         if (res != EOF)
769                 ctx->filePos += res;
770         return res;
771 }
772
773 static int
774 _ReadByte(ArchiveHandle *AH)
775 {
776         lclContext *ctx = (lclContext *) AH->formatData;
777         size_t          res;
778         unsigned char c;
779
780         res = tarRead(&c, 1, ctx->FH);
781         if (res != 1)
782                 die_horribly(AH, modulename, "unexpected end of file\n");
783         ctx->filePos += 1;
784         return c;
785 }
786
787 static size_t
788 _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
789 {
790         lclContext *ctx = (lclContext *) AH->formatData;
791         size_t          res;
792
793         res = tarWrite((void *) buf, len, ctx->FH);
794         ctx->filePos += res;
795         return res;
796 }
797
798 static size_t
799 _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
800 {
801         lclContext *ctx = (lclContext *) AH->formatData;
802         size_t          res;
803
804         res = tarRead(buf, len, ctx->FH);
805         ctx->filePos += res;
806         return res;
807 }
808
809 static void
810 _CloseArchive(ArchiveHandle *AH)
811 {
812         lclContext *ctx = (lclContext *) AH->formatData;
813         TAR_MEMBER *th;
814         RestoreOptions *ropt;
815         int                     savVerbose,
816                                 i;
817
818         if (AH->mode == archModeWrite)
819         {
820                 /*
821                  * Write the Header & TOC to the archive FIRST
822                  */
823                 th = tarOpen(AH, "toc.dat", 'w');
824                 ctx->FH = th;
825                 WriteHead(AH);
826                 WriteToc(AH);
827                 tarClose(AH, th);               /* Not needed any more */
828
829                 /*
830                  * Now send the data (tables & blobs)
831                  */
832                 WriteDataChunks(AH);
833
834                 /*
835                  * Now this format wants to append a script which does a full restore
836                  * if the files have been extracted.
837                  */
838                 th = tarOpen(AH, "restore.sql", 'w');
839                 tarPrintf(AH, th, "create temporary table pgdump_restore_path(p text);\n");
840                 tarPrintf(AH, th, "--\n"
841                                   "-- NOTE:\n"
842                                   "--\n"
843                                   "-- File paths need to be edited. Search for $$PATH$$ and\n"
844                                   "-- replace it with the path to the directory containing\n"
845                                   "-- the extracted data files.\n"
846                                   "--\n"
847                                   "-- Edit the following to match the path where the\n"
848                                   "-- tar archive has been extracted.\n"
849                                   "--\n");
850                 tarPrintf(AH, th, "insert into pgdump_restore_path values('/tmp');\n\n");
851
852                 AH->CustomOutPtr = _scriptOut;
853
854                 ctx->isSpecialScript = 1;
855                 ctx->scriptTH = th;
856
857                 ropt = NewRestoreOptions();
858                 ropt->dropSchema = 1;
859                 ropt->compression = 0;
860                 ropt->superuser = NULL;
861                 ropt->suppressDumpWarnings = true;
862
863                 savVerbose = AH->public.verbose;
864                 AH->public.verbose = 0;
865
866                 RestoreArchive((Archive *) AH, ropt);
867
868                 AH->public.verbose = savVerbose;
869
870                 tarClose(AH, th);
871
872                 /* Add a block of NULLs since it's de-rigeur. */
873                 for (i = 0; i < 512; i++)
874                 {
875                         if (fputc(0, ctx->tarFH) == EOF)
876                                 die_horribly(AH, modulename,
877                                            "could not write null block at end of tar archive\n");
878                 }
879         }
880
881         AH->FH = NULL;
882 }
883
884 static size_t
885 _scriptOut(ArchiveHandle *AH, const void *buf, size_t len)
886 {
887         lclContext *ctx = (lclContext *) AH->formatData;
888
889         return tarWrite(buf, len, ctx->scriptTH);
890 }
891
892 /*
893  * BLOB support
894  */
895
896 /*
897  * Called by the archiver when starting to save all BLOB DATA (not schema).
898  * This routine should save whatever format-specific information is needed
899  * to read the BLOBs back into memory.
900  *
901  * It is called just prior to the dumper's DataDumper routine.
902  *
903  * Optional, but strongly recommended.
904  *
905  */
906 static void
907 _StartBlobs(ArchiveHandle *AH, TocEntry *te)
908 {
909         lclContext *ctx = (lclContext *) AH->formatData;
910         char            fname[K_STD_BUF_SIZE];
911
912         sprintf(fname, "blobs.toc");
913         ctx->blobToc = tarOpen(AH, fname, 'w');
914 }
915
916 /*
917  * Called by the archiver when the dumper calls StartBlob.
918  *
919  * Mandatory.
920  *
921  * Must save the passed OID for retrieval at restore-time.
922  */
923 static void
924 _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
925 {
926         lclContext *ctx = (lclContext *) AH->formatData;
927         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
928         char            fname[255];
929         char       *sfx;
930
931         if (oid == 0)
932                 die_horribly(AH, modulename, "invalid OID for large object (%u)\n", oid);
933
934         if (AH->compression != 0)
935                 sfx = ".gz";
936         else
937                 sfx = "";
938
939         sprintf(fname, "blob_%u.dat%s", oid, sfx);
940
941         tarPrintf(AH, ctx->blobToc, "%u %s\n", oid, fname);
942
943         tctx->TH = tarOpen(AH, fname, 'w');
944 }
945
946 /*
947  * Called by the archiver when the dumper calls EndBlob.
948  *
949  * Optional.
950  *
951  */
952 static void
953 _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
954 {
955         lclTocEntry *tctx = (lclTocEntry *) te->formatData;
956
957         tarClose(AH, tctx->TH);
958 }
959
960 /*
961  * Called by the archiver when finishing saving all BLOB DATA.
962  *
963  * Optional.
964  *
965  */
966 static void
967 _EndBlobs(ArchiveHandle *AH, TocEntry *te)
968 {
969         lclContext *ctx = (lclContext *) AH->formatData;
970
971         /* Write out a fake zero OID to mark end-of-blobs. */
972         /* WriteInt(AH, 0); */
973
974         tarClose(AH, ctx->blobToc);
975 }
976
977
978
979 /*------------
980  * TAR Support
981  *------------
982  */
983
984 static int
985 tarPrintf(ArchiveHandle *AH, TAR_MEMBER *th, const char *fmt,...)
986 {
987         char       *p = NULL;
988         va_list         ap;
989         size_t          bSize = strlen(fmt) + 256;              /* Should be enough */
990         int                     cnt = -1;
991
992         /*
993          * This is paranoid: deal with the possibility that vsnprintf is willing
994          * to ignore trailing null
995          */
996
997         /*
998          * or returns > 0 even if string does not fit. It may be the case that it
999          * returns cnt = bufsize
1000          */
1001         while (cnt < 0 || cnt >= (bSize - 1))
1002         {
1003                 if (p != NULL)
1004                         free(p);
1005                 bSize *= 2;
1006                 p = (char *) malloc(bSize);
1007                 if (p == NULL)
1008                         die_horribly(AH, modulename, "out of memory\n");
1009                 va_start(ap, fmt);
1010                 cnt = vsnprintf(p, bSize, fmt, ap);
1011                 va_end(ap);
1012         }
1013         cnt = tarWrite(p, cnt, th);
1014         free(p);
1015         return cnt;
1016 }
1017
1018 static int
1019 _tarChecksum(char *header)
1020 {
1021         int                     i,
1022                                 sum;
1023
1024         sum = 0;
1025         for (i = 0; i < 512; i++)
1026                 if (i < 148 || i >= 156)
1027                         sum += 0xFF & header[i];
1028         return sum + 256;                       /* Assume 8 blanks in checksum field */
1029 }
1030
1031 bool
1032 isValidTarHeader(char *header)
1033 {
1034         int                     sum;
1035         int                     chk = _tarChecksum(header);
1036
1037         sscanf(&header[148], "%8o", &sum);
1038
1039         if (sum != chk)
1040                 return false;
1041
1042         /* POSIX format */
1043         if (strncmp(&header[257], "ustar00", 7) == 0)
1044                 return true;
1045         /* older format */
1046         if (strncmp(&header[257], "ustar  ", 7) == 0)
1047                 return true;
1048
1049         return false;
1050 }
1051
1052 /* Given the member, write the TAR header & copy the file */
1053 static void
1054 _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
1055 {
1056         lclContext *ctx = (lclContext *) AH->formatData;
1057         FILE       *tmp = th->tmpFH;    /* Grab it for convenience */
1058         char            buf[32768];
1059         size_t          cnt;
1060         pgoff_t         len = 0;
1061         size_t          res;
1062         size_t          i,
1063                                 pad;
1064
1065         /*
1066          * Find file len & go back to start.
1067          */
1068         fseeko(tmp, 0, SEEK_END);
1069         th->fileLen = ftello(tmp);
1070         fseeko(tmp, 0, SEEK_SET);
1071
1072         /*
1073          * Some compilers will throw a warning knowing this test can never be true
1074          * because pgoff_t can't exceed the compared maximum on their platform.
1075          */
1076         if (th->fileLen > MAX_TAR_MEMBER_FILELEN)
1077                 die_horribly(AH, modulename, "archive member too large for tar format\n");
1078
1079         _tarWriteHeader(th);
1080
1081         while ((cnt = fread(buf, 1, sizeof(buf), tmp)) > 0)
1082         {
1083                 res = fwrite(buf, 1, cnt, th->tarFH);
1084                 if (res != cnt)
1085                         die_horribly(AH, modulename,
1086                                                  "could not write to output file: %s\n",
1087                                                  strerror(errno));
1088                 len += res;
1089         }
1090
1091         if (fclose(tmp) != 0)           /* This *should* delete it... */
1092                 die_horribly(AH, modulename, "could not close temporary file: %s\n",
1093                                          strerror(errno));
1094
1095         if (len != th->fileLen)
1096         {
1097                 char            buf1[32],
1098                                         buf2[32];
1099
1100                 snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) len);
1101                 snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) th->fileLen);
1102                 die_horribly(AH, modulename, "actual file length (%s) does not match expected (%s)\n",
1103                                          buf1, buf2);
1104         }
1105
1106         pad = ((len + 511) & ~511) - len;
1107         for (i = 0; i < pad; i++)
1108         {
1109                 if (fputc('\0', th->tarFH) == EOF)
1110                         die_horribly(AH, modulename, "could not output padding at end of tar member\n");
1111         }
1112
1113         ctx->tarFHpos += len + pad;
1114 }
1115
1116 /* Locate the file in the archive, read header and position to data */
1117 static TAR_MEMBER *
1118 _tarPositionTo(ArchiveHandle *AH, const char *filename)
1119 {
1120         lclContext *ctx = (lclContext *) AH->formatData;
1121         TAR_MEMBER *th = calloc(1, sizeof(TAR_MEMBER));
1122         char            c;
1123         char            header[512];
1124         size_t          i,
1125                                 len,
1126                                 blks;
1127         int                     id;
1128
1129         th->AH = AH;
1130
1131         /* Go to end of current file, if any */
1132         if (ctx->tarFHpos != 0)
1133         {
1134                 char            buf1[100],
1135                                         buf2[100];
1136
1137                 snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ctx->tarFHpos);
1138                 snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ctx->tarNextMember);
1139                 ahlog(AH, 4, "moving from position %s to next member at file position %s\n",
1140                           buf1, buf2);
1141
1142                 while (ctx->tarFHpos < ctx->tarNextMember)
1143                         _tarReadRaw(AH, &c, 1, NULL, ctx->tarFH);
1144         }
1145
1146         {
1147                 char            buf[100];
1148
1149                 snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ctx->tarFHpos);
1150                 ahlog(AH, 4, "now at file position %s\n", buf);
1151         }
1152
1153         /* We are at the start of the file. or at the next member */
1154
1155         /* Get the header */
1156         if (!_tarGetHeader(AH, th))
1157         {
1158                 if (filename)
1159                         die_horribly(AH, modulename, "could not find header for file %s in tar archive\n", filename);
1160                 else
1161
1162                         /*
1163                          * We're just scanning the archibe for the next file, so return
1164                          * null
1165                          */
1166                 {
1167                         free(th);
1168                         return NULL;
1169                 }
1170         }
1171
1172         while (filename != NULL && strcmp(th->targetFile, filename) != 0)
1173         {
1174                 ahlog(AH, 4, "skipping tar member %s\n", th->targetFile);
1175
1176                 id = atoi(th->targetFile);
1177                 if ((TocIDRequired(AH, id, AH->ropt) & REQ_DATA) != 0)
1178                         die_horribly(AH, modulename, "dumping data out of order is not supported in this archive format: "
1179                                 "%s is required, but comes before %s in the archive file.\n",
1180                                                  th->targetFile, filename);
1181
1182                 /* Header doesn't match, so read to next header */
1183                 len = ((th->fileLen + 511) & ~511);             /* Padded length */
1184                 blks = len >> 9;                /* # of 512 byte blocks */
1185
1186                 for (i = 0; i < blks; i++)
1187                         _tarReadRaw(AH, &header[0], 512, NULL, ctx->tarFH);
1188
1189                 if (!_tarGetHeader(AH, th))
1190                         die_horribly(AH, modulename, "could not find header for file %s in tar archive\n", filename);
1191
1192         }
1193
1194         ctx->tarNextMember = ctx->tarFHpos + ((th->fileLen + 511) & ~511);
1195         th->pos = 0;
1196
1197         return th;
1198 }
1199
1200 /* Read & verify a header */
1201 static int
1202 _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
1203 {
1204         lclContext *ctx = (lclContext *) AH->formatData;
1205         char            h[512];
1206         char            tag[100];
1207         int                     sum,
1208                                 chk;
1209         size_t          len;
1210         unsigned long ullen;
1211         pgoff_t         hPos;
1212         bool            gotBlock = false;
1213
1214         while (!gotBlock)
1215         {
1216 #if 0
1217                 if (ftello(ctx->tarFH) != ctx->tarFHpos)
1218                 {
1219                         char            buf1[100],
1220                                                 buf2[100];
1221
1222                         snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) ftello(ctx->tarFH));
1223                         snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) ftello(ctx->tarFHpos));
1224                         die_horribly(AH, modulename,
1225                           "mismatch in actual vs. predicted file position (%s vs. %s)\n",
1226                                                  buf1, buf2);
1227                 }
1228 #endif
1229
1230                 /* Save the pos for reporting purposes */
1231                 hPos = ctx->tarFHpos;
1232
1233                 /* Read a 512 byte block, return EOF, exit if short */
1234                 len = _tarReadRaw(AH, h, 512, NULL, ctx->tarFH);
1235                 if (len == 0)                   /* EOF */
1236                         return 0;
1237
1238                 if (len != 512)
1239                         die_horribly(AH, modulename,
1240                                                  ngettext("incomplete tar header found (%lu byte)\n",
1241                                                                   "incomplete tar header found (%lu bytes)\n",
1242                                                                   len),
1243                                                  (unsigned long) len);
1244
1245                 /* Calc checksum */
1246                 chk = _tarChecksum(h);
1247                 sscanf(&h[148], "%8o", &sum);
1248
1249                 /*
1250                  * If the checksum failed, see if it is a null block. If so, silently
1251                  * continue to the next block.
1252                  */
1253                 if (chk == sum)
1254                         gotBlock = true;
1255                 else
1256                 {
1257                         int                     i;
1258
1259                         for (i = 0; i < 512; i++)
1260                         {
1261                                 if (h[i] != 0)
1262                                 {
1263                                         gotBlock = true;
1264                                         break;
1265                                 }
1266                         }
1267                 }
1268         }
1269
1270         sscanf(&h[0], "%99s", tag);
1271         sscanf(&h[124], "%12lo", &ullen);
1272         len = (size_t) ullen;
1273
1274         {
1275                 char            buf[100];
1276
1277                 snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) hPos);
1278                 ahlog(AH, 3, "TOC Entry %s at %s (length %lu, checksum %d)\n",
1279                           tag, buf, (unsigned long) len, sum);
1280         }
1281
1282         if (chk != sum)
1283         {
1284                 char            buf[100];
1285
1286                 snprintf(buf, sizeof(buf), INT64_FORMAT, (int64) ftello(ctx->tarFH));
1287                 die_horribly(AH, modulename,
1288                                          "corrupt tar header found in %s "
1289                                          "(expected %d, computed %d) file position %s\n",
1290                                          tag, sum, chk, buf);
1291         }
1292
1293         th->targetFile = strdup(tag);
1294         th->fileLen = len;
1295
1296         return 1;
1297 }
1298
1299
1300 /*
1301  * Utility routine to print possibly larger than 32 bit integers in a
1302  * portable fashion.  Filled with zeros.
1303  */
1304 static void
1305 print_val(char *s, uint64 val, unsigned int base, size_t len)
1306 {
1307         int                     i;
1308
1309         for (i = len; i > 0; i--)
1310         {
1311                 int                     digit = val % base;
1312
1313                 s[i - 1] = '0' + digit;
1314                 val = val / base;
1315         }
1316 }
1317
1318
1319 static void
1320 _tarWriteHeader(TAR_MEMBER *th)
1321 {
1322         char            h[512];
1323         int                     lastSum = 0;
1324         int                     sum;
1325
1326         memset(h, 0, sizeof(h));
1327
1328         /* Name 100 */
1329         sprintf(&h[0], "%.99s", th->targetFile);
1330
1331         /* Mode 8 */
1332         sprintf(&h[100], "100600 ");
1333
1334         /* User ID 8 */
1335         sprintf(&h[108], "004000 ");
1336
1337         /* Group 8 */
1338         sprintf(&h[116], "002000 ");
1339
1340         /* File size 12 - 11 digits, 1 space, no NUL */
1341         print_val(&h[124], th->fileLen, 8, 11);
1342         sprintf(&h[135], " ");
1343
1344         /* Mod Time 12 */
1345         sprintf(&h[136], "%011o ", (int) time(NULL));
1346
1347         /* Checksum 8 */
1348         sprintf(&h[148], "%06o ", lastSum);
1349
1350         /* Type - regular file */
1351         sprintf(&h[156], "0");
1352
1353         /* Link tag 100 (NULL) */
1354
1355         /* Magic 6 + Version 2 */
1356         sprintf(&h[257], "ustar00");
1357
1358 #if 0
1359         /* User 32 */
1360         sprintf(&h[265], "%.31s", "");          /* How do I get username reliably? Do
1361                                                                                  * I need to? */
1362
1363         /* Group 32 */
1364         sprintf(&h[297], "%.31s", "");          /* How do I get group reliably? Do I
1365                                                                                  * need to? */
1366
1367         /* Maj Dev 8 */
1368         sprintf(&h[329], "%6o ", 0);
1369
1370         /* Min Dev 8 */
1371         sprintf(&h[337], "%6o ", 0);
1372 #endif
1373
1374         while ((sum = _tarChecksum(h)) != lastSum)
1375         {
1376                 sprintf(&h[148], "%06o ", sum);
1377                 lastSum = sum;
1378         }
1379
1380         if (fwrite(h, 1, 512, th->tarFH) != 512)
1381                 die_horribly(th->AH, modulename, "could not write to output file: %s\n", strerror(errno));
1382 }