LCOV - differential code coverage report
Current view: top level - src/backend/storage/file - copydir.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 68.2 % 66 45 1 5 11 4 2 21 5 17 13 19 2 4
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 2 2 2 1 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * copydir.c
       4                 :  *    copies a directory
       5                 :  *
       6                 :  * Portions Copyright (c) 1996-2023, 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                 : 
      24                 : #include "common/file_utils.h"
      25                 : #include "miscadmin.h"
      26                 : #include "pgstat.h"
      27                 : #include "storage/copydir.h"
      28                 : #include "storage/fd.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 GNC         621 : copydir(const char *fromdir, const char *todir, bool recurse)
      38                 : {
      39                 :     DIR        *xldir;
      40                 :     struct dirent *xlde;
      41                 :     char        fromfile[MAXPGPATH * 2];
      42                 :     char        tofile[MAXPGPATH * 2];
      43                 : 
      44 CBC         621 :     if (MakePGDirectory(todir) != 0)
      45 UBC           0 :         ereport(ERROR,
      46                 :                 (errcode_for_file_access(),
      47                 :                  errmsg("could not create directory \"%s\": %m", todir)));
      48                 : 
      49 CBC         621 :     xldir = AllocateDir(fromdir);
      50                 : 
      51          186965 :     while ((xlde = ReadDir(xldir, fromdir)) != NULL)
      52                 :     {
      53                 :         PGFileType  xlde_type;
      54                 : 
      55                 :         /* If we got a cancel signal during the copy of the directory, quit */
      56          186344 :         CHECK_FOR_INTERRUPTS();
      57                 : 
      58          186344 :         if (strcmp(xlde->d_name, ".") == 0 ||
      59          185723 :             strcmp(xlde->d_name, "..") == 0)
      60            1242 :             continue;
      61                 : 
      62          185102 :         snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
      63          185102 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      64                 : 
      65 GNC      185102 :         xlde_type = get_dirent_type(fromfile, xlde, false, ERROR);
      66                 : 
      67          185102 :         if (xlde_type == PGFILETYPE_DIR)
      68 EUB             :         {
      69                 :             /* recurse to handle subdirectories */
      70 LBC           0 :             if (recurse)
      71               0 :                 copydir(fromfile, tofile, true);
      72                 :         }
      73 GNC      185102 :         else if (xlde_type == PGFILETYPE_REG)
      74 GIC      185102 :             copy_file(fromfile, tofile);
      75                 :     }
      76             621 :     FreeDir(xldir);
      77                 : 
      78                 :     /*
      79 ECB             :      * Be paranoid here and fsync all files to ensure the copy is really done.
      80                 :      * But if fsync is disabled, we're done.
      81                 :      */
      82 GBC         621 :     if (!enableFsync)
      83 GIC         621 :         return;
      84 EUB             : 
      85 UIC           0 :     xldir = AllocateDir(todir);
      86 EUB             : 
      87 UBC           0 :     while ((xlde = ReadDir(xldir, todir)) != NULL)
      88 EUB             :     {
      89 UIC           0 :         if (strcmp(xlde->d_name, ".") == 0 ||
      90               0 :             strcmp(xlde->d_name, "..") == 0)
      91               0 :             continue;
      92                 : 
      93               0 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      94 EUB             : 
      95                 :         /*
      96                 :          * We don't need to sync subdirectories here since the recursive
      97                 :          * copydir will do it before it returns
      98                 :          */
      99 UNC           0 :         if (get_dirent_type(tofile, xlde, false, ERROR) == PGFILETYPE_REG)
     100 UBC           0 :             fsync_fname(tofile, false);
     101                 :     }
     102 UIC           0 :     FreeDir(xldir);
     103                 : 
     104                 :     /*
     105                 :      * It's important to fsync the destination directory itself as individual
     106                 :      * file fsyncs don't guarantee that the directory entry for the file is
     107 ECB             :      * synced. Recent versions of ext4 have made the window much wider but
     108                 :      * it's been true for ext3 and other filesystems in the past.
     109                 :      */
     110 UIC           0 :     fsync_fname(todir, true);
     111                 : }
     112                 : 
     113                 : /*
     114                 :  * copy one file
     115                 :  */
     116                 : void
     117 GNC      185111 : copy_file(const char *fromfile, const char *tofile)
     118                 : {
     119                 :     char       *buffer;
     120                 :     int         srcfd;
     121                 :     int         dstfd;
     122                 :     int         nbytes;
     123                 :     off_t       offset;
     124                 :     off_t       flush_offset;
     125                 : 
     126                 :     /* Size of copy buffer (read and write requests) */
     127                 : #define COPY_BUF_SIZE (8 * BLCKSZ)
     128                 : 
     129                 :     /*
     130                 :      * Size of data flush requests.  It seems beneficial on most platforms to
     131                 :      * do this every 1MB or so.  But macOS, at least with early releases of
     132 ECB             :      * APFS, is really unfriendly to small mmap/msync requests, so there do it
     133                 :      * only every 32MB.
     134                 :      */
     135                 : #if defined(__darwin__)
     136                 : #define FLUSH_DISTANCE (32 * 1024 * 1024)
     137                 : #else
     138                 : #define FLUSH_DISTANCE (1024 * 1024)
     139 EUB             : #endif
     140                 : 
     141                 :     /* Use palloc to ensure we get a maxaligned buffer */
     142 GIC      185111 :     buffer = palloc(COPY_BUF_SIZE);
     143 ECB             : 
     144                 :     /*
     145 EUB             :      * Open the files
     146                 :      */
     147 GIC      185111 :     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
     148          185111 :     if (srcfd < 0)
     149 UIC           0 :         ereport(ERROR,
     150                 :                 (errcode_for_file_access(),
     151                 :                  errmsg("could not open file \"%s\": %m", fromfile)));
     152 ECB             : 
     153 CBC      185111 :     dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
     154 GIC      185111 :     if (dstfd < 0)
     155 UIC           0 :         ereport(ERROR,
     156 ECB             :                 (errcode_for_file_access(),
     157                 :                  errmsg("could not create file \"%s\": %m", tofile)));
     158                 : 
     159                 :     /*
     160                 :      * Do the data copying.
     161                 :      */
     162 GIC      185111 :     flush_offset = 0;
     163 CBC      372060 :     for (offset = 0;; offset += nbytes)
     164                 :     {
     165 ECB             :         /* If we got a cancel signal during the copy of the file, quit */
     166 CBC      372060 :         CHECK_FOR_INTERRUPTS();
     167                 : 
     168                 :         /*
     169 ECB             :          * We fsync the files later, but during the copy, flush them every so
     170                 :          * often to avoid spamming the cache and hopefully get the kernel to
     171                 :          * start writing them out before the fsync comes.
     172                 :          */
     173 GBC      372060 :         if (offset - flush_offset >= FLUSH_DISTANCE)
     174                 :         {
     175 GIC          32 :             pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     176 CBC          32 :             flush_offset = offset;
     177 ECB             :         }
     178                 : 
     179 CBC      372060 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
     180          372060 :         nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
     181 GIC      372060 :         pgstat_report_wait_end();
     182          372060 :         if (nbytes < 0)
     183 UBC           0 :             ereport(ERROR,
     184 EUB             :                     (errcode_for_file_access(),
     185                 :                      errmsg("could not read file \"%s\": %m", fromfile)));
     186 GIC      372060 :         if (nbytes == 0)
     187          185111 :             break;
     188          186949 :         errno = 0;
     189 CBC      186949 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
     190 GIC      186949 :         if ((int) write(dstfd, buffer, nbytes) != nbytes)
     191                 :         {
     192 ECB             :             /* if write didn't set errno, assume problem is no disk space */
     193 LBC           0 :             if (errno == 0)
     194 UIC           0 :                 errno = ENOSPC;
     195 LBC           0 :             ereport(ERROR,
     196 EUB             :                     (errcode_for_file_access(),
     197                 :                      errmsg("could not write to file \"%s\": %m", tofile)));
     198                 :         }
     199 GIC      186949 :         pgstat_report_wait_end();
     200 ECB             :     }
     201 EUB             : 
     202 GIC      185111 :     if (offset > flush_offset)
     203          153440 :         pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     204                 : 
     205 CBC      185111 :     if (CloseTransientFile(dstfd) != 0)
     206 LBC           0 :         ereport(ERROR,
     207                 :                 (errcode_for_file_access(),
     208                 :                  errmsg("could not close file \"%s\": %m", tofile)));
     209                 : 
     210 GIC      185111 :     if (CloseTransientFile(srcfd) != 0)
     211 UIC           0 :         ereport(ERROR,
     212                 :                 (errcode_for_file_access(),
     213                 :                  errmsg("could not close file \"%s\": %m", fromfile)));
     214                 : 
     215 GIC      185111 :     pfree(buffer);
     216          185111 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a