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