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