diff options
Diffstat (limited to 'src/port/pgmkdirp.c')
-rw-r--r-- | src/port/pgmkdirp.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c new file mode 100644 index 0000000..d943559 --- /dev/null +++ b/src/port/pgmkdirp.c @@ -0,0 +1,148 @@ +/* + * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears + * the following copyright notice: + * + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "c.h" + +#include <sys/stat.h> + + +/* + * pg_mkdir_p --- create a directory and, if necessary, parent directories + * + * This is equivalent to "mkdir -p" except we don't complain if the target + * directory already exists. + * + * We assume the path is in canonical form, i.e., uses / as the separator. + * + * omode is the file permissions bits for the target directory. Note that any + * parent directories that have to be created get permissions according to the + * prevailing umask, but with u+wx forced on to ensure we can create there. + * (We declare omode as int, not mode_t, to minimize dependencies for port.h.) + * + * Returns 0 on success, -1 (with errno set) on failure. + * + * Note that on failure, the path arg has been modified to show the particular + * directory level we had problems with. + */ +int +pg_mkdir_p(char *path, int omode) +{ + struct stat sb; + mode_t numask, + oumask; + int last, + retval; + char *p; + + retval = 0; + p = path; + +#ifdef WIN32 + /* skip network and drive specifiers for win32 */ + if (strlen(p) >= 2) + { + if (p[0] == '/' && p[1] == '/') + { + /* network drive */ + p = strstr(p + 2, "/"); + if (p == NULL) + { + errno = EINVAL; + return -1; + } + } + else if (p[1] == ':' && + ((p[0] >= 'a' && p[0] <= 'z') || + (p[0] >= 'A' && p[0] <= 'Z'))) + { + /* local drive */ + p += 2; + } + } +#endif + + /* + * POSIX 1003.2: For each dir operand that does not name an existing + * directory, effects equivalent to those caused by the following command + * shall occur: + * + * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir + * + * We change the user's umask and then restore it, instead of doing + * chmod's. Note we assume umask() can't change errno. + */ + oumask = umask(0); + numask = oumask & ~(S_IWUSR | S_IXUSR); + (void) umask(numask); + + if (p[0] == '/') /* Skip leading '/'. */ + ++p; + for (last = 0; !last; ++p) + { + if (p[0] == '\0') + last = 1; + else if (p[0] != '/') + continue; + *p = '\0'; + if (!last && p[1] == '\0') + last = 1; + + if (last) + (void) umask(oumask); + + /* check for pre-existing directory */ + if (stat(path, &sb) == 0) + { + if (!S_ISDIR(sb.st_mode)) + { + if (last) + errno = EEXIST; + else + errno = ENOTDIR; + retval = -1; + break; + } + } + else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) + { + retval = -1; + break; + } + if (!last) + *p = '/'; + } + + /* ensure we restored umask */ + (void) umask(oumask); + + return retval; +} |