xref: /freebsd/bin/mkdir/mkdir.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
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 #endif
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 
47 #include <err.h>
48 #include <errno.h>
49 #include <libgen.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sysexits.h>
54 #include <unistd.h>
55 
56 static int	build(char *, mode_t);
57 static void	usage(void);
58 
59 int vflag;
60 
61 int
62 main(int argc, char *argv[])
63 {
64 	int ch, exitval, success, pflag;
65 	mode_t omode, *set = (mode_t *)NULL;
66 	char *mode;
67 
68 	omode = pflag = 0;
69 	mode = NULL;
70 	while ((ch = getopt(argc, argv, "m:pv")) != -1)
71 		switch(ch) {
72 		case 'm':
73 			mode = optarg;
74 			break;
75 		case 'p':
76 			pflag = 1;
77 			break;
78 		case 'v':
79 			vflag = 1;
80 			break;
81 		case '?':
82 		default:
83 			usage();
84 		}
85 
86 	argc -= optind;
87 	argv += optind;
88 	if (argv[0] == NULL)
89 		usage();
90 
91 	if (mode == NULL) {
92 		omode = S_IRWXU | S_IRWXG | S_IRWXO;
93 	} else {
94 		if ((set = setmode(mode)) == NULL)
95 			errx(1, "invalid file mode: %s", mode);
96 		omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
97 		free(set);
98 	}
99 
100 	for (exitval = 0; *argv != NULL; ++argv) {
101 		success = 1;
102 		if (pflag) {
103 			if (build(*argv, omode))
104 				success = 0;
105 		} else if (mkdir(*argv, omode) < 0) {
106 			if (errno == ENOTDIR || errno == ENOENT)
107 				warn("%s", dirname(*argv));
108 			else
109 				warn("%s", *argv);
110 			success = 0;
111 		} else if (vflag)
112 			(void)printf("%s\n", *argv);
113 
114 		if (!success)
115 			exitval = 1;
116 		/*
117 		 * The mkdir() and umask() calls both honor only the low
118 		 * nine bits, so if you try to set a mode including the
119 		 * sticky, setuid, setgid bits you lose them.  Don't do
120 		 * this unless the user has specifically requested a mode,
121 		 * as chmod will (obviously) ignore the umask.
122 		 */
123 		if (success && mode != NULL && chmod(*argv, omode) == -1) {
124 			warn("%s", *argv);
125 			exitval = 1;
126 		}
127 	}
128 	exit(exitval);
129 }
130 
131 int
132 build(char *path, mode_t omode)
133 {
134 	struct stat sb;
135 	mode_t numask, oumask;
136 	int first, last, retval;
137 	char *p;
138 
139 	p = path;
140 	oumask = 0;
141 	retval = 0;
142 	if (p[0] == '/')		/* Skip leading '/'. */
143 		++p;
144 	for (first = 1, last = 0; !last ; ++p) {
145 		if (p[0] == '\0')
146 			last = 1;
147 		else if (p[0] != '/')
148 			continue;
149 		*p = '\0';
150 		if (p[1] == '\0')
151 			last = 1;
152 		if (first) {
153 			/*
154 			 * POSIX 1003.2:
155 			 * For each dir operand that does not name an existing
156 			 * directory, effects equivalent to those cased by the
157 			 * following command shall occcur:
158 			 *
159 			 * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
160 			 *    mkdir [-m mode] dir
161 			 *
162 			 * We change the user's umask and then restore it,
163 			 * instead of doing chmod's.
164 			 */
165 			oumask = umask(0);
166 			numask = oumask & ~(S_IWUSR | S_IXUSR);
167 			(void)umask(numask);
168 			first = 0;
169 		}
170 		if (last)
171 			(void)umask(oumask);
172 		if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
173 			if (errno == EEXIST || errno == EISDIR) {
174 				if (stat(path, &sb) < 0) {
175 					warn("%s", path);
176 					retval = 1;
177 					break;
178 				} else if (!S_ISDIR(sb.st_mode)) {
179 					if (last)
180 						errno = EEXIST;
181 					else
182 						errno = ENOTDIR;
183 					warn("%s", path);
184 					retval = 1;
185 					break;
186 				}
187 			} else {
188 				warn("%s", path);
189 				retval = 1;
190 				break;
191 			}
192 		} else if (vflag)
193 			printf("%s\n", path);
194 		if (!last)
195 		    *p = '/';
196 	}
197 	if (!first && !last)
198 		(void)umask(oumask);
199 	return (retval);
200 }
201 
202 void
203 usage(void)
204 {
205 
206 	(void)fprintf(stderr, "usage: mkdir [-pv] [-m mode] directory ...\n");
207 	exit (EX_USAGE);
208 }
209