LCOV - code coverage report
Current view: top level - src/backend/storage/file - copydir.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 52.9 % 87 46
Test Date: 2026-03-23 14:16:05 Functions: 66.7 % 3 2
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * copydir.c
       4              :  *    copies a directory
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, 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              : #ifdef HAVE_COPYFILE_H
      22              : #include <copyfile.h>
      23              : #endif
      24              : #include <fcntl.h>
      25              : #include <unistd.h>
      26              : 
      27              : #include "common/file_utils.h"
      28              : #include "miscadmin.h"
      29              : #include "pgstat.h"
      30              : #include "storage/copydir.h"
      31              : #include "storage/fd.h"
      32              : #include "utils/wait_event.h"
      33              : 
      34              : /* GUCs */
      35              : int         file_copy_method = FILE_COPY_METHOD_COPY;
      36              : 
      37              : static void clone_file(const char *fromfile, const char *tofile);
      38              : 
      39              : /*
      40              :  * copydir: copy a directory
      41              :  *
      42              :  * If recurse is false, subdirectories are ignored.  Anything that's not
      43              :  * a directory or a regular file is ignored.
      44              :  *
      45              :  * This function uses the file_copy_method GUC.  New uses of this function must
      46              :  * be documented in doc/src/sgml/config.sgml.
      47              :  */
      48              : void
      49          159 : copydir(const char *fromdir, const char *todir, bool recurse)
      50              : {
      51              :     DIR        *xldir;
      52              :     struct dirent *xlde;
      53              :     char        fromfile[MAXPGPATH * 2];
      54              :     char        tofile[MAXPGPATH * 2];
      55              : 
      56          159 :     if (MakePGDirectory(todir) != 0)
      57            0 :         ereport(ERROR,
      58              :                 (errcode_for_file_access(),
      59              :                  errmsg("could not create directory \"%s\": %m", todir)));
      60              : 
      61          159 :     xldir = AllocateDir(fromdir);
      62              : 
      63        51423 :     while ((xlde = ReadDir(xldir, fromdir)) != NULL)
      64              :     {
      65              :         PGFileType  xlde_type;
      66              : 
      67              :         /* If we got a cancel signal during the copy of the directory, quit */
      68        51264 :         CHECK_FOR_INTERRUPTS();
      69              : 
      70        51264 :         if (strcmp(xlde->d_name, ".") == 0 ||
      71        51105 :             strcmp(xlde->d_name, "..") == 0)
      72          318 :             continue;
      73              : 
      74        50946 :         snprintf(fromfile, sizeof(fromfile), "%s/%s", fromdir, xlde->d_name);
      75        50946 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
      76              : 
      77        50946 :         xlde_type = get_dirent_type(fromfile, xlde, false, ERROR);
      78              : 
      79        50946 :         if (xlde_type == PGFILETYPE_DIR)
      80              :         {
      81              :             /* recurse to handle subdirectories */
      82            0 :             if (recurse)
      83            0 :                 copydir(fromfile, tofile, true);
      84              :         }
      85        50946 :         else if (xlde_type == PGFILETYPE_REG)
      86              :         {
      87        50946 :             if (file_copy_method == FILE_COPY_METHOD_CLONE)
      88            0 :                 clone_file(fromfile, tofile);
      89              :             else
      90        50946 :                 copy_file(fromfile, tofile);
      91              :         }
      92              :     }
      93          159 :     FreeDir(xldir);
      94              : 
      95              :     /*
      96              :      * Be paranoid here and fsync all files to ensure the copy is really done.
      97              :      * But if fsync is disabled, we're done.
      98              :      */
      99          159 :     if (!enableFsync)
     100          159 :         return;
     101              : 
     102            0 :     xldir = AllocateDir(todir);
     103              : 
     104            0 :     while ((xlde = ReadDir(xldir, todir)) != NULL)
     105              :     {
     106            0 :         if (strcmp(xlde->d_name, ".") == 0 ||
     107            0 :             strcmp(xlde->d_name, "..") == 0)
     108            0 :             continue;
     109              : 
     110            0 :         snprintf(tofile, sizeof(tofile), "%s/%s", todir, xlde->d_name);
     111              : 
     112              :         /*
     113              :          * We don't need to sync subdirectories here since the recursive
     114              :          * copydir will do it before it returns
     115              :          */
     116            0 :         if (get_dirent_type(tofile, xlde, false, ERROR) == PGFILETYPE_REG)
     117            0 :             fsync_fname(tofile, false);
     118              :     }
     119            0 :     FreeDir(xldir);
     120              : 
     121              :     /*
     122              :      * It's important to fsync the destination directory itself as individual
     123              :      * file fsyncs don't guarantee that the directory entry for the file is
     124              :      * synced. Recent versions of ext4 have made the window much wider but
     125              :      * it's been true for ext3 and other filesystems in the past.
     126              :      */
     127            0 :     fsync_fname(todir, true);
     128              : }
     129              : 
     130              : /*
     131              :  * copy one file
     132              :  */
     133              : void
     134        50955 : copy_file(const char *fromfile, const char *tofile)
     135              : {
     136              :     char       *buffer;
     137              :     int         srcfd;
     138              :     int         dstfd;
     139              :     int         nbytes;
     140              :     off_t       offset;
     141              :     off_t       flush_offset;
     142              : 
     143              :     /* Size of copy buffer (read and write requests) */
     144              : #define COPY_BUF_SIZE (8 * BLCKSZ)
     145              : 
     146              :     /*
     147              :      * Size of data flush requests.  It seems beneficial on most platforms to
     148              :      * do this every 1MB or so.  But macOS, at least with early releases of
     149              :      * APFS, is really unfriendly to small mmap/msync requests, so there do it
     150              :      * only every 32MB.
     151              :      */
     152              : #if defined(__darwin__)
     153              : #define FLUSH_DISTANCE (32 * 1024 * 1024)
     154              : #else
     155              : #define FLUSH_DISTANCE (1024 * 1024)
     156              : #endif
     157              : 
     158              :     /* Use palloc to ensure we get a maxaligned buffer */
     159        50955 :     buffer = palloc(COPY_BUF_SIZE);
     160              : 
     161              :     /*
     162              :      * Open the files
     163              :      */
     164        50955 :     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
     165        50955 :     if (srcfd < 0)
     166            0 :         ereport(ERROR,
     167              :                 (errcode_for_file_access(),
     168              :                  errmsg("could not open file \"%s\": %m", fromfile)));
     169              : 
     170        50955 :     dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY);
     171        50955 :     if (dstfd < 0)
     172            0 :         ereport(ERROR,
     173              :                 (errcode_for_file_access(),
     174              :                  errmsg("could not create file \"%s\": %m", tofile)));
     175              : 
     176              :     /*
     177              :      * Do the data copying.
     178              :      */
     179        50955 :     flush_offset = 0;
     180        50955 :     for (offset = 0;; offset += nbytes)
     181              :     {
     182              :         /* If we got a cancel signal during the copy of the file, quit */
     183       101423 :         CHECK_FOR_INTERRUPTS();
     184              : 
     185              :         /*
     186              :          * We fsync the files later, but during the copy, flush them every so
     187              :          * often to avoid spamming the cache and hopefully get the kernel to
     188              :          * start writing them out before the fsync comes.
     189              :          */
     190       101423 :         if (offset - flush_offset >= FLUSH_DISTANCE)
     191              :         {
     192           32 :             pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     193           32 :             flush_offset = offset;
     194              :         }
     195              : 
     196       101423 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ);
     197       101423 :         nbytes = read(srcfd, buffer, COPY_BUF_SIZE);
     198       101423 :         pgstat_report_wait_end();
     199       101423 :         if (nbytes < 0)
     200            0 :             ereport(ERROR,
     201              :                     (errcode_for_file_access(),
     202              :                      errmsg("could not read file \"%s\": %m", fromfile)));
     203       101423 :         if (nbytes == 0)
     204        50955 :             break;
     205        50468 :         errno = 0;
     206        50468 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_WRITE);
     207        50468 :         if ((int) write(dstfd, buffer, nbytes) != nbytes)
     208              :         {
     209              :             /* if write didn't set errno, assume problem is no disk space */
     210            0 :             if (errno == 0)
     211            0 :                 errno = ENOSPC;
     212            0 :             ereport(ERROR,
     213              :                     (errcode_for_file_access(),
     214              :                      errmsg("could not write to file \"%s\": %m", tofile)));
     215              :         }
     216        50468 :         pgstat_report_wait_end();
     217              :     }
     218              : 
     219        50955 :     if (offset > flush_offset)
     220        41566 :         pg_flush_data(dstfd, flush_offset, offset - flush_offset);
     221              : 
     222        50955 :     if (CloseTransientFile(dstfd) != 0)
     223            0 :         ereport(ERROR,
     224              :                 (errcode_for_file_access(),
     225              :                  errmsg("could not close file \"%s\": %m", tofile)));
     226              : 
     227        50955 :     if (CloseTransientFile(srcfd) != 0)
     228            0 :         ereport(ERROR,
     229              :                 (errcode_for_file_access(),
     230              :                  errmsg("could not close file \"%s\": %m", fromfile)));
     231              : 
     232        50955 :     pfree(buffer);
     233        50955 : }
     234              : 
     235              : /*
     236              :  * clone one file
     237              :  */
     238              : static void
     239            0 : clone_file(const char *fromfile, const char *tofile)
     240              : {
     241              : #if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
     242              :     if (copyfile(fromfile, tofile, NULL, COPYFILE_CLONE_FORCE) < 0)
     243              :         ereport(ERROR,
     244              :                 (errcode_for_file_access(),
     245              :                  errmsg("could not clone file \"%s\" to \"%s\": %m",
     246              :                         fromfile, tofile)));
     247              : #elif defined(HAVE_COPY_FILE_RANGE)
     248              :     int         srcfd;
     249              :     int         dstfd;
     250              :     ssize_t     nbytes;
     251              : 
     252            0 :     srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY);
     253            0 :     if (srcfd < 0)
     254            0 :         ereport(ERROR,
     255              :                 (errcode_for_file_access(),
     256              :                  errmsg("could not open file \"%s\": %m", fromfile)));
     257              : 
     258            0 :     dstfd = OpenTransientFile(tofile, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY);
     259            0 :     if (dstfd < 0)
     260            0 :         ereport(ERROR,
     261              :                 (errcode_for_file_access(),
     262              :                  errmsg("could not create file \"%s\": %m", tofile)));
     263              : 
     264              :     do
     265              :     {
     266              :         /*
     267              :          * Don't copy too much at once, so we can check for interrupts from
     268              :          * time to time if it falls back to a slow copy.
     269              :          */
     270            0 :         CHECK_FOR_INTERRUPTS();
     271            0 :         pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_COPY);
     272            0 :         nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, 1024 * 1024, 0);
     273            0 :         if (nbytes < 0 && errno != EINTR)
     274            0 :             ereport(ERROR,
     275              :                     (errcode_for_file_access(),
     276              :                      errmsg("could not clone file \"%s\" to \"%s\": %m",
     277              :                             fromfile, tofile)));
     278            0 :         pgstat_report_wait_end();
     279              :     }
     280            0 :     while (nbytes != 0);
     281              : 
     282            0 :     if (CloseTransientFile(dstfd) != 0)
     283            0 :         ereport(ERROR,
     284              :                 (errcode_for_file_access(),
     285              :                  errmsg("could not close file \"%s\": %m", tofile)));
     286              : 
     287            0 :     if (CloseTransientFile(srcfd) != 0)
     288            0 :         ereport(ERROR,
     289              :                 (errcode_for_file_access(),
     290              :                  errmsg("could not close file \"%s\": %m", fromfile)));
     291              : #else
     292              :     /* If there is no CLONE support this function should not be called. */
     293              :     pg_unreachable();
     294              : #endif
     295            0 : }
        

Generated by: LCOV version 2.0-1