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

Generated by: LCOV version 1.13