| /* |
| * 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); |
| |
| if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) == 0) |
| { |
| /* path does not pre-exist and is successfully created */ |
| } |
| else if (errno == EEXIST) |
| { |
| /* path exists, double check it is a dir */ |
| |
| if (stat(path, &sb) < 0) |
| { |
| retval = -1; |
| break; |
| } |
| else if (!S_ISDIR(sb.st_mode)) |
| { |
| /* path is not a dir at all */ |
| if (last) |
| errno = EEXIST; |
| else |
| errno = ENOTDIR; |
| retval = -1; |
| break; |
| } |
| |
| /* now we know that path is a dir */ |
| } |
| else |
| { |
| retval = -1; |
| break; |
| } |
| if (!last) |
| *p = '/'; |
| } |
| |
| /* ensure we restored umask */ |
| (void) umask(oumask); |
| |
| return retval; |
| } |