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  * Copyright (c) 2013-2019, PostgreSQL Global Development Group
7  *
8  *-------------------------------------------------------------------------
9  */
10 
11 #include "postgres_fe.h"
12 
13 #include <sys/stat.h>
14 #include <unistd.h>
15 
16 #include "catalog/pg_tablespace_d.h"
17 #include "common/string.h"
18 #include "datapagemap.h"
19 #include "filemap.h"
20 #include "pg_rewind.h"
21 #include "storage/fd.h"
22 
24 
25 static bool isRelDataFile(const char *path);
26 static char *datasegpath(RelFileNode rnode, ForkNumber forknum,
27  BlockNumber segno);
28 static int path_cmp(const void *a, const void *b);
29 static int final_filemap_cmp(const void *a, const void *b);
30 static void filemap_list_to_array(filemap_t *map);
31 static bool check_file_excluded(const char *path, bool is_source);
32 
33 /*
34  * The contents of these directories are removed or recreated during server
35  * start so they are not included in data processed by pg_rewind.
36  *
37  * Note: those lists should be kept in sync with what basebackup.c provides.
38  * Some of the values, contrary to what basebackup.c uses, are hardcoded as
39  * they are defined in backend-only headers. So this list is maintained
40  * with a best effort in mind.
41  */
42 static const char *excludeDirContents[] =
43 {
44  /*
45  * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even
46  * when stats_temp_directory is set because PGSS_TEXT_FILE is always
47  * created there.
48  */
49  "pg_stat_tmp", /* defined as PG_STAT_TMP_DIR */
50 
51  /*
52  * It is generally not useful to backup the contents of this directory
53  * even if the intention is to restore to another master. See backup.sgml
54  * for a more detailed description.
55  */
56  "pg_replslot",
57 
58  /* Contents removed on startup, see dsm_cleanup_for_mmap(). */
59  "pg_dynshmem", /* defined as PG_DYNSHMEM_DIR */
60 
61  /* Contents removed on startup, see AsyncShmemInit(). */
62  "pg_notify",
63 
64  /*
65  * Old contents are loaded for possible debugging but are not required for
66  * normal operation, see OldSerXidInit().
67  */
68  "pg_serial",
69 
70  /* Contents removed on startup, see DeleteAllExportedSnapshotFiles(). */
71  "pg_snapshots",
72 
73  /* Contents zeroed on startup, see StartupSUBTRANS(). */
74  "pg_subtrans",
75 
76  /* end of list */
77  NULL
78 };
79 
80 /*
81  * List of files excluded from filemap processing.
82  */
83 static const char *excludeFiles[] =
84 {
85  /* Skip auto conf temporary file. */
86  "postgresql.auto.conf.tmp", /* defined as PG_AUTOCONF_FILENAME */
87 
88  /* Skip current log file temporary file */
89  "current_logfiles.tmp", /* defined as LOG_METAINFO_DATAFILE_TMP */
90 
91  /* Skip relation cache because it is rebuilt on startup */
92  "pg_internal.init", /* defined as RELCACHE_INIT_FILENAME */
93 
94  /*
95  * If there's a backup_label or tablespace_map file, it belongs to a
96  * backup started by the user with pg_start_backup(). It is *not* correct
97  * for this backup. Our backup_label is written later on separately.
98  */
99  "backup_label", /* defined as BACKUP_LABEL_FILE */
100  "tablespace_map", /* defined as TABLESPACE_MAP */
101 
102  "postmaster.pid",
103  "postmaster.opts",
104 
105  /* end of list */
106  NULL
107 };
108 
109 /*
110  * Create a new file map (stored in the global pointer "filemap").
111  */
112 void
114 {
115  filemap_t *map;
116 
117  map = pg_malloc(sizeof(filemap_t));
118  map->first = map->last = NULL;
119  map->nlist = 0;
120  map->array = NULL;
121  map->narray = 0;
122 
123  Assert(filemap == NULL);
124  filemap = map;
125 }
126 
127 /*
128  * Callback for processing source file list.
129  *
130  * This is called once for every file in the source server. We decide what
131  * action needs to be taken for the file, depending on whether the file
132  * exists in the target and whether the size matches.
133  */
134 void
135 process_source_file(const char *path, file_type_t type, size_t newsize,
136  const char *link_target)
137 {
138  bool exists;
139  char localpath[MAXPGPATH];
140  struct stat statbuf;
141  filemap_t *map = filemap;
143  size_t oldsize = 0;
144  file_entry_t *entry;
145 
146  Assert(map->array == NULL);
147 
148  /*
149  * Skip any files matching the exclusion filters. This has the effect to
150  * remove all those files on the target.
151  */
152  if (check_file_excluded(path, true))
153  return;
154 
155  /*
156  * Pretend that pg_wal is a directory, even if it's really a symlink. We
157  * don't want to mess with the symlink itself, nor complain if it's a
158  * symlink in source but not in target or vice versa.
159  */
160  if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
161  type = FILE_TYPE_DIRECTORY;
162 
163  /*
164  * Skip temporary files, .../pgsql_tmp/... and .../pgsql_tmp.* in source.
165  * This has the effect that all temporary files in the destination will be
166  * removed.
167  */
168  if (strstr(path, "/" PG_TEMP_FILE_PREFIX) != NULL)
169  return;
170  if (strstr(path, "/" PG_TEMP_FILES_DIR "/") != NULL)
171  return;
172 
173  /*
174  * sanity check: a filename that looks like a data file better be a
175  * regular file
176  */
177  if (type != FILE_TYPE_REGULAR && isRelDataFile(path))
178  pg_fatal("data file \"%s\" in source is not a regular file", path);
179 
180  snprintf(localpath, sizeof(localpath), "%s/%s", datadir_target, path);
181 
182  /* Does the corresponding file exist in the target data dir? */
183  if (lstat(localpath, &statbuf) < 0)
184  {
185  if (errno != ENOENT)
186  pg_fatal("could not stat file \"%s\": %m",
187  localpath);
188 
189  exists = false;
190  }
191  else
192  exists = true;
193 
194  switch (type)
195  {
196  case FILE_TYPE_DIRECTORY:
197  if (exists && !S_ISDIR(statbuf.st_mode) && strcmp(path, "pg_wal") != 0)
198  {
199  /* it's a directory in source, but not in target. Strange.. */
200  pg_fatal("\"%s\" is not a directory", localpath);
201  }
202 
203  if (!exists)
204  action = FILE_ACTION_CREATE;
205  else
206  action = FILE_ACTION_NONE;
207  oldsize = 0;
208  break;
209 
210  case FILE_TYPE_SYMLINK:
211  if (exists &&
212 #ifndef WIN32
213  !S_ISLNK(statbuf.st_mode)
214 #else
215  !pgwin32_is_junction(localpath)
216 #endif
217  )
218  {
219  /*
220  * It's a symbolic link in source, but not in target.
221  * Strange..
222  */
223  pg_fatal("\"%s\" is not a symbolic link", localpath);
224  }
225 
226  if (!exists)
227  action = FILE_ACTION_CREATE;
228  else
229  action = FILE_ACTION_NONE;
230  oldsize = 0;
231  break;
232 
233  case FILE_TYPE_REGULAR:
234  if (exists && !S_ISREG(statbuf.st_mode))
235  pg_fatal("\"%s\" is not a regular file", localpath);
236 
237  if (!exists || !isRelDataFile(path))
238  {
239  /*
240  * File exists in source, but not in target. Or it's a
241  * non-data file that we have no special processing for. Copy
242  * it in toto.
243  *
244  * An exception: PG_VERSIONs should be identical, but avoid
245  * overwriting it for paranoia.
246  */
247  if (pg_str_endswith(path, "PG_VERSION"))
248  {
249  action = FILE_ACTION_NONE;
250  oldsize = statbuf.st_size;
251  }
252  else
253  {
254  action = FILE_ACTION_COPY;
255  oldsize = 0;
256  }
257  }
258  else
259  {
260  /*
261  * It's a data file that exists in both.
262  *
263  * If it's larger in target, we can truncate it. There will
264  * also be a WAL record of the truncation in the source
265  * system, so WAL replay would eventually truncate the target
266  * too, but we might as well do it now.
267  *
268  * If it's smaller in the target, it means that it has been
269  * truncated in the target, or enlarged in the source, or
270  * both. If it was truncated in the target, we need to copy
271  * the missing tail from the source system. If it was enlarged
272  * in the source system, there will be WAL records in the
273  * source system for the new blocks, so we wouldn't need to
274  * copy them here. But we don't know which scenario we're
275  * dealing with, and there's no harm in copying the missing
276  * blocks now, so do it now.
277  *
278  * If it's the same size, do nothing here. Any blocks modified
279  * in the target will be copied based on parsing the target
280  * system's WAL, and any blocks modified in the source will be
281  * updated after rewinding, when the source system's WAL is
282  * replayed.
283  */
284  oldsize = statbuf.st_size;
285  if (oldsize < newsize)
286  action = FILE_ACTION_COPY_TAIL;
287  else if (oldsize > newsize)
288  action = FILE_ACTION_TRUNCATE;
289  else
290  action = FILE_ACTION_NONE;
291  }
292  break;
293  }
294 
295  /* Create a new entry for this file */
296  entry = pg_malloc(sizeof(file_entry_t));
297  entry->path = pg_strdup(path);
298  entry->type = type;
299  entry->action = action;
300  entry->oldsize = oldsize;
301  entry->newsize = newsize;
302  entry->link_target = link_target ? pg_strdup(link_target) : NULL;
303  entry->next = NULL;
304  entry->pagemap.bitmap = NULL;
305  entry->pagemap.bitmapsize = 0;
306  entry->isrelfile = isRelDataFile(path);
307 
308  if (map->last)
309  {
310  map->last->next = entry;
311  map->last = entry;
312  }
313  else
314  map->first = map->last = entry;
315  map->nlist++;
316 }
317 
318 /*
319  * Callback for processing target file list.
320  *
321  * All source files must be already processed before calling this. This only
322  * marks target data directory's files that didn't exist in the source for
323  * deletion.
324  */
325 void
326 process_target_file(const char *path, file_type_t type, size_t oldsize,
327  const char *link_target)
328 {
329  bool exists;
330  char localpath[MAXPGPATH];
331  struct stat statbuf;
333  file_entry_t *key_ptr;
334  filemap_t *map = filemap;
335  file_entry_t *entry;
336 
337  /*
338  * Do not apply any exclusion filters here. This has advantage to remove
339  * from the target data folder all paths which have been filtered out from
340  * the source data folder when processing the source files.
341  */
342 
343  snprintf(localpath, sizeof(localpath), "%s/%s", datadir_target, path);
344  if (lstat(localpath, &statbuf) < 0)
345  {
346  if (errno != ENOENT)
347  pg_fatal("could not stat file \"%s\": %m",
348  localpath);
349 
350  exists = false;
351  }
352 
353  if (map->array == NULL)
354  {
355  /* on first call, initialize lookup array */
356  if (map->nlist == 0)
357  {
358  /* should not happen */
359  pg_fatal("source file list is empty");
360  }
361 
363 
364  Assert(map->array != NULL);
365 
366  qsort(map->array, map->narray, sizeof(file_entry_t *), path_cmp);
367  }
368 
369  /*
370  * Like in process_source_file, pretend that xlog is always a directory.
371  */
372  if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
373  type = FILE_TYPE_DIRECTORY;
374 
375  key.path = (char *) path;
376  key_ptr = &key;
377  exists = (bsearch(&key_ptr, map->array, map->narray, sizeof(file_entry_t *),
378  path_cmp) != NULL);
379 
380  /* Remove any file or folder that doesn't exist in the source system. */
381  if (!exists)
382  {
383  entry = pg_malloc(sizeof(file_entry_t));
384  entry->path = pg_strdup(path);
385  entry->type = type;
386  entry->action = FILE_ACTION_REMOVE;
387  entry->oldsize = oldsize;
388  entry->newsize = 0;
389  entry->link_target = link_target ? pg_strdup(link_target) : NULL;
390  entry->next = NULL;
391  entry->pagemap.bitmap = NULL;
392  entry->pagemap.bitmapsize = 0;
393  entry->isrelfile = isRelDataFile(path);
394 
395  if (map->last == NULL)
396  map->first = entry;
397  else
398  map->last->next = entry;
399  map->last = entry;
400  map->nlist++;
401  }
402  else
403  {
404  /*
405  * We already handled all files that exist in the source system in
406  * process_source_file().
407  */
408  }
409 }
410 
411 /*
412  * This callback gets called while we read the WAL in the target, for every
413  * block that have changed in the target system. It makes note of all the
414  * changed blocks in the pagemap of the file.
415  */
416 void
418 {
419  char *path;
421  file_entry_t *key_ptr;
422  file_entry_t *entry;
423  BlockNumber blkno_inseg;
424  int segno;
425  filemap_t *map = filemap;
426  file_entry_t **e;
427 
428  Assert(map->array);
429 
430  segno = blkno / RELSEG_SIZE;
431  blkno_inseg = blkno % RELSEG_SIZE;
432 
433  path = datasegpath(rnode, forknum, segno);
434 
435  key.path = (char *) path;
436  key_ptr = &key;
437 
438  e = bsearch(&key_ptr, map->array, map->narray, sizeof(file_entry_t *),
439  path_cmp);
440  if (e)
441  entry = *e;
442  else
443  entry = NULL;
444  pfree(path);
445 
446  if (entry)
447  {
448  Assert(entry->isrelfile);
449 
450  switch (entry->action)
451  {
452  case FILE_ACTION_NONE:
454  /* skip if we're truncating away the modified block anyway */
455  if ((blkno_inseg + 1) * BLCKSZ <= entry->newsize)
456  datapagemap_add(&entry->pagemap, blkno_inseg);
457  break;
458 
460 
461  /*
462  * skip the modified block if it is part of the "tail" that
463  * we're copying anyway.
464  */
465  if ((blkno_inseg + 1) * BLCKSZ <= entry->oldsize)
466  datapagemap_add(&entry->pagemap, blkno_inseg);
467  break;
468 
469  case FILE_ACTION_COPY:
470  case FILE_ACTION_REMOVE:
471  break;
472 
473  case FILE_ACTION_CREATE:
474  pg_fatal("unexpected page modification for directory or symbolic link \"%s\"", entry->path);
475  }
476  }
477  else
478  {
479  /*
480  * If we don't have any record of this file in the file map, it means
481  * that it's a relation that doesn't exist in the source system, and
482  * it was subsequently removed in the target system, too. We can
483  * safely ignore it.
484  */
485  }
486 }
487 
488 /*
489  * Is this the path of file that pg_rewind can skip copying?
490  */
491 static bool
492 check_file_excluded(const char *path, bool is_source)
493 {
494  char localpath[MAXPGPATH];
495  int excludeIdx;
496  const char *filename;
497 
498  /* check individual files... */
499  for (excludeIdx = 0; excludeFiles[excludeIdx] != NULL; excludeIdx++)
500  {
501  filename = last_dir_separator(path);
502  if (filename == NULL)
503  filename = path;
504  else
505  filename++;
506  if (strcmp(filename, excludeFiles[excludeIdx]) == 0)
507  {
508  if (is_source)
509  pg_log_debug("entry \"%s\" excluded from source file list",
510  path);
511  else
512  pg_log_debug("entry \"%s\" excluded from target file list",
513  path);
514  return true;
515  }
516  }
517 
518  /*
519  * ... And check some directories. Note that this includes any contents
520  * within the directories themselves.
521  */
522  for (excludeIdx = 0; excludeDirContents[excludeIdx] != NULL; excludeIdx++)
523  {
524  snprintf(localpath, sizeof(localpath), "%s/",
525  excludeDirContents[excludeIdx]);
526  if (strstr(path, localpath) == path)
527  {
528  if (is_source)
529  pg_log_debug("entry \"%s\" excluded from source file list",
530  path);
531  else
532  pg_log_debug("entry \"%s\" excluded from target file list",
533  path);
534  return true;
535  }
536  }
537 
538  return false;
539 }
540 
541 /*
542  * Convert the linked list of entries in map->first/last to the array,
543  * map->array.
544  */
545 static void
547 {
548  int narray;
549  file_entry_t *entry,
550  *next;
551 
552  map->array = (file_entry_t **)
553  pg_realloc(map->array,
554  (map->nlist + map->narray) * sizeof(file_entry_t *));
555 
556  narray = map->narray;
557  for (entry = map->first; entry != NULL; entry = next)
558  {
559  map->array[narray++] = entry;
560  next = entry->next;
561  entry->next = NULL;
562  }
563  Assert(narray == map->nlist + map->narray);
564  map->narray = narray;
565  map->nlist = 0;
566  map->first = map->last = NULL;
567 }
568 
569 void
571 {
572  filemap_t *map = filemap;
573 
575  qsort(map->array, map->narray, sizeof(file_entry_t *),
577 }
578 
579 static const char *
581 {
582  switch (action)
583  {
584  case FILE_ACTION_NONE:
585  return "NONE";
586  case FILE_ACTION_COPY:
587  return "COPY";
589  return "TRUNCATE";
591  return "COPY_TAIL";
592  case FILE_ACTION_CREATE:
593  return "CREATE";
594  case FILE_ACTION_REMOVE:
595  return "REMOVE";
596 
597  default:
598  return "unknown";
599  }
600 }
601 
602 /*
603  * Calculate the totals needed for progress reports.
604  */
605 void
607 {
608  file_entry_t *entry;
609  int i;
610  filemap_t *map = filemap;
611 
612  map->total_size = 0;
613  map->fetch_size = 0;
614 
615  for (i = 0; i < map->narray; i++)
616  {
617  entry = map->array[i];
618 
619  if (entry->type != FILE_TYPE_REGULAR)
620  continue;
621 
622  map->total_size += entry->newsize;
623 
624  if (entry->action == FILE_ACTION_COPY)
625  {
626  map->fetch_size += entry->newsize;
627  continue;
628  }
629 
630  if (entry->action == FILE_ACTION_COPY_TAIL)
631  map->fetch_size += (entry->newsize - entry->oldsize);
632 
633  if (entry->pagemap.bitmapsize > 0)
634  {
636  BlockNumber blk;
637 
638  iter = datapagemap_iterate(&entry->pagemap);
639  while (datapagemap_next(iter, &blk))
640  map->fetch_size += BLCKSZ;
641 
642  pg_free(iter);
643  }
644  }
645 }
646 
647 void
649 {
650  filemap_t *map = filemap;
651  file_entry_t *entry;
652  int i;
653 
654  for (i = 0; i < map->narray; i++)
655  {
656  entry = map->array[i];
657  if (entry->action != FILE_ACTION_NONE ||
658  entry->pagemap.bitmapsize > 0)
659  {
660  pg_log_debug("%s (%s)", entry->path,
661  action_to_str(entry->action));
662 
663  if (entry->pagemap.bitmapsize > 0)
664  datapagemap_print(&entry->pagemap);
665  }
666  }
667  fflush(stdout);
668 }
669 
670 /*
671  * Does it look like a relation data file?
672  *
673  * For our purposes, only files belonging to the main fork are considered
674  * relation files. Other forks are always copied in toto, because we cannot
675  * reliably track changes to them, because WAL only contains block references
676  * for the main fork.
677  */
678 static bool
679 isRelDataFile(const char *path)
680 {
681  RelFileNode rnode;
682  unsigned int segNo;
683  int nmatch;
684  bool matched;
685 
686  /*----
687  * Relation data files can be in one of the following directories:
688  *
689  * global/
690  * shared relations
691  *
692  * base/<db oid>/
693  * regular relations, default tablespace
694  *
695  * pg_tblspc/<tblspc oid>/<tblspc version>/
696  * within a non-default tablespace (the name of the directory
697  * depends on version)
698  *
699  * And the relation data files themselves have a filename like:
700  *
701  * <oid>.<segment number>
702  *
703  *----
704  */
705  rnode.spcNode = InvalidOid;
706  rnode.dbNode = InvalidOid;
707  rnode.relNode = InvalidOid;
708  segNo = 0;
709  matched = false;
710 
711  nmatch = sscanf(path, "global/%u.%u", &rnode.relNode, &segNo);
712  if (nmatch == 1 || nmatch == 2)
713  {
714  rnode.spcNode = GLOBALTABLESPACE_OID;
715  rnode.dbNode = 0;
716  matched = true;
717  }
718  else
719  {
720  nmatch = sscanf(path, "base/%u/%u.%u",
721  &rnode.dbNode, &rnode.relNode, &segNo);
722  if (nmatch == 2 || nmatch == 3)
723  {
724  rnode.spcNode = DEFAULTTABLESPACE_OID;
725  matched = true;
726  }
727  else
728  {
729  nmatch = sscanf(path, "pg_tblspc/%u/" TABLESPACE_VERSION_DIRECTORY "/%u/%u.%u",
730  &rnode.spcNode, &rnode.dbNode, &rnode.relNode,
731  &segNo);
732  if (nmatch == 3 || nmatch == 4)
733  matched = true;
734  }
735  }
736 
737  /*
738  * The sscanf tests above can match files that have extra characters at
739  * the end. To eliminate such cases, cross-check that GetRelationPath
740  * creates the exact same filename, when passed the RelFileNode
741  * information we extracted from the filename.
742  */
743  if (matched)
744  {
745  char *check_path = datasegpath(rnode, MAIN_FORKNUM, segNo);
746 
747  if (strcmp(check_path, path) != 0)
748  matched = false;
749 
750  pfree(check_path);
751  }
752 
753  return matched;
754 }
755 
756 /*
757  * A helper function to create the path of a relation file and segment.
758  *
759  * The returned path is palloc'd
760  */
761 static char *
763 {
764  char *path;
765  char *segpath;
766 
767  path = relpathperm(rnode, forknum);
768  if (segno > 0)
769  {
770  segpath = psprintf("%s.%u", path, segno);
771  pfree(path);
772  return segpath;
773  }
774  else
775  return path;
776 }
777 
778 static int
779 path_cmp(const void *a, const void *b)
780 {
781  file_entry_t *fa = *((file_entry_t **) a);
782  file_entry_t *fb = *((file_entry_t **) b);
783 
784  return strcmp(fa->path, fb->path);
785 }
786 
787 /*
788  * In the final stage, the filemap is sorted so that removals come last.
789  * From disk space usage point of view, it would be better to do removals
790  * first, but for now, safety first. If a whole directory is deleted, all
791  * files and subdirectories inside it need to removed first. On creation,
792  * parent directory needs to be created before files and directories inside
793  * it. To achieve that, the file_action_t enum is ordered so that we can
794  * just sort on that first. Furthermore, sort REMOVE entries in reverse
795  * path order, so that "foo/bar" subdirectory is removed before "foo".
796  */
797 static int
798 final_filemap_cmp(const void *a, const void *b)
799 {
800  file_entry_t *fa = *((file_entry_t **) a);
801  file_entry_t *fb = *((file_entry_t **) b);
802 
803  if (fa->action > fb->action)
804  return 1;
805  if (fa->action < fb->action)
806  return -1;
807 
808  if (fa->action == FILE_ACTION_REMOVE)
809  return strcmp(fb->path, fa->path);
810  else
811  return strcmp(fa->path, fb->path);
812 }
void datapagemap_add(datapagemap_t *map, BlockNumber blkno)
Definition: datapagemap.c:32
void calculate_totals(void)
Definition: filemap.c:606
static int final_filemap_cmp(const void *a, const void *b)
Definition: filemap.c:798
char * datadir_target
Definition: pg_rewind.c:53
#define relpathperm(rnode, forknum)
Definition: relpath.h:83
static int fa(void)
Definition: preproc-init.c:85
static int32 next
Definition: blutils.c:213
file_entry_t * first
Definition: filemap.h:67
bool pg_str_endswith(const char *str, const char *end)
Definition: string.c:31
file_entry_t ** array
Definition: filemap.h:78
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void process_target_file(const char *path, file_type_t type, size_t oldsize, const char *link_target)
Definition: filemap.c:326
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
size_t newsize
Definition: filemap.h:50
#define PG_TEMP_FILES_DIR
Definition: pg_checksums.c:58
#define pg_fatal(...)
Definition: pg_rewind.h:41
char * bitmap
Definition: datapagemap.h:17
uint32 BlockNumber
Definition: block.h:31
static bool check_file_excluded(const char *path, bool is_source)
Definition: filemap.c:492
void filemap_create(void)
Definition: filemap.c:113
static int fb(int x)
Definition: preproc-init.c:92
datapagemap_t pagemap
Definition: filemap.h:53
int narray
Definition: filemap.h:79
void filemap_finalize(void)
Definition: filemap.c:570
uint64 fetch_size
Definition: filemap.h:86
void process_block_change(ForkNumber forknum, RelFileNode rnode, BlockNumber blkno)
Definition: filemap.c:417
file_type_t type
Definition: filemap.h:44
static char * datasegpath(RelFileNode rnode, ForkNumber forknum, BlockNumber segno)
Definition: filemap.c:762
bool isrelfile
Definition: filemap.h:51
void pfree(void *pointer)
Definition: mcxt.c:1056
#define PG_TEMP_FILE_PREFIX
Definition: pg_checksums.c:59
int nlist
Definition: filemap.h:69
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
Definition: datapagemap.c:87
#define pg_log_debug(...)
Definition: logging.h:91
#define MAXPGPATH
file_action_t action
Definition: filemap.h:46
char * link_target
Definition: filemap.h:56
#define TABLESPACE_VERSION_DIRECTORY
Definition: relpath.h:26
struct file_entry_t * next
Definition: filemap.h:58
filemap_t * filemap
Definition: filemap.c:23
static const char * action_to_str(file_action_t action)
Definition: filemap.c:580
size_t oldsize
Definition: filemap.h:49
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
uint64 total_size
Definition: filemap.h:85
void * pg_realloc(void *ptr, size_t size)
Definition: fe_memutils.c:65
void print_filemap(void)
Definition: filemap.c:648
static const char * excludeFiles[]
Definition: filemap.c:83
ForkNumber
Definition: relpath.h:40
#define S_ISREG(m)
Definition: win32_port.h:299
#define stat(a, b)
Definition: win32_port.h:255
char * last_dir_separator(const char *filename)
Definition: path.c:138
void datapagemap_print(datapagemap_t *map)
Definition: datapagemap.c:117
int bitmapsize
Definition: datapagemap.h:18
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:739
file_action_t
Definition: filemap.h:23
static bool isRelDataFile(const char *path)
Definition: filemap.c:679
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
Definition: datapagemap.c:75
char * path
Definition: filemap.h:43
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void process_source_file(const char *path, file_type_t type, size_t newsize, const char *link_target)
Definition: filemap.c:135
static void filemap_list_to_array(filemap_t *map)
Definition: filemap.c:546
#define S_ISDIR(m)
Definition: win32_port.h:296
#define lstat(path, sb)
Definition: win32_port.h:244
file_entry_t * last
Definition: filemap.h:68
static char * filename
Definition: pg_dumpall.c:90
e
Definition: preproc-init.c:82
int i
Definition: filemap.h:41
static int path_cmp(const void *a, const void *b)
Definition: filemap.c:779
#define qsort(a, b, c, d)
Definition: port.h:488
file_type_t
Definition: filemap.h:34
static const char * excludeDirContents[]
Definition: filemap.c:42
#define snprintf
Definition: port.h:192
bool pgwin32_is_junction(const char *path)