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