LCOV - code coverage report
Current view: top level - src/backend/storage/file - copydir.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 42 69 60.9 %
Date: 2017-09-29 15:12:54 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-2017, 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           3 : 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           3 :     if (mkdir(todir, S_IRWXU) != 0)
      45           0 :         ereport(ERROR,
      46             :                 (errcode_for_file_access(),
      47             :                  errmsg("could not create directory \"%s\": %m", todir)));
      48             : 
      49           3 :     xldir = AllocateDir(fromdir);
      50           3 :     if (xldir == NULL)
      51           0 :         ereport(ERROR,
      52             :                 (errcode_for_file_access(),
      53             :                  errmsg("could not open directory \"%s\": %m", fromdir)));
      54             : 
      55         891 :     while ((xlde = ReadDir(xldir, fromdir)) != NULL)
      56             :     {
      57             :         struct stat fst;
      58             : 
      59             :         /* If we got a cancel signal during the copy of the directory, quit */
      60         885 :         CHECK_FOR_INTERRUPTS();
      61             : 
      62        1767 :         if (strcmp(xlde->d_name, ".") == 0 ||
      63         882 :             strcmp(xlde->d_name, "..") == 0)
      64           6 :             continue;
      65             : 
      66         879 :         snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
      67         879 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      68             : 
      69         879 :         if (lstat(fromfile, &fst) < 0)
      70           0 :             ereport(ERROR,
      71             :                     (errcode_for_file_access(),
      72             :                      errmsg("could not stat file \"%s\": %m", fromfile)));
      73             : 
      74         879 :         if (S_ISDIR(fst.st_mode))
      75             :         {
      76             :             /* recurse to handle subdirectories */
      77           0 :             if (recurse)
      78           0 :                 copydir(fromfile, tofile, true);
      79             :         }
      80         879 :         else if (S_ISREG(fst.st_mode))
      81         879 :             copy_file(fromfile, tofile);
      82             :     }
      83           3 :     FreeDir(xldir);
      84             : 
      85             :     /*
      86             :      * Be paranoid here and fsync all files to ensure the copy is really done.
      87             :      * But if fsync is disabled, we're done.
      88             :      */
      89           3 :     if (!enableFsync)
      90           6 :         return;
      91             : 
      92           0 :     xldir = AllocateDir(todir);
      93           0 :     if (xldir == NULL)
      94           0 :         ereport(ERROR,
      95             :                 (errcode_for_file_access(),
      96             :                  errmsg("could not open directory \"%s\": %m", todir)));
      97             : 
      98           0 :     while ((xlde = ReadDir(xldir, todir)) != NULL)
      99             :     {
     100             :         struct stat fst;
     101             : 
     102           0 :         if (strcmp(xlde->d_name, ".") == 0 ||
     103           0 :             strcmp(xlde->d_name, "..") == 0)
     104           0 :             continue;
     105             : 
     106           0 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
     107             : 
     108             :         /*
     109             :          * We don't need to sync subdirectories here since the recursive
     110             :          * copydir will do it before it returns
     111             :          */
     112           0 :         if (lstat(tofile, &fst) < 0)
     113           0 :             ereport(ERROR,
     114             :                     (errcode_for_file_access(),
     115             :                      errmsg("could not stat file \"%s\": %m", tofile)));
     116             : 
     117           0 :         if (S_ISREG(fst.st_mode))
     118           0 :             fsync_fname(tofile, false);
     119             :     }
     120           0 :     FreeDir(xldir);
     121             : 
     122             :     /*
     123             :      * It's important to fsync the destination directory itself as individual
     124             :      * file fsyncs don't guarantee that the directory entry for the file is
     125             :      * synced. Recent versions of ext4 have made the window much wider but
     126             :      * it's been true for ext3 and other filesystems in the past.
     127             :      */
     128           0 :     fsync_fname(todir, true);
     129             : }
     130             : 
     131             : /*
     132             :  * copy one file
     133             :  */
     134             : void
     135         879 : copy_file(char *fromfile, char *tofile)
     136             : {
     137             :     char       *buffer;
     138             :     int         srcfd;
     139             :     int         dstfd;
     140             :     int         nbytes;
     141             :     off_t       offset;
     142             : 
     143             :     /* Use palloc to ensure we get a maxaligned buffer */
     144             : #define COPY_BUF_SIZE (8 * BLCKSZ)
     145             : 
     146         879 :     buffer = palloc(COPY_BUF_SIZE);
     147             : 
     148             :     /*
     149             :      * Open the files
     150             :      */
     151         879 :     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY, 0);
     152         879 :     if (srcfd < 0)
     153           0 :         ereport(ERROR,
     154             :                 (errcode_for_file_access(),
     155             :                  errmsg("could not open file \"%s\": %m", fromfile)));
     156             : 
     157         879 :     dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
     158             :                               S_IRUSR | S_IWUSR);
     159         879 :     if (dstfd < 0)
     160           0 :         ereport(ERROR,
     161             :                 (errcode_for_file_access(),
     162             :                  errmsg("could not create file \"%s\": %m", tofile)));
     163             : 
     164             :     /*
     165             :      * Do the data copying.
     166             :      */
     167        1719 :     for (offset = 0;; offset += nbytes)
     168             :     {
     169             :         /* If we got a cancel signal during the copy of the file, quit */
     170        1719 :         CHECK_FOR_INTERRUPTS();
     171             : 
     172        1719 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
     173        1719 :         nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
     174        1719 :         pgstat_report_wait_end();
     175        1719 :         if (nbytes < 0)
     176           0 :             ereport(ERROR,
     177             :                     (errcode_for_file_access(),
     178             :                      errmsg("could not read file \"%s\": %m", fromfile)));
     179        1719 :         if (nbytes == 0)
     180         879 :             break;
     181         840 :         errno = 0;
     182         840 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
     183         840 :         if ((int) write(dstfd, buffer, nbytes) != nbytes)
     184             :         {
     185           0 :             pgstat_report_wait_end();
     186             :             /* if write didn't set errno, assume problem is no disk space */
     187           0 :             if (errno == 0)
     188           0 :                 errno = ENOSPC;
     189           0 :             ereport(ERROR,
     190             :                     (errcode_for_file_access(),
     191             :                      errmsg("could not write to file \"%s\": %m", tofile)));
     192             :         }
     193         840 :         pgstat_report_wait_end();
     194             : 
     195             :         /*
     196             :          * We fsync the files later but first flush them to avoid spamming the
     197             :          * cache and hopefully get the kernel to start writing them out before
     198             :          * the fsync comes.
     199             :          */
     200         840 :         pg_flush_data(dstfd, offset, nbytes);
     201         840 :     }
     202             : 
     203         879 :     if (CloseTransientFile(dstfd))
     204           0 :         ereport(ERROR,
     205             :                 (errcode_for_file_access(),
     206             :                  errmsg("could not close file \"%s\": %m", tofile)));
     207             : 
     208         879 :     CloseTransientFile(srcfd);
     209             : 
     210         879 :     pfree(buffer);
     211         879 : }

Generated by: LCOV version 1.11