LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - filemap.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 207 255 81.2 %
Date: 2019-09-19 02:07:14 Functions: 13 14 92.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13