LCOV - code coverage report
Current view: top level - src/port - pgmkdirp.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 79.5 % 39 31
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 1 1
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.1 % 26 19

             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                 :           1 : pg_mkdir_p(char *path, int omode)
      58                 :             : {
      59                 :           1 :         struct stat sb;
      60                 :           1 :         mode_t          numask,
      61                 :             :                                 oumask;
      62                 :           1 :         int                     last,
      63                 :             :                                 retval;
      64                 :           1 :         char       *p;
      65                 :             : 
      66                 :           1 :         retval = 0;
      67                 :           1 :         p = path;
      68                 :             : 
      69                 :             : #ifdef WIN32
      70                 :             :         /* skip network and drive specifiers for win32 */
      71                 :             :         if (strlen(p) >= 2)
      72                 :             :         {
      73                 :             :                 if (p[0] == '/' && p[1] == '/')
      74                 :             :                 {
      75                 :             :                         /* network drive */
      76                 :             :                         p = strchr(p + 2, '/');
      77                 :             :                         if (p == NULL)
      78                 :             :                         {
      79                 :             :                                 errno = EINVAL;
      80                 :             :                                 return -1;
      81                 :             :                         }
      82                 :             :                 }
      83                 :             :                 else if (p[1] == ':' &&
      84                 :             :                                  ((p[0] >= 'a' && p[0] <= 'z') ||
      85                 :             :                                   (p[0] >= 'A' && p[0] <= 'Z')))
      86                 :             :                 {
      87                 :             :                         /* local drive */
      88                 :             :                         p += 2;
      89                 :             :                 }
      90                 :             :         }
      91                 :             : #endif
      92                 :             : 
      93                 :             :         /*
      94                 :             :          * POSIX 1003.2: For each dir operand that does not name an existing
      95                 :             :          * directory, effects equivalent to those caused by the following command
      96                 :             :          * shall occur:
      97                 :             :          *
      98                 :             :          * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir
      99                 :             :          *
     100                 :             :          * We change the user's umask and then restore it, instead of doing
     101                 :             :          * chmod's.  Note we assume umask() can't change errno.
     102                 :             :          */
     103                 :           1 :         oumask = umask(0);
     104                 :           1 :         numask = oumask & ~(S_IWUSR | S_IXUSR);
     105                 :           1 :         (void) umask(numask);
     106                 :             : 
     107         [ -  + ]:           1 :         if (p[0] == '/')                        /* Skip leading '/'. */
     108                 :           1 :                 ++p;
     109         [ +  + ]:          53 :         for (last = 0; !last; ++p)
     110                 :             :         {
     111         [ +  + ]:          52 :                 if (p[0] == '\0')
     112                 :           1 :                         last = 1;
     113         [ +  + ]:          51 :                 else if (p[0] != '/')
     114                 :          47 :                         continue;
     115                 :           5 :                 *p = '\0';
     116   [ +  +  -  + ]:           5 :                 if (!last && p[1] == '\0')
     117                 :           0 :                         last = 1;
     118                 :             : 
     119         [ +  + ]:           5 :                 if (last)
     120                 :           1 :                         (void) umask(oumask);
     121                 :             : 
     122                 :             :                 /* check for pre-existing directory */
     123         [ +  + ]:           5 :                 if (stat(path, &sb) == 0)
     124                 :             :                 {
     125         [ +  - ]:           4 :                         if (!S_ISDIR(sb.st_mode))
     126                 :             :                         {
     127         [ #  # ]:           0 :                                 if (last)
     128                 :           0 :                                         errno = EEXIST;
     129                 :             :                                 else
     130                 :           0 :                                         errno = ENOTDIR;
     131                 :           0 :                                 retval = -1;
     132                 :           0 :                                 break;
     133                 :             :                         }
     134                 :           4 :                 }
     135   [ +  -  +  - ]:           1 :                 else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
     136                 :             :                 {
     137                 :           0 :                         retval = -1;
     138                 :           0 :                         break;
     139                 :             :                 }
     140         [ +  + ]:           5 :                 if (!last)
     141                 :           4 :                         *p = '/';
     142                 :           5 :         }
     143                 :             : 
     144                 :             :         /* ensure we restored umask */
     145                 :           1 :         (void) umask(oumask);
     146                 :             : 
     147                 :           2 :         return retval;
     148                 :           1 : }
        

Generated by: LCOV version 2.3.2-1