Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_backup_directory.c
4 : : *
5 : : * A directory format dump is a directory, which contains a "toc.dat" file
6 : : * for the TOC, and a separate file for each data entry, named "<oid>.dat".
7 : : * Large objects are stored in separate files named "blob_<oid>.dat",
8 : : * and there's a plain-text TOC file for each BLOBS TOC entry named
9 : : * "blobs_<dumpID>.toc" (or just "blobs.toc" in archive versions before 16).
10 : : *
11 : : * If compression is used, each data file is individually compressed and the
12 : : * ".gz" suffix is added to the filenames. The TOC files are never
13 : : * compressed by pg_dump, however they are accepted with the .gz suffix too,
14 : : * in case the user has manually compressed them with 'gzip'.
15 : : *
16 : : * NOTE: This format is identical to the files written in the tar file in
17 : : * the 'tar' format, except that we don't write the restore.sql file (TODO),
18 : : * and the tar format doesn't support compression. Please keep the formats in
19 : : * sync.
20 : : *
21 : : *
22 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
23 : : * Portions Copyright (c) 1994, Regents of the University of California
24 : : * Portions Copyright (c) 2000, Philip Warner
25 : : *
26 : : * Rights are granted to use this software in any way so long
27 : : * as this notice is not removed.
28 : : *
29 : : * The author is not responsible for loss or damages that may
30 : : * result from its use.
31 : : *
32 : : * IDENTIFICATION
33 : : * src/bin/pg_dump/pg_backup_directory.c
34 : : *
35 : : *-------------------------------------------------------------------------
36 : : */
37 : : #include "postgres_fe.h"
38 : :
39 : : #include <dirent.h>
40 : : #include <sys/stat.h>
41 : :
42 : : #include "common/file_utils.h"
43 : : #include "compress_io.h"
44 : : #include "dumputils.h"
45 : : #include "parallel.h"
46 : : #include "pg_backup_utils.h"
47 : :
48 : : typedef struct
49 : : {
50 : : /*
51 : : * Our archive location. This is basically what the user specified as his
52 : : * backup file but of course here it is a directory.
53 : : */
54 : : char *directory;
55 : :
56 : : CompressFileHandle *dataFH; /* currently open data file */
57 : : CompressFileHandle *LOsTocFH; /* file handle for blobs_NNN.toc */
58 : : ParallelState *pstate; /* for parallel backup / restore */
59 : : } lclContext;
60 : :
61 : : typedef struct
62 : : {
63 : : char *filename; /* filename excluding the directory (basename) */
64 : : } lclTocEntry;
65 : :
66 : : /* prototypes for private functions */
67 : : static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
68 : : static void _StartData(ArchiveHandle *AH, TocEntry *te);
69 : : static void _EndData(ArchiveHandle *AH, TocEntry *te);
70 : : static void _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
71 : : static int _WriteByte(ArchiveHandle *AH, const int i);
72 : : static int _ReadByte(ArchiveHandle *AH);
73 : : static void _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
74 : : static void _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
75 : : static void _CloseArchive(ArchiveHandle *AH);
76 : : static void _ReopenArchive(ArchiveHandle *AH);
77 : : static void _PrintTocData(ArchiveHandle *AH, TocEntry *te);
78 : :
79 : : static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
80 : : static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
81 : : static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
82 : :
83 : : static void _StartLOs(ArchiveHandle *AH, TocEntry *te);
84 : : static void _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
85 : : static void _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
86 : : static void _EndLOs(ArchiveHandle *AH, TocEntry *te);
87 : : static void _LoadLOs(ArchiveHandle *AH, TocEntry *te);
88 : :
89 : : static void _PrepParallelRestore(ArchiveHandle *AH);
90 : : static void _Clone(ArchiveHandle *AH);
91 : : static void _DeClone(ArchiveHandle *AH);
92 : :
93 : : static int _WorkerJobRestoreDirectory(ArchiveHandle *AH, TocEntry *te);
94 : : static int _WorkerJobDumpDirectory(ArchiveHandle *AH, TocEntry *te);
95 : :
96 : : static void setFilePath(ArchiveHandle *AH, char *buf,
97 : : const char *relativeFilename);
98 : :
99 : : /*
100 : : * Init routine required by ALL formats. This is a global routine
101 : : * and should be declared in pg_backup_archiver.h
102 : : *
103 : : * Its task is to create any extra archive context (using AH->formatData),
104 : : * and to initialize the supported function pointers.
105 : : *
106 : : * It should also prepare whatever its input source is for reading/writing,
107 : : * and in the case of a read mode connection, it should load the Header & TOC.
108 : : */
109 : : void
110 : 28 : InitArchiveFmt_Directory(ArchiveHandle *AH)
111 : : {
112 : : lclContext *ctx;
113 : :
114 : : /* Assuming static functions, this can be copied for each format. */
115 : 28 : AH->ArchiveEntryPtr = _ArchiveEntry;
116 : 28 : AH->StartDataPtr = _StartData;
117 : 28 : AH->WriteDataPtr = _WriteData;
118 : 28 : AH->EndDataPtr = _EndData;
119 : 28 : AH->WriteBytePtr = _WriteByte;
120 : 28 : AH->ReadBytePtr = _ReadByte;
121 : 28 : AH->WriteBufPtr = _WriteBuf;
122 : 28 : AH->ReadBufPtr = _ReadBuf;
123 : 28 : AH->ClosePtr = _CloseArchive;
124 : 28 : AH->ReopenPtr = _ReopenArchive;
125 : 28 : AH->PrintTocDataPtr = _PrintTocData;
126 : 28 : AH->ReadExtraTocPtr = _ReadExtraToc;
127 : 28 : AH->WriteExtraTocPtr = _WriteExtraToc;
128 : 28 : AH->PrintExtraTocPtr = _PrintExtraToc;
129 : :
130 : 28 : AH->StartLOsPtr = _StartLOs;
131 : 28 : AH->StartLOPtr = _StartLO;
132 : 28 : AH->EndLOPtr = _EndLO;
133 : 28 : AH->EndLOsPtr = _EndLOs;
134 : :
135 : 28 : AH->PrepParallelRestorePtr = _PrepParallelRestore;
136 : 28 : AH->ClonePtr = _Clone;
137 : 28 : AH->DeClonePtr = _DeClone;
138 : :
139 : 28 : AH->WorkerJobRestorePtr = _WorkerJobRestoreDirectory;
140 : 28 : AH->WorkerJobDumpPtr = _WorkerJobDumpDirectory;
141 : :
142 : : /* Set up our private context */
143 : 28 : ctx = pg_malloc0_object(lclContext);
144 : 28 : AH->formatData = ctx;
145 : :
146 : 28 : ctx->dataFH = NULL;
147 : 28 : ctx->LOsTocFH = NULL;
148 : :
149 : : /*
150 : : * Now open the TOC file
151 : : */
152 : :
153 [ + - - + ]: 28 : if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
154 : 0 : pg_fatal("no output directory specified");
155 : :
156 : 28 : ctx->directory = AH->fSpec;
157 : :
158 [ + + ]: 28 : if (AH->mode == archModeWrite)
159 : : {
160 : : /* we accept an empty existing directory */
161 : 12 : create_or_open_dir(ctx->directory);
162 : : }
163 : : else
164 : : { /* Read Mode */
165 : : char fname[MAXPGPATH];
166 : : CompressFileHandle *tocFH;
167 : :
168 : 16 : setFilePath(AH, fname, "toc.dat");
169 : :
170 : 16 : tocFH = InitDiscoverCompressFileHandle(fname, PG_BINARY_R);
171 [ - + ]: 16 : if (tocFH == NULL)
172 : 0 : pg_fatal("could not open input file \"%s\": %m", fname);
173 : :
174 : 16 : ctx->dataFH = tocFH;
175 : :
176 : : /*
177 : : * The TOC of a directory format dump shares the format code of the
178 : : * tar format.
179 : : */
180 : 16 : AH->format = archTar;
181 : 16 : ReadHead(AH);
182 : 16 : AH->format = archDirectory;
183 : 16 : ReadToc(AH);
184 : :
185 : : /* Nothing else in the file, so close it again... */
186 [ - + ]: 16 : if (!EndCompressFileHandle(tocFH))
187 : 0 : pg_fatal("could not close TOC file: %m");
188 : 16 : ctx->dataFH = NULL;
189 : : }
190 : 28 : }
191 : :
192 : : /*
193 : : * Called by the Archiver when the dumper creates a new TOC entry.
194 : : *
195 : : * We determine the filename for this entry.
196 : : */
197 : : static void
198 : 1075 : _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
199 : : {
200 : : lclTocEntry *tctx;
201 : : char fn[MAXPGPATH];
202 : :
203 : 1075 : tctx = pg_malloc0_object(lclTocEntry);
204 [ + + ]: 1075 : if (strcmp(te->desc, "BLOBS") == 0)
205 : : {
206 : 6 : snprintf(fn, MAXPGPATH, "blobs_%d.toc", te->dumpId);
207 : 6 : tctx->filename = pg_strdup(fn);
208 : : }
209 [ + + ]: 1069 : else if (te->dataDumper)
210 : : {
211 : 121 : snprintf(fn, MAXPGPATH, "%d.dat", te->dumpId);
212 : 121 : tctx->filename = pg_strdup(fn);
213 : : }
214 : : else
215 : 948 : tctx->filename = NULL;
216 : :
217 : 1075 : te->formatData = tctx;
218 : 1075 : }
219 : :
220 : : /*
221 : : * Called by the Archiver to save any extra format-related TOC entry
222 : : * data.
223 : : *
224 : : * Use the Archiver routines to write data - they are non-endian, and
225 : : * maintain other important file information.
226 : : */
227 : : static void
228 : 1075 : _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
229 : : {
230 : 1075 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
231 : :
232 : : /*
233 : : * A dumpable object has set tctx->filename, any other object has not.
234 : : * (see _ArchiveEntry).
235 : : */
236 [ + + ]: 1075 : if (tctx->filename)
237 : 127 : WriteStr(AH, tctx->filename);
238 : : else
239 : 948 : WriteStr(AH, "");
240 : 1075 : }
241 : :
242 : : /*
243 : : * Called by the Archiver to read any extra format-related TOC data.
244 : : *
245 : : * Needs to match the order defined in _WriteExtraToc, and should also
246 : : * use the Archiver input routines.
247 : : */
248 : : static void
249 : 1531 : _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
250 : : {
251 : 1531 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
252 : :
253 [ + - ]: 1531 : if (tctx == NULL)
254 : : {
255 : 1531 : tctx = pg_malloc0_object(lclTocEntry);
256 : 1531 : te->formatData = tctx;
257 : : }
258 : :
259 : 1531 : tctx->filename = ReadStr(AH);
260 [ + + ]: 1531 : if (strlen(tctx->filename) == 0)
261 : : {
262 : 1351 : free(tctx->filename);
263 : 1351 : tctx->filename = NULL;
264 : : }
265 [ - + ]: 180 : else if (first_dir_separator(tctx->filename) != NULL)
266 : 0 : pg_fatal("invalid file name in archive: \"%s\"", tctx->filename);
267 : 1531 : }
268 : :
269 : : /*
270 : : * Called by the Archiver when restoring an archive to output a comment
271 : : * that includes useful information about the TOC entry.
272 : : */
273 : : static void
274 : 935 : _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
275 : : {
276 : 935 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
277 : :
278 [ - + - - ]: 935 : if (AH->public.verbose && tctx->filename)
279 : 0 : ahprintf(AH, "-- File: %s\n", tctx->filename);
280 : 935 : }
281 : :
282 : : /*
283 : : * Called by the archiver when saving TABLE DATA (not schema). This routine
284 : : * should save whatever format-specific information is needed to read
285 : : * the archive back.
286 : : *
287 : : * It is called just prior to the dumper's 'DataDumper' routine being called.
288 : : *
289 : : * We create the data file for writing.
290 : : */
291 : : static void
292 : 121 : _StartData(ArchiveHandle *AH, TocEntry *te)
293 : : {
294 : 121 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
295 : 121 : lclContext *ctx = (lclContext *) AH->formatData;
296 : : char fname[MAXPGPATH];
297 : :
298 : 121 : setFilePath(AH, fname, tctx->filename);
299 : :
300 : 121 : ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
301 : :
302 [ - + ]: 121 : if (!ctx->dataFH->open_write_func(fname, PG_BINARY_W, ctx->dataFH))
303 : 0 : pg_fatal("could not open output file \"%s\": %m", fname);
304 : 121 : }
305 : :
306 : : /*
307 : : * Called by archiver when dumper calls WriteData. This routine is
308 : : * called for both LO and table data; it is the responsibility of
309 : : * the format to manage each kind of data using StartLO/StartData.
310 : : *
311 : : * It should only be called from within a DataDumper routine.
312 : : *
313 : : * We write the data to the open data file.
314 : : */
315 : : static void
316 : 24662 : _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
317 : : {
318 : 24662 : lclContext *ctx = (lclContext *) AH->formatData;
319 : 24662 : CompressFileHandle *CFH = ctx->dataFH;
320 : :
321 [ + + ]: 24662 : if (dLen <= 0)
322 : 6 : return;
323 : 24656 : CFH->write_func(data, dLen, CFH);
324 : : }
325 : :
326 : : /*
327 : : * Called by the archiver when a dumper's 'DataDumper' routine has
328 : : * finished.
329 : : *
330 : : * We close the data file.
331 : : */
332 : : static void
333 : 121 : _EndData(ArchiveHandle *AH, TocEntry *te)
334 : : {
335 : 121 : lclContext *ctx = (lclContext *) AH->formatData;
336 : :
337 : : /* Close the file */
338 [ - + ]: 121 : if (!EndCompressFileHandle(ctx->dataFH))
339 : 0 : pg_fatal("could not close data file: %m");
340 : :
341 : 121 : ctx->dataFH = NULL;
342 : 121 : }
343 : :
344 : : /*
345 : : * Print data for a given file (can be a LO as well)
346 : : */
347 : : static void
348 : 134 : _PrintFileData(ArchiveHandle *AH, char *filename)
349 : : {
350 : : size_t cnt;
351 : : char *buf;
352 : : size_t buflen;
353 : : CompressFileHandle *CFH;
354 : :
355 [ - + ]: 134 : if (!filename)
356 : 0 : return;
357 : :
358 : 134 : CFH = InitDiscoverCompressFileHandle(filename, PG_BINARY_R);
359 [ - + ]: 134 : if (!CFH)
360 : 0 : pg_fatal("could not open input file \"%s\": %m", filename);
361 : :
362 : 134 : buflen = DEFAULT_IO_BUFFER_SIZE;
363 : 134 : buf = pg_malloc(buflen);
364 : :
365 [ + + ]: 267 : while ((cnt = CFH->read_func(buf, buflen, CFH)) > 0)
366 : : {
367 : 133 : ahwrite(buf, 1, cnt, AH);
368 : : }
369 : :
370 : 133 : pg_free(buf);
371 [ - + ]: 133 : if (!EndCompressFileHandle(CFH))
372 : 0 : pg_fatal("could not close data file \"%s\": %m", filename);
373 : : }
374 : :
375 : : /*
376 : : * Print data for a given TOC entry
377 : : */
378 : : static void
379 : 134 : _PrintTocData(ArchiveHandle *AH, TocEntry *te)
380 : : {
381 : 134 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
382 : :
383 [ - + ]: 134 : if (!tctx->filename)
384 : 0 : return;
385 : :
386 [ + + ]: 134 : if (strcmp(te->desc, "BLOBS") == 0)
387 : 5 : _LoadLOs(AH, te);
388 : : else
389 : : {
390 : : char fname[MAXPGPATH];
391 : :
392 : 129 : setFilePath(AH, fname, tctx->filename);
393 : 129 : _PrintFileData(AH, fname);
394 : : }
395 : : }
396 : :
397 : : static void
398 : 5 : _LoadLOs(ArchiveHandle *AH, TocEntry *te)
399 : : {
400 : : Oid oid;
401 : 5 : lclContext *ctx = (lclContext *) AH->formatData;
402 : 5 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
403 : : CompressFileHandle *CFH;
404 : : char tocfname[MAXPGPATH];
405 : : char line[MAXPGPATH];
406 : :
407 : 5 : StartRestoreLOs(AH);
408 : :
409 : : /*
410 : : * Note: before archive v16, there was always only one BLOBS TOC entry,
411 : : * now there can be multiple. Furthermore, although the actual filename
412 : : * was always "blobs.toc" before v16, the value of tctx->filename did not
413 : : * match that before commit 548e50976 fixed it. For simplicity we assume
414 : : * it must be "blobs.toc" in all archives before v16.
415 : : */
416 [ - + ]: 5 : if (AH->version < K_VERS_1_16)
417 : 0 : setFilePath(AH, tocfname, "blobs.toc");
418 : : else
419 : 5 : setFilePath(AH, tocfname, tctx->filename);
420 : :
421 : 5 : CFH = ctx->LOsTocFH = InitDiscoverCompressFileHandle(tocfname, PG_BINARY_R);
422 : :
423 [ - + ]: 5 : if (ctx->LOsTocFH == NULL)
424 : 0 : pg_fatal("could not open large object TOC file \"%s\" for input: %m",
425 : : tocfname);
426 : :
427 : : /* Read the LOs TOC file line-by-line, and process each LO */
428 [ + + ]: 10 : while ((CFH->gets_func(line, MAXPGPATH, CFH)) != NULL)
429 : : {
430 : : char lofname[MAXPGPATH + 1];
431 : : char path[MAXPGPATH];
432 : :
433 : : /* Can't overflow because line and lofname are the same length */
434 [ - + ]: 5 : if (sscanf(line, "%u %" CppAsString2(MAXPGPATH) "s\n", &oid, lofname) != 2)
435 : 0 : pg_fatal("invalid line in large object TOC file \"%s\": \"%s\"",
436 : : tocfname, line);
437 [ - + ]: 5 : if (first_dir_separator(lofname) != NULL)
438 : 0 : pg_fatal("invalid file name in archive: \"%s\"", lofname);
439 : :
440 : 5 : StartRestoreLO(AH, oid, AH->public.ropt->dropSchema);
441 : 5 : snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, lofname);
442 : 5 : _PrintFileData(AH, path);
443 : 5 : EndRestoreLO(AH, oid);
444 : : }
445 [ - + ]: 5 : if (!CFH->eof_func(CFH))
446 : 0 : pg_fatal("error reading large object TOC file \"%s\"",
447 : : tocfname);
448 : :
449 [ - + ]: 5 : if (!EndCompressFileHandle(ctx->LOsTocFH))
450 : 0 : pg_fatal("could not close large object TOC file \"%s\": %m",
451 : : tocfname);
452 : :
453 : 5 : ctx->LOsTocFH = NULL;
454 : :
455 : 5 : EndRestoreLOs(AH);
456 : 5 : }
457 : :
458 : :
459 : : /*
460 : : * Write a byte of data to the archive.
461 : : * Called by the archiver to do integer & byte output to the archive.
462 : : * These routines are only used to read & write the headers & TOC.
463 : : */
464 : : static int
465 : 106399 : _WriteByte(ArchiveHandle *AH, const int i)
466 : : {
467 : 106399 : unsigned char c = (unsigned char) i;
468 : 106399 : lclContext *ctx = (lclContext *) AH->formatData;
469 : 106399 : CompressFileHandle *CFH = ctx->dataFH;
470 : :
471 : 106399 : CFH->write_func(&c, 1, CFH);
472 : 106399 : return 1;
473 : : }
474 : :
475 : : /*
476 : : * Read a byte of data from the archive.
477 : : * Called by the archiver to read bytes & integers from the archive.
478 : : * These routines are only used to read & write headers & TOC.
479 : : * EOF should be treated as a fatal error.
480 : : */
481 : : static int
482 : 151397 : _ReadByte(ArchiveHandle *AH)
483 : : {
484 : 151397 : lclContext *ctx = (lclContext *) AH->formatData;
485 : 151397 : CompressFileHandle *CFH = ctx->dataFH;
486 : :
487 : 151397 : return CFH->getc_func(CFH);
488 : : }
489 : :
490 : : /*
491 : : * Write a buffer of data to the archive.
492 : : * Called by the archiver to write a block of bytes to the TOC or a data file.
493 : : */
494 : : static void
495 : 11920 : _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
496 : : {
497 : 11920 : lclContext *ctx = (lclContext *) AH->formatData;
498 : 11920 : CompressFileHandle *CFH = ctx->dataFH;
499 : :
500 : 11920 : CFH->write_func(buf, len, CFH);
501 : 11920 : }
502 : :
503 : : /*
504 : : * Read a block of bytes from the archive.
505 : : *
506 : : * Called by the archiver to read a block of bytes from the archive
507 : : */
508 : : static void
509 : 16962 : _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
510 : : {
511 : 16962 : lclContext *ctx = (lclContext *) AH->formatData;
512 : 16962 : CompressFileHandle *CFH = ctx->dataFH;
513 : :
514 : : /*
515 : : * We do not expect a short read, so fail if we get one. The read_func
516 : : * already dealt with any outright I/O error.
517 : : */
518 [ - + ]: 16962 : if (CFH->read_func(buf, len, CFH) != len)
519 : 0 : pg_fatal("could not read from input file: end of file");
520 : 16962 : }
521 : :
522 : : /*
523 : : * Close the archive.
524 : : *
525 : : * When writing the archive, this is the routine that actually starts
526 : : * the process of saving it to files. No data should be written prior
527 : : * to this point, since the user could sort the TOC after creating it.
528 : : *
529 : : * If an archive is to be written, this routine must call:
530 : : * WriteHead to save the archive header
531 : : * WriteToc to save the TOC entries
532 : : * WriteDataChunks to save all data & LOs.
533 : : */
534 : : static void
535 : 27 : _CloseArchive(ArchiveHandle *AH)
536 : : {
537 : 27 : lclContext *ctx = (lclContext *) AH->formatData;
538 : :
539 [ + + ]: 27 : if (AH->mode == archModeWrite)
540 : : {
541 : : CompressFileHandle *tocFH;
542 : 12 : pg_compress_specification compression_spec = {0};
543 : : char fname[MAXPGPATH];
544 : :
545 : 12 : setFilePath(AH, fname, "toc.dat");
546 : :
547 : : /* this will actually fork the processes for a parallel backup */
548 : 12 : ctx->pstate = ParallelBackupStart(AH);
549 : :
550 : : /* The TOC is always created uncompressed */
551 : 12 : compression_spec.algorithm = PG_COMPRESSION_NONE;
552 : 12 : tocFH = InitCompressFileHandle(compression_spec);
553 [ - + ]: 12 : if (!tocFH->open_write_func(fname, PG_BINARY_W, tocFH))
554 : 0 : pg_fatal("could not open output file \"%s\": %m", fname);
555 : 12 : ctx->dataFH = tocFH;
556 : :
557 : : /*
558 : : * Write 'tar' in the format field of the toc.dat file. The directory
559 : : * is compatible with 'tar', so there's no point having a different
560 : : * format code for it.
561 : : */
562 : 12 : AH->format = archTar;
563 : 12 : WriteHead(AH);
564 : 12 : AH->format = archDirectory;
565 : 12 : WriteToc(AH);
566 [ - + ]: 12 : if (!EndCompressFileHandle(tocFH))
567 : 0 : pg_fatal("could not close TOC file: %m");
568 : 12 : WriteDataChunks(AH, ctx->pstate);
569 : :
570 : 12 : ParallelBackupEnd(AH, ctx->pstate);
571 : :
572 : : /*
573 : : * In directory mode, there is no need to sync all the entries
574 : : * individually. Just recurse once through all the files generated.
575 : : */
576 [ + + ]: 12 : if (AH->dosync)
577 : 2 : sync_dir_recurse(ctx->directory, AH->sync_method);
578 : : }
579 : 27 : AH->FH = NULL;
580 : 27 : }
581 : :
582 : : /*
583 : : * Reopen the archive's file handle.
584 : : */
585 : : static void
586 : 17 : _ReopenArchive(ArchiveHandle *AH)
587 : : {
588 : : /*
589 : : * Our TOC is in memory, our data files are opened by each child anyway as
590 : : * they are separate. We support reopening the archive by just doing
591 : : * nothing.
592 : : */
593 : 17 : }
594 : :
595 : : /*
596 : : * LO support
597 : : */
598 : :
599 : : /*
600 : : * Called by the archiver when starting to save BLOB DATA (not schema).
601 : : * It is called just prior to the dumper's DataDumper routine.
602 : : *
603 : : * We open the large object TOC file here, so that we can append a line to
604 : : * it for each LO.
605 : : */
606 : : static void
607 : 6 : _StartLOs(ArchiveHandle *AH, TocEntry *te)
608 : : {
609 : 6 : lclContext *ctx = (lclContext *) AH->formatData;
610 : 6 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
611 : 6 : pg_compress_specification compression_spec = {0};
612 : : char fname[MAXPGPATH];
613 : :
614 : 6 : setFilePath(AH, fname, tctx->filename);
615 : :
616 : : /* The LO TOC file is never compressed */
617 : 6 : compression_spec.algorithm = PG_COMPRESSION_NONE;
618 : 6 : ctx->LOsTocFH = InitCompressFileHandle(compression_spec);
619 [ - + ]: 6 : if (!ctx->LOsTocFH->open_write_func(fname, "ab", ctx->LOsTocFH))
620 : 0 : pg_fatal("could not open output file \"%s\": %m", fname);
621 : 6 : }
622 : :
623 : : /*
624 : : * Called by the archiver when we're about to start dumping a LO.
625 : : *
626 : : * We create a file to write the LO to.
627 : : */
628 : : static void
629 : 6 : _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
630 : : {
631 : 6 : lclContext *ctx = (lclContext *) AH->formatData;
632 : : char fname[MAXPGPATH];
633 : :
634 : 6 : snprintf(fname, MAXPGPATH, "%s/blob_%u.dat", ctx->directory, oid);
635 : :
636 : 6 : ctx->dataFH = InitCompressFileHandle(AH->compression_spec);
637 [ - + ]: 6 : if (!ctx->dataFH->open_write_func(fname, PG_BINARY_W, ctx->dataFH))
638 : 0 : pg_fatal("could not open output file \"%s\": %m", fname);
639 : 6 : }
640 : :
641 : : /*
642 : : * Called by the archiver when the dumper is finished writing a LO.
643 : : *
644 : : * We close the LO file and write an entry to the LO TOC file for it.
645 : : */
646 : : static void
647 : 6 : _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
648 : : {
649 : 6 : lclContext *ctx = (lclContext *) AH->formatData;
650 : 6 : CompressFileHandle *CFH = ctx->LOsTocFH;
651 : : char buf[50];
652 : : int len;
653 : :
654 : : /* Close the BLOB data file itself */
655 [ - + ]: 6 : if (!EndCompressFileHandle(ctx->dataFH))
656 : 0 : pg_fatal("could not close LO data file: %m");
657 : 6 : ctx->dataFH = NULL;
658 : :
659 : : /* register the LO in blobs_NNN.toc */
660 : 6 : len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
661 : 6 : CFH->write_func(buf, len, CFH);
662 : 6 : }
663 : :
664 : : /*
665 : : * Called by the archiver when finishing saving BLOB DATA.
666 : : *
667 : : * We close the LOs TOC file.
668 : : */
669 : : static void
670 : 6 : _EndLOs(ArchiveHandle *AH, TocEntry *te)
671 : : {
672 : 6 : lclContext *ctx = (lclContext *) AH->formatData;
673 : :
674 [ - + ]: 6 : if (!EndCompressFileHandle(ctx->LOsTocFH))
675 : 0 : pg_fatal("could not close LOs TOC file: %m");
676 : 6 : ctx->LOsTocFH = NULL;
677 : 6 : }
678 : :
679 : : /*
680 : : * Gets a relative file name and prepends the output directory, writing the
681 : : * result to buf. The caller needs to make sure that buf is MAXPGPATH bytes
682 : : * big. Can't use a static char[MAXPGPATH] inside the function because we run
683 : : * multithreaded on Windows.
684 : : */
685 : : static void
686 : 309 : setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
687 : : {
688 : 309 : lclContext *ctx = (lclContext *) AH->formatData;
689 : : char *dname;
690 : :
691 : 309 : dname = ctx->directory;
692 : :
693 [ - + ]: 309 : if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
694 : 0 : pg_fatal("file name too long: \"%s\"", dname);
695 : :
696 : 309 : strcpy(buf, dname);
697 : 309 : strcat(buf, "/");
698 : 309 : strcat(buf, relativeFilename);
699 : 309 : }
700 : :
701 : : /*
702 : : * Prepare for parallel restore.
703 : : *
704 : : * The main thing that needs to happen here is to fill in TABLE DATA and BLOBS
705 : : * TOC entries' dataLength fields with appropriate values to guide the
706 : : * ordering of restore jobs. The source of said data is format-dependent,
707 : : * as is the exact meaning of the values.
708 : : *
709 : : * A format module might also choose to do other setup here.
710 : : */
711 : : static void
712 : 5 : _PrepParallelRestore(ArchiveHandle *AH)
713 : : {
714 : : TocEntry *te;
715 : :
716 [ + + ]: 124 : for (te = AH->toc->next; te != AH->toc; te = te->next)
717 : : {
718 : 119 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
719 : : char fname[MAXPGPATH];
720 : : struct stat st;
721 : :
722 : : /*
723 : : * A dumpable object has set tctx->filename, any other object has not.
724 : : * (see _ArchiveEntry).
725 : : */
726 [ + + ]: 119 : if (tctx->filename == NULL)
727 : 99 : continue;
728 : :
729 : : /* We may ignore items not due to be restored */
730 [ - + ]: 20 : if ((te->reqs & (REQ_DATA | REQ_STATS)) == 0)
731 : 0 : continue;
732 : :
733 : : /*
734 : : * Stat the file and, if successful, put its size in dataLength. When
735 : : * using compression, the physical file size might not be a very good
736 : : * guide to the amount of work involved in restoring the file, but we
737 : : * only need an approximate indicator of that.
738 : : */
739 : 20 : setFilePath(AH, fname, tctx->filename);
740 : :
741 [ - + ]: 20 : if (stat(fname, &st) == 0)
742 : 0 : te->dataLength = st.st_size;
743 [ + - ]: 20 : else if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE)
744 : : {
745 [ + - ]: 20 : if (AH->compression_spec.algorithm == PG_COMPRESSION_GZIP)
746 : 20 : strlcat(fname, ".gz", sizeof(fname));
747 [ # # ]: 0 : else if (AH->compression_spec.algorithm == PG_COMPRESSION_LZ4)
748 : 0 : strlcat(fname, ".lz4", sizeof(fname));
749 [ # # ]: 0 : else if (AH->compression_spec.algorithm == PG_COMPRESSION_ZSTD)
750 : 0 : strlcat(fname, ".zst", sizeof(fname));
751 : :
752 [ + - ]: 20 : if (stat(fname, &st) == 0)
753 : 20 : te->dataLength = st.st_size;
754 : : }
755 : :
756 : : /*
757 : : * If this is a BLOBS entry, what we stat'd was blobs_NNN.toc, which
758 : : * most likely is a lot smaller than the actual blob data. We don't
759 : : * have a cheap way to estimate how much smaller, but fortunately it
760 : : * doesn't matter too much as long as we get the LOs processed
761 : : * reasonably early. Arbitrarily scale up by a factor of 1K.
762 : : */
763 [ - + ]: 20 : if (strcmp(te->desc, "BLOBS") == 0)
764 : 0 : te->dataLength *= 1024;
765 : : }
766 : 5 : }
767 : :
768 : : /*
769 : : * Clone format-specific fields during parallel restoration.
770 : : */
771 : : static void
772 : 28 : _Clone(ArchiveHandle *AH)
773 : : {
774 : 28 : lclContext *ctx = (lclContext *) AH->formatData;
775 : :
776 : 28 : AH->formatData = pg_malloc_object(lclContext);
777 : 28 : memcpy(AH->formatData, ctx, sizeof(lclContext));
778 : 28 : ctx = (lclContext *) AH->formatData;
779 : :
780 : : /*
781 : : * TOC-entry-local state isn't an issue because any one TOC entry is
782 : : * touched by just one worker child.
783 : : */
784 : :
785 : : /*
786 : : * We also don't copy the ParallelState pointer (pstate), only the leader
787 : : * process ever writes to it.
788 : : */
789 : 28 : }
790 : :
791 : : static void
792 : 28 : _DeClone(ArchiveHandle *AH)
793 : : {
794 : 28 : lclContext *ctx = (lclContext *) AH->formatData;
795 : :
796 : 28 : free(ctx);
797 : 28 : }
798 : :
799 : : /*
800 : : * This function is executed in the child of a parallel backup for a
801 : : * directory-format archive and dumps the actual data for one TOC entry.
802 : : */
803 : : static int
804 : 72 : _WorkerJobDumpDirectory(ArchiveHandle *AH, TocEntry *te)
805 : : {
806 : : /*
807 : : * This function returns void. We either fail and die horribly or
808 : : * succeed... A failure will be detected by the parent when the child dies
809 : : * unexpectedly.
810 : : */
811 : 72 : WriteDataChunksForTocEntry(AH, te);
812 : :
813 : 72 : return 0;
814 : : }
815 : :
816 : : /*
817 : : * This function is executed in the child of a parallel restore from a
818 : : * directory-format archive and restores the actual data for one TOC entry.
819 : : */
820 : : static int
821 : 55 : _WorkerJobRestoreDirectory(ArchiveHandle *AH, TocEntry *te)
822 : : {
823 : 55 : return parallel_restore(AH, te);
824 : : }
|