PostgreSQL Source Code git master
filemap.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * filemap.c
4 * A data structure for keeping track of files that have changed.
5 *
6 * This source file contains the logic to decide what to do with different
7 * kinds of files, and the data structure to support it. Before modifying
8 * anything, pg_rewind collects information about all the files and their
9 * attributes in the target and source data directories. It also scans the
10 * WAL log in the target, and collects information about data blocks that
11 * were changed. All this information is stored in a hash table, using the
12 * file path relative to the root of the data directory as the key.
13 *
14 * After collecting all the information required, the decide_file_actions()
15 * function scans the hash table and decides what action needs to be taken
16 * for each file. Finally, it sorts the array to the final order that the
17 * actions should be executed in.
18 *
19 * Copyright (c) 2013-2025, PostgreSQL Global Development Group
20 *
21 *-------------------------------------------------------------------------
22 */
23
24#include "postgres_fe.h"
25
26#include <sys/stat.h>
27#include <unistd.h>
28
29#include "catalog/pg_tablespace_d.h"
30#include "common/file_utils.h"
32#include "common/string.h"
33#include "datapagemap.h"
34#include "filemap.h"
35#include "pg_rewind.h"
36
37/*
38 * Define a hash table which we can use to store information about the files
39 * appearing in source and target systems.
40 */
41#define SH_PREFIX filehash
42#define SH_ELEMENT_TYPE file_entry_t
43#define SH_KEY_TYPE const char *
44#define SH_KEY path
45#define SH_HASH_KEY(tb, key) hash_string(key)
46#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
47#define SH_SCOPE static inline
48#define SH_RAW_ALLOCATOR pg_malloc0
49#define SH_DECLARE
50#define SH_DEFINE
51#include "lib/simplehash.h"
52
53#define FILEHASH_INITIAL_SIZE 1000
54
55static filehash_hash *filehash;
56
57static bool isRelDataFile(const char *path);
58static char *datasegpath(RelFileLocator rlocator, ForkNumber forknum,
59 BlockNumber segno);
60
61static file_entry_t *insert_filehash_entry(const char *path);
62static file_entry_t *lookup_filehash_entry(const char *path);
63
64/*
65 * A separate hash table which tracks WAL files that must not be deleted.
66 */
67typedef struct keepwal_entry
68{
69 const char *path;
72
73#define SH_PREFIX keepwal
74#define SH_ELEMENT_TYPE keepwal_entry
75#define SH_KEY_TYPE const char *
76#define SH_KEY path
77#define SH_HASH_KEY(tb, key) hash_string(key)
78#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
79#define SH_SCOPE static inline
80#define SH_RAW_ALLOCATOR pg_malloc0
81#define SH_DECLARE
82#define SH_DEFINE
83#include "lib/simplehash.h"
84
85#define KEEPWAL_INITIAL_SIZE 1000
86
87
88static keepwal_hash *keepwal = NULL;
89static bool keepwal_entry_exists(const char *path);
90
91static int final_filemap_cmp(const void *a, const void *b);
92
93static bool check_file_excluded(const char *path, bool is_source);
94
95/*
96 * Definition of one element part of an exclusion list, used to exclude
97 * contents when rewinding. "name" is the name of the file or path to
98 * check for exclusion. If "match_prefix" is true, any items matching
99 * the name as prefix are excluded.
100 */
102{
103 const char *name;
104 bool match_prefix;
105};
106
107/*
108 * The contents of these directories are removed or recreated during server
109 * start so they are not included in data processed by pg_rewind.
110 *
111 * Note: those lists should be kept in sync with what basebackup.c provides.
112 * Some of the values, contrary to what basebackup.c uses, are hardcoded as
113 * they are defined in backend-only headers. So this list is maintained
114 * with a best effort in mind.
115 */
116static const char *const excludeDirContents[] =
117{
118 /*
119 * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped
120 * because extensions like pg_stat_statements store data there.
121 */
122 "pg_stat_tmp", /* defined as PG_STAT_TMP_DIR */
123
124 /*
125 * It is generally not useful to backup the contents of this directory
126 * even if the intention is to restore to another primary. See backup.sgml
127 * for a more detailed description.
128 */
129 "pg_replslot", /* defined as PG_REPLSLOT_DIR */
130
131 /* Contents removed on startup, see dsm_cleanup_for_mmap(). */
132 "pg_dynshmem", /* defined as PG_DYNSHMEM_DIR */
133
134 /* Contents removed on startup, see AsyncShmemInit(). */
135 "pg_notify",
136
137 /*
138 * Old contents are loaded for possible debugging but are not required for
139 * normal operation, see SerialInit().
140 */
141 "pg_serial",
142
143 /* Contents removed on startup, see DeleteAllExportedSnapshotFiles(). */
144 "pg_snapshots",
145
146 /* Contents zeroed on startup, see StartupSUBTRANS(). */
147 "pg_subtrans",
148
149 /* end of list */
150 NULL
151};
152
153/*
154 * List of files excluded from filemap processing. Files are excluded
155 * if their prefix match.
156 */
157static const struct exclude_list_item excludeFiles[] =
158{
159 /* Skip auto conf temporary file. */
160 {"postgresql.auto.conf.tmp", false}, /* defined as PG_AUTOCONF_FILENAME */
161
162 /* Skip current log file temporary file */
163 {"current_logfiles.tmp", false}, /* defined as
164 * LOG_METAINFO_DATAFILE_TMP */
165
166 /* Skip relation cache because it is rebuilt on startup */
167 {"pg_internal.init", true}, /* defined as RELCACHE_INIT_FILENAME */
168
169 /*
170 * If there is a backup_label or tablespace_map file, it indicates that a
171 * recovery failed and this cluster probably can't be rewound, but exclude
172 * them anyway if they are found.
173 */
174 {"backup_label", false}, /* defined as BACKUP_LABEL_FILE */
175 {"tablespace_map", false}, /* defined as TABLESPACE_MAP */
176
177 /*
178 * If there's a backup_manifest, it belongs to a backup that was used to
179 * start this server. It is *not* correct for this backup. Our
180 * backup_manifest is injected into the backup separately if users want
181 * it.
182 */
183 {"backup_manifest", false},
184
185 {"postmaster.pid", false},
186 {"postmaster.opts", false},
187
188 /* end of list */
189 {NULL, false}
190};
191
192/*
193 * Initialize the hash table for the file map.
194 */
195void
197{
198 filehash = filehash_create(FILEHASH_INITIAL_SIZE, NULL);
199}
200
201/* Look up entry for 'path', creating a new one if it doesn't exist */
202static file_entry_t *
203insert_filehash_entry(const char *path)
204{
205 file_entry_t *entry;
206 bool found;
207
208 entry = filehash_insert(filehash, path, &found);
209 if (!found)
210 {
211 entry->path = pg_strdup(path);
212 entry->isrelfile = isRelDataFile(path);
213
214 entry->target_exists = false;
216 entry->target_size = 0;
217 entry->target_link_target = NULL;
218 entry->target_pages_to_overwrite.bitmap = NULL;
220
221 entry->source_exists = false;
223 entry->source_size = 0;
224 entry->source_link_target = NULL;
225
227 }
228
229 return entry;
230}
231
232static file_entry_t *
233lookup_filehash_entry(const char *path)
234{
235 return filehash_lookup(filehash, path);
236}
237
238/*
239 * Initialize a hash table to store WAL file names that must be kept.
240 */
241void
243{
244 /* An initial hash size out of thin air */
245 keepwal = keepwal_create(KEEPWAL_INITIAL_SIZE, NULL);
246}
247
248/* Mark the given file to prevent its removal */
249void
250keepwal_add_entry(const char *path)
251{
252 keepwal_entry *entry;
253 bool found;
254
255 /* Should only be called with keepwal initialized */
256 Assert(keepwal != NULL);
257
258 entry = keepwal_insert(keepwal, path, &found);
259
260 if (!found)
261 entry->path = pg_strdup(path);
262}
263
264/* Return true if file is marked as not to be removed, false otherwise */
265static bool
266keepwal_entry_exists(const char *path)
267{
268 return keepwal_lookup(keepwal, path) != NULL;
269}
270
271/*
272 * Callback for processing source file list.
273 *
274 * This is called once for every file in the source server. We record the
275 * type and size of the file, so that decide_file_action() can later decide what
276 * to do with it.
277 */
278void
279process_source_file(const char *path, file_type_t type, size_t size,
280 const char *link_target)
281{
282 file_entry_t *entry;
283
284 /*
285 * Pretend that pg_wal is a directory, even if it's really a symlink. We
286 * don't want to mess with the symlink itself, nor complain if it's a
287 * symlink in source but not in target or vice versa.
288 */
289 if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
291
292 /*
293 * sanity check: a filename that looks like a data file better be a
294 * regular file
295 */
296 if (type != FILE_TYPE_REGULAR && isRelDataFile(path))
297 pg_fatal("data file \"%s\" in source is not a regular file", path);
298
299 /* Remember this source file */
300 entry = insert_filehash_entry(path);
301 if (entry->source_exists)
302 pg_fatal("duplicate source file \"%s\"", path);
303 entry->source_exists = true;
304 entry->source_type = type;
305 entry->source_size = size;
306 entry->source_link_target = link_target ? pg_strdup(link_target) : NULL;
307}
308
309/*
310 * Callback for processing target file list.
311 *
312 * Record the type and size of the file, like process_source_file() does.
313 */
314void
315process_target_file(const char *path, file_type_t type, size_t size,
316 const char *link_target)
317{
318 file_entry_t *entry;
319
320 /*
321 * Do not apply any exclusion filters here. This has advantage to remove
322 * from the target data folder all paths which have been filtered out from
323 * the source data folder when processing the source files.
324 */
325
326 /*
327 * Like in process_source_file, pretend that pg_wal is always a directory.
328 */
329 if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
331
332 /* Remember this target file */
333 entry = insert_filehash_entry(path);
334 if (entry->target_exists)
335 pg_fatal("duplicate source file \"%s\"", path);
336 entry->target_exists = true;
337 entry->target_type = type;
338 entry->target_size = size;
339 entry->target_link_target = link_target ? pg_strdup(link_target) : NULL;
340}
341
342/*
343 * This callback gets called while we read the WAL in the target, for every
344 * block that has changed in the target system. It decides if the given
345 * 'blkno' in the target relfile needs to be overwritten from the source, and
346 * if so, records it in 'target_pages_to_overwrite' bitmap.
347 *
348 * NOTE: All the files on both systems must have already been added to the
349 * hash table!
350 */
351void
353 BlockNumber blkno)
354{
355 char *path;
356 file_entry_t *entry;
357 BlockNumber blkno_inseg;
358 int segno;
359
360 segno = blkno / RELSEG_SIZE;
361 blkno_inseg = blkno % RELSEG_SIZE;
362
363 path = datasegpath(rlocator, forknum, segno);
364 entry = lookup_filehash_entry(path);
365 pfree(path);
366
367 /*
368 * If the block still exists in both systems, remember it. Otherwise we
369 * can safely ignore it.
370 *
371 * If the block is beyond the EOF in the source system, or the file
372 * doesn't exist in the source at all, we're going to truncate/remove it
373 * away from the target anyway. Likewise, if it doesn't exist in the
374 * target anymore, we will copy it over with the "tail" from the source
375 * system, anyway.
376 *
377 * It is possible to find WAL for a file that doesn't exist on either
378 * system anymore. It means that the relation was dropped later in the
379 * target system, and independently on the source system too, or that it
380 * was created and dropped in the target system and it never existed in
381 * the source. Either way, we can safely ignore it.
382 */
383 if (entry)
384 {
385 Assert(entry->isrelfile);
386
387 if (entry->target_exists)
388 {
389 if (entry->target_type != FILE_TYPE_REGULAR)
390 pg_fatal("unexpected page modification for non-regular file \"%s\"",
391 entry->path);
392
393 if (entry->source_exists)
394 {
395 off_t end_offset;
396
397 end_offset = (blkno_inseg + 1) * BLCKSZ;
398 if (end_offset <= entry->source_size && end_offset <= entry->target_size)
399 datapagemap_add(&entry->target_pages_to_overwrite, blkno_inseg);
400 }
401 }
402 }
403}
404
405/*
406 * Is this the path of file that pg_rewind can skip copying?
407 */
408static bool
409check_file_excluded(const char *path, bool is_source)
410{
411 char localpath[MAXPGPATH];
412 int excludeIdx;
413 const char *filename;
414
415 /*
416 * Skip all temporary files, .../pgsql_tmp/... and .../pgsql_tmp.*
417 */
418 if (strstr(path, "/" PG_TEMP_FILE_PREFIX) != NULL ||
419 strstr(path, "/" PG_TEMP_FILES_DIR "/") != NULL)
420 {
421 return true;
422 }
423
424 /* check individual files... */
425 for (excludeIdx = 0; excludeFiles[excludeIdx].name != NULL; excludeIdx++)
426 {
427 int cmplen = strlen(excludeFiles[excludeIdx].name);
428
430 if (filename == NULL)
431 filename = path;
432 else
433 filename++;
434
435 if (!excludeFiles[excludeIdx].match_prefix)
436 cmplen++;
437 if (strncmp(filename, excludeFiles[excludeIdx].name, cmplen) == 0)
438 {
439 if (is_source)
440 pg_log_debug("entry \"%s\" excluded from source file list",
441 path);
442 else
443 pg_log_debug("entry \"%s\" excluded from target file list",
444 path);
445 return true;
446 }
447 }
448
449 /*
450 * ... And check some directories. Note that this includes any contents
451 * within the directories themselves.
452 */
453 for (excludeIdx = 0; excludeDirContents[excludeIdx] != NULL; excludeIdx++)
454 {
455 snprintf(localpath, sizeof(localpath), "%s/",
456 excludeDirContents[excludeIdx]);
457 if (strstr(path, localpath) == path)
458 {
459 if (is_source)
460 pg_log_debug("entry \"%s\" excluded from source file list",
461 path);
462 else
463 pg_log_debug("entry \"%s\" excluded from target file list",
464 path);
465 return true;
466 }
467 }
468
469 return false;
470}
471
472static const char *
474{
475 switch (action)
476 {
477 case FILE_ACTION_NONE:
478 return "NONE";
479 case FILE_ACTION_COPY:
480 return "COPY";
482 return "TRUNCATE";
484 return "COPY_TAIL";
486 return "CREATE";
488 return "REMOVE";
489
490 default:
491 return "unknown";
492 }
493}
494
495/*
496 * Calculate the totals needed for progress reports.
497 */
498void
500{
501 file_entry_t *entry;
502 int i;
503
504 filemap->total_size = 0;
505 filemap->fetch_size = 0;
506
507 for (i = 0; i < filemap->nentries; i++)
508 {
509 entry = filemap->entries[i];
510
511 if (entry->source_type != FILE_TYPE_REGULAR)
512 continue;
513
514 filemap->total_size += entry->source_size;
515
516 if (entry->action == FILE_ACTION_COPY)
517 {
518 filemap->fetch_size += entry->source_size;
519 continue;
520 }
521
522 if (entry->action == FILE_ACTION_COPY_TAIL)
523 filemap->fetch_size += (entry->source_size - entry->target_size);
524
526 {
528 BlockNumber blk;
529
531 while (datapagemap_next(iter, &blk))
532 filemap->fetch_size += BLCKSZ;
533
534 pg_free(iter);
535 }
536 }
537}
538
539void
541{
542 file_entry_t *entry;
543 int i;
544
545 for (i = 0; i < filemap->nentries; i++)
546 {
547 entry = filemap->entries[i];
548 if (entry->action != FILE_ACTION_NONE ||
550 {
551 pg_log_debug("%s (%s)", entry->path,
552 action_to_str(entry->action));
553
556 }
557 }
558 fflush(stdout);
559}
560
561/*
562 * Does it look like a relation data file?
563 *
564 * For our purposes, only files belonging to the main fork are considered
565 * relation files. Other forks are always copied in toto, because we cannot
566 * reliably track changes to them, because WAL only contains block references
567 * for the main fork.
568 */
569static bool
570isRelDataFile(const char *path)
571{
572 RelFileLocator rlocator;
573 unsigned int segNo;
574 int nmatch;
575 bool matched;
576
577 /*----
578 * Relation data files can be in one of the following directories:
579 *
580 * global/
581 * shared relations
582 *
583 * base/<db oid>/
584 * regular relations, default tablespace
585 *
586 * pg_tblspc/<tblspc oid>/<tblspc version>/
587 * within a non-default tablespace (the name of the directory
588 * depends on version)
589 *
590 * And the relation data files themselves have a filename like:
591 *
592 * <oid>.<segment number>
593 *
594 *----
595 */
596 rlocator.spcOid = InvalidOid;
597 rlocator.dbOid = InvalidOid;
599 segNo = 0;
600 matched = false;
601
602 nmatch = sscanf(path, "global/%u.%u", &rlocator.relNumber, &segNo);
603 if (nmatch == 1 || nmatch == 2)
604 {
605 rlocator.spcOid = GLOBALTABLESPACE_OID;
606 rlocator.dbOid = 0;
607 matched = true;
608 }
609 else
610 {
611 nmatch = sscanf(path, "base/%u/%u.%u",
612 &rlocator.dbOid, &rlocator.relNumber, &segNo);
613 if (nmatch == 2 || nmatch == 3)
614 {
615 rlocator.spcOid = DEFAULTTABLESPACE_OID;
616 matched = true;
617 }
618 else
619 {
620 nmatch = sscanf(path, "pg_tblspc/%u/" TABLESPACE_VERSION_DIRECTORY "/%u/%u.%u",
621 &rlocator.spcOid, &rlocator.dbOid, &rlocator.relNumber,
622 &segNo);
623 if (nmatch == 3 || nmatch == 4)
624 matched = true;
625 }
626 }
627
628 /*
629 * The sscanf tests above can match files that have extra characters at
630 * the end. To eliminate such cases, cross-check that GetRelationPath
631 * creates the exact same filename, when passed the RelFileLocator
632 * information we extracted from the filename.
633 */
634 if (matched)
635 {
636 char *check_path = datasegpath(rlocator, MAIN_FORKNUM, segNo);
637
638 if (strcmp(check_path, path) != 0)
639 matched = false;
640
641 pfree(check_path);
642 }
643
644 return matched;
645}
646
647/*
648 * A helper function to create the path of a relation file and segment.
649 *
650 * The returned path is palloc'd
651 */
652static char *
654{
655 char *path;
656 char *segpath;
657
658 path = relpathperm(rlocator, forknum);
659 if (segno > 0)
660 {
661 segpath = psprintf("%s.%u", path, segno);
662 pfree(path);
663 return segpath;
664 }
665 else
666 return path;
667}
668
669/*
670 * In the final stage, the filemap is sorted so that removals come last.
671 * From disk space usage point of view, it would be better to do removals
672 * first, but for now, safety first. If a whole directory is deleted, all
673 * files and subdirectories inside it need to removed first. On creation,
674 * parent directory needs to be created before files and directories inside
675 * it. To achieve that, the file_action_t enum is ordered so that we can
676 * just sort on that first. Furthermore, sort REMOVE entries in reverse
677 * path order, so that "foo/bar" subdirectory is removed before "foo".
678 */
679static int
680final_filemap_cmp(const void *a, const void *b)
681{
682 file_entry_t *fa = *((file_entry_t **) a);
683 file_entry_t *fb = *((file_entry_t **) b);
684
685 if (fa->action > fb->action)
686 return 1;
687 if (fa->action < fb->action)
688 return -1;
689
690 if (fa->action == FILE_ACTION_REMOVE)
691 return strcmp(fb->path, fa->path);
692 else
693 return strcmp(fa->path, fb->path);
694}
695
696/*
697 * Decide what action to perform to a file.
698 */
699static file_action_t
701{
702 const char *path = entry->path;
703
704 /*
705 * Don't touch the control file. It is handled specially, after copying
706 * all the other files.
707 */
708 if (strcmp(path, "global/pg_control") == 0)
709 return FILE_ACTION_NONE;
710
711 /* Skip macOS system files */
712 if (strstr(path, ".DS_Store") != NULL)
713 return FILE_ACTION_NONE;
714
715 /*
716 * Remove all files matching the exclusion filters in the target.
717 */
718 if (check_file_excluded(path, true))
719 {
720 if (entry->target_exists)
721 return FILE_ACTION_REMOVE;
722 else
723 return FILE_ACTION_NONE;
724 }
725
726 /*
727 * Handle cases where the file is missing from one of the systems.
728 */
729 if (!entry->target_exists && entry->source_exists)
730 {
731 /*
732 * File exists in source, but not in target. Copy it in toto. (If it's
733 * a relation data file, WAL replay after rewinding should re-create
734 * it anyway. But there's no harm in copying it now.)
735 */
736 switch (entry->source_type)
737 {
740 return FILE_ACTION_CREATE;
742 return FILE_ACTION_COPY;
744 pg_fatal("unknown file type for \"%s\"", entry->path);
745 break;
746 }
747 }
748 else if (entry->target_exists && !entry->source_exists)
749 {
750 /*
751 * For files that exist in target but not in source, we check the
752 * keepwal hash table; any files listed therein must not be removed.
753 */
754 if (keepwal_entry_exists(path))
755 {
756 pg_log_debug("Not removing file \"%s\" because it is required for recovery", path);
757 return FILE_ACTION_NONE;
758 }
759 return FILE_ACTION_REMOVE;
760 }
761 else if (!entry->target_exists && !entry->source_exists)
762 {
763 /*
764 * Doesn't exist in either server. Why does it have an entry in the
765 * first place??
766 */
767 Assert(false);
768 return FILE_ACTION_NONE;
769 }
770
771 /*
772 * Otherwise, the file exists on both systems
773 */
774 Assert(entry->target_exists && entry->source_exists);
775
776 if (entry->source_type != entry->target_type)
777 {
778 /* But it's a different kind of object. Strange.. */
779 pg_fatal("file \"%s\" is of different type in source and target", entry->path);
780 }
781
782 /*
783 * PG_VERSION files should be identical on both systems, but avoid
784 * overwriting them for paranoia.
785 */
786 if (pg_str_endswith(entry->path, "PG_VERSION"))
787 return FILE_ACTION_NONE;
788
789 switch (entry->source_type)
790 {
792 return FILE_ACTION_NONE;
793
795
796 /*
797 * XXX: Should we check if it points to the same target?
798 */
799 return FILE_ACTION_NONE;
800
802 if (!entry->isrelfile)
803 {
804 /*
805 * It's a non-data file that we have no special processing
806 * for. Copy it in toto.
807 */
808 return FILE_ACTION_COPY;
809 }
810 else
811 {
812 /*
813 * It's a data file that exists in both systems.
814 *
815 * If it's larger in target, we can truncate it. There will
816 * also be a WAL record of the truncation in the source
817 * system, so WAL replay would eventually truncate the target
818 * too, but we might as well do it now.
819 *
820 * If it's smaller in the target, it means that it has been
821 * truncated in the target, or enlarged in the source, or
822 * both. If it was truncated in the target, we need to copy
823 * the missing tail from the source system. If it was enlarged
824 * in the source system, there will be WAL records in the
825 * source system for the new blocks, so we wouldn't need to
826 * copy them here. But we don't know which scenario we're
827 * dealing with, and there's no harm in copying the missing
828 * blocks now, so do it now.
829 *
830 * If it's the same size, do nothing here. Any blocks modified
831 * in the target will be copied based on parsing the target
832 * system's WAL, and any blocks modified in the source will be
833 * updated after rewinding, when the source system's WAL is
834 * replayed.
835 */
836 if (entry->target_size < entry->source_size)
838 else if (entry->target_size > entry->source_size)
840 else
841 return FILE_ACTION_NONE;
842 }
843 break;
844
846 pg_fatal("unknown file type for \"%s\"", path);
847 break;
848 }
849
850 /* unreachable */
851 pg_fatal("could not decide what to do with file \"%s\"", path);
852}
853
854/*
855 * Decide what to do with each file.
856 *
857 * Returns a 'filemap' with the entries in the order that their actions
858 * should be executed.
859 */
860filemap_t *
862{
863 int i;
864 filehash_iterator it;
865 file_entry_t *entry;
866 filemap_t *filemap;
867
868 filehash_start_iterate(filehash, &it);
869 while ((entry = filehash_iterate(filehash, &it)) != NULL)
870 {
871 entry->action = decide_file_action(entry);
872 }
873
874 /*
875 * Turn the hash table into an array, and sort in the order that the
876 * actions should be performed.
877 */
878 filemap = pg_malloc(offsetof(filemap_t, entries) +
879 filehash->members * sizeof(file_entry_t *));
880 filemap->nentries = filehash->members;
881 filehash_start_iterate(filehash, &it);
882 i = 0;
883 while ((entry = filehash_iterate(filehash, &it)) != NULL)
884 {
885 filemap->entries[i++] = entry;
886 }
887
888 qsort(&filemap->entries, filemap->nentries, sizeof(file_entry_t *),
890
891 return filemap;
892}
uint32 BlockNumber
Definition: block.h:31
#define Assert(condition)
Definition: c.h:815
uint32_t uint32
Definition: c.h:488
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
Definition: datapagemap.c:87
void datapagemap_print(datapagemap_t *map)
Definition: datapagemap.c:117
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
Definition: datapagemap.c:75
void datapagemap_add(datapagemap_t *map, BlockNumber blkno)
Definition: datapagemap.c:32
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
#define PG_TEMP_FILES_DIR
Definition: file_utils.h:62
#define PG_TEMP_FILE_PREFIX
Definition: file_utils.h:63
static const struct exclude_list_item excludeFiles[]
Definition: filemap.c:157
struct keepwal_entry keepwal_entry
static bool isRelDataFile(const char *path)
Definition: filemap.c:570
void filehash_init(void)
Definition: filemap.c:196
static bool keepwal_entry_exists(const char *path)
Definition: filemap.c:266
static const char * action_to_str(file_action_t action)
Definition: filemap.c:473
void process_source_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:279
static const char *const excludeDirContents[]
Definition: filemap.c:116
void print_filemap(filemap_t *filemap)
Definition: filemap.c:540
static char * datasegpath(RelFileLocator rlocator, ForkNumber forknum, BlockNumber segno)
Definition: filemap.c:653
void keepwal_init(void)
Definition: filemap.c:242
static filehash_hash * filehash
Definition: filemap.c:55
void process_target_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:315
filemap_t * decide_file_actions(void)
Definition: filemap.c:861
void process_target_wal_block_change(ForkNumber forknum, RelFileLocator rlocator, BlockNumber blkno)
Definition: filemap.c:352
void keepwal_add_entry(const char *path)
Definition: filemap.c:250
static keepwal_hash * keepwal
Definition: filemap.c:88
static file_action_t decide_file_action(file_entry_t *entry)
Definition: filemap.c:700
static file_entry_t * lookup_filehash_entry(const char *path)
Definition: filemap.c:233
#define KEEPWAL_INITIAL_SIZE
Definition: filemap.c:85
static file_entry_t * insert_filehash_entry(const char *path)
Definition: filemap.c:203
static bool check_file_excluded(const char *path, bool is_source)
Definition: filemap.c:409
#define FILEHASH_INITIAL_SIZE
Definition: filemap.c:53
static int final_filemap_cmp(const void *a, const void *b)
Definition: filemap.c:680
void calculate_totals(filemap_t *filemap)
Definition: filemap.c:499
file_action_t
Definition: filemap.h:17
@ FILE_ACTION_REMOVE
Definition: filemap.h:27
@ FILE_ACTION_COPY
Definition: filemap.h:21
@ FILE_ACTION_NONE
Definition: filemap.h:24
@ FILE_ACTION_COPY_TAIL
Definition: filemap.h:22
@ FILE_ACTION_UNDECIDED
Definition: filemap.h:18
@ FILE_ACTION_TRUNCATE
Definition: filemap.h:26
@ FILE_ACTION_CREATE
Definition: filemap.h:20
file_type_t
Definition: filemap.h:31
@ FILE_TYPE_UNDEFINED
Definition: filemap.h:32
@ FILE_TYPE_REGULAR
Definition: filemap.h:34
@ FILE_TYPE_SYMLINK
Definition: filemap.h:36
@ FILE_TYPE_DIRECTORY
Definition: filemap.h:35
int b
Definition: isn.c:69
int a
Definition: isn.c:68
int i
Definition: isn.c:72
static void const char fflush(stdout)
#define pg_log_debug(...)
Definition: logging.h:133
void pfree(void *pointer)
Definition: mcxt.c:1521
#define pg_fatal(...)
#define MAXPGPATH
static char * filename
Definition: pg_dumpall.c:119
char * last_dir_separator(const char *filename)
Definition: path.c:140
#define snprintf
Definition: port.h:238
#define qsort(a, b, c, d)
Definition: port.h:474
#define InvalidOid
Definition: postgres_ext.h:37
static int fa(void)
Definition: preproc-init.c:85
static int fb(int x)
Definition: preproc-init.c:92
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
#define InvalidRelFileNumber
Definition: relpath.h:26
#define relpathperm(rlocator, forknum)
Definition: relpath.h:98
#define TABLESPACE_VERSION_DIRECTORY
Definition: relpath.h:33
static pg_noinline void Size size
Definition: slab.c:607
bool pg_str_endswith(const char *str, const char *end)
Definition: string.c:31
RelFileNumber relNumber
int bitmapsize
Definition: datapagemap.h:17
char * bitmap
Definition: datapagemap.h:16
const char * name
Definition: basebackup.c:139
Definition: filemap.h:50
datapagemap_t target_pages_to_overwrite
Definition: filemap.h:68
const char * path
Definition: filemap.h:53
size_t source_size
Definition: filemap.h:75
bool source_exists
Definition: filemap.h:73
bool target_exists
Definition: filemap.h:59
char * source_link_target
Definition: filemap.h:76
file_type_t source_type
Definition: filemap.h:74
char * target_link_target
Definition: filemap.h:62
size_t target_size
Definition: filemap.h:61
file_action_t action
Definition: filemap.h:81
file_type_t target_type
Definition: filemap.h:60
bool isrelfile
Definition: filemap.h:54
file_entry_t * entries[FLEXIBLE_ARRAY_MEMBER]
Definition: filemap.h:96
int nentries
Definition: filemap.h:95
uint64 total_size
Definition: filemap.h:92
uint64 fetch_size
Definition: filemap.h:93
Definition: filemap.c:68
const char * path
Definition: filemap.c:69
uint32 status
Definition: filemap.c:70
const char * type
const char * name