LCOV - code coverage report
Current view: top level - src/backend/storage/file - copydir.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 43 69 62.3 %
Date: 2019-09-19 02:07:14 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * copydir.c
       4             :  *    copies a directory
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *  While "xcopy /e /i /q" works fine for copying directories, on Windows XP
      10             :  *  it requires a Window handle which prevents it from working when invoked
      11             :  *  as a service.
      12             :  *
      13             :  * IDENTIFICATION
      14             :  *    src/backend/storage/file/copydir.c
      15             :  *
      16             :  *-------------------------------------------------------------------------
      17             :  */
      18             : 
      19             : #include "postgres.h"
      20             : 
      21             : #include <fcntl.h>
      22             : #include <unistd.h>
      23             : #include <sys/stat.h>
      24             : 
      25             : #include "storage/copydir.h"
      26             : #include "storage/fd.h"
      27             : #include "miscadmin.h"
      28             : #include "pgstat.h"
      29             : 
      30             : /*
      31             :  * copydir: copy a directory
      32             :  *
      33             :  * If recurse is false, subdirectories are ignored.  Anything that's not
      34             :  * a directory or a regular file is ignored.
      35             :  */
      36             : void
      37         880 : copydir(char *fromdir, char *todir, bool recurse)
      38             : {
      39             :     DIR        *xldir;
      40             :     struct dirent *xlde;
      41             :     char        fromfile[MAXPGPATH * 2];
      42             :     char        tofile[MAXPGPATH * 2];
      43             : 
      44         880 :     if (MakePGDirectory(todir) != 0)
      45           0 :         ereport(ERROR,
      46             :                 (errcode_for_file_access(),
      47             :                  errmsg("could not create directory \"%s\": %m", todir)));
      48             : 
      49         880 :     xldir = AllocateDir(fromdir);
      50             : 
      51      269284 :     while ((xlde = ReadDir(xldir, fromdir)) != NULL)
      52             :     {
      53             :         struct stat fst;
      54             : 
      55             :         /* If we got a cancel signal during the copy of the directory, quit */
      56      267524 :         CHECK_FOR_INTERRUPTS();
      57             : 
      58      534168 :         if (strcmp(xlde->d_name, ".") == 0 ||
      59      266644 :             strcmp(xlde->d_name, "..") == 0)
      60        1760 :             continue;
      61             : 
      62      265764 :         snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
      63      265764 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      64             : 
      65      265764 :         if (lstat(fromfile, &fst) < 0)
      66           0 :             ereport(ERROR,
      67             :                     (errcode_for_file_access(),
      68             :                      errmsg("could not stat file \"%s\": %m", fromfile)));
      69             : 
      70      265764 :         if (S_ISDIR(fst.st_mode))
      71             :         {
      72             :             /* recurse to handle subdirectories */
      73           0 :             if (recurse)
      74           0 :                 copydir(fromfile, tofile, true);
      75             :         }
      76      265764 :         else if (S_ISREG(fst.st_mode))
      77      265764 :             copy_file(fromfile, tofile);
      78             :     }
      79         880 :     FreeDir(xldir);
      80             : 
      81             :     /*
      82             :      * Be paranoid here and fsync all files to ensure the copy is really done.
      83             :      * But if fsync is disabled, we're done.
      84             :      */
      85         880 :     if (!enableFsync)
      86         880 :         return;
      87             : 
      88           0 :     xldir = AllocateDir(todir);
      89             : 
      90           0 :     while ((xlde = ReadDir(xldir, todir)) != NULL)
      91             :     {
      92             :         struct stat fst;
      93             : 
      94           0 :         if (strcmp(xlde->d_name, ".") == 0 ||
      95           0 :             strcmp(xlde->d_name, "..") == 0)
      96           0 :             continue;
      97             : 
      98           0 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      99             : 
     100             :         /*
     101             :          * We don't need to sync subdirectories here since the recursive
     102             :          * copydir will do it before it returns
     103             :          */
     104           0 :         if (lstat(tofile, &fst) < 0)
     105           0 :             ereport(ERROR,
     106             :                     (errcode_for_file_access(),
     107             :                      errmsg("could not stat file \"%s\": %m", tofile)));
     108             : 
     109           0 :         if (S_ISREG(fst.st_mode))
     110           0 :             fsync_fname(tofile, false);
     111             :     }
     112           0 :     FreeDir(xldir);
     113             : 
     114             :     /*
     115             :      * It's important to fsync the destination directory itself as individual
     116             :      * file fsyncs don't guarantee that the directory entry for the file is
     117             :      * synced. Recent versions of ext4 have made the window much wider but
     118             :      * it's been true for ext3 and other filesystems in the past.
     119             :      */
     120           0 :     fsync_fname(todir, true);
     121             : }
     122             : 
     123             : /*
     124             :  * copy one file
     125             :  */
     126             : void
     127      265768 : copy_file(char *fromfile, char *tofile)
     128             : {
     129             :     char       *buffer;
     130             :     int         srcfd;
     131             :     int         dstfd;
     132             :     int         nbytes;
     133             :     off_t       offset;
     134             :     off_t       flush_offset;
     135             : 
     136             :     /* Size of copy buffer (read and write requests) */
     137             : #define COPY_BUF_SIZE (8 * BLCKSZ)
     138             : 
     139             :     /*
     140             :      * Size of data flush requests.  It seems beneficial on most platforms to
     141             :      * do this every 1MB or so.  But macOS, at least with early releases of
     142             :      * APFS, is really unfriendly to small mmap/msync requests, so there do it
     143             :      * only every 32MB.
     144             :      */
     145             : #if defined(__darwin__)
     146             : #define FLUSH_DISTANCE (32 * 1024 * 1024)
     147             : #else
     148             : #define FLUSH_DISTANCE (1024 * 1024)
     149             : #endif
     150             : 
     151             :     /* Use palloc to ensure we get a maxaligned buffer */
     152      265768 :     buffer = palloc(COPY_BUF_SIZE);
     153             : 
     154             :     /*
     155             :      * Open the files
     156             :      */
     157      265768 :     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
     158      265768 :     if (srcfd < 0)
     159           0 :         ereport(ERROR,
     160             :                 (errcode_for_file_access(),
     161             :                  errmsg("could not open file \"%s\": %m", fromfile)));
     162             : 
     163      265768 :     dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
     164      265768 :     if (dstfd < 0)
     165           0 :         ereport(ERROR,
     166             :                 (errcode_for_file_access(),
     167             :                  errmsg("could not create file \"%s\": %m", tofile)));
     168             : 
     169             :     /*
     170             :      * Do the data copying.
     171             :      */
     172      265768 :     flush_offset = 0;
     173      533300 :     for (offset = 0;; offset += nbytes)
     174             :     {
     175             :         /* If we got a cancel signal during the copy of the file, quit */
     176      800832 :         CHECK_FOR_INTERRUPTS();
     177             : 
     178             :         /*
     179             :          * We fsync the files later, but during the copy, flush them every so
     180             :          * often to avoid spamming the cache and hopefully get the kernel to
     181             :          * start writing them out before the fsync comes.
     182             :          */
     183      533300 :         if (offset - flush_offset >= FLUSH_DISTANCE)
     184             :         {
     185           0 :             pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     186           0 :             flush_offset = offset;
     187             :         }
     188             : 
     189      533300 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
     190      533300 :         nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
     191      533300 :         pgstat_report_wait_end();
     192      533300 :         if (nbytes < 0)
     193           0 :             ereport(ERROR,
     194             :                     (errcode_for_file_access(),
     195             :                      errmsg("could not read file \"%s\": %m", fromfile)));
     196      533300 :         if (nbytes == 0)
     197      265768 :             break;
     198      267532 :         errno = 0;
     199      267532 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
     200      267532 :         if ((int) write(dstfd, buffer, nbytes) != nbytes)
     201             :         {
     202             :             /* if write didn't set errno, assume problem is no disk space */
     203           0 :             if (errno == 0)
     204           0 :                 errno = ENOSPC;
     205           0 :             ereport(ERROR,
     206             :                     (errcode_for_file_access(),
     207             :                      errmsg("could not write to file \"%s\": %m", tofile)));
     208             :         }
     209      267532 :         pgstat_report_wait_end();
     210             :     }
     211             : 
     212      265768 :     if (offset > flush_offset)
     213      218244 :         pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     214             : 
     215      265768 :     if (CloseTransientFile(dstfd) != 0)
     216           0 :         ereport(ERROR,
     217             :                 (errcode_for_file_access(),
     218             :                  errmsg("could not close file \"%s\": %m", tofile)));
     219             : 
     220      265768 :     if (CloseTransientFile(srcfd) != 0)
     221           0 :         ereport(ERROR,
     222             :                 (errcode_for_file_access(),
     223             :                  errmsg("could not close file \"%s\": %m", fromfile)));
     224             : 
     225      265768 :     pfree(buffer);
     226      265768 : }

Generated by: LCOV version 1.13