Age Owner Branch data TLA Line data Source code
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 : : * The tar format also includes a 'restore.sql' script which is there for
9 : : * the benefit of humans. This script is never used by pg_restore.
10 : : *
11 : : * NOTE: If you untar the created 'tar' file, the resulting files are
12 : : * compatible with the 'directory' format. Please keep the two formats in
13 : : * sync.
14 : : *
15 : : * See the headers to pg_backup_directory & pg_restore for more details.
16 : : *
17 : : * Copyright (c) 2000, Philip Warner
18 : : * Rights are granted to use this software in any way so long
19 : : * as this notice is not removed.
20 : : *
21 : : * The author is not responsible for loss or damages that may
22 : : * result from its use.
23 : : *
24 : : *
25 : : * IDENTIFICATION
26 : : * src/bin/pg_dump/pg_backup_tar.c
27 : : *
28 : : *-------------------------------------------------------------------------
29 : : */
30 : : #include "postgres_fe.h"
31 : :
32 : : #include <sys/stat.h>
33 : : #include <ctype.h>
34 : : #include <limits.h>
35 : : #include <unistd.h>
36 : :
37 : : #include "common/file_utils.h"
38 : : #include "fe_utils/string_utils.h"
39 : : #include "pg_backup_archiver.h"
40 : : #include "pg_backup_tar.h"
41 : : #include "pg_backup_utils.h"
42 : : #include "pgtar.h"
43 : :
44 : : static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
45 : : static void _StartData(ArchiveHandle *AH, TocEntry *te);
46 : : static void _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
47 : : static void _EndData(ArchiveHandle *AH, TocEntry *te);
48 : : static int _WriteByte(ArchiveHandle *AH, const int i);
49 : : static int _ReadByte(ArchiveHandle *AH);
50 : : static void _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
51 : : static void _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
52 : : static void _CloseArchive(ArchiveHandle *AH);
53 : : static void _PrintTocData(ArchiveHandle *AH, TocEntry *te);
54 : : static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
55 : : static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
56 : : static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
57 : :
58 : : static void _StartLOs(ArchiveHandle *AH, TocEntry *te);
59 : : static void _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
60 : : static void _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
61 : : static void _EndLOs(ArchiveHandle *AH, TocEntry *te);
62 : :
63 : : #define K_STD_BUF_SIZE 1024
64 : :
65 : :
66 : : typedef struct
67 : : {
68 : : FILE *nFH;
69 : : FILE *tarFH;
70 : : FILE *tmpFH;
71 : : char *targetFile;
72 : : char mode;
73 : : pgoff_t pos;
74 : : pgoff_t fileLen;
75 : : ArchiveHandle *AH;
76 : : } TAR_MEMBER;
77 : :
78 : : typedef struct
79 : : {
80 : : int hasSeek;
81 : : pgoff_t filePos;
82 : : TAR_MEMBER *loToc;
83 : : FILE *tarFH;
84 : : pgoff_t tarFHpos;
85 : : pgoff_t tarNextMember;
86 : : TAR_MEMBER *FH;
87 : : int isSpecialScript;
88 : : TAR_MEMBER *scriptTH;
89 : : } lclContext;
90 : :
91 : : typedef struct
92 : : {
93 : : TAR_MEMBER *TH;
94 : : char *filename;
95 : : } lclTocEntry;
96 : :
97 : : static void _LoadLOs(ArchiveHandle *AH, TocEntry *te);
98 : :
99 : : static TAR_MEMBER *tarOpen(ArchiveHandle *AH, const char *filename, char mode);
100 : : static void tarClose(ArchiveHandle *AH, TAR_MEMBER *th);
101 : :
102 : : #ifdef __NOT_USED__
103 : : static char *tarGets(char *buf, size_t len, TAR_MEMBER *th);
104 : : #endif
105 : : static int tarPrintf(TAR_MEMBER *th, const char *fmt,...) pg_attribute_printf(2, 3);
106 : :
107 : : static void _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th);
108 : : static TAR_MEMBER *_tarPositionTo(ArchiveHandle *AH, const char *filename);
109 : : static size_t tarRead(void *buf, size_t len, TAR_MEMBER *th);
110 : : static size_t tarWrite(const void *buf, size_t len, TAR_MEMBER *th);
111 : : static void _tarWriteHeader(TAR_MEMBER *th);
112 : : static int _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th);
113 : : static size_t _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh);
114 : :
115 : : static size_t _scriptOut(ArchiveHandle *AH, const void *buf, size_t len);
116 : :
117 : : /*
118 : : * Initializer
119 : : */
120 : : void
8424 bruce@momjian.us 121 :CBC 5 : InitArchiveFmt_Tar(ArchiveHandle *AH)
122 : : {
123 : : lclContext *ctx;
124 : :
125 : : /* Assuming static functions, this can be copied for each format. */
126 : 5 : AH->ArchiveEntryPtr = _ArchiveEntry;
127 : 5 : AH->StartDataPtr = _StartData;
128 : 5 : AH->WriteDataPtr = _WriteData;
129 : 5 : AH->EndDataPtr = _EndData;
130 : 5 : AH->WriteBytePtr = _WriteByte;
131 : 5 : AH->ReadBytePtr = _ReadByte;
132 : 5 : AH->WriteBufPtr = _WriteBuf;
133 : 5 : AH->ReadBufPtr = _ReadBuf;
134 : 5 : AH->ClosePtr = _CloseArchive;
5550 andrew@dunslane.net 135 : 5 : AH->ReopenPtr = NULL;
8424 bruce@momjian.us 136 : 5 : AH->PrintTocDataPtr = _PrintTocData;
137 : 5 : AH->ReadExtraTocPtr = _ReadExtraToc;
138 : 5 : AH->WriteExtraTocPtr = _WriteExtraToc;
139 : 5 : AH->PrintExtraTocPtr = _PrintExtraToc;
140 : :
496 peter@eisentraut.org 141 : 5 : AH->StartLOsPtr = _StartLOs;
142 : 5 : AH->StartLOPtr = _StartLO;
143 : 5 : AH->EndLOPtr = _EndLO;
144 : 5 : AH->EndLOsPtr = _EndLOs;
5550 andrew@dunslane.net 145 : 5 : AH->ClonePtr = NULL;
146 : 5 : AH->DeClonePtr = NULL;
147 : :
4039 148 : 5 : AH->WorkerJobDumpPtr = NULL;
149 : 5 : AH->WorkerJobRestorePtr = NULL;
150 : :
151 : : /*
152 : : * Set up some special context used in compressing data.
153 : : */
580 peter@eisentraut.org 154 : 5 : ctx = pg_malloc0_object(lclContext);
8424 bruce@momjian.us 155 : 5 : AH->formatData = (void *) ctx;
156 : 5 : ctx->filePos = 0;
7494 157 : 5 : ctx->isSpecialScript = 0;
158 : :
159 : : /*
160 : : * Now open the tar file, and load the TOC if we're in read mode.
161 : : */
8424 162 [ + + ]: 5 : if (AH->mode == archModeWrite)
163 : : {
164 [ + + + - ]: 3 : if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
165 : : {
8668 pjw@rhyme.com.au 166 : 2 : ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
6013 tgl@sss.pgh.pa.us 167 [ - + ]: 2 : if (ctx->tarFH == NULL)
737 tgl@sss.pgh.pa.us 168 :UBC 0 : pg_fatal("could not open TOC file \"%s\" for output: %m",
169 : : AH->fSpec);
170 : : }
171 : : else
172 : : {
8668 pjw@rhyme.com.au 173 :CBC 1 : ctx->tarFH = stdout;
6013 tgl@sss.pgh.pa.us 174 [ - + ]: 1 : if (ctx->tarFH == NULL)
737 tgl@sss.pgh.pa.us 175 :UBC 0 : pg_fatal("could not open TOC file for output: %m");
176 : : }
177 : :
8668 pjw@rhyme.com.au 178 :CBC 3 : ctx->tarFHpos = 0;
179 : :
180 : : /*
181 : : * Make unbuffered since we will dup() it, and the buffers screw each
182 : : * other
183 : : */
184 : : /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
185 : :
7842 bruce@momjian.us 186 : 3 : ctx->hasSeek = checkSeek(ctx->tarFH);
187 : :
188 : : /*
189 : : * We don't support compression because reading the files back is not
190 : : * possible since gzdopen uses buffered IO which totally screws file
191 : : * positioning.
192 : : */
499 michael@paquier.xyz 193 [ + + ]: 3 : if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
737 tgl@sss.pgh.pa.us 194 : 1 : pg_fatal("compression is not supported by tar archive format");
195 : : }
196 : : else
197 : : { /* Read Mode */
8424 bruce@momjian.us 198 [ + - + - ]: 2 : if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
199 : : {
8668 pjw@rhyme.com.au 200 : 2 : ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
6013 tgl@sss.pgh.pa.us 201 [ - + ]: 2 : if (ctx->tarFH == NULL)
737 tgl@sss.pgh.pa.us 202 :UBC 0 : pg_fatal("could not open TOC file \"%s\" for input: %m",
203 : : AH->fSpec);
204 : : }
205 : : else
206 : : {
8668 pjw@rhyme.com.au 207 : 0 : ctx->tarFH = stdin;
6013 tgl@sss.pgh.pa.us 208 [ # # ]: 0 : if (ctx->tarFH == NULL)
737 209 : 0 : pg_fatal("could not open TOC file for input: %m");
210 : : }
211 : :
212 : : /*
213 : : * Make unbuffered since we will dup() it, and the buffers screw each
214 : : * other
215 : : */
216 : : /* setvbuf(ctx->tarFH, NULL, _IONBF, 0); */
217 : :
8668 pjw@rhyme.com.au 218 :CBC 2 : ctx->tarFHpos = 0;
219 : :
7842 bruce@momjian.us 220 : 2 : ctx->hasSeek = checkSeek(ctx->tarFH);
221 : :
8424 222 : 2 : ctx->FH = (void *) tarOpen(AH, "toc.dat", 'r');
8668 pjw@rhyme.com.au 223 : 2 : ReadHead(AH);
224 : 2 : ReadToc(AH);
8424 bruce@momjian.us 225 : 2 : tarClose(AH, ctx->FH); /* Nothing else in the file... */
226 : : }
8668 pjw@rhyme.com.au 227 : 4 : }
228 : :
229 : : /*
230 : : * - Start a new TOC entry
231 : : * Setup the output file name.
232 : : */
233 : : static void
8424 bruce@momjian.us 234 : 305 : _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
235 : : {
236 : : lclTocEntry *ctx;
237 : : char fn[K_STD_BUF_SIZE];
238 : :
580 peter@eisentraut.org 239 : 305 : ctx = pg_malloc0_object(lclTocEntry);
8401 pjw@rhyme.com.au 240 [ + + ]: 305 : if (te->dataDumper != NULL)
241 : : {
745 michael@paquier.xyz 242 : 35 : snprintf(fn, sizeof(fn), "%d.dat", te->dumpId);
4524 bruce@momjian.us 243 : 35 : ctx->filename = pg_strdup(fn);
244 : : }
245 : : else
246 : : {
8668 pjw@rhyme.com.au 247 : 270 : ctx->filename = NULL;
248 : 270 : ctx->TH = NULL;
249 : : }
8424 bruce@momjian.us 250 : 305 : te->formatData = (void *) ctx;
8668 pjw@rhyme.com.au 251 : 305 : }
252 : :
253 : : static void
8424 bruce@momjian.us 254 : 305 : _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
255 : : {
256 : 305 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
257 : :
258 [ + + ]: 305 : if (ctx->filename)
8668 pjw@rhyme.com.au 259 : 35 : WriteStr(AH, ctx->filename);
260 : : else
261 : 270 : WriteStr(AH, "");
262 : 305 : }
263 : :
264 : : static void
8424 bruce@momjian.us 265 : 305 : _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
266 : : {
267 : 305 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
268 : :
269 [ + - ]: 305 : if (ctx == NULL)
270 : : {
580 peter@eisentraut.org 271 : 305 : ctx = pg_malloc0_object(lclTocEntry);
8424 bruce@momjian.us 272 : 305 : te->formatData = (void *) ctx;
273 : : }
274 : :
275 : 305 : ctx->filename = ReadStr(AH);
276 [ + + ]: 305 : if (strlen(ctx->filename) == 0)
277 : : {
8668 pjw@rhyme.com.au 278 : 270 : free(ctx->filename);
279 : 270 : ctx->filename = NULL;
280 : : }
8424 bruce@momjian.us 281 : 305 : ctx->TH = NULL;
8668 pjw@rhyme.com.au 282 : 305 : }
283 : :
284 : : static void
8424 bruce@momjian.us 285 : 594 : _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
286 : : {
287 : 594 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
288 : :
7347 tgl@sss.pgh.pa.us 289 [ - + - - ]: 594 : if (AH->public.verbose && ctx->filename != NULL)
8401 pjw@rhyme.com.au 290 :UBC 0 : ahprintf(AH, "-- File: %s\n", ctx->filename);
8668 pjw@rhyme.com.au 291 :CBC 594 : }
292 : :
293 : : static void
8424 bruce@momjian.us 294 : 33 : _StartData(ArchiveHandle *AH, TocEntry *te)
295 : : {
296 : 33 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
297 : :
8668 pjw@rhyme.com.au 298 : 33 : tctx->TH = tarOpen(AH, tctx->filename, 'w');
299 : 33 : }
300 : :
301 : : static TAR_MEMBER *
8424 bruce@momjian.us 302 : 80 : tarOpen(ArchiveHandle *AH, const char *filename, char mode)
303 : : {
304 : 80 : lclContext *ctx = (lclContext *) AH->formatData;
305 : : TAR_MEMBER *tm;
306 : :
8668 pjw@rhyme.com.au 307 [ + + ]: 80 : if (mode == 'r')
308 : : {
309 : 39 : tm = _tarPositionTo(AH, filename);
8424 bruce@momjian.us 310 [ - + ]: 39 : if (!tm) /* Not found */
311 : : {
5164 tgl@sss.pgh.pa.us 312 [ # # ]:UBC 0 : if (filename)
313 : : {
314 : : /*
315 : : * Couldn't find the requested file. Future: do SEEK(0) and
316 : : * retry.
317 : : */
737 318 : 0 : pg_fatal("could not find file \"%s\" in archive", filename);
319 : : }
320 : : else
321 : : {
322 : : /* Any file OK, none left, so return NULL */
8668 pjw@rhyme.com.au 323 : 0 : return NULL;
324 : : }
325 : : }
326 : :
499 michael@paquier.xyz 327 [ + - ]:CBC 39 : if (AH->compression_spec.algorithm == PG_COMPRESSION_NONE)
8668 pjw@rhyme.com.au 328 : 39 : tm->nFH = ctx->tarFH;
329 : : else
737 tgl@sss.pgh.pa.us 330 :UBC 0 : pg_fatal("compression is not supported by tar archive format");
331 : : }
332 : : else
333 : : {
334 : : int old_umask;
335 : :
580 peter@eisentraut.org 336 :CBC 41 : tm = pg_malloc0_object(TAR_MEMBER);
337 : :
338 : : /*
339 : : * POSIX does not require, but permits, tmpfile() to restrict file
340 : : * permissions. Given an OS crash after we write data, the filesystem
341 : : * might retain the data but forget tmpfile()'s unlink(). If so, the
342 : : * file mode protects confidentiality of the data written.
343 : : */
3129 noah@leadboat.com 344 : 41 : old_umask = umask(S_IRWXG | S_IRWXO);
345 : :
346 : : #ifndef WIN32
8668 pjw@rhyme.com.au 347 : 41 : tm->tmpFH = tmpfile();
348 : : #else
349 : :
350 : : /*
351 : : * On WIN32, tmpfile() generates a filename in the root directory,
352 : : * which requires administrative permissions on certain systems. Loop
353 : : * until we find a unique file name we can create.
354 : : */
355 : : while (1)
356 : : {
357 : : char *name;
358 : : int fd;
359 : :
360 : : name = _tempnam(NULL, "pg_temp_");
361 : : if (name == NULL)
362 : : break;
363 : : fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_BINARY |
364 : : O_TEMPORARY, S_IRUSR | S_IWUSR);
365 : : free(name);
366 : :
367 : : if (fd != -1) /* created a file */
368 : : {
369 : : tm->tmpFH = fdopen(fd, "w+b");
370 : : break;
371 : : }
372 : : else if (errno != EEXIST) /* failure other than file exists */
373 : : break;
374 : : }
375 : : #endif
376 : :
8424 bruce@momjian.us 377 [ - + ]: 41 : if (tm->tmpFH == NULL)
737 tgl@sss.pgh.pa.us 378 :UBC 0 : pg_fatal("could not generate temporary file name: %m");
379 : :
3129 noah@leadboat.com 380 :CBC 41 : umask(old_umask);
381 : :
499 michael@paquier.xyz 382 [ + - ]: 41 : if (AH->compression_spec.algorithm == PG_COMPRESSION_NONE)
8668 pjw@rhyme.com.au 383 : 41 : tm->nFH = tm->tmpFH;
384 : : else
737 tgl@sss.pgh.pa.us 385 :UBC 0 : pg_fatal("compression is not supported by tar archive format");
386 : :
8668 pjw@rhyme.com.au 387 :CBC 41 : tm->AH = AH;
4524 bruce@momjian.us 388 : 41 : tm->targetFile = pg_strdup(filename);
389 : : }
390 : :
8668 pjw@rhyme.com.au 391 : 80 : tm->mode = mode;
392 : 80 : tm->tarFH = ctx->tarFH;
393 : :
394 : 80 : return tm;
395 : : }
396 : :
397 : : static void
8424 bruce@momjian.us 398 : 80 : tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
399 : : {
499 michael@paquier.xyz 400 [ - + ]: 80 : if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
737 tgl@sss.pgh.pa.us 401 :UBC 0 : pg_fatal("compression is not supported by tar archive format");
402 : :
8668 pjw@rhyme.com.au 403 [ + + ]:CBC 80 : if (th->mode == 'w')
8424 bruce@momjian.us 404 : 41 : _tarAddFile(AH, th); /* This will close the temp file */
405 : :
406 : : /*
407 : : * else Nothing to do for normal read since we don't dup() normal file
408 : : * handle, and we don't use temp files.
409 : : */
410 : :
668 peter@eisentraut.org 411 : 80 : free(th->targetFile);
412 : :
8668 pjw@rhyme.com.au 413 : 80 : th->nFH = NULL;
414 : 80 : }
415 : :
416 : : #ifdef __NOT_USED__
417 : : static char *
418 : : tarGets(char *buf, size_t len, TAR_MEMBER *th)
419 : : {
420 : : char *s;
421 : : size_t cnt = 0;
422 : : char c = ' ';
423 : : int eof = 0;
424 : :
425 : : /* Can't read past logical EOF */
426 : : if (len > (th->fileLen - th->pos))
427 : : len = th->fileLen - th->pos;
428 : :
429 : : while (cnt < len && c != '\n')
430 : : {
431 : : if (_tarReadRaw(th->AH, &c, 1, th, NULL) <= 0)
432 : : {
433 : : eof = 1;
434 : : break;
435 : : }
436 : : buf[cnt++] = c;
437 : : }
438 : :
439 : : if (eof && cnt == 0)
440 : : s = NULL;
441 : : else
442 : : {
443 : : buf[cnt++] = '\0';
444 : : s = buf;
445 : : }
446 : :
447 : : if (s)
448 : : {
449 : : len = strlen(s);
450 : : th->pos += len;
451 : : }
452 : :
453 : : return s;
454 : : }
455 : : #endif
456 : :
457 : : /*
458 : : * Just read bytes from the archive. This is the low level read routine
459 : : * that is used for ALL reads on a tar file.
460 : : */
461 : : static size_t
7908 peter_e@gmx.net 462 : 48648 : _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
463 : : {
8424 bruce@momjian.us 464 : 48648 : lclContext *ctx = (lclContext *) AH->formatData;
465 : : size_t avail;
7908 peter_e@gmx.net 466 : 48648 : size_t used = 0;
467 : 48648 : size_t res = 0;
468 : :
1448 peter@eisentraut.org 469 [ + + - + ]: 48648 : Assert(th || fh);
470 : :
8668 pjw@rhyme.com.au 471 : 48648 : avail = AH->lookaheadLen - AH->lookaheadPos;
472 [ - + ]: 48648 : if (avail > 0)
473 : : {
474 : : /* We have some lookahead bytes to use */
8424 bruce@momjian.us 475 [ # # ]:UBC 0 : if (avail >= len) /* Just use the lookahead buffer */
8668 pjw@rhyme.com.au 476 : 0 : used = len;
477 : : else
478 : 0 : used = avail;
479 : :
480 : : /* Copy, and adjust buffer pos */
6096 tgl@sss.pgh.pa.us 481 : 0 : memcpy(buf, AH->lookahead + AH->lookaheadPos, used);
8668 pjw@rhyme.com.au 482 : 0 : AH->lookaheadPos += used;
483 : :
484 : : /* Adjust required length */
485 : 0 : len -= used;
486 : : }
487 : :
488 : : /* Read the file if len > 0 */
8668 pjw@rhyme.com.au 489 [ + - ]:CBC 48648 : if (len > 0)
490 : : {
491 [ + + ]: 48648 : if (fh)
492 : : {
8424 bruce@momjian.us 493 : 17108 : res = fread(&((char *) buf)[used], 1, len, fh);
3632 494 [ - + - - ]: 17108 : if (res != len && !feof(fh))
3632 bruce@momjian.us 495 [ # # ]:UBC 0 : READ_ERROR_EXIT(fh);
496 : : }
8668 pjw@rhyme.com.au 497 [ + - ]:CBC 31540 : else if (th)
498 : : {
745 michael@paquier.xyz 499 : 31540 : res = fread(&((char *) buf)[used], 1, len, th->nFH);
500 [ - + - - ]: 31540 : if (res != len && !feof(th->nFH))
745 michael@paquier.xyz 501 [ # # ]:UBC 0 : READ_ERROR_EXIT(th->nFH);
502 : : }
503 : : }
504 : :
8668 pjw@rhyme.com.au 505 :CBC 48648 : ctx->tarFHpos += res + used;
506 : :
507 : 48648 : return (res + used);
508 : : }
509 : :
510 : : static size_t
7908 peter_e@gmx.net 511 : 31916 : tarRead(void *buf, size_t len, TAR_MEMBER *th)
512 : : {
513 : : size_t res;
514 : :
8668 pjw@rhyme.com.au 515 [ + + ]: 31916 : if (th->pos + len > th->fileLen)
516 : 69 : len = th->fileLen - th->pos;
517 : :
518 [ + + ]: 31916 : if (len <= 0)
519 : 376 : return 0;
520 : :
521 : 31540 : res = _tarReadRaw(th->AH, buf, len, th, NULL);
522 : :
523 : 31540 : th->pos += res;
524 : :
525 : 31540 : return res;
526 : : }
527 : :
528 : : static size_t
7908 peter_e@gmx.net 529 : 33682 : tarWrite(const void *buf, size_t len, TAR_MEMBER *th)
530 : : {
531 : : size_t res;
532 : :
745 michael@paquier.xyz 533 : 33682 : res = fwrite(buf, 1, len, th->nFH);
534 : :
8668 pjw@rhyme.com.au 535 : 33682 : th->pos += res;
536 : 33682 : return res;
537 : : }
538 : :
539 : : static void
7908 peter_e@gmx.net 540 : 66 : _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
541 : : {
8424 bruce@momjian.us 542 : 66 : lclTocEntry *tctx = (lclTocEntry *) AH->currToc->formatData;
543 : :
3632 544 [ - + ]: 66 : if (tarWrite(data, dLen, tctx->TH) != dLen)
3632 bruce@momjian.us 545 :UBC 0 : WRITE_ERROR_EXIT;
8668 pjw@rhyme.com.au 546 :CBC 66 : }
547 : :
548 : : static void
8424 bruce@momjian.us 549 : 33 : _EndData(ArchiveHandle *AH, TocEntry *te)
550 : : {
551 : 33 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
552 : :
553 : : /* Close the file */
554 : 33 : tarClose(AH, tctx->TH);
555 : 33 : tctx->TH = NULL;
8668 pjw@rhyme.com.au 556 : 33 : }
557 : :
558 : : /*
559 : : * Print data for a given file
560 : : */
561 : : static void
3014 tgl@sss.pgh.pa.us 562 : 33 : _PrintFileData(ArchiveHandle *AH, char *filename)
563 : : {
8424 bruce@momjian.us 564 : 33 : lclContext *ctx = (lclContext *) AH->formatData;
565 : : char buf[4096];
566 : : size_t cnt;
567 : : TAR_MEMBER *th;
568 : :
569 [ - + ]: 33 : if (!filename)
8668 pjw@rhyme.com.au 570 :UBC 0 : return;
571 : :
8668 pjw@rhyme.com.au 572 :CBC 33 : th = tarOpen(AH, filename, 'r');
573 : 33 : ctx->FH = th;
574 : :
8424 bruce@momjian.us 575 [ + + ]: 69 : while ((cnt = tarRead(buf, 4095, th)) > 0)
576 : : {
8668 pjw@rhyme.com.au 577 : 36 : buf[cnt] = '\0';
578 : 36 : ahwrite(buf, 1, cnt, AH);
579 : : }
580 : :
8424 bruce@momjian.us 581 : 33 : tarClose(AH, th);
582 : : }
583 : :
584 : :
585 : : /*
586 : : * Print data for a given TOC entry
587 : : */
588 : : static void
3014 tgl@sss.pgh.pa.us 589 : 70 : _PrintTocData(ArchiveHandle *AH, TocEntry *te)
590 : : {
8424 bruce@momjian.us 591 : 70 : lclContext *ctx = (lclContext *) AH->formatData;
592 : 70 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
593 : : int pos1;
594 : :
595 [ - + ]: 70 : if (!tctx->filename)
8668 pjw@rhyme.com.au 596 :UBC 0 : return;
597 : :
598 : : /*
599 : : * If we're writing the special restore.sql script, emit a suitable
600 : : * command to include each table's data from the corresponding file.
601 : : *
602 : : * In the COPY case this is a bit klugy because the regular COPY command
603 : : * was already printed before we get control.
604 : : */
8668 pjw@rhyme.com.au 605 [ + + ]:CBC 70 : if (ctx->isSpecialScript)
606 : : {
4215 tgl@sss.pgh.pa.us 607 [ + + ]: 35 : if (te->copyStmt)
608 : : {
609 : : /* Abort the COPY FROM stdin */
610 : 33 : ahprintf(AH, "\\.\n");
611 : :
612 : : /*
613 : : * The COPY statement should look like "COPY ... FROM stdin;\n",
614 : : * see dumpTableData().
615 : : */
616 : 33 : pos1 = (int) strlen(te->copyStmt) - 13;
617 [ + - + - ]: 33 : if (pos1 < 6 || strncmp(te->copyStmt, "COPY ", 5) != 0 ||
618 [ - + ]: 33 : strcmp(te->copyStmt + pos1, " FROM stdin;\n") != 0)
737 tgl@sss.pgh.pa.us 619 :UBC 0 : pg_fatal("unexpected COPY statement syntax: \"%s\"",
620 : : te->copyStmt);
621 : :
622 : : /* Emit all but the FROM part ... */
4215 tgl@sss.pgh.pa.us 623 :CBC 33 : ahwrite(te->copyStmt, 1, pos1, AH);
624 : : /* ... and insert modified FROM */
625 : 33 : ahprintf(AH, " FROM '$$PATH$$/%s';\n\n", tctx->filename);
626 : : }
627 : : else
628 : : {
629 : : /* --inserts mode, no worries, just include the data file */
630 : 2 : ahprintf(AH, "\\i $$PATH$$/%s\n\n", tctx->filename);
631 : : }
632 : :
8668 pjw@rhyme.com.au 633 : 35 : return;
634 : : }
635 : :
636 [ + + ]: 35 : if (strcmp(te->desc, "BLOBS") == 0)
13 tgl@sss.pgh.pa.us 637 :GNC 2 : _LoadLOs(AH, te);
638 : : else
3014 tgl@sss.pgh.pa.us 639 :CBC 33 : _PrintFileData(AH, tctx->filename);
640 : : }
641 : :
642 : : static void
13 tgl@sss.pgh.pa.us 643 :GNC 2 : _LoadLOs(ArchiveHandle *AH, TocEntry *te)
644 : : {
645 : : Oid oid;
8424 bruce@momjian.us 646 :CBC 2 : lclContext *ctx = (lclContext *) AH->formatData;
647 : : TAR_MEMBER *th;
648 : : size_t cnt;
496 peter@eisentraut.org 649 : 2 : bool foundLO = false;
650 : : char buf[4096];
651 : :
652 : 2 : StartRestoreLOs(AH);
653 : :
654 : : /*
655 : : * The blobs_NNN.toc or blobs.toc file is fairly useless to us because it
656 : : * will appear only after the associated blob_NNN.dat files. For archive
657 : : * versions >= 16 we can look at the BLOBS entry's te->tag to discover the
658 : : * OID of the first blob we want to restore, and then search forward to
659 : : * find the appropriate blob_<oid>.dat file. For older versions we rely
660 : : * on the knowledge that there was only one BLOBS entry and just search
661 : : * for the first blob_<oid>.dat file. Once we find the first blob file to
662 : : * restore, restore all blobs until we reach the blobs[_NNN].toc file.
663 : : */
13 tgl@sss.pgh.pa.us 664 [ + - ]:GNC 2 : if (AH->version >= K_VERS_1_16)
665 : : {
666 : : /* We rely on atooid to not complain about nnnn..nnnn tags */
667 : 2 : oid = atooid(te->tag);
668 : 2 : snprintf(buf, sizeof(buf), "blob_%u.dat", oid);
669 : 2 : th = tarOpen(AH, buf, 'r'); /* Advance to first desired file */
670 : : }
671 : : else
13 tgl@sss.pgh.pa.us 672 :UNC 0 : th = tarOpen(AH, NULL, 'r'); /* Open next file */
673 : :
8668 pjw@rhyme.com.au 674 [ + - ]:CBC 4 : while (th != NULL)
675 : : {
676 : 4 : ctx->FH = th;
677 : :
7991 tgl@sss.pgh.pa.us 678 [ + + ]: 4 : if (strncmp(th->targetFile, "blob_", 5) == 0)
679 : : {
680 : 2 : oid = atooid(&th->targetFile[5]);
681 [ + - ]: 2 : if (oid != 0)
682 : : {
1840 peter@eisentraut.org 683 : 2 : pg_log_info("restoring large object with OID %u", oid);
684 : :
496 685 : 2 : StartRestoreLO(AH, oid, AH->public.ropt->dropSchema);
686 : :
7991 tgl@sss.pgh.pa.us 687 [ + + ]: 3 : while ((cnt = tarRead(buf, 4095, th)) > 0)
688 : : {
689 : 1 : buf[cnt] = '\0';
690 : 1 : ahwrite(buf, 1, cnt, AH);
691 : : }
496 peter@eisentraut.org 692 : 2 : EndRestoreLO(AH, oid);
693 : 2 : foundLO = true;
694 : : }
6374 tgl@sss.pgh.pa.us 695 : 2 : tarClose(AH, th);
696 : : }
697 : : else
698 : : {
699 : 2 : tarClose(AH, th);
700 : :
701 : : /*
702 : : * Once we have found the first LO, stop at the first non-LO entry
703 : : * (which will be 'blobs[_NNN].toc'). This coding would eat all
704 : : * the rest of the archive if there are no LOs ... but this
705 : : * function shouldn't be called at all in that case.
706 : : */
496 peter@eisentraut.org 707 [ + - ]: 2 : if (foundLO)
6374 tgl@sss.pgh.pa.us 708 : 2 : break;
709 : : }
710 : :
8668 pjw@rhyme.com.au 711 : 2 : th = tarOpen(AH, NULL, 'r');
712 : : }
496 peter@eisentraut.org 713 : 2 : EndRestoreLOs(AH);
8668 pjw@rhyme.com.au 714 : 2 : }
715 : :
716 : :
717 : : static int
8424 bruce@momjian.us 718 : 28429 : _WriteByte(ArchiveHandle *AH, const int i)
719 : : {
720 : 28429 : lclContext *ctx = (lclContext *) AH->formatData;
721 : 28429 : char b = i; /* Avoid endian problems */
722 : :
3632 723 [ - + ]: 28429 : if (tarWrite(&b, 1, ctx->FH) != 1)
3632 bruce@momjian.us 724 :UBC 0 : WRITE_ERROR_EXIT;
725 : :
3632 bruce@momjian.us 726 :CBC 28429 : ctx->filePos += 1;
727 : 28429 : return 1;
728 : : }
729 : :
730 : : static int
8424 731 : 28429 : _ReadByte(ArchiveHandle *AH)
732 : : {
733 : 28429 : lclContext *ctx = (lclContext *) AH->formatData;
734 : : size_t res;
735 : : unsigned char c;
736 : :
737 : 28429 : res = tarRead(&c, 1, ctx->FH);
6096 tgl@sss.pgh.pa.us 738 [ - + ]: 28429 : if (res != 1)
739 : : /* We already would have exited for errors on reads, must be EOF */
737 tgl@sss.pgh.pa.us 740 :UBC 0 : pg_fatal("could not read from input file: end of file");
6096 tgl@sss.pgh.pa.us 741 :CBC 28429 : ctx->filePos += 1;
8424 bruce@momjian.us 742 : 28429 : return c;
743 : : }
744 : :
745 : : static void
7908 peter_e@gmx.net 746 : 3415 : _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
747 : : {
8424 bruce@momjian.us 748 : 3415 : lclContext *ctx = (lclContext *) AH->formatData;
749 : :
3632 750 [ - + ]: 3415 : if (tarWrite(buf, len, ctx->FH) != len)
3632 bruce@momjian.us 751 :UBC 0 : WRITE_ERROR_EXIT;
752 : :
3632 bruce@momjian.us 753 :CBC 3415 : ctx->filePos += len;
8668 pjw@rhyme.com.au 754 : 3415 : }
755 : :
756 : : static void
7908 peter_e@gmx.net 757 : 3415 : _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
758 : : {
8424 bruce@momjian.us 759 : 3415 : lclContext *ctx = (lclContext *) AH->formatData;
760 : :
3632 761 [ - + ]: 3415 : if (tarRead(buf, len, ctx->FH) != len)
762 : : /* We already would have exited for errors on reads, must be EOF */
737 tgl@sss.pgh.pa.us 763 :UBC 0 : pg_fatal("could not read from input file: end of file");
764 : :
3632 bruce@momjian.us 765 :CBC 3415 : ctx->filePos += len;
8668 pjw@rhyme.com.au 766 : 3415 : }
767 : :
768 : : static void
3014 tgl@sss.pgh.pa.us 769 : 4 : _CloseArchive(ArchiveHandle *AH)
770 : : {
8424 bruce@momjian.us 771 : 4 : lclContext *ctx = (lclContext *) AH->formatData;
772 : : TAR_MEMBER *th;
773 : : RestoreOptions *ropt;
774 : : RestoreOptions *savRopt;
775 : : DumpOptions *savDopt;
776 : : int savVerbose,
777 : : i;
778 : :
779 [ + + ]: 4 : if (AH->mode == archModeWrite)
780 : : {
781 : : /*
782 : : * Write the Header & TOC to the archive FIRST
783 : : */
8668 pjw@rhyme.com.au 784 : 2 : th = tarOpen(AH, "toc.dat", 'w');
785 : 2 : ctx->FH = th;
786 : 2 : WriteHead(AH);
787 : 2 : WriteToc(AH);
8424 bruce@momjian.us 788 : 2 : tarClose(AH, th); /* Not needed any more */
789 : :
790 : : /*
791 : : * Now send the data (tables & LOs)
792 : : */
3014 tgl@sss.pgh.pa.us 793 : 2 : WriteDataChunks(AH, NULL);
794 : :
795 : : /*
796 : : * Now this format wants to append a script which does a full restore
797 : : * if the files have been extracted.
798 : : */
8668 pjw@rhyme.com.au 799 : 2 : th = tarOpen(AH, "restore.sql", 'w');
800 : :
1328 peter@eisentraut.org 801 : 2 : tarPrintf(th, "--\n"
802 : : "-- NOTE:\n"
803 : : "--\n"
804 : : "-- File paths need to be edited. Search for $$PATH$$ and\n"
805 : : "-- replace it with the path to the directory containing\n"
806 : : "-- the extracted data files.\n"
807 : : "--\n");
808 : :
8668 pjw@rhyme.com.au 809 : 2 : AH->CustomOutPtr = _scriptOut;
810 : :
811 : 2 : ctx->isSpecialScript = 1;
812 : 2 : ctx->scriptTH = th;
813 : :
814 : 2 : ropt = NewRestoreOptions();
3014 tgl@sss.pgh.pa.us 815 : 2 : memcpy(ropt, AH->public.ropt, sizeof(RestoreOptions));
4325 816 : 2 : ropt->filename = NULL;
8668 pjw@rhyme.com.au 817 : 2 : ropt->dropSchema = 1;
8010 tgl@sss.pgh.pa.us 818 : 2 : ropt->superuser = NULL;
8390 pjw@rhyme.com.au 819 : 2 : ropt->suppressDumpWarnings = true;
820 : :
3014 tgl@sss.pgh.pa.us 821 : 2 : savDopt = AH->public.dopt;
822 : 2 : savRopt = AH->public.ropt;
823 : :
824 : 2 : SetArchiveOptions((Archive *) AH, NULL, ropt);
825 : :
8668 pjw@rhyme.com.au 826 : 2 : savVerbose = AH->public.verbose;
827 : 2 : AH->public.verbose = 0;
828 : :
4338 tgl@sss.pgh.pa.us 829 : 2 : RestoreArchive((Archive *) AH);
830 : :
3014 831 : 2 : SetArchiveOptions((Archive *) AH, savDopt, savRopt);
832 : :
8668 pjw@rhyme.com.au 833 : 2 : AH->public.verbose = savVerbose;
834 : :
835 : 2 : tarClose(AH, th);
836 : :
4215 tgl@sss.pgh.pa.us 837 : 2 : ctx->isSpecialScript = 0;
838 : :
839 : : /*
840 : : * EOF marker for tar files is two blocks of NULLs.
841 : : */
1451 rhaas@postgresql.org 842 [ + + ]: 2050 : for (i = 0; i < TAR_BLOCK_SIZE * 2; i++)
843 : : {
8493 pjw@rhyme.com.au 844 [ - + ]: 2048 : if (fputc(0, ctx->tarFH) == EOF)
3632 bruce@momjian.us 845 :UBC 0 : WRITE_ERROR_EXIT;
846 : : }
847 : :
848 : : /* Sync the output file if one is defined */
2580 andrew@dunslane.net 849 [ + + + - ]:CBC 2 : if (AH->dosync && AH->fSpec)
1840 peter@eisentraut.org 850 : 1 : (void) fsync_fname(AH->fSpec, false);
851 : : }
852 : :
8424 bruce@momjian.us 853 : 4 : AH->FH = NULL;
8668 pjw@rhyme.com.au 854 : 4 : }
855 : :
856 : : static size_t
7908 peter_e@gmx.net 857 : 1768 : _scriptOut(ArchiveHandle *AH, const void *buf, size_t len)
858 : : {
8424 bruce@momjian.us 859 : 1768 : lclContext *ctx = (lclContext *) AH->formatData;
860 : :
8668 pjw@rhyme.com.au 861 : 1768 : return tarWrite(buf, len, ctx->scriptTH);
862 : : }
863 : :
864 : : /*
865 : : * Large Object support
866 : : */
867 : :
868 : : /*
869 : : * Called by the archiver when starting to save BLOB DATA (not schema).
870 : : * This routine should save whatever format-specific information is needed
871 : : * to read the LOs back into memory.
872 : : *
873 : : * It is called just prior to the dumper's DataDumper routine.
874 : : *
875 : : * Optional, but strongly recommended.
876 : : *
877 : : */
878 : : static void
496 peter@eisentraut.org 879 : 2 : _StartLOs(ArchiveHandle *AH, TocEntry *te)
880 : : {
8424 bruce@momjian.us 881 : 2 : lclContext *ctx = (lclContext *) AH->formatData;
882 : : char fname[K_STD_BUF_SIZE];
883 : :
13 tgl@sss.pgh.pa.us 884 :GNC 2 : sprintf(fname, "blobs_%d.toc", te->dumpId);
496 peter@eisentraut.org 885 :CBC 2 : ctx->loToc = tarOpen(AH, fname, 'w');
8668 pjw@rhyme.com.au 886 : 2 : }
887 : :
888 : : /*
889 : : * Called by the archiver when the dumper calls StartLO.
890 : : *
891 : : * Mandatory.
892 : : *
893 : : * Must save the passed OID for retrieval at restore-time.
894 : : */
895 : : static void
496 peter@eisentraut.org 896 : 2 : _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
897 : : {
8424 bruce@momjian.us 898 : 2 : lclContext *ctx = (lclContext *) AH->formatData;
899 : 2 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
900 : : char fname[255];
901 : :
902 [ - + ]: 2 : if (oid == 0)
737 tgl@sss.pgh.pa.us 903 :UBC 0 : pg_fatal("invalid OID for large object (%u)", oid);
904 : :
499 michael@paquier.xyz 905 [ - + ]:CBC 2 : if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
737 tgl@sss.pgh.pa.us 906 :UBC 0 : pg_fatal("compression is not supported by tar archive format");
907 : :
745 michael@paquier.xyz 908 :CBC 2 : sprintf(fname, "blob_%u.dat", oid);
909 : :
496 peter@eisentraut.org 910 : 2 : tarPrintf(ctx->loToc, "%u %s\n", oid, fname);
911 : :
8668 pjw@rhyme.com.au 912 : 2 : tctx->TH = tarOpen(AH, fname, 'w');
913 : 2 : }
914 : :
915 : : /*
916 : : * Called by the archiver when the dumper calls EndLO.
917 : : *
918 : : * Optional.
919 : : *
920 : : */
921 : : static void
496 peter@eisentraut.org 922 : 2 : _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
923 : : {
8424 bruce@momjian.us 924 : 2 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
925 : :
8668 pjw@rhyme.com.au 926 : 2 : tarClose(AH, tctx->TH);
927 : 2 : }
928 : :
929 : : /*
930 : : * Called by the archiver when finishing saving BLOB DATA.
931 : : *
932 : : * Optional.
933 : : *
934 : : */
935 : : static void
496 peter@eisentraut.org 936 : 2 : _EndLOs(ArchiveHandle *AH, TocEntry *te)
937 : : {
8424 bruce@momjian.us 938 : 2 : lclContext *ctx = (lclContext *) AH->formatData;
939 : :
940 : : /* Write out a fake zero OID to mark end-of-LOs. */
941 : : /* WriteInt(AH, 0); */
942 : :
496 peter@eisentraut.org 943 : 2 : tarClose(AH, ctx->loToc);
8668 pjw@rhyme.com.au 944 : 2 : }
945 : :
946 : :
947 : :
948 : : /*------------
949 : : * TAR Support
950 : : *------------
951 : : */
952 : :
953 : : static int
1328 peter@eisentraut.org 954 : 4 : tarPrintf(TAR_MEMBER *th, const char *fmt,...)
955 : : {
2027 tgl@sss.pgh.pa.us 956 : 4 : int save_errno = errno;
957 : : char *p;
3825 958 : 4 : size_t len = 128; /* initial assumption about buffer size */
959 : : size_t cnt;
960 : :
961 : : for (;;)
8451 962 : 2 : {
963 : : va_list args;
964 : :
965 : : /* Allocate work buffer. */
3825 966 : 6 : p = (char *) pg_malloc(len);
967 : :
968 : : /* Try to format the data. */
2027 969 : 6 : errno = save_errno;
3825 970 : 6 : va_start(args, fmt);
971 : 6 : cnt = pvsnprintf(p, len, fmt, args);
972 : 6 : va_end(args);
973 : :
974 [ + + ]: 6 : if (cnt < len)
975 : 4 : break; /* success */
976 : :
977 : : /* Release buffer and loop around to try again with larger len. */
978 : 2 : free(p);
979 : 2 : len = cnt;
980 : : }
981 : :
8668 pjw@rhyme.com.au 982 : 4 : cnt = tarWrite(p, cnt, th);
983 : 4 : free(p);
3825 tgl@sss.pgh.pa.us 984 : 4 : return (int) cnt;
985 : : }
986 : :
987 : : bool
8424 bruce@momjian.us 988 : 1 : isValidTarHeader(char *header)
989 : : {
990 : : int sum;
4121 magnus@hagander.net 991 : 1 : int chk = tarChecksum(header);
992 : :
257 rhaas@postgresql.org 993 :GNC 1 : sum = read_tar_number(&header[TAR_OFFSET_CHECKSUM], 8);
994 : :
7743 tgl@sss.pgh.pa.us 995 [ - + ]:CBC 1 : if (sum != chk)
7743 tgl@sss.pgh.pa.us 996 :UBC 0 : return false;
997 : :
998 : : /* POSIX tar format */
257 rhaas@postgresql.org 999 [ + - ]:GNC 1 : if (memcmp(&header[TAR_OFFSET_MAGIC], "ustar\0", 6) == 0 &&
1000 [ + - ]: 1 : memcmp(&header[TAR_OFFSET_VERSION], "00", 2) == 0)
7743 tgl@sss.pgh.pa.us 1001 :CBC 1 : return true;
1002 : : /* GNU tar format */
257 rhaas@postgresql.org 1003 [ # # ]:UNC 0 : if (memcmp(&header[TAR_OFFSET_MAGIC], "ustar \0", 8) == 0)
4216 tgl@sss.pgh.pa.us 1004 :UBC 0 : return true;
1005 : : /* not-quite-POSIX format written by pre-9.3 pg_dump */
257 rhaas@postgresql.org 1006 [ # # ]:UNC 0 : if (memcmp(&header[TAR_OFFSET_MAGIC], "ustar00\0", 8) == 0)
7743 tgl@sss.pgh.pa.us 1007 :UBC 0 : return true;
1008 : :
1009 : 0 : return false;
1010 : : }
1011 : :
1012 : : /* Given the member, write the TAR header & copy the file */
1013 : : static void
8424 bruce@momjian.us 1014 :CBC 41 : _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
1015 : : {
1016 : 41 : lclContext *ctx = (lclContext *) AH->formatData;
8204 1017 : 41 : FILE *tmp = th->tmpFH; /* Grab it for convenience */
1018 : : char buf[32768];
1019 : : size_t cnt;
6264 magnus@hagander.net 1020 : 41 : pgoff_t len = 0;
1021 : : size_t res;
1022 : : size_t i,
1023 : : pad;
1024 : :
1025 : : /*
1026 : : * Find file len & go back to start.
1027 : : */
1344 tgl@sss.pgh.pa.us 1028 [ - + ]: 41 : if (fseeko(tmp, 0, SEEK_END) != 0)
737 tgl@sss.pgh.pa.us 1029 :UBC 0 : pg_fatal("error during file seek: %m");
7908 peter_e@gmx.net 1030 :CBC 41 : th->fileLen = ftello(tmp);
3717 sfrost@snowman.net 1031 [ - + ]: 41 : if (th->fileLen < 0)
737 tgl@sss.pgh.pa.us 1032 :UBC 0 : pg_fatal("could not determine seek position in archive file: %m");
1344 tgl@sss.pgh.pa.us 1033 [ - + ]:CBC 41 : if (fseeko(tmp, 0, SEEK_SET) != 0)
737 tgl@sss.pgh.pa.us 1034 :UBC 0 : pg_fatal("error during file seek: %m");
1035 : :
8668 pjw@rhyme.com.au 1036 :CBC 41 : _tarWriteHeader(th);
1037 : :
6073 tgl@sss.pgh.pa.us 1038 [ + + ]: 85 : while ((cnt = fread(buf, 1, sizeof(buf), tmp)) > 0)
1039 : : {
3632 bruce@momjian.us 1040 [ - + ]: 44 : if ((res = fwrite(buf, 1, cnt, th->tarFH)) != cnt)
3632 bruce@momjian.us 1041 :UBC 0 : WRITE_ERROR_EXIT;
8493 pjw@rhyme.com.au 1042 :CBC 44 : len += res;
1043 : : }
3632 bruce@momjian.us 1044 [ - + ]: 41 : if (!feof(tmp))
3632 bruce@momjian.us 1045 [ # # ]:UBC 0 : READ_ERROR_EXIT(tmp);
1046 : :
8424 bruce@momjian.us 1047 [ - + ]:CBC 41 : if (fclose(tmp) != 0) /* This *should* delete it... */
737 tgl@sss.pgh.pa.us 1048 :UBC 0 : pg_fatal("could not close temporary file: %m");
1049 : :
8668 pjw@rhyme.com.au 1050 [ - + ]:CBC 41 : if (len != th->fileLen)
737 tgl@sss.pgh.pa.us 1051 :UBC 0 : pg_fatal("actual file length (%lld) does not match expected (%lld)",
1052 : : (long long) len, (long long) th->fileLen);
1053 : :
1451 rhaas@postgresql.org 1054 :CBC 41 : pad = tarPaddingBytesRequired(len);
8424 bruce@momjian.us 1055 [ + + ]: 18583 : for (i = 0; i < pad; i++)
1056 : : {
1057 [ - + ]: 18542 : if (fputc('\0', th->tarFH) == EOF)
3632 bruce@momjian.us 1058 :UBC 0 : WRITE_ERROR_EXIT;
1059 : : }
1060 : :
8668 pjw@rhyme.com.au 1061 :CBC 41 : ctx->tarFHpos += len + pad;
1062 : 41 : }
1063 : :
1064 : : /* Locate the file in the archive, read header and position to data */
1065 : : static TAR_MEMBER *
8424 bruce@momjian.us 1066 : 39 : _tarPositionTo(ArchiveHandle *AH, const char *filename)
1067 : : {
1068 : 39 : lclContext *ctx = (lclContext *) AH->formatData;
580 peter@eisentraut.org 1069 : 39 : TAR_MEMBER *th = pg_malloc0_object(TAR_MEMBER);
1070 : : char c;
1071 : : char header[TAR_BLOCK_SIZE];
1072 : : size_t i,
1073 : : len,
1074 : : blks;
1075 : : int id;
1076 : :
8668 pjw@rhyme.com.au 1077 : 39 : th->AH = AH;
1078 : :
1079 : : /* Go to end of current file, if any */
1080 [ + + ]: 39 : if (ctx->tarFHpos != 0)
1081 : : {
758 tgl@sss.pgh.pa.us 1082 [ - + ]: 37 : pg_log_debug("moving from position %lld to next member at file position %lld",
1083 : : (long long) ctx->tarFHpos, (long long) ctx->tarNextMember);
1084 : :
8668 pjw@rhyme.com.au 1085 [ + + ]: 17106 : while (ctx->tarFHpos < ctx->tarNextMember)
1086 : 17069 : _tarReadRaw(AH, &c, 1, NULL, ctx->tarFH);
1087 : : }
1088 : :
758 tgl@sss.pgh.pa.us 1089 [ - + ]: 39 : pg_log_debug("now at file position %lld", (long long) ctx->tarFHpos);
1090 : :
1091 : : /* We are at the start of the file, or at the next member */
1092 : :
1093 : : /* Get the header */
8668 pjw@rhyme.com.au 1094 [ - + ]: 39 : if (!_tarGetHeader(AH, th))
1095 : : {
8668 pjw@rhyme.com.au 1096 [ # # ]:UBC 0 : if (filename)
737 tgl@sss.pgh.pa.us 1097 : 0 : pg_fatal("could not find header for file \"%s\" in tar archive", filename);
1098 : : else
1099 : : {
1100 : : /*
1101 : : * We're just scanning the archive for the next file, so return
1102 : : * null
1103 : : */
8668 pjw@rhyme.com.au 1104 : 0 : free(th);
1105 : 0 : return NULL;
1106 : : }
1107 : : }
1108 : :
8424 bruce@momjian.us 1109 [ + + - + ]:CBC 39 : while (filename != NULL && strcmp(th->targetFile, filename) != 0)
1110 : : {
1840 peter@eisentraut.org 1111 [ # # ]:UBC 0 : pg_log_debug("skipping tar member %s", th->targetFile);
1112 : :
8668 pjw@rhyme.com.au 1113 : 0 : id = atoi(th->targetFile);
4338 tgl@sss.pgh.pa.us 1114 [ # # ]: 0 : if ((TocIDRequired(AH, id) & REQ_DATA) != 0)
737 1115 : 0 : pg_fatal("restoring data out of order is not supported in this archive format: "
1116 : : "\"%s\" is required, but comes before \"%s\" in the archive file.",
1117 : : th->targetFile, filename);
1118 : :
1119 : : /* Header doesn't match, so read to next header */
1451 rhaas@postgresql.org 1120 : 0 : len = th->fileLen;
1121 : 0 : len += tarPaddingBytesRequired(th->fileLen);
1068 tgl@sss.pgh.pa.us 1122 : 0 : blks = len / TAR_BLOCK_SIZE; /* # of tar blocks */
1123 : :
8424 bruce@momjian.us 1124 [ # # ]: 0 : for (i = 0; i < blks; i++)
1451 rhaas@postgresql.org 1125 : 0 : _tarReadRaw(AH, &header[0], TAR_BLOCK_SIZE, NULL, ctx->tarFH);
1126 : :
8668 pjw@rhyme.com.au 1127 [ # # ]: 0 : if (!_tarGetHeader(AH, th))
737 tgl@sss.pgh.pa.us 1128 : 0 : pg_fatal("could not find header for file \"%s\" in tar archive", filename);
1129 : : }
1130 : :
1451 rhaas@postgresql.org 1131 :CBC 78 : ctx->tarNextMember = ctx->tarFHpos + th->fileLen
1132 : 39 : + tarPaddingBytesRequired(th->fileLen);
8668 pjw@rhyme.com.au 1133 : 39 : th->pos = 0;
1134 : :
1135 : 39 : return th;
1136 : : }
1137 : :
1138 : : /* Read & verify a header */
1139 : : static int
8424 bruce@momjian.us 1140 : 39 : _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
1141 : : {
1142 : 39 : lclContext *ctx = (lclContext *) AH->formatData;
1143 : : char h[TAR_BLOCK_SIZE];
1144 : : char tag[100 + 1];
1145 : : int sum,
1146 : : chk;
1147 : : pgoff_t len;
1148 : : pgoff_t hPos;
1149 : 39 : bool gotBlock = false;
1150 : :
8461 pjw@rhyme.com.au 1151 [ + + ]: 78 : while (!gotBlock)
1152 : : {
1153 : : /* Save the pos for reporting purposes */
1154 : 39 : hPos = ctx->tarFHpos;
1155 : :
1156 : : /* Read the next tar block, return EOF, exit if short */
1451 rhaas@postgresql.org 1157 : 39 : len = _tarReadRaw(AH, h, TAR_BLOCK_SIZE, NULL, ctx->tarFH);
8424 bruce@momjian.us 1158 [ - + ]: 39 : if (len == 0) /* EOF */
8461 pjw@rhyme.com.au 1159 :UBC 0 : return 0;
1160 : :
1451 rhaas@postgresql.org 1161 [ - + ]:CBC 39 : if (len != TAR_BLOCK_SIZE)
737 tgl@sss.pgh.pa.us 1162 :UBC 0 : pg_fatal(ngettext("incomplete tar header found (%lu byte)",
1163 : : "incomplete tar header found (%lu bytes)",
1164 : : len),
1165 : : (unsigned long) len);
1166 : :
1167 : : /* Calc checksum */
4121 magnus@hagander.net 1168 :CBC 39 : chk = tarChecksum(h);
257 rhaas@postgresql.org 1169 :GNC 39 : sum = read_tar_number(&h[TAR_OFFSET_CHECKSUM], 8);
1170 : :
1171 : : /*
1172 : : * If the checksum failed, see if it is a null block. If so, silently
1173 : : * continue to the next block.
1174 : : */
8424 bruce@momjian.us 1175 [ + - ]:CBC 39 : if (chk == sum)
8461 pjw@rhyme.com.au 1176 : 39 : gotBlock = true;
1177 : : else
1178 : : {
1179 : : int i;
1180 : :
1451 rhaas@postgresql.org 1181 [ # # ]:UBC 0 : for (i = 0; i < TAR_BLOCK_SIZE; i++)
1182 : : {
6871 neilc@samurai.com 1183 [ # # ]: 0 : if (h[i] != 0)
1184 : : {
8424 bruce@momjian.us 1185 : 0 : gotBlock = true;
8461 pjw@rhyme.com.au 1186 : 0 : break;
1187 : : }
1188 : : }
1189 : : }
1190 : : }
1191 : :
1192 : : /* Name field is 100 bytes, might not be null-terminated */
257 rhaas@postgresql.org 1193 :GNC 39 : strlcpy(tag, &h[TAR_OFFSET_NAME], 100 + 1);
1194 : :
1195 : 39 : len = read_tar_number(&h[TAR_OFFSET_SIZE], 12);
1196 : :
758 tgl@sss.pgh.pa.us 1197 [ - + ]:CBC 39 : pg_log_debug("TOC Entry %s at %llu (length %llu, checksum %d)",
1198 : : tag, (unsigned long long) hPos, (unsigned long long) len, sum);
1199 : :
8668 pjw@rhyme.com.au 1200 [ - + ]: 39 : if (chk != sum)
737 tgl@sss.pgh.pa.us 1201 :UBC 0 : pg_fatal("corrupt tar header found in %s (expected %d, computed %d) file position %llu",
1202 : : tag, sum, chk, (unsigned long long) ftello(ctx->tarFH));
1203 : :
4524 bruce@momjian.us 1204 :CBC 39 : th->targetFile = pg_strdup(tag);
8668 pjw@rhyme.com.au 1205 : 39 : th->fileLen = len;
1206 : :
1207 : 39 : return 1;
1208 : : }
1209 : :
1210 : :
1211 : : static void
8424 bruce@momjian.us 1212 : 41 : _tarWriteHeader(TAR_MEMBER *th)
1213 : : {
1214 : : char h[TAR_BLOCK_SIZE];
1215 : :
3067 tgl@sss.pgh.pa.us 1216 : 41 : tarCreateHeader(h, th->targetFile, NULL, th->fileLen,
1217 : : 0600, 04000, 02000, time(NULL));
1218 : :
1219 : : /* Now write the completed header. */
1451 rhaas@postgresql.org 1220 [ - + ]: 41 : if (fwrite(h, 1, TAR_BLOCK_SIZE, th->tarFH) != TAR_BLOCK_SIZE)
3632 bruce@momjian.us 1221 :UBC 0 : WRITE_ERROR_EXIT;
8668 pjw@rhyme.com.au 1222 :CBC 41 : }
|