1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Creates directory and it's parents if the parents do not 32 * exist yet. 33 * 34 * Returns -1 if fails for reasons other than non-existing 35 * parents. 36 * Does NOT simplify pathnames with . or .. in them. 37 */ 38 39 #include <sys/types.h> 40 #include <libgen.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <errno.h> 44 #include <string.h> 45 #include <sys/stat.h> 46 47 static char *simplify(const char *str); 48 49 int 50 mkdirp(const char *d, mode_t mode) 51 { 52 char *endptr, *ptr, *slash, *str; 53 54 str = simplify(d); 55 56 /* If space couldn't be allocated for the simplified names, return. */ 57 58 if (str == NULL) 59 return (-1); 60 61 /* Try to make the directory */ 62 63 if (mkdir(str, mode) == 0) { 64 free(str); 65 return (0); 66 } 67 if (errno != ENOENT) { 68 free(str); 69 return (-1); 70 } 71 endptr = strrchr(str, '\0'); 72 slash = strrchr(str, '/'); 73 74 /* Search upward for the non-existing parent */ 75 76 while (slash != NULL) { 77 78 ptr = slash; 79 *ptr = '\0'; 80 81 /* If reached an existing parent, break */ 82 83 if (access(str, F_OK) == 0) 84 break; 85 86 /* If non-existing parent */ 87 88 else { 89 slash = strrchr(str, '/'); 90 91 /* If under / or current directory, make it. */ 92 93 if (slash == NULL || slash == str) { 94 if (mkdir(str, mode) != 0 && errno != EEXIST) { 95 free(str); 96 return (-1); 97 } 98 break; 99 } 100 } 101 } 102 103 /* Create directories starting from upmost non-existing parent */ 104 105 while ((ptr = strchr(str, '\0')) != endptr) { 106 *ptr = '/'; 107 if (mkdir(str, mode) != 0 && errno != EEXIST) { 108 /* 109 * If the mkdir fails because str already 110 * exists (EEXIST), then str has the form 111 * "existing-dir/..", and this is really 112 * ok. (Remember, this loop is creating the 113 * portion of the path that didn't exist) 114 */ 115 free(str); 116 return (-1); 117 } 118 } 119 free(str); 120 return (0); 121 } 122 123 /* 124 * simplify - given a pathname, simplify that path by removing 125 * duplicate contiguous slashes. 126 * 127 * A simplified copy of the argument is returned to the 128 * caller, or NULL is returned on error. 129 * 130 * The caller should handle error reporting based upon the 131 * returned value, and should free the returned value, 132 * when appropriate. 133 */ 134 135 static char * 136 simplify(const char *str) 137 { 138 int i; 139 size_t mbPathlen; /* length of multi-byte path */ 140 size_t wcPathlen; /* length of wide-character path */ 141 wchar_t *wptr; /* scratch pointer */ 142 wchar_t *wcPath; /* wide-character version of the path */ 143 char *mbPath; /* The copy fo the path to be returned */ 144 145 /* 146 * bail out if there is nothing there. 147 */ 148 149 if (!str) { 150 errno = ENOENT; 151 return (NULL); 152 } 153 154 /* 155 * Get a copy of the argument. 156 */ 157 158 if ((mbPath = strdup(str)) == NULL) { 159 return (NULL); 160 } 161 162 /* 163 * convert the multi-byte version of the path to a 164 * wide-character rendering, for doing our figuring. 165 */ 166 167 mbPathlen = strlen(mbPath); 168 169 if ((wcPath = calloc(mbPathlen+1, sizeof (wchar_t))) == NULL) { 170 free(mbPath); 171 return (NULL); 172 } 173 174 if ((wcPathlen = mbstowcs(wcPath, mbPath, mbPathlen)) == (size_t)-1) { 175 free(mbPath); 176 free(wcPath); 177 return (NULL); 178 } 179 180 /* 181 * remove duplicate slashes first ("//../" -> "/") 182 */ 183 184 for (wptr = wcPath, i = 0; i < wcPathlen; i++) { 185 *wptr++ = wcPath[i]; 186 187 if (wcPath[i] == '/') { 188 i++; 189 190 while (wcPath[i] == '/') { 191 i++; 192 } 193 194 i--; 195 } 196 } 197 198 *wptr = '\0'; 199 200 /* 201 * now convert back to the multi-byte format. 202 */ 203 204 if (wcstombs(mbPath, wcPath, mbPathlen) == (size_t)-1) { 205 free(mbPath); 206 free(wcPath); 207 return (NULL); 208 } 209 210 free(wcPath); 211 return (mbPath); 212 } 213