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