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