xref: /openbsd-src/sbin/mknod/mknod.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: mknod.c,v 1.29 2016/03/07 19:16:06 tb Exp $	*/
2 /*	$NetBSD: mknod.c,v 1.8 1995/08/11 00:08:18 jtc Exp $	*/
3 
4 /*
5  * Copyright (c) 1997-2016 Theo de Raadt <deraadt@openbsd.org>,
6  *	Marc Espie <espie@openbsd.org>,	Todd Miller <millert@openbsd.org>,
7  *	Martin Natano <natano@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #include <err.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <locale.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 
34 extern char *__progname;
35 
36 struct node {
37 	const char *name;
38 	mode_t mode;
39 	dev_t dev;
40 	char mflag;
41 };
42 
43 static int domakenodes(struct node *, int);
44 static dev_t compute_device(int, char **);
45 __dead static void usage(int);
46 
47 int
48 main(int argc, char *argv[])
49 {
50 	struct node *node;
51 	int ismkfifo;
52 	int n = 0;
53 	int mode = DEFFILEMODE;
54 	int mflag = 0;
55 	void *set;
56 	int ch;
57 
58 	setlocale(LC_ALL, "");
59 
60 	if (pledge("stdio dpath", NULL) == -1)
61 		err(1, "pledge");
62 
63 	node = reallocarray(NULL, sizeof(struct node), argc);
64 	if (!node)
65 		err(1, NULL);
66 
67 	ismkfifo = strcmp(__progname, "mkfifo") == 0;
68 
69 	/* we parse all arguments upfront */
70 	while (argc > 1) {
71 		while ((ch = getopt(argc, argv, "m:")) != -1) {
72 			switch (ch) {
73 			case 'm':
74 				if (!(set = setmode(optarg)))
75 					errx(1, "invalid file mode '%s'",
76 					    optarg);
77 				/*
78 				 * In symbolic mode strings, the + and -
79 				 * operators are interpreted relative to
80 				 * an assumed initial mode of a=rw.
81 				 */
82 				mode = getmode(set, DEFFILEMODE);
83 				if ((mode & ACCESSPERMS) != mode)
84 					errx(1, "forbidden mode: %o", mode);
85 				mflag = 1;
86 				free(set);
87 				break;
88 			default:
89 				usage(ismkfifo);
90 			}
91 		}
92 		argc -= optind;
93 		argv += optind;
94 
95 		if (ismkfifo) {
96 			while (*argv) {
97 				node[n].mode = mode | S_IFIFO;
98 				node[n].mflag = mflag;
99 				node[n].name = *argv;
100 				node[n].dev = 0;
101 				n++;
102 				argv++;
103 			}
104 			/* XXX no multiple getopt */
105 			break;
106 		} else {
107 			if (argc < 2)
108 				usage(ismkfifo);
109 			node[n].mode = mode;
110 			node[n].mflag = mflag;
111 			node[n].name = argv[0];
112 			if (strlen(argv[1]) != 1)
113 				errx(1, "invalid device type '%s'", argv[1]);
114 
115 			/* XXX computation offset by one for next getopt */
116 			switch(argv[1][0]) {
117 			case 'p':
118 				node[n].mode |= S_IFIFO;
119 				node[n].dev = 0;
120 				argv++;
121 				argc--;
122 				break;
123 			case 'b':
124 				node[n].mode |= S_IFBLK;
125 				goto common;
126 			case 'c':
127 				node[n].mode |= S_IFCHR;
128 common:
129 				node[n].dev = compute_device(argc, argv);
130 				argv+=3;
131 				argc-=3;
132 				break;
133 			default:
134 				errx(1, "invalid device type '%s'", argv[1]);
135 			}
136 			n++;
137 		}
138 		optind = 1;
139 		optreset = 1;
140 	}
141 
142 	if (n == 0)
143 		usage(ismkfifo);
144 
145 	return (domakenodes(node, n));
146 }
147 
148 static dev_t
149 compute_device(int argc, char **argv)
150 {
151 	dev_t dev;
152 	char *endp;
153 	unsigned long major, minor;
154 
155 	if (argc < 4)
156 		usage(0);
157 
158 	errno = 0;
159 	major = strtoul(argv[2], &endp, 0);
160 	if (endp == argv[2] || *endp != '\0')
161 		errx(1, "invalid major number '%s'", argv[2]);
162 	if (errno == ERANGE && major == ULONG_MAX)
163 		errx(1, "major number too large: '%s'", argv[2]);
164 
165 	errno = 0;
166 	minor = strtoul(argv[3], &endp, 0);
167 	if (endp == argv[3] || *endp != '\0')
168 		errx(1, "invalid minor number '%s'", argv[3]);
169 	if (errno == ERANGE && minor == ULONG_MAX)
170 		errx(1, "minor number too large: '%s'", argv[3]);
171 
172 	dev = makedev(major, minor);
173 	if (major(dev) != major || minor(dev) != minor)
174 		errx(1, "major or minor number too large (%lu %lu)", major,
175 		    minor);
176 
177 	return dev;
178 }
179 
180 static int
181 domakenodes(struct node *node, int n)
182 {
183 	int done_umask = 0;
184 	int rv = 0;
185 	int i;
186 
187 	for (i = 0; i != n; i++) {
188 		int r;
189 		/*
190 		 * If the user specified a mode via `-m', don't allow the umask
191 		 * to modify it.  If no `-m' flag was specified, the default
192 		 * mode is the value of the bitwise inclusive or of S_IRUSR,
193 		 * S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH as
194 		 * modified by the umask.
195 		 */
196 		if (node[i].mflag && !done_umask) {
197 			(void)umask(0);
198 			done_umask = 1;
199 		}
200 
201 		r = mknod(node[i].name, node[i].mode, node[i].dev);
202 		if (r < 0) {
203 			warn("%s", node[i].name);
204 			rv = 1;
205 		}
206 	}
207 
208 	free(node);
209 	return rv;
210 }
211 
212 __dead static void
213 usage(int ismkfifo)
214 {
215 
216 	if (ismkfifo == 1)
217 		(void)fprintf(stderr, "usage: %s [-m mode] fifo_name ...\n",
218 		    __progname);
219 	else {
220 		(void)fprintf(stderr,
221 		    "usage: %s [-m mode] name b|c major minor\n",
222 		    __progname);
223 		(void)fprintf(stderr, "       %s [-m mode] name p\n",
224 		    __progname);
225 	}
226 	exit(1);
227 }
228