LCOV - code coverage report
Current view: top level - src/bin/pg_rewind - file_ops.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 74 115 64.3 %
Date: 2019-06-18 07:06:57 Functions: 10 12 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * file_ops.c
       4             :  *    Helper functions for operating on files.
       5             :  *
       6             :  * Most of the functions in this file are helper functions for writing to
       7             :  * the target data directory. The functions check the --dry-run flag, and
       8             :  * do nothing if it's enabled. You should avoid accessing the target files
       9             :  * directly but if you do, make sure you honor the --dry-run mode!
      10             :  *
      11             :  * Portions Copyright (c) 2013-2019, PostgreSQL Global Development Group
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include <sys/stat.h>
      18             : #include <fcntl.h>
      19             : #include <unistd.h>
      20             : 
      21             : #include "common/file_perm.h"
      22             : #include "file_ops.h"
      23             : #include "filemap.h"
      24             : #include "pg_rewind.h"
      25             : 
      26             : /*
      27             :  * Currently open target file.
      28             :  */
      29             : static int  dstfd = -1;
      30             : static char dstpath[MAXPGPATH] = "";
      31             : 
      32             : static void create_target_dir(const char *path);
      33             : static void remove_target_dir(const char *path);
      34             : static void create_target_symlink(const char *path, const char *link);
      35             : static void remove_target_symlink(const char *path);
      36             : 
      37             : /*
      38             :  * Open a target file for writing. If 'trunc' is true and the file already
      39             :  * exists, it will be truncated.
      40             :  */
      41             : void
      42        9420 : open_target_file(const char *path, bool trunc)
      43             : {
      44             :     int         mode;
      45             : 
      46        9420 :     if (dry_run)
      47           0 :         return;
      48             : 
      49       12790 :     if (dstfd != -1 && !trunc &&
      50        3370 :         strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
      51         380 :         return;                 /* already open */
      52             : 
      53        9040 :     close_target_file();
      54             : 
      55        9040 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
      56             : 
      57        9040 :     mode = O_WRONLY | O_CREAT | PG_BINARY;
      58        9040 :     if (trunc)
      59        6050 :         mode |= O_TRUNC;
      60        9040 :     dstfd = open(dstpath, mode, pg_file_create_mode);
      61        9040 :     if (dstfd < 0)
      62           0 :         pg_fatal("could not open target file \"%s\": %m",
      63             :                  dstpath);
      64             : }
      65             : 
      66             : /*
      67             :  * Close target file, if it's open.
      68             :  */
      69             : void
      70        9064 : close_target_file(void)
      71             : {
      72        9064 :     if (dstfd == -1)
      73          24 :         return;
      74             : 
      75        9040 :     if (close(dstfd) != 0)
      76           0 :         pg_fatal("could not close target file \"%s\": %m",
      77             :                  dstpath);
      78             : 
      79        9040 :     dstfd = -1;
      80             : }
      81             : 
      82             : void
      83       55154 : write_target_range(char *buf, off_t begin, size_t size)
      84             : {
      85             :     int         writeleft;
      86             :     char       *p;
      87             : 
      88             :     /* update progress report */
      89       55154 :     fetch_done += size;
      90       55154 :     progress_report(false);
      91             : 
      92       55154 :     if (dry_run)
      93           0 :         return;
      94             : 
      95       55154 :     if (lseek(dstfd, begin, SEEK_SET) == -1)
      96           0 :         pg_fatal("could not seek in target file \"%s\": %m",
      97             :                  dstpath);
      98             : 
      99       55154 :     writeleft = size;
     100       55154 :     p = buf;
     101      165462 :     while (writeleft > 0)
     102             :     {
     103             :         int         writelen;
     104             : 
     105       55154 :         errno = 0;
     106       55154 :         writelen = write(dstfd, p, writeleft);
     107       55154 :         if (writelen < 0)
     108             :         {
     109             :             /* if write didn't set errno, assume problem is no disk space */
     110           0 :             if (errno == 0)
     111           0 :                 errno = ENOSPC;
     112           0 :             pg_fatal("could not write file \"%s\": %m",
     113             :                      dstpath);
     114             :         }
     115             : 
     116       55154 :         p += writelen;
     117       55154 :         writeleft -= writelen;
     118             :     }
     119             : 
     120             :     /* keep the file open, in case we need to copy more blocks in it */
     121             : }
     122             : 
     123             : 
     124             : void
     125        1392 : remove_target(file_entry_t *entry)
     126             : {
     127             :     Assert(entry->action == FILE_ACTION_REMOVE);
     128             : 
     129        1392 :     switch (entry->type)
     130             :     {
     131             :         case FILE_TYPE_DIRECTORY:
     132          12 :             remove_target_dir(entry->path);
     133          12 :             break;
     134             : 
     135             :         case FILE_TYPE_REGULAR:
     136        1380 :             remove_target_file(entry->path, false);
     137        1380 :             break;
     138             : 
     139             :         case FILE_TYPE_SYMLINK:
     140           0 :             remove_target_symlink(entry->path);
     141           0 :             break;
     142             :     }
     143        1392 : }
     144             : 
     145             : void
     146          12 : create_target(file_entry_t *entry)
     147             : {
     148             :     Assert(entry->action == FILE_ACTION_CREATE);
     149             : 
     150          12 :     switch (entry->type)
     151             :     {
     152             :         case FILE_TYPE_DIRECTORY:
     153          12 :             create_target_dir(entry->path);
     154          12 :             break;
     155             : 
     156             :         case FILE_TYPE_SYMLINK:
     157           0 :             create_target_symlink(entry->path, entry->link_target);
     158           0 :             break;
     159             : 
     160             :         case FILE_TYPE_REGULAR:
     161             :             /* can't happen. Regular files are created with open_target_file. */
     162           0 :             pg_fatal("invalid action (CREATE) for regular file");
     163             :             break;
     164             :     }
     165          12 : }
     166             : 
     167             : /*
     168             :  * Remove a file from target data directory.  If missing_ok is true, it
     169             :  * is fine for the target file to not exist.
     170             :  */
     171             : void
     172        1380 : remove_target_file(const char *path, bool missing_ok)
     173             : {
     174             :     char        dstpath[MAXPGPATH];
     175             : 
     176        1380 :     if (dry_run)
     177           0 :         return;
     178             : 
     179        1380 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     180        1380 :     if (unlink(dstpath) != 0)
     181             :     {
     182           0 :         if (errno == ENOENT && missing_ok)
     183           0 :             return;
     184             : 
     185           0 :         pg_fatal("could not remove file \"%s\": %m",
     186             :                  dstpath);
     187             :     }
     188             : }
     189             : 
     190             : void
     191           4 : truncate_target_file(const char *path, off_t newsize)
     192             : {
     193             :     char        dstpath[MAXPGPATH];
     194             :     int         fd;
     195             : 
     196           4 :     if (dry_run)
     197           0 :         return;
     198             : 
     199           4 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     200             : 
     201           4 :     fd = open(dstpath, O_WRONLY, pg_file_create_mode);
     202           4 :     if (fd < 0)
     203           0 :         pg_fatal("could not open file \"%s\" for truncation: %m",
     204             :                  dstpath);
     205             : 
     206           4 :     if (ftruncate(fd, newsize) != 0)
     207           0 :         pg_fatal("could not truncate file \"%s\" to %u: %m",
     208             :                  dstpath, (unsigned int) newsize);
     209             : 
     210           4 :     close(fd);
     211             : }
     212             : 
     213             : static void
     214          12 : create_target_dir(const char *path)
     215             : {
     216             :     char        dstpath[MAXPGPATH];
     217             : 
     218          12 :     if (dry_run)
     219           0 :         return;
     220             : 
     221          12 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     222          12 :     if (mkdir(dstpath, pg_dir_create_mode) != 0)
     223           0 :         pg_fatal("could not create directory \"%s\": %m",
     224             :                  dstpath);
     225             : }
     226             : 
     227             : static void
     228          12 : remove_target_dir(const char *path)
     229             : {
     230             :     char        dstpath[MAXPGPATH];
     231             : 
     232          12 :     if (dry_run)
     233           0 :         return;
     234             : 
     235          12 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     236          12 :     if (rmdir(dstpath) != 0)
     237           0 :         pg_fatal("could not remove directory \"%s\": %m",
     238             :                  dstpath);
     239             : }
     240             : 
     241             : static void
     242           0 : create_target_symlink(const char *path, const char *link)
     243             : {
     244             :     char        dstpath[MAXPGPATH];
     245             : 
     246           0 :     if (dry_run)
     247           0 :         return;
     248             : 
     249           0 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     250           0 :     if (symlink(link, dstpath) != 0)
     251           0 :         pg_fatal("could not create symbolic link at \"%s\": %m",
     252             :                  dstpath);
     253             : }
     254             : 
     255             : static void
     256           0 : remove_target_symlink(const char *path)
     257             : {
     258             :     char        dstpath[MAXPGPATH];
     259             : 
     260           0 :     if (dry_run)
     261           0 :         return;
     262             : 
     263           0 :     snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
     264           0 :     if (unlink(dstpath) != 0)
     265           0 :         pg_fatal("could not remove symbolic link \"%s\": %m",
     266             :                  dstpath);
     267             : }
     268             : 
     269             : 
     270             : /*
     271             :  * Read a file into memory. The file to be read is <datadir>/<path>.
     272             :  * The file contents are returned in a malloc'd buffer, and *filesize
     273             :  * is set to the length of the file.
     274             :  *
     275             :  * The returned buffer is always zero-terminated; the size of the returned
     276             :  * buffer is actually *filesize + 1. That's handy when reading a text file.
     277             :  * This function can be used to read binary files as well, you can just
     278             :  * ignore the zero-terminator in that case.
     279             :  *
     280             :  * This function is used to implement the fetchFile function in the "fetch"
     281             :  * interface (see fetch.c), but is also called directly.
     282             :  */
     283             : char *
     284          36 : slurpFile(const char *datadir, const char *path, size_t *filesize)
     285             : {
     286             :     int         fd;
     287             :     char       *buffer;
     288             :     struct stat statbuf;
     289             :     char        fullpath[MAXPGPATH];
     290             :     int         len;
     291             :     int         r;
     292             : 
     293          36 :     snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
     294             : 
     295          36 :     if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
     296           0 :         pg_fatal("could not open file \"%s\" for reading: %m",
     297             :                  fullpath);
     298             : 
     299          36 :     if (fstat(fd, &statbuf) < 0)
     300           0 :         pg_fatal("could not open file \"%s\" for reading: %m",
     301             :                  fullpath);
     302             : 
     303          36 :     len = statbuf.st_size;
     304             : 
     305          36 :     buffer = pg_malloc(len + 1);
     306             : 
     307          36 :     r = read(fd, buffer, len);
     308          36 :     if (r != len)
     309             :     {
     310           0 :         if (r < 0)
     311           0 :             pg_fatal("could not read file \"%s\": %m",
     312             :                      fullpath);
     313             :         else
     314           0 :             pg_fatal("could not read file \"%s\": read %d of %zu",
     315             :                      fullpath, r, (Size) len);
     316             :     }
     317          36 :     close(fd);
     318             : 
     319             :     /* Zero-terminate the buffer. */
     320          36 :     buffer[len] = '\0';
     321             : 
     322          36 :     if (filesize)
     323          28 :         *filesize = len;
     324          36 :     return buffer;
     325             : }

Generated by: LCOV version 1.13