xref: /netbsd-src/sbin/mknod/mknod.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: mknod.c,v 1.35 2005/10/01 20:24:44 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Charles M. Hannum.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #if HAVE_NBTOOL_CONFIG_H
40 #include "nbtool_config.h"
41 #endif
42 
43 #include <sys/cdefs.h>
44 #ifndef lint
45 __COPYRIGHT("@(#) Copyright (c) 1998 The NetBSD Foundation, Inc.  All rights reserved.\n");
46 __RCSID("$NetBSD: mknod.c,v 1.35 2005/10/01 20:24:44 christos Exp $");
47 #endif /* not lint */
48 
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/param.h>
52 #if !HAVE_NBTOOL_CONFIG_H
53 #include <sys/sysctl.h>
54 #endif
55 
56 #include <err.h>
57 #include <errno.h>
58 #include <limits.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 #include <pwd.h>
63 #include <grp.h>
64 #include <string.h>
65 #include <ctype.h>
66 
67 #include "pack_dev.h"
68 
69 static int gid_name(const char *, gid_t *);
70 static portdev_t callPack(pack_t *, int, u_long *);
71 
72 	int	main(int, char *[]);
73 static	void	usage(void);
74 
75 #ifdef KERN_DRIVERS
76 static struct kinfo_drivers *kern_drivers;
77 static int num_drivers;
78 
79 static void get_device_info(void);
80 static void print_device_info(char **);
81 static int major_from_name(const char *, mode_t);
82 #endif
83 
84 #define	MAXARGS	3		/* 3 for bsdos, 2 for rest */
85 
86 int
87 main(int argc, char **argv)
88 {
89 	char	*name, *p;
90 	mode_t	 mode;
91 	portdev_t	 dev;
92 	pack_t	*pack;
93 	u_long	 numbers[MAXARGS];
94 	int	 n, ch, fifo, hasformat;
95 	int	 r_flag = 0;		/* force: delete existing entry */
96 #ifdef KERN_DRIVERS
97 	int	 l_flag = 0;		/* list device names and numbers */
98 	int	 major;
99 #endif
100 	void	*modes = 0;
101 	uid_t	 uid = -1;
102 	gid_t	 gid = -1;
103 	int	 rval;
104 
105 	dev = 0;
106 	fifo = hasformat = 0;
107 	pack = pack_native;
108 
109 #ifdef KERN_DRIVERS
110 	while ((ch = getopt(argc, argv, "lrRF:g:m:u:")) != -1) {
111 #else
112 	while ((ch = getopt(argc, argv, "rRF:g:m:u:")) != -1) {
113 #endif
114 		switch (ch) {
115 
116 #ifdef KERN_DRIVERS
117 		case 'l':
118 			l_flag = 1;
119 			break;
120 #endif
121 
122 		case 'r':
123 			r_flag = 1;
124 			break;
125 
126 		case 'R':
127 			r_flag = 2;
128 			break;
129 
130 		case 'F':
131 			pack = pack_find(optarg);
132 			if (pack == NULL)
133 				errx(1, "invalid format: %s", optarg);
134 			hasformat++;
135 			break;
136 
137 		case 'g':
138 			if (optarg[0] == '#') {
139 				gid = strtol(optarg + 1, &p, 10);
140 				if (*p == 0)
141 					break;
142 			}
143 			if (gid_name(optarg, &gid) == 0)
144 				break;
145 			gid = strtol(optarg, &p, 10);
146 			if (*p == 0)
147 				break;
148 			errx(1, "%s: invalid group name", optarg);
149 
150 		case 'm':
151 			modes = setmode(optarg);
152 			if (modes == NULL)
153 				err(1, "Cannot set file mode `%s'", optarg);
154 			break;
155 
156 		case 'u':
157 			if (optarg[0] == '#') {
158 				uid = strtol(optarg + 1, &p, 10);
159 				if (*p == 0)
160 					break;
161 			}
162 			if (uid_from_user(optarg, &uid) == 0)
163 				break;
164 			uid = strtol(optarg, &p, 10);
165 			if (*p == 0)
166 				break;
167 			errx(1, "%s: invalid user name", optarg);
168 
169 		default:
170 		case '?':
171 			usage();
172 		}
173 	}
174 	argc -= optind;
175 	argv += optind;
176 
177 #ifdef KERN_DRIVERS
178 	if (l_flag) {
179 		print_device_info(argv);
180 		return 0;
181 	}
182 #endif
183 
184 	if (argc < 2 || argc > 10)
185 		usage();
186 
187 	name = *argv;
188 	argc--;
189 	argv++;
190 
191 	umask(mode = umask(0));
192 	mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mode;
193 
194 	if (argv[0][1] != '\0')
195 		goto badtype;
196 	switch (*argv[0]) {
197 	case 'c':
198 		mode |= S_IFCHR;
199 		break;
200 
201 	case 'b':
202 		mode |= S_IFBLK;
203 		break;
204 
205 	case 'p':
206 		if (hasformat)
207 			errx(1, "format is meaningless for fifos");
208 		mode |= S_IFIFO;
209 		fifo = 1;
210 		break;
211 
212 	default:
213  badtype:
214 		errx(1, "node type must be 'b', 'c' or 'p'.");
215 	}
216 	argc--;
217 	argv++;
218 
219 	if (fifo) {
220 		if (argc != 0)
221 			usage();
222 	} else {
223 		if (argc < 1 || argc > MAXARGS)
224 			usage();
225 	}
226 
227 	for (n = 0; n < argc; n++) {
228 		errno = 0;
229 		numbers[n] = strtoul(argv[n], &p, 0);
230 		if (*p == 0 && errno == 0)
231 			continue;
232 #ifdef KERN_DRIVERS
233 		if (n == 0) {
234 			major = major_from_name(argv[0], mode);
235 			if (major != -1) {
236 				numbers[0] = major;
237 				continue;
238 			}
239 			if (!isdigit(*(unsigned char *)argv[0]))
240 				errx(1, "unknown driver: %s", argv[0]);
241 		}
242 #endif
243 		errx(1, "invalid number: %s", argv[n]);
244 	}
245 
246 	switch (argc) {
247 	case 0:
248 		dev = 0;
249 		break;
250 
251 	case 1:
252 		dev = numbers[0];
253 		break;
254 
255 	default:
256 		dev = callPack(pack, argc, numbers);
257 		break;
258 	}
259 
260 	if (modes != NULL)
261 		mode = getmode(modes, mode);
262 	umask(0);
263 	rval = fifo ? mkfifo(name, mode) : mknod(name, mode, dev);
264 	if (rval < 0 && errno == EEXIST && r_flag) {
265 		struct stat sb;
266 		if (lstat(name, &sb) != 0 || (!fifo && sb.st_rdev != dev))
267 			sb.st_mode = 0;
268 
269 		if ((sb.st_mode & S_IFMT) == (mode & S_IFMT)) {
270 			if (r_flag == 1)
271 				/* Ignore permissions and user/group */
272 				return 0;
273 			if (sb.st_mode != mode)
274 				rval = chmod(name, mode);
275 			else
276 				rval = 0;
277 		} else {
278 			unlink(name);
279 			rval = fifo ? mkfifo(name, mode)
280 				    : mknod(name, mode, dev);
281 		}
282 	}
283 	if (rval < 0)
284 		err(1, "%s", name);
285 	if ((uid != -1 || gid != -1) && chown(name, uid, gid) == -1)
286 		/* XXX Should we unlink the files here? */
287 		warn("%s: uid/gid not changed", name);
288 
289 	return 0;
290 }
291 
292 static void
293 usage(void)
294 {
295 	const char *progname = getprogname();
296 
297 	(void)fprintf(stderr,
298 	    "usage: %s [-rR] [-F format] [-m mode] [-u user] [-g group]\n",
299 	    progname);
300 	(void)fprintf(stderr,
301 #ifdef KERN_DRIVERS
302 	    "                   [ name [b | c] [major | driver] minor\n"
303 #else
304 	    "                   [ name [b | c] major minor\n"
305 #endif
306 	    "                   | name [b | c] major unit subunit\n"
307 	    "                   | name [b | c] number\n"
308 	    "                   | name p ]\n");
309 #ifdef KERN_DRIVERS
310 	(void)fprintf(stderr, "       %s -l [driver] ...\n", progname);
311 #endif
312 	exit(1);
313 }
314 
315 static int
316 gid_name(const char *name, gid_t *gid)
317 {
318 	struct group *g;
319 
320 	g = getgrnam(name);
321 	if (!g)
322 		return -1;
323 	*gid = g->gr_gid;
324 	return 0;
325 }
326 
327 static portdev_t
328 callPack(pack_t *f, int n, u_long *numbers)
329 {
330 	portdev_t d;
331 	const char *error = NULL;
332 
333 	d = (*f)(n, numbers, &error);
334 	if (error != NULL)
335 		errx(1, "%s", error);
336 	return d;
337 }
338 
339 #ifdef KERN_DRIVERS
340 static void
341 get_device_info(void)
342 {
343 	static int mib[2] = {CTL_KERN, KERN_DRIVERS};
344 	size_t len;
345 
346 	if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0)
347 		err(1, "kern.drivers" );
348 	kern_drivers = malloc(len);
349 	if (kern_drivers == NULL)
350 		err(1, "malloc");
351 	if (sysctl(mib, 2, kern_drivers, &len, NULL, 0) != 0)
352 		err(1, "kern.drivers" );
353 
354 	num_drivers = len / sizeof *kern_drivers;
355 }
356 
357 static void
358 print_device_info(char **names)
359 {
360 	int i;
361 	struct kinfo_drivers *kd;
362 
363 	if (kern_drivers == NULL)
364 		get_device_info();
365 
366 	do {
367 		kd = kern_drivers;
368 		for (i = 0; i < num_drivers; kd++, i++) {
369 			if (*names && strcmp(*names, kd->d_name))
370 				continue;
371 			printf("%s", kd->d_name);
372 			if (kd->d_cmajor != -1)
373 				printf(" character major %d", kd->d_cmajor);
374 			if (kd->d_bmajor != -1)
375 				printf(" block major %d", kd->d_bmajor);
376 			printf("\n");
377 		}
378 	} while (*names && *++names);
379 }
380 
381 static int
382 major_from_name(const char *name, mode_t mode)
383 {
384 	int i;
385 	struct kinfo_drivers *kd;
386 
387 	if (kern_drivers == NULL)
388 		get_device_info();
389 
390 	kd = kern_drivers;
391 	for (i = 0; i < num_drivers; kd++, i++) {
392 		if (strcmp(name, kd->d_name))
393 			continue;
394 		if (mode & S_IFCHR)
395 			return kd->d_cmajor;
396 		return kd->d_bmajor;
397 	}
398 	return -1;
399 }
400 #endif
401