xref: /freebsd/sys/contrib/openzfs/lib/libspl/mkdirp.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
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
mkdirp(const char * d,mode_t mode)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 *
simplify(const char * str)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