LCOV - code coverage report
Current view: top level - src/port - pgmkdirp.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 86.7 % 30 26
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 1 1
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 80.8 % 26 21

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears
       3                 :             :  * the following copyright notice:
       4                 :             :  *
       5                 :             :  * Copyright (c) 1983, 1992, 1993
       6                 :             :  *  The Regents of the University of California.  All rights reserved.
       7                 :             :  *
       8                 :             :  * Redistribution and use in source and binary forms, with or without
       9                 :             :  * modification, are permitted provided that the following conditions
      10                 :             :  * are met:
      11                 :             :  * 1. Redistributions of source code must retain the above copyright
      12                 :             :  *    notice, this list of conditions and the following disclaimer.
      13                 :             :  * 2. Redistributions in binary form must reproduce the above copyright
      14                 :             :  *    notice, this list of conditions and the following disclaimer in the
      15                 :             :  *    documentation and/or other materials provided with the distribution.
      16                 :             :  * 4. Neither the name of the University nor the names of its contributors
      17                 :             :  *    may be used to endorse or promote products derived from this software
      18                 :             :  *    without specific prior written permission.
      19                 :             :  *
      20                 :             :  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      21                 :             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      22                 :             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      23                 :             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      24                 :             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      25                 :             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      26                 :             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      27                 :             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      28                 :             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      29                 :             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      30                 :             :  * SUCH DAMAGE.
      31                 :             :  */
      32                 :             : 
      33                 :             : #include "c.h"
      34                 :             : 
      35                 :             : #include <sys/stat.h>
      36                 :             : 
      37                 :             : 
      38                 :             : /*
      39                 :             :  * pg_mkdir_p --- create a directory and, if necessary, parent directories
      40                 :             :  *
      41                 :             :  * This is equivalent to "mkdir -p" except we don't complain if the target
      42                 :             :  * directory already exists.
      43                 :             :  *
      44                 :             :  * We assume the path is in canonical form, i.e., uses / as the separator.
      45                 :             :  *
      46                 :             :  * omode is the file permissions bits for the target directory.  Note that any
      47                 :             :  * parent directories that have to be created get permissions according to the
      48                 :             :  * prevailing umask, but with u+wx forced on to ensure we can create there.
      49                 :             :  * (We declare omode as int, not mode_t, to minimize dependencies for port.h.)
      50                 :             :  *
      51                 :             :  * Returns 0 on success, -1 (with errno set) on failure.
      52                 :             :  *
      53                 :             :  * Note that on failure, the path arg has been modified to show the particular
      54                 :             :  * directory level we had problems with.
      55                 :             :  */
      56                 :             : int
      57                 :         568 : pg_mkdir_p(char *path, int omode)
      58                 :             : {
      59                 :             :     mode_t      numask,
      60                 :             :                 oumask;
      61                 :             :     int         last,
      62                 :             :                 retval;
      63                 :             :     char       *p;
      64                 :             : 
      65                 :         568 :     retval = 0;
      66                 :         568 :     p = path;
      67                 :             : 
      68                 :             : #ifdef WIN32
      69                 :             :     /* skip network and drive specifiers for win32 */
      70                 :             :     if (strlen(p) >= 2)
      71                 :             :     {
      72                 :             :         if (p[0] == '/' && p[1] == '/')
      73                 :             :         {
      74                 :             :             /* network drive */
      75                 :             :             p = strchr(p + 2, '/');
      76                 :             :             if (p == NULL)
      77                 :             :             {
      78                 :             :                 errno = EINVAL;
      79                 :             :                 return -1;
      80                 :             :             }
      81                 :             :         }
      82                 :             :         else if (p[1] == ':' &&
      83                 :             :                  ((p[0] >= 'a' && p[0] <= 'z') ||
      84                 :             :                   (p[0] >= 'A' && p[0] <= 'Z')))
      85                 :             :         {
      86                 :             :             /* local drive */
      87                 :             :             p += 2;
      88                 :             :         }
      89                 :             :     }
      90                 :             : #endif
      91                 :             : 
      92                 :             :     /*
      93                 :             :      * POSIX 1003.2: For each dir operand that does not name an existing
      94                 :             :      * directory, effects equivalent to those caused by the following command
      95                 :             :      * shall occur:
      96                 :             :      *
      97                 :             :      * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir
      98                 :             :      *
      99                 :             :      * We change the user's umask and then restore it, instead of doing
     100                 :             :      * chmod's.  Note we assume umask() can't change errno.
     101                 :             :      */
     102                 :         568 :     oumask = umask(0);
     103                 :         568 :     numask = oumask & ~(S_IWUSR | S_IXUSR);
     104                 :         568 :     (void) umask(numask);
     105                 :             : 
     106         [ +  - ]:         568 :     if (p[0] == '/')            /* Skip leading '/'. */
     107                 :         568 :         ++p;
     108         [ +  + ]:       64674 :     for (last = 0; !last; ++p)
     109                 :             :     {
     110         [ +  + ]:       64106 :         if (p[0] == '\0')
     111                 :         568 :             last = 1;
     112         [ +  + ]:       63538 :         else if (p[0] != '/')
     113                 :       57367 :             continue;
     114                 :        6739 :         *p = '\0';
     115   [ +  +  -  + ]:        6739 :         if (!last && p[1] == '\0')
     116                 :           0 :             last = 1;
     117                 :             : 
     118         [ +  + ]:        6739 :         if (last)
     119                 :         568 :             (void) umask(oumask);
     120                 :             : 
     121   [ +  +  +  + ]:        6739 :         if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
     122                 :             :         {
     123                 :             :             /*
     124                 :             :              * If we got EEXIST because there's already a directory there,
     125                 :             :              * don't complain.
     126                 :             :              */
     127                 :             : #ifndef WIN32
     128                 :        6023 :             int         save_errno = errno;
     129                 :             :             struct stat sb;
     130                 :             : 
     131   [ +  -  +  - ]:       12046 :             if (save_errno != EEXIST ||
     132                 :        6023 :                 stat(path, &sb) != 0 ||
     133         [ -  + ]:        6023 :                 !S_ISDIR(sb.st_mode))
     134                 :             :             {
     135                 :             :                 /* Don't let stat replace mkdir's errno */
     136                 :           0 :                 errno = save_errno;
     137                 :           0 :                 retval = -1;
     138                 :           0 :                 break;
     139                 :             :             }
     140                 :             : #else                           /* WIN32 */
     141                 :             :             /*
     142                 :             :              * On Windows, stat() opens a handle and can transiently fail on a
     143                 :             :              * directory another process is concurrently creating.  Probe with
     144                 :             :              * a path-based attribute query instead: it requests only
     145                 :             :              * FILE_READ_ATTRIBUTES and is exempt from share-mode denial, so
     146                 :             :              * it reliably sees a concurrently-created directory.  We assume
     147                 :             :              * GetFileAttributes() won't change errno.
     148                 :             :              */
     149                 :             :             DWORD       attr = GetFileAttributes(path);
     150                 :             : 
     151                 :             :             if (errno != EEXIST ||
     152                 :             :                 attr == INVALID_FILE_ATTRIBUTES ||
     153                 :             :                 !(attr & FILE_ATTRIBUTE_DIRECTORY))
     154                 :             :             {
     155                 :             :                 retval = -1;
     156                 :             :                 break;
     157                 :             :             }
     158                 :             : #endif                          /* WIN32 */
     159                 :             :         }
     160                 :             : 
     161         [ +  + ]:        6739 :         if (!last)
     162                 :        6171 :             *p = '/';
     163                 :             :     }
     164                 :             : 
     165                 :             :     /* ensure we restored umask */
     166                 :         568 :     (void) umask(oumask);
     167                 :             : 
     168                 :         568 :     return retval;
     169                 :             : }
        

Generated by: LCOV version 2.0-1