xref: /netbsd-src/external/cddl/osnet/lib/libzfs/mkdirp.c (revision fc8ec0b896620494ee06c82427885881da50d528)
1 /*	$NetBSD: mkdirp.c,v 1.1 2009/08/07 20:57:56 haad Exp $	*/
2 
3 /*
4  * CDDL HEADER START
5  *
6  * The contents of this file are subject to the terms of the
7  * Common Development and Distribution License, Version 1.0 only
8  * (the "License").  You may not use this file except in compliance
9  * with the License.
10  *
11  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12  * or http://www.opensolaris.org/os/licensing.
13  * See the License for the specific language governing permissions
14  * and limitations under the License.
15  *
16  * When distributing Covered Code, include this CDDL HEADER in each
17  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18  * If applicable, add the following below this CDDL HEADER, with the
19  * fields enclosed by brackets "[]" replaced with your own identifying
20  * information: Portions Copyright [yyyy] [name of copyright owner]
21  *
22  * CDDL HEADER END
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 
52 static char *simplify(const char *str);
53 
54 int
mkdirp(const char * d,mode_t mode)55 mkdirp(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 
140 static char *
simplify(const char * str)141 simplify(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