LCOV - differential code coverage report
Current view: top level - src/common - file_utils.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: 67.9 % 168 114 50 5 29 9 11 50 33 20 23 55 2 4
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 10 10 8 2 9 1
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * File-processing utility routines.
       4                 :  *
       5                 :  * Assorted utility functions to work on files.
       6                 :  *
       7                 :  *
       8                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       9                 :  * Portions Copyright (c) 1994, Regents of the University of California
      10                 :  *
      11                 :  * src/common/file_utils.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 :  */
      15                 : 
      16                 : #ifndef FRONTEND
      17                 : #include "postgres.h"
      18                 : #else
      19                 : #include "postgres_fe.h"
      20                 : #endif
      21                 : 
      22                 : #include <dirent.h>
      23                 : #include <fcntl.h>
      24                 : #include <sys/stat.h>
      25                 : #include <unistd.h>
      26                 : 
      27                 : #include "common/file_utils.h"
      28                 : #ifdef FRONTEND
      29                 : #include "common/logging.h"
      30                 : #endif
      31                 : #include "port/pg_iovec.h"
      32                 : 
      33                 : #ifdef FRONTEND
      34                 : 
      35                 : /* Define PG_FLUSH_DATA_WORKS if we have an implementation for pg_flush_data */
      36                 : #if defined(HAVE_SYNC_FILE_RANGE)
      37                 : #define PG_FLUSH_DATA_WORKS 1
      38                 : #elif defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED)
      39                 : #define PG_FLUSH_DATA_WORKS 1
      40                 : #endif
      41                 : 
      42                 : /*
      43                 :  * pg_xlog has been renamed to pg_wal in version 10.
      44                 :  */
      45                 : #define MINIMUM_VERSION_FOR_PG_WAL  100000
      46                 : 
      47                 : #ifdef PG_FLUSH_DATA_WORKS
      48                 : static int  pre_sync_fname(const char *fname, bool isdir);
      49                 : #endif
      50                 : static void walkdir(const char *path,
      51                 :                     int (*action) (const char *fname, bool isdir),
      52                 :                     bool process_symlinks);
      53                 : 
      54                 : /*
      55                 :  * Issue fsync recursively on PGDATA and all its contents.
      56                 :  *
      57                 :  * We fsync regular files and directories wherever they are, but we follow
      58                 :  * symlinks only for pg_wal (or pg_xlog) and immediately under pg_tblspc.
      59                 :  * Other symlinks are presumed to point at files we're not responsible for
      60                 :  * fsyncing, and might not have privileges to write at all.
      61                 :  *
      62                 :  * serverVersion indicates the version of the server to be fsync'd.
      63                 :  */
      64                 : void
      65 GIC           4 : fsync_pgdata(const char *pg_data,
      66 ECB             :              int serverVersion)
      67                 : {
      68                 :     bool        xlog_is_symlink;
      69                 :     char        pg_wal[MAXPGPATH];
      70                 :     char        pg_tblspc[MAXPGPATH];
      71                 : 
      72                 :     /* handle renaming of pg_xlog to pg_wal in post-10 clusters */
      73 GIC           4 :     snprintf(pg_wal, MAXPGPATH, "%s/%s", pg_data,
      74 ECB             :              serverVersion < MINIMUM_VERSION_FOR_PG_WAL ? "pg_xlog" : "pg_wal");
      75 GIC           4 :     snprintf(pg_tblspc, MAXPGPATH, "%s/pg_tblspc", pg_data);
      76 ECB             : 
      77                 :     /*
      78                 :      * If pg_wal is a symlink, we'll need to recurse into it separately,
      79                 :      * because the first walkdir below will ignore it.
      80                 :      */
      81 GIC           4 :     xlog_is_symlink = false;
      82 ECB             : 
      83                 :     {
      84                 :         struct stat st;
      85                 : 
      86 CBC           4 :         if (lstat(pg_wal, &st) < 0)
      87 UBC           0 :             pg_log_error("could not stat file \"%s\": %m", pg_wal);
      88 CBC           4 :         else if (S_ISLNK(st.st_mode))
      89               1 :             xlog_is_symlink = true;
      90                 :     }
      91                 : 
      92                 :     /*
      93 ECB             :      * If possible, hint to the kernel that we're soon going to fsync the data
      94                 :      * directory and its contents.
      95                 :      */
      96                 : #ifdef PG_FLUSH_DATA_WORKS
      97 GIC           4 :     walkdir(pg_data, pre_sync_fname, false);
      98               4 :     if (xlog_is_symlink)
      99               1 :         walkdir(pg_wal, pre_sync_fname, false);
     100               4 :     walkdir(pg_tblspc, pre_sync_fname, true);
     101                 : #endif
     102                 : 
     103                 :     /*
     104                 :      * Now we do the fsync()s in the same order.
     105                 :      *
     106                 :      * The main call ignores symlinks, so in addition to specially processing
     107                 :      * pg_wal if it's a symlink, pg_tblspc has to be visited separately with
     108 ECB             :      * process_symlinks = true.  Note that if there are any plain directories
     109                 :      * in pg_tblspc, they'll get fsync'd twice.  That's not an expected case
     110                 :      * so we don't worry about optimizing it.
     111                 :      */
     112 CBC           4 :     walkdir(pg_data, fsync_fname, false);
     113 GIC           4 :     if (xlog_is_symlink)
     114               1 :         walkdir(pg_wal, fsync_fname, false);
     115               4 :     walkdir(pg_tblspc, fsync_fname, true);
     116               4 : }
     117                 : 
     118                 : /*
     119                 :  * Issue fsync recursively on the given directory and all its contents.
     120 ECB             :  *
     121                 :  * This is a convenient wrapper on top of walkdir().
     122                 :  */
     123                 : void
     124 GIC           5 : fsync_dir_recurse(const char *dir)
     125                 : {
     126                 :     /*
     127 ECB             :      * If possible, hint to the kernel that we're soon going to fsync the data
     128                 :      * directory and its contents.
     129                 :      */
     130                 : #ifdef PG_FLUSH_DATA_WORKS
     131 CBC           5 :     walkdir(dir, pre_sync_fname, false);
     132                 : #endif
     133                 : 
     134 GIC           5 :     walkdir(dir, fsync_fname, false);
     135               5 : }
     136                 : 
     137                 : /*
     138                 :  * walkdir: recursively walk a directory, applying the action to each
     139                 :  * regular file and directory (including the named directory itself).
     140                 :  *
     141                 :  * If process_symlinks is true, the action and recursion are also applied
     142                 :  * to regular files and directories that are pointed to by symlinks in the
     143                 :  * given directory; otherwise symlinks are ignored.  Symlinks are always
     144                 :  * ignored in subdirectories, ie we intentionally don't pass down the
     145                 :  * process_symlinks flag to recursive calls.
     146                 :  *
     147                 :  * Errors are reported but not considered fatal.
     148 ECB             :  *
     149                 :  * See also walkdir in fd.c, which is a backend version of this logic.
     150                 :  */
     151                 : static void
     152 GIC         228 : walkdir(const char *path,
     153                 :         int (*action) (const char *fname, bool isdir),
     154                 :         bool process_symlinks)
     155 ECB             : {
     156                 :     DIR        *dir;
     157                 :     struct dirent *de;
     158 EUB             : 
     159 GBC         228 :     dir = opendir(path);
     160 GIC         228 :     if (dir == NULL)
     161                 :     {
     162 LBC           0 :         pg_log_error("could not open directory \"%s\": %m", path);
     163 UIC           0 :         return;
     164                 :     }
     165                 : 
     166 CBC        8960 :     while (errno = 0, (de = readdir(dir)) != NULL)
     167 ECB             :     {
     168                 :         char        subpath[MAXPGPATH * 2];
     169                 : 
     170 CBC        8732 :         if (strcmp(de->d_name, ".") == 0 ||
     171 GIC        8504 :             strcmp(de->d_name, "..") == 0)
     172 CBC         456 :             continue;
     173                 : 
     174            8276 :         snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
     175 ECB             : 
     176 CBC        8276 :         switch (get_dirent_type(subpath, de, process_symlinks, PG_LOG_ERROR))
     177 ECB             :         {
     178 CBC        8074 :             case PGFILETYPE_REG:
     179            8074 :                 (*action) (subpath, false);
     180            8074 :                 break;
     181 GIC         200 :             case PGFILETYPE_DIR:
     182             200 :                 walkdir(subpath, action, false);
     183             200 :                 break;
     184               2 :             default:
     185                 : 
     186                 :                 /*
     187 ECB             :                  * Errors are already reported directly by get_dirent_type(),
     188                 :                  * and any remaining symlinks and unknown file types are
     189                 :                  * ignored.
     190                 :                  */
     191 CBC           2 :                 break;
     192 EUB             :         }
     193                 :     }
     194 ECB             : 
     195 GIC         228 :     if (errno)
     196 UIC           0 :         pg_log_error("could not read directory \"%s\": %m", path);
     197                 : 
     198 GIC         228 :     (void) closedir(dir);
     199                 : 
     200                 :     /*
     201                 :      * It's important to fsync the destination directory itself as individual
     202 ECB             :      * file fsyncs don't guarantee that the directory entry for the file is
     203                 :      * synced.  Recent versions of ext4 have made the window much wider but
     204                 :      * it's been an issue for ext3 and other filesystems in the past.
     205                 :      */
     206 GIC         228 :     (*action) (path, true);
     207                 : }
     208                 : 
     209                 : /*
     210                 :  * Hint to the OS that it should get ready to fsync() this file.
     211                 :  *
     212                 :  * Ignores errors trying to open unreadable files, and reports other errors
     213                 :  * non-fatally.
     214 ECB             :  */
     215                 : #ifdef PG_FLUSH_DATA_WORKS
     216                 : 
     217                 : static int
     218 CBC        4151 : pre_sync_fname(const char *fname, bool isdir)
     219                 : {
     220 ECB             :     int         fd;
     221                 : 
     222 GBC        4151 :     fd = open(fname, O_RDONLY | PG_BINARY, 0);
     223 EUB             : 
     224 GBC        4151 :     if (fd < 0)
     225 EUB             :     {
     226 UIC           0 :         if (errno == EACCES || (isdir && errno == EISDIR))
     227               0 :             return 0;
     228               0 :         pg_log_error("could not open file \"%s\": %m", fname);
     229               0 :         return -1;
     230                 :     }
     231                 : 
     232                 :     /*
     233                 :      * We do what pg_flush_data() would do in the backend: prefer to use
     234 ECB             :      * sync_file_range, but fall back to posix_fadvise.  We ignore errors
     235                 :      * because this is only a hint.
     236                 :      */
     237                 : #if defined(HAVE_SYNC_FILE_RANGE)
     238 GIC        4151 :     (void) sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE);
     239                 : #elif defined(USE_POSIX_FADVISE) && defined(POSIX_FADV_DONTNEED)
     240                 :     (void) posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
     241 ECB             : #else
     242                 : #error PG_FLUSH_DATA_WORKS should not have been defined
     243                 : #endif
     244                 : 
     245 GIC        4151 :     (void) close(fd);
     246            4151 :     return 0;
     247                 : }
     248                 : 
     249                 : #endif                          /* PG_FLUSH_DATA_WORKS */
     250                 : 
     251                 : /*
     252                 :  * fsync_fname -- Try to fsync a file or directory
     253                 :  *
     254                 :  * Ignores errors trying to open unreadable files, or trying to fsync
     255 ECB             :  * directories on systems where that isn't allowed/required.  All other errors
     256                 :  * are fatal.
     257                 :  */
     258                 : int
     259 GIC        4199 : fsync_fname(const char *fname, bool isdir)
     260                 : {
     261                 :     int         fd;
     262                 :     int         flags;
     263                 :     int         returncode;
     264                 : 
     265                 :     /*
     266                 :      * Some OSs require directories to be opened read-only whereas other
     267 ECB             :      * systems don't allow us to fsync files opened read-only; so we need both
     268                 :      * cases here.  Using O_RDWR will cause us to fail to fsync files that are
     269                 :      * not writable by our userid, but we assume that's OK.
     270                 :      */
     271 CBC        4199 :     flags = PG_BINARY;
     272 GIC        4199 :     if (!isdir)
     273            4067 :         flags |= O_RDWR;
     274                 :     else
     275             132 :         flags |= O_RDONLY;
     276                 : 
     277                 :     /*
     278 ECB             :      * Open the file, silently ignoring errors about unreadable files (or
     279                 :      * unsupported operations, e.g. opening a directory under Windows), and
     280                 :      * logging others.
     281 EUB             :      */
     282 GBC        4199 :     fd = open(fname, flags, 0);
     283            4199 :     if (fd < 0)
     284 EUB             :     {
     285 UIC           0 :         if (errno == EACCES || (isdir && errno == EISDIR))
     286               0 :             return 0;
     287 LBC           0 :         pg_log_error("could not open file \"%s\": %m", fname);
     288 UIC           0 :         return -1;
     289                 :     }
     290                 : 
     291 GIC        4199 :     returncode = fsync(fd);
     292                 : 
     293 ECB             :     /*
     294                 :      * Some OSes don't allow us to fsync directories at all, so we can ignore
     295 EUB             :      * those errors. Anything else needs to be reported.
     296                 :      */
     297 GBC        4199 :     if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL)))
     298                 :     {
     299 UIC           0 :         pg_log_error("could not fsync file \"%s\": %m", fname);
     300 LBC           0 :         (void) close(fd);
     301               0 :         exit(EXIT_FAILURE);
     302                 :     }
     303                 : 
     304 GIC        4199 :     (void) close(fd);
     305            4199 :     return 0;
     306                 : }
     307                 : 
     308                 : /*
     309                 :  * fsync_parent_path -- fsync the parent path of a file or directory
     310                 :  *
     311 ECB             :  * This is aimed at making file operations persistent on disk in case of
     312                 :  * an OS crash or power failure.
     313                 :  */
     314                 : int
     315 CBC          14 : fsync_parent_path(const char *fname)
     316 ECB             : {
     317                 :     char        parentpath[MAXPGPATH];
     318                 : 
     319 GIC          14 :     strlcpy(parentpath, fname, MAXPGPATH);
     320              14 :     get_parent_directory(parentpath);
     321                 : 
     322                 :     /*
     323 ECB             :      * get_parent_directory() returns an empty string if the input argument is
     324 EUB             :      * just a file name (see comments in path.c), so handle that as being the
     325                 :      * current directory.
     326 ECB             :      */
     327 GBC          14 :     if (strlen(parentpath) == 0)
     328 UIC           0 :         strlcpy(parentpath, ".", MAXPGPATH);
     329 ECB             : 
     330 GIC          14 :     if (fsync_fname(parentpath, true) != 0)
     331 UIC           0 :         return -1;
     332                 : 
     333 GIC          14 :     return 0;
     334                 : }
     335                 : 
     336                 : /*
     337                 :  * durable_rename -- rename(2) wrapper, issuing fsyncs required for durability
     338 ECB             :  *
     339                 :  * Wrapper around rename, similar to the backend version.
     340                 :  */
     341                 : int
     342 GIC           3 : durable_rename(const char *oldfile, const char *newfile)
     343                 : {
     344                 :     int         fd;
     345                 : 
     346                 :     /*
     347                 :      * First fsync the old and target path (if it exists), to ensure that they
     348                 :      * are properly persistent on disk. Syncing the target file is not
     349 ECB             :      * strictly necessary, but it makes it easier to reason about crashes;
     350 EUB             :      * because it's then guaranteed that either source or target file exists
     351                 :      * after a crash.
     352 ECB             :      */
     353 CBC           3 :     if (fsync_fname(oldfile, false) != 0)
     354 UIC           0 :         return -1;
     355 ECB             : 
     356 GIC           3 :     fd = open(newfile, PG_BINARY | O_RDWR, 0);
     357 GBC           3 :     if (fd < 0)
     358 EUB             :     {
     359 GIC           3 :         if (errno != ENOENT)
     360                 :         {
     361 UIC           0 :             pg_log_error("could not open file \"%s\": %m", newfile);
     362               0 :             return -1;
     363 EUB             :         }
     364                 :     }
     365                 :     else
     366                 :     {
     367 UBC           0 :         if (fsync(fd) != 0)
     368                 :         {
     369               0 :             pg_log_error("could not fsync file \"%s\": %m", newfile);
     370 UIC           0 :             close(fd);
     371               0 :             exit(EXIT_FAILURE);
     372                 :         }
     373 LBC           0 :         close(fd);
     374                 :     }
     375 EUB             : 
     376                 :     /* Time to do the real deal... */
     377 GBC           3 :     if (rename(oldfile, newfile) != 0)
     378                 :     {
     379 UIC           0 :         pg_log_error("could not rename file \"%s\" to \"%s\": %m",
     380                 :                      oldfile, newfile);
     381               0 :         return -1;
     382                 :     }
     383                 : 
     384 ECB             :     /*
     385 EUB             :      * To guarantee renaming the file is persistent, fsync the file with its
     386                 :      * new name, and its containing directory.
     387 ECB             :      */
     388 GBC           3 :     if (fsync_fname(newfile, false) != 0)
     389 UIC           0 :         return -1;
     390 ECB             : 
     391 GIC           3 :     if (fsync_parent_path(newfile) != 0)
     392 UIC           0 :         return -1;
     393                 : 
     394 GIC           3 :     return 0;
     395                 : }
     396                 : 
     397                 : #endif                          /* FRONTEND */
     398                 : 
     399                 : /*
     400                 :  * Return the type of a directory entry.
     401                 :  *
     402 ECB             :  * In frontend code, elevel should be a level from logging.h; in backend code
     403                 :  * it should be a level from elog.h.
     404                 :  */
     405                 : PGFileType
     406 GIC      321423 : get_dirent_type(const char *path,
     407                 :                 const struct dirent *de,
     408                 :                 bool look_through_symlinks,
     409                 :                 int elevel)
     410                 : {
     411                 :     PGFileType  result;
     412                 : 
     413                 :     /*
     414                 :      * Some systems tell us the type directly in the dirent struct, but that's
     415                 :      * a BSD and Linux extension not required by POSIX.  Even when the
     416 ECB             :      * interface is present, sometimes the type is unknown, depending on the
     417                 :      * filesystem.
     418                 :      */
     419                 : #if defined(DT_REG) && defined(DT_DIR) && defined(DT_LNK)
     420 CBC      321423 :     if (de->d_type == DT_REG)
     421          318685 :         result = PGFILETYPE_REG;
     422 GIC        2738 :     else if (de->d_type == DT_DIR)
     423 GBC        2719 :         result = PGFILETYPE_DIR;
     424 GIC          19 :     else if (de->d_type == DT_LNK && !look_through_symlinks)
     425              19 :         result = PGFILETYPE_LNK;
     426                 :     else
     427 UIC           0 :         result = PGFILETYPE_UNKNOWN;
     428 ECB             : #else
     429                 :     result = PGFILETYPE_UNKNOWN;
     430                 : #endif
     431                 : 
     432 GIC      321423 :     if (result == PGFILETYPE_UNKNOWN)
     433                 :     {
     434 EUB             :         struct stat fst;
     435                 :         int         sret;
     436                 : 
     437                 : 
     438 UIC           0 :         if (look_through_symlinks)
     439 UBC           0 :             sret = stat(path, &fst);
     440                 :         else
     441               0 :             sret = lstat(path, &fst);
     442                 : 
     443               0 :         if (sret < 0)
     444                 :         {
     445               0 :             result = PGFILETYPE_ERROR;
     446                 : #ifdef FRONTEND
     447 UIC           0 :             pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
     448                 : #else
     449               0 :             ereport(elevel,
     450 EUB             :                     (errcode_for_file_access(),
     451                 :                      errmsg("could not stat file \"%s\": %m", path)));
     452                 : #endif
     453                 :         }
     454 UBC           0 :         else if (S_ISREG(fst.st_mode))
     455               0 :             result = PGFILETYPE_REG;
     456 UIC           0 :         else if (S_ISDIR(fst.st_mode))
     457               0 :             result = PGFILETYPE_DIR;
     458               0 :         else if (S_ISLNK(fst.st_mode))
     459               0 :             result = PGFILETYPE_LNK;
     460                 :     }
     461                 : 
     462 GIC      321423 :     return result;
     463                 : }
     464                 : 
     465                 : /*
     466                 :  * pg_pwritev_with_retry
     467                 :  *
     468                 :  * Convenience wrapper for pg_pwritev() that retries on partial write.  If an
     469                 :  * error is returned, it is unspecified how much has been written.
     470                 :  */
     471                 : ssize_t
     472 GNC      383622 : pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, off_t offset)
     473                 : {
     474                 :     struct iovec iov_copy[PG_IOV_MAX];
     475          383622 :     ssize_t     sum = 0;
     476                 :     ssize_t     part;
     477                 : 
     478                 :     /* We'd better have space to make a copy, in case we need to retry. */
     479          383622 :     if (iovcnt > PG_IOV_MAX)
     480                 :     {
     481 UNC           0 :         errno = EINVAL;
     482               0 :         return -1;
     483                 :     }
     484                 : 
     485                 :     for (;;)
     486                 :     {
     487                 :         /* Write as much as we can. */
     488 GNC      383622 :         part = pg_pwritev(fd, iov, iovcnt, offset);
     489          383622 :         if (part < 0)
     490 UNC           0 :             return -1;
     491                 : 
     492                 : #ifdef SIMULATE_SHORT_WRITE
     493                 :         part = Min(part, 4096);
     494                 : #endif
     495                 : 
     496                 :         /* Count our progress. */
     497 GNC      383622 :         sum += part;
     498          383622 :         offset += part;
     499                 : 
     500                 :         /* Step over iovecs that are done. */
     501         2014487 :         while (iovcnt > 0 && iov->iov_len <= part)
     502                 :         {
     503         1630865 :             part -= iov->iov_len;
     504         1630865 :             ++iov;
     505         1630865 :             --iovcnt;
     506                 :         }
     507                 : 
     508                 :         /* Are they all done? */
     509          383622 :         if (iovcnt == 0)
     510                 :         {
     511                 :             /* We don't expect the kernel to write more than requested. */
     512          383622 :             Assert(part == 0);
     513          383622 :             break;
     514                 :         }
     515                 : 
     516                 :         /*
     517                 :          * Move whatever's left to the front of our mutable copy and adjust
     518                 :          * the leading iovec.
     519                 :          */
     520 UNC           0 :         Assert(iovcnt > 0);
     521               0 :         memmove(iov_copy, iov, sizeof(*iov) * iovcnt);
     522               0 :         Assert(iov->iov_len > part);
     523               0 :         iov_copy[0].iov_base = (char *) iov_copy[0].iov_base + part;
     524               0 :         iov_copy[0].iov_len -= part;
     525               0 :         iov = iov_copy;
     526                 :     }
     527                 : 
     528 GNC      383622 :     return sum;
     529                 : }
     530                 : 
     531                 : /*
     532                 :  * pg_pwrite_zeros
     533                 :  *
     534                 :  * Writes zeros to file worth "size" bytes at "offset" (from the start of the
     535                 :  * file), using vectored I/O.
     536                 :  *
     537                 :  * Returns the total amount of data written.  On failure, a negative value
     538                 :  * is returned with errno set.
     539                 :  */
     540                 : ssize_t
     541          345216 : pg_pwrite_zeros(int fd, size_t size, off_t offset)
     542                 : {
     543                 :     static const PGIOAlignedBlock zbuffer = {{0}};  /* worth BLCKSZ */
     544          345216 :     void       *zerobuf_addr = unconstify(PGIOAlignedBlock *, &zbuffer)->data;
     545                 :     struct iovec iov[PG_IOV_MAX];
     546          345216 :     size_t      remaining_size = size;
     547          345216 :     ssize_t     total_written = 0;
     548                 : 
     549                 :     /* Loop, writing as many blocks as we can for each system call. */
     550          728838 :     while (remaining_size > 0)
     551                 :     {
     552          383622 :         int         iovcnt = 0;
     553                 :         ssize_t     written;
     554                 : 
     555         2014487 :         for (; iovcnt < PG_IOV_MAX && remaining_size > 0; iovcnt++)
     556                 :         {
     557                 :             size_t      this_iov_size;
     558                 : 
     559         1630865 :             iov[iovcnt].iov_base = zerobuf_addr;
     560                 : 
     561         1630865 :             if (remaining_size < BLCKSZ)
     562 UNC           0 :                 this_iov_size = remaining_size;
     563                 :             else
     564 GNC     1630865 :                 this_iov_size = BLCKSZ;
     565                 : 
     566         1630865 :             iov[iovcnt].iov_len = this_iov_size;
     567         1630865 :             remaining_size -= this_iov_size;
     568                 :         }
     569                 : 
     570          383622 :         written = pg_pwritev_with_retry(fd, iov, iovcnt, offset);
     571                 : 
     572          383622 :         if (written < 0)
     573 UNC           0 :             return written;
     574                 : 
     575 GNC      383622 :         offset += written;
     576          383622 :         total_written += written;
     577                 :     }
     578                 : 
     579          345216 :     Assert(total_written == size);
     580                 : 
     581          345216 :     return total_written;
     582                 : }
        

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