1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1983, 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/stat.h>
34
35#include <err.h>
36#include <errno.h>
37#include <libgen.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sysexits.h>
42#include <unistd.h>
43
44static int	build(char *, mode_t);
45static void	usage(void);
46
47static int	vflag;
48
49int
50main(int argc, char *argv[])
51{
52	int ch, exitval, success, pflag;
53	mode_t omode;
54	void *set = NULL;
55	char *mode;
56
57	omode = pflag = 0;
58	mode = NULL;
59	while ((ch = getopt(argc, argv, "m:pv")) != -1)
60		switch(ch) {
61		case 'm':
62			mode = optarg;
63			break;
64		case 'p':
65			pflag = 1;
66			break;
67		case 'v':
68			vflag = 1;
69			break;
70		case '?':
71		default:
72			usage();
73		}
74
75	argc -= optind;
76	argv += optind;
77	if (argv[0] == NULL)
78		usage();
79
80	if (mode == NULL) {
81		omode = S_IRWXU | S_IRWXG | S_IRWXO;
82	} else {
83		if ((set = setmode(mode)) == NULL)
84			errx(1, "invalid file mode: %s", mode);
85		omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
86		free(set);
87	}
88
89	for (exitval = 0; *argv != NULL; ++argv) {
90		if (pflag) {
91			success = build(*argv, omode);
92		} else if (mkdir(*argv, omode) < 0) {
93			if (errno == ENOTDIR || errno == ENOENT)
94				warn("%s", dirname(*argv));
95			else
96				warn("%s", *argv);
97			success = 0;
98		} else {
99			success = 1;
100			if (vflag)
101				(void)printf("%s\n", *argv);
102		}
103		if (!success)
104			exitval = 1;
105		/*
106		 * The mkdir() and umask() calls both honor only the low
107		 * nine bits, so if you try to set a mode including the
108		 * sticky, setuid, setgid bits you lose them.  Don't do
109		 * this unless the user has specifically requested a mode,
110		 * as chmod will (obviously) ignore the umask.  Do this
111		 * on newly created directories only.
112		 */
113		if (success == 1 && mode != NULL && chmod(*argv, omode) == -1) {
114			warn("%s", *argv);
115			exitval = 1;
116		}
117	}
118	exit(exitval);
119}
120
121
122/*
123 * Returns 1 if a directory has been created,
124 * 2 if it already existed, and 0 on failure.
125 */
126static int
127build(char *path, mode_t omode)
128{
129	struct stat sb;
130	mode_t numask, oumask;
131	int first, last, retval;
132	char *p;
133
134	p = path;
135	oumask = 0;
136	retval = 1;
137	if (p[0] == '/')		/* Skip leading '/'. */
138		++p;
139	for (first = 1, last = 0; !last ; ++p) {
140		if (p[0] == '\0')
141			last = 1;
142		else if (p[0] != '/')
143			continue;
144		*p = '\0';
145		if (!last && p[1] == '\0')
146			last = 1;
147		if (first) {
148			/*
149			 * POSIX 1003.2:
150			 * For each dir operand that does not name an existing
151			 * directory, effects equivalent to those caused by the
152			 * following command shall occur:
153			 *
154			 * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
155			 *    mkdir [-m mode] dir
156			 *
157			 * We change the user's umask and then restore it,
158			 * instead of doing chmod's.
159			 */
160			oumask = umask(0);
161			numask = oumask & ~(S_IWUSR | S_IXUSR);
162			(void)umask(numask);
163			first = 0;
164		}
165		if (last)
166			(void)umask(oumask);
167		if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
168			if (errno == EEXIST || errno == EISDIR) {
169				if (stat(path, &sb) < 0) {
170					warn("%s", path);
171					retval = 0;
172					break;
173				} else if (!S_ISDIR(sb.st_mode)) {
174					if (last)
175						errno = EEXIST;
176					else
177						errno = ENOTDIR;
178					warn("%s", path);
179					retval = 0;
180					break;
181				}
182				if (last)
183					retval = 2;
184			} else {
185				warn("%s", path);
186				retval = 0;
187				break;
188			}
189		} else if (vflag)
190			printf("%s\n", path);
191		if (!last)
192		    *p = '/';
193	}
194	if (!first && !last)
195		(void)umask(oumask);
196	return (retval);
197}
198
199static void
200usage(void)
201{
202
203	(void)fprintf(stderr,
204	    "usage: mkdir [-pv] [-m mode] directory_name ...\n");
205	exit (EX_USAGE);
206}
207