1 /*
2 * Copyright (c) 1983, 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #if 0
31 #ifndef lint
32 static char const copyright[] =
33 "@(#) Copyright (c) 1983, 1992, 1993\n\
34 The Regents of the University of California. All rights reserved.\n";
35 #endif /* not lint */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94";
39 #endif /* not lint */
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD: src/bin/mkdir/mkdir.c,v 1.28 2004/04/06 20:06:48 markm Exp $");
42 #endif
43
44 #include "config.h"
45 #include <sys/types.h>
46 #include <sys/stat.h>
47
48 #include "err.h"
49 #include <errno.h>
50 #ifndef _MSC_VER
51 # include <libgen.h>
52 #endif
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #ifndef __HAIKU__
57 # include <sysexits.h>
58 #endif
59 #include <unistd.h>
60 #ifdef HAVE_ALLOCA_H
61 # include <alloca.h>
62 #endif
63 #include "getopt.h"
64 #ifdef __HAIKU__
65 # include "haikufakes.h"
66 #endif
67 #ifdef _MSC_VER
68 # include <malloc.h>
69 # include "mscfakes.h"
70 #endif
71 #include "kmkbuiltin.h"
72
73
74 static int vflag;
75 static struct option long_options[] =
76 {
77 { "help", no_argument, 0, 261 },
78 { "version", no_argument, 0, 262 },
79 { 0, 0, 0, 0 },
80 };
81
82
83 extern void * bsd_setmode(const char *p);
84 extern mode_t bsd_getmode(const void *bbox, mode_t omode);
85
86 static int build(char *, mode_t);
87 static int usage(FILE *);
88
89
90 int
kmk_builtin_mkdir(int argc,char * argv[],char ** envp)91 kmk_builtin_mkdir(int argc, char *argv[], char **envp)
92 {
93 int ch, exitval, success, pflag;
94 mode_t omode, *set = (mode_t *)NULL;
95 char *mode;
96
97 omode = pflag = 0;
98 mode = NULL;
99
100 /* reinitialize globals */
101 vflag = 0;
102
103 /* kmk: reset getopt and set progname */
104 g_progname = argv[0];
105 opterr = 1;
106 optarg = NULL;
107 optopt = 0;
108 optind = 0; /* init */
109 while ((ch = getopt_long(argc, argv, "m:pv", long_options, NULL)) != -1)
110 switch(ch) {
111 case 'm':
112 mode = optarg;
113 break;
114 case 'p':
115 pflag = 1;
116 break;
117 case 'v':
118 vflag = 1;
119 break;
120 case 261:
121 usage(stdout);
122 return 0;
123 case 262:
124 return kbuild_version(argv[0]);
125 case '?':
126 default:
127 return usage(stderr);
128 }
129
130 argc -= optind;
131 argv += optind;
132 if (argv[0] == NULL)
133 return usage(stderr);
134
135 if (mode == NULL) {
136 omode = S_IRWXU | S_IRWXG | S_IRWXO;
137 } else {
138 if ((set = bsd_setmode(mode)) == NULL)
139 return errx(1, "invalid file mode: %s", mode);
140 omode = bsd_getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
141 free(set);
142 }
143
144 for (exitval = 0; *argv != NULL; ++argv) {
145 success = 1;
146 if (pflag) {
147 if (build(*argv, omode))
148 success = 0;
149 } else if (mkdir(*argv, omode) < 0) {
150 if (errno == ENOTDIR || errno == ENOENT)
151 warn("mkdir: %s", dirname(*argv));
152 else
153 warn("mkdir: %s", *argv);
154 success = 0;
155 } else if (vflag)
156 (void)printf("%s\n", *argv);
157
158 if (!success)
159 exitval = 1;
160 /*
161 * The mkdir() and umask() calls both honor only the low
162 * nine bits, so if you try to set a mode including the
163 * sticky, setuid, setgid bits you lose them. Don't do
164 * this unless the user has specifically requested a mode,
165 * as chmod will (obviously) ignore the umask.
166 */
167 if (success && mode != NULL && chmod(*argv, omode) == -1) {
168 warn("chmod: %s", *argv);
169 exitval = 1;
170 }
171 }
172 return exitval;
173 }
174
175 static int
build(char * path,mode_t omode)176 build(char *path, mode_t omode)
177 {
178 struct stat sb;
179 mode_t numask, oumask;
180 int first, last, retval;
181 char *p;
182
183 const size_t len = strlen(path);
184 p = alloca(len + 1);
185 path = memcpy(p, path, len + 1);
186
187 #if defined(_MSC_VER) || defined(__EMX__)
188 /* correct slashes. */
189 p = strchr(path, '\\');
190 while (p) {
191 *p++ = '/';
192 p = strchr(p, '\\');
193 }
194 #endif
195
196 p = path;
197 oumask = 0;
198 retval = 0;
199 #if defined(_MSC_VER) || defined(__EMX__)
200 if ( ( (p[0] >= 'A' && p[0] <= 'Z')
201 || (p[0] >= 'a' && p[0] <= 'z'))
202 && p[1] == ':')
203 p += 2; /* skip the drive letter */
204 else if ( p[0] == '/'
205 && p[1] == '/'
206 && p[2] != '/')
207 {
208 /* UNC */
209 p += 2;
210 while (*p && *p != '/') /* server */
211 p++;
212 while (*p == '/')
213 p++;
214 while (*p && *p != '/') /* share */
215 p++;
216 }
217 #endif
218 if (p[0] == '/') /* Skip leading '/'. */
219 ++p;
220 for (first = 1, last = 0; !last ; ++p) {
221 if (p[0] == '\0')
222 last = 1;
223 else if (p[0] != '/')
224 continue;
225 *p = '\0';
226 if (!last && p[1] == '\0')
227 last = 1;
228 if (first) {
229 /*
230 * POSIX 1003.2:
231 * For each dir operand that does not name an existing
232 * directory, effects equivalent to those cased by the
233 * following command shall occcur:
234 *
235 * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
236 * mkdir [-m mode] dir
237 *
238 * We change the user's umask and then restore it,
239 * instead of doing chmod's.
240 */
241 oumask = umask(0);
242 numask = oumask & ~(S_IWUSR | S_IXUSR);
243 (void)umask(numask);
244 first = 0;
245 }
246 if (last)
247 (void)umask(oumask);
248 if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
249 if (errno == EEXIST || errno == EISDIR
250 || errno == ENOSYS /* (solaris crap) */
251 || errno == EACCES /* (ditto) */) {
252 if (stat(path, &sb) < 0) {
253 warn("stat: %s", path);
254 retval = 1;
255 break;
256 } else if (!S_ISDIR(sb.st_mode)) {
257 if (last)
258 errno = EEXIST;
259 else
260 errno = ENOTDIR;
261 warn("st_mode: %s", path);
262 retval = 1;
263 break;
264 }
265 } else {
266 warn("mkdir: %s", path);
267 retval = 1;
268 break;
269 }
270 } else if (vflag)
271 printf("%s\n", path);
272 if (!last)
273 *p = '/';
274 }
275 if (!first && !last)
276 (void)umask(oumask);
277 return (retval);
278 }
279
280 static int
usage(FILE * pf)281 usage(FILE *pf)
282 {
283 fprintf(pf, "usage: %s [-pv] [-m mode] directory ...\n"
284 " or: %s --help\n"
285 " or: %s --version\n",
286 g_progname, g_progname, g_progname);
287 return EX_USAGE;
288 }
289