xref: /openbsd-src/sbin/mknod/mknod.c (revision df69c215c7c66baf660f3f65414fd34796c96152)
1 /*	$OpenBSD: mknod.c,v 1.31 2019/06/28 13:32:44 deraadt 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 <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 extern char *__progname;
34 
35 struct node {
36 	const char *name;
37 	mode_t mode;
38 	dev_t dev;
39 	char mflag;
40 };
41 
42 static int domakenodes(struct node *, int);
43 static dev_t compute_device(int, char **);
44 __dead static void usage(int);
45 
46 int
main(int argc,char * argv[])47 main(int argc, char *argv[])
48 {
49 	struct node *node;
50 	int ismkfifo;
51 	int n = 0;
52 	int mode = DEFFILEMODE;
53 	int mflag = 0;
54 	void *set;
55 	int ch;
56 
57 	if (pledge("stdio dpath", NULL) == -1)
58 		err(1, "pledge");
59 
60 	node = reallocarray(NULL, sizeof(struct node), argc);
61 	if (!node)
62 		err(1, NULL);
63 
64 	ismkfifo = strcmp(__progname, "mkfifo") == 0;
65 
66 	/* we parse all arguments upfront */
67 	while (argc > 1) {
68 		while ((ch = getopt(argc, argv, "m:")) != -1) {
69 			switch (ch) {
70 			case 'm':
71 				if (!(set = setmode(optarg)))
72 					errx(1, "invalid file mode '%s'",
73 					    optarg);
74 				/*
75 				 * In symbolic mode strings, the + and -
76 				 * operators are interpreted relative to
77 				 * an assumed initial mode of a=rw.
78 				 */
79 				mode = getmode(set, DEFFILEMODE);
80 				if ((mode & ACCESSPERMS) != mode)
81 					errx(1, "forbidden mode: %o", mode);
82 				mflag = 1;
83 				free(set);
84 				break;
85 			default:
86 				usage(ismkfifo);
87 			}
88 		}
89 		argc -= optind;
90 		argv += optind;
91 
92 		if (ismkfifo) {
93 			while (*argv) {
94 				node[n].mode = mode | S_IFIFO;
95 				node[n].mflag = mflag;
96 				node[n].name = *argv;
97 				node[n].dev = 0;
98 				n++;
99 				argv++;
100 			}
101 			/* XXX no multiple getopt */
102 			break;
103 		} else {
104 			if (argc < 2)
105 				usage(ismkfifo);
106 			node[n].mode = mode;
107 			node[n].mflag = mflag;
108 			node[n].name = argv[0];
109 			if (strlen(argv[1]) != 1)
110 				errx(1, "invalid device type '%s'", argv[1]);
111 
112 			/* XXX computation offset by one for next getopt */
113 			switch(argv[1][0]) {
114 			case 'p':
115 				node[n].mode |= S_IFIFO;
116 				node[n].dev = 0;
117 				argv++;
118 				argc--;
119 				break;
120 			case 'b':
121 				node[n].mode |= S_IFBLK;
122 				goto common;
123 			case 'c':
124 				node[n].mode |= S_IFCHR;
125 common:
126 				node[n].dev = compute_device(argc, argv);
127 				argv+=3;
128 				argc-=3;
129 				break;
130 			default:
131 				errx(1, "invalid device type '%s'", argv[1]);
132 			}
133 			n++;
134 		}
135 		optind = 1;
136 		optreset = 1;
137 	}
138 
139 	if (n == 0)
140 		usage(ismkfifo);
141 
142 	return (domakenodes(node, n));
143 }
144 
145 static dev_t
compute_device(int argc,char ** argv)146 compute_device(int argc, char **argv)
147 {
148 	dev_t dev;
149 	char *endp;
150 	unsigned long major, minor;
151 
152 	if (argc < 4)
153 		usage(0);
154 
155 	errno = 0;
156 	major = strtoul(argv[2], &endp, 0);
157 	if (endp == argv[2] || *endp != '\0')
158 		errx(1, "invalid major number '%s'", argv[2]);
159 	if (errno == ERANGE && major == ULONG_MAX)
160 		errx(1, "major number too large: '%s'", argv[2]);
161 
162 	errno = 0;
163 	minor = strtoul(argv[3], &endp, 0);
164 	if (endp == argv[3] || *endp != '\0')
165 		errx(1, "invalid minor number '%s'", argv[3]);
166 	if (errno == ERANGE && minor == ULONG_MAX)
167 		errx(1, "minor number too large: '%s'", argv[3]);
168 
169 	dev = makedev(major, minor);
170 	if (major(dev) != major || minor(dev) != minor)
171 		errx(1, "major or minor number too large (%lu %lu)", major,
172 		    minor);
173 
174 	return dev;
175 }
176 
177 static int
domakenodes(struct node * node,int n)178 domakenodes(struct node *node, int n)
179 {
180 	int done_umask = 0;
181 	int rv = 0;
182 	int i;
183 
184 	for (i = 0; i != n; i++) {
185 		int r;
186 		/*
187 		 * If the user specified a mode via `-m', don't allow the umask
188 		 * to modify it.  If no `-m' flag was specified, the default
189 		 * mode is the value of the bitwise inclusive or of S_IRUSR,
190 		 * S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH as
191 		 * modified by the umask.
192 		 */
193 		if (node[i].mflag && !done_umask) {
194 			(void)umask(0);
195 			done_umask = 1;
196 		}
197 
198 		r = mknod(node[i].name, node[i].mode, node[i].dev);
199 		if (r == -1) {
200 			warn("%s", node[i].name);
201 			rv = 1;
202 		}
203 	}
204 
205 	free(node);
206 	return rv;
207 }
208 
209 __dead static void
usage(int ismkfifo)210 usage(int ismkfifo)
211 {
212 
213 	if (ismkfifo == 1)
214 		(void)fprintf(stderr, "usage: %s [-m mode] fifo_name ...\n",
215 		    __progname);
216 	else {
217 		(void)fprintf(stderr,
218 		    "usage: %s [-m mode] name b|c major minor\n",
219 		    __progname);
220 		(void)fprintf(stderr, "       %s [-m mode] name p\n",
221 		    __progname);
222 	}
223 	exit(1);
224 }
225