00001 /* 00002 * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears 00003 * the following copyright notice: 00004 * 00005 * Copyright (c) 1983, 1992, 1993 00006 * The Regents of the University of California. All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions 00010 * are met: 00011 * 1. Redistributions of source code must retain the above copyright 00012 * notice, this list of conditions and the following disclaimer. 00013 * 2. Redistributions in binary form must reproduce the above copyright 00014 * notice, this list of conditions and the following disclaimer in the 00015 * documentation and/or other materials provided with the distribution. 00016 * 4. Neither the name of the University nor the names of its contributors 00017 * may be used to endorse or promote products derived from this software 00018 * without specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 00021 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 00024 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00025 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00026 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00027 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00028 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00029 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00030 * SUCH DAMAGE. 00031 */ 00032 00033 #include "c.h" 00034 00035 #include <sys/stat.h> 00036 00037 00038 /* 00039 * pg_mkdir_p --- create a directory and, if necessary, parent directories 00040 * 00041 * This is equivalent to "mkdir -p" except we don't complain if the target 00042 * directory already exists. 00043 * 00044 * We assume the path is in canonical form, i.e., uses / as the separator. 00045 * 00046 * omode is the file permissions bits for the target directory. Note that any 00047 * parent directories that have to be created get permissions according to the 00048 * prevailing umask, but with u+wx forced on to ensure we can create there. 00049 * (We declare omode as int, not mode_t, to minimize dependencies for port.h.) 00050 * 00051 * Returns 0 on success, -1 (with errno set) on failure. 00052 * 00053 * Note that on failure, the path arg has been modified to show the particular 00054 * directory level we had problems with. 00055 */ 00056 int 00057 pg_mkdir_p(char *path, int omode) 00058 { 00059 struct stat sb; 00060 mode_t numask, 00061 oumask; 00062 int last, 00063 retval; 00064 char *p; 00065 00066 retval = 0; 00067 p = path; 00068 00069 #ifdef WIN32 00070 /* skip network and drive specifiers for win32 */ 00071 if (strlen(p) >= 2) 00072 { 00073 if (p[0] == '/' && p[1] == '/') 00074 { 00075 /* network drive */ 00076 p = strstr(p + 2, "/"); 00077 if (p == NULL) 00078 { 00079 errno = EINVAL; 00080 return -1; 00081 } 00082 } 00083 else if (p[1] == ':' && 00084 ((p[0] >= 'a' && p[0] <= 'z') || 00085 (p[0] >= 'A' && p[0] <= 'Z'))) 00086 { 00087 /* local drive */ 00088 p += 2; 00089 } 00090 } 00091 #endif 00092 00093 /* 00094 * POSIX 1003.2: For each dir operand that does not name an existing 00095 * directory, effects equivalent to those caused by the following command 00096 * shall occcur: 00097 * 00098 * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir 00099 * 00100 * We change the user's umask and then restore it, instead of doing 00101 * chmod's. Note we assume umask() can't change errno. 00102 */ 00103 oumask = umask(0); 00104 numask = oumask & ~(S_IWUSR | S_IXUSR); 00105 (void) umask(numask); 00106 00107 if (p[0] == '/') /* Skip leading '/'. */ 00108 ++p; 00109 for (last = 0; !last; ++p) 00110 { 00111 if (p[0] == '\0') 00112 last = 1; 00113 else if (p[0] != '/') 00114 continue; 00115 *p = '\0'; 00116 if (!last && p[1] == '\0') 00117 last = 1; 00118 00119 if (last) 00120 (void) umask(oumask); 00121 00122 /* check for pre-existing directory */ 00123 if (stat(path, &sb) == 0) 00124 { 00125 if (!S_ISDIR(sb.st_mode)) 00126 { 00127 if (last) 00128 errno = EEXIST; 00129 else 00130 errno = ENOTDIR; 00131 retval = -1; 00132 break; 00133 } 00134 } 00135 else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) 00136 { 00137 retval = -1; 00138 break; 00139 } 00140 if (!last) 00141 *p = '/'; 00142 } 00143 00144 /* ensure we restored umask */ 00145 (void) umask(oumask); 00146 00147 return retval; 00148 }