LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - copy_fetch.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 78 90 86.7 %
Date: 2019-09-22 07:07:17 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * copy_fetch.c
       4             :  *    Functions for using a data directory as the source.
       5             :  *
       6             :  * Portions Copyright (c) 2013-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  *-------------------------------------------------------------------------
       9             :  */
      10             : #include "postgres_fe.h"
      11             : 
      12             : #include <sys/stat.h>
      13             : #include <dirent.h>
      14             : #include <fcntl.h>
      15             : #include <unistd.h>
      16             : 
      17             : #include "datapagemap.h"
      18             : #include "fetch.h"
      19             : #include "file_ops.h"
      20             : #include "filemap.h"
      21             : #include "pg_rewind.h"
      22             : 
      23             : static void recurse_dir(const char *datadir, const char *path,
      24             :                         process_file_callback_t callback);
      25             : 
      26             : static void execute_pagemap(datapagemap_t *pagemap, const char *path);
      27             : 
      28             : /*
      29             :  * Traverse through all files in a data directory, calling 'callback'
      30             :  * for each file.
      31             :  */
      32             : void
      33          24 : traverse_datadir(const char *datadir, process_file_callback_t callback)
      34             : {
      35          24 :     recurse_dir(datadir, NULL, callback);
      36          24 : }
      37             : 
      38             : /*
      39             :  * recursive part of traverse_datadir
      40             :  *
      41             :  * parentpath is the current subdirectory's path relative to datadir,
      42             :  * or NULL at the top level.
      43             :  */
      44             : static void
      45         666 : recurse_dir(const char *datadir, const char *parentpath,
      46             :             process_file_callback_t callback)
      47             : {
      48             :     DIR        *xldir;
      49             :     struct dirent *xlde;
      50             :     char        fullparentpath[MAXPGPATH];
      51             : 
      52         666 :     if (parentpath)
      53         642 :         snprintf(fullparentpath, MAXPGPATH, "%s/%s", datadir, parentpath);
      54             :     else
      55          24 :         snprintf(fullparentpath, MAXPGPATH, "%s", datadir);
      56             : 
      57         666 :     xldir = opendir(fullparentpath);
      58         666 :     if (xldir == NULL)
      59           0 :         pg_fatal("could not open directory \"%s\": %m",
      60             :                  fullparentpath);
      61             : 
      62       32660 :     while (errno = 0, (xlde = readdir(xldir)) != NULL)
      63             :     {
      64             :         struct stat fst;
      65             :         char        fullpath[MAXPGPATH * 2];
      66             :         char        path[MAXPGPATH * 2];
      67             : 
      68       61990 :         if (strcmp(xlde->d_name, ".") == 0 ||
      69       30662 :             strcmp(xlde->d_name, "..") == 0)
      70        1332 :             continue;
      71             : 
      72       29996 :         snprintf(fullpath, sizeof(fullpath), "%s/%s", fullparentpath, xlde->d_name);
      73             : 
      74       29996 :         if (lstat(fullpath, &fst) < 0)
      75             :         {
      76           0 :             if (errno == ENOENT)
      77             :             {
      78             :                 /*
      79             :                  * File doesn't exist anymore. This is ok, if the new master
      80             :                  * is running and the file was just removed. If it was a data
      81             :                  * file, there should be a WAL record of the removal. If it
      82             :                  * was something else, it couldn't have been anyway.
      83             :                  *
      84             :                  * TODO: But complain if we're processing the target dir!
      85             :                  */
      86             :             }
      87             :             else
      88           0 :                 pg_fatal("could not stat file \"%s\": %m",
      89             :                          fullpath);
      90             :         }
      91             : 
      92       29996 :         if (parentpath)
      93       29424 :             snprintf(path, sizeof(path), "%s/%s", parentpath, xlde->d_name);
      94             :         else
      95         572 :             snprintf(path, sizeof(path), "%s", xlde->d_name);
      96             : 
      97       29996 :         if (S_ISREG(fst.st_mode))
      98       29354 :             callback(path, FILE_TYPE_REGULAR, fst.st_size, NULL);
      99         642 :         else if (S_ISDIR(fst.st_mode))
     100             :         {
     101         638 :             callback(path, FILE_TYPE_DIRECTORY, 0, NULL);
     102             :             /* recurse to handle subdirectories */
     103         638 :             recurse_dir(datadir, path, callback);
     104             :         }
     105             : #ifndef WIN32
     106           4 :         else if (S_ISLNK(fst.st_mode))
     107             : #else
     108             :         else if (pgwin32_is_junction(fullpath))
     109             : #endif
     110             :         {
     111             : #if defined(HAVE_READLINK) || defined(WIN32)
     112             :             char        link_target[MAXPGPATH];
     113             :             int         len;
     114             : 
     115           4 :             len = readlink(fullpath, link_target, sizeof(link_target));
     116           4 :             if (len < 0)
     117           0 :                 pg_fatal("could not read symbolic link \"%s\": %m",
     118             :                          fullpath);
     119           4 :             if (len >= sizeof(link_target))
     120           0 :                 pg_fatal("symbolic link \"%s\" target is too long",
     121             :                          fullpath);
     122           4 :             link_target[len] = '\0';
     123             : 
     124           4 :             callback(path, FILE_TYPE_SYMLINK, 0, link_target);
     125             : 
     126             :             /*
     127             :              * If it's a symlink within pg_tblspc, we need to recurse into it,
     128             :              * to process all the tablespaces.  We also follow a symlink if
     129             :              * it's for pg_wal.  Symlinks elsewhere are ignored.
     130             :              */
     131           8 :             if ((parentpath && strcmp(parentpath, "pg_tblspc") == 0) ||
     132           4 :                 strcmp(path, "pg_wal") == 0)
     133           4 :                 recurse_dir(datadir, path, callback);
     134             : #else
     135             :             pg_fatal("\"%s\" is a symbolic link, but symbolic links are not supported on this platform",
     136             :                      fullpath);
     137             : #endif                          /* HAVE_READLINK */
     138             :         }
     139             :     }
     140             : 
     141         666 :     if (errno)
     142           0 :         pg_fatal("could not read directory \"%s\": %m",
     143             :                  fullparentpath);
     144             : 
     145         666 :     if (closedir(xldir))
     146           0 :         pg_fatal("could not close directory \"%s\": %m",
     147             :                  fullparentpath);
     148         666 : }
     149             : 
     150             : /*
     151             :  * Copy a file from source to target, between 'begin' and 'end' offsets.
     152             :  *
     153             :  * If 'trunc' is true, any existing file with the same name is truncated.
     154             :  */
     155             : static void
     156        3092 : rewind_copy_file_range(const char *path, off_t begin, off_t end, bool trunc)
     157             : {
     158             :     PGAlignedBlock buf;
     159             :     char        srcpath[MAXPGPATH];
     160             :     int         srcfd;
     161             : 
     162        3092 :     snprintf(srcpath, sizeof(srcpath), "%s/%s", datadir_source, path);
     163             : 
     164        3092 :     srcfd = open(srcpath, O_RDONLY | PG_BINARY, 0);
     165        3092 :     if (srcfd < 0)
     166           0 :         pg_fatal("could not open source file \"%s\": %m",
     167             :                  srcpath);
     168             : 
     169        3092 :     if (lseek(srcfd, begin, SEEK_SET) == -1)
     170           0 :         pg_fatal("could not seek in source file: %m");
     171             : 
     172        3092 :     open_target_file(path, trunc);
     173             : 
     174       58014 :     while (end - begin > 0)
     175             :     {
     176             :         int         readlen;
     177             :         int         len;
     178             : 
     179       51830 :         if (end - begin > sizeof(buf))
     180       48848 :             len = sizeof(buf);
     181             :         else
     182        2982 :             len = end - begin;
     183             : 
     184       51830 :         readlen = read(srcfd, buf.data, len);
     185             : 
     186       51830 :         if (readlen < 0)
     187           0 :             pg_fatal("could not read file \"%s\": %m",
     188             :                      srcpath);
     189       51830 :         else if (readlen == 0)
     190           0 :             pg_fatal("unexpected EOF while reading file \"%s\"", srcpath);
     191             : 
     192       51830 :         write_target_range(buf.data, begin, readlen);
     193       51830 :         begin += readlen;
     194             :     }
     195             : 
     196        3092 :     if (close(srcfd) != 0)
     197           0 :         pg_fatal("could not close file \"%s\": %m", srcpath);
     198        3092 : }
     199             : 
     200             : /*
     201             :  * Copy all relation data files from datadir_source to datadir_target, which
     202             :  * are marked in the given data page map.
     203             :  */
     204             : void
     205           8 : copy_executeFileMap(filemap_t *map)
     206             : {
     207             :     file_entry_t *entry;
     208             :     int         i;
     209             : 
     210       10650 :     for (i = 0; i < map->narray; i++)
     211             :     {
     212       10642 :         entry = map->array[i];
     213       10642 :         execute_pagemap(&entry->pagemap, entry->path);
     214             : 
     215       10642 :         switch (entry->action)
     216             :         {
     217             :             case FILE_ACTION_NONE:
     218             :                 /* ok, do nothing.. */
     219        6918 :                 break;
     220             : 
     221             :             case FILE_ACTION_COPY:
     222        3030 :                 rewind_copy_file_range(entry->path, 0, entry->newsize, true);
     223        3030 :                 break;
     224             : 
     225             :             case FILE_ACTION_TRUNCATE:
     226           2 :                 truncate_target_file(entry->path, entry->newsize);
     227           2 :                 break;
     228             : 
     229             :             case FILE_ACTION_COPY_TAIL:
     230           2 :                 rewind_copy_file_range(entry->path, entry->oldsize,
     231           2 :                                        entry->newsize, false);
     232           2 :                 break;
     233             : 
     234             :             case FILE_ACTION_CREATE:
     235           6 :                 create_target(entry);
     236           6 :                 break;
     237             : 
     238             :             case FILE_ACTION_REMOVE:
     239         684 :                 remove_target(entry);
     240         684 :                 break;
     241             :         }
     242             :     }
     243             : 
     244           8 :     close_target_file();
     245           8 : }
     246             : 
     247             : static void
     248       10642 : execute_pagemap(datapagemap_t *pagemap, const char *path)
     249             : {
     250             :     datapagemap_iterator_t *iter;
     251             :     BlockNumber blkno;
     252             :     off_t       offset;
     253             : 
     254       10642 :     iter = datapagemap_iterate(pagemap);
     255       21344 :     while (datapagemap_next(iter, &blkno))
     256             :     {
     257          60 :         offset = blkno * BLCKSZ;
     258          60 :         rewind_copy_file_range(path, offset, offset + BLCKSZ, false);
     259             :         /* Ok, this block has now been copied from new data dir to old */
     260             :     }
     261       10642 :     pg_free(iter);
     262       10642 : }

Generated by: LCOV version 1.13