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