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