1/*	$NetBSD: mknod.c,v 1.39 2009/02/13 01:37:23 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.39 2009/02/13 01:37:23 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
63static int gid_name(const char *, gid_t *);
64static portdev_t callPack(pack_t *, int, u_long *);
65
66__dead static	void	usage(void);
67
68#ifdef KERN_DRIVERS
69static struct kinfo_drivers *kern_drivers;
70static int num_drivers;
71
72static void get_device_info(void);
73static void print_device_info(char **);
74static int major_from_name(const char *, mode_t);
75#endif
76
77#define	MAXARGS	3		/* 3 for bsdos, 2 for rest */
78
79int
80main(int argc, char **argv)
81{
82	char	*name, *p;
83	mode_t	 mode;
84	portdev_t	 dev;
85	pack_t	*pack;
86	u_long	 numbers[MAXARGS];
87	int	 n, ch, fifo, hasformat;
88	int	 r_flag = 0;		/* force: delete existing entry */
89#ifdef KERN_DRIVERS
90	int	 l_flag = 0;		/* list device names and numbers */
91	int	 major;
92#endif
93	void	*modes = 0;
94	uid_t	 uid = -1;
95	gid_t	 gid = -1;
96	int	 rval;
97
98	dev = 0;
99	fifo = hasformat = 0;
100	pack = pack_native;
101
102#ifdef KERN_DRIVERS
103	while ((ch = getopt(argc, argv, "lrRF:g:m:u:")) != -1) {
104#else
105	while ((ch = getopt(argc, argv, "rRF:g:m:u:")) != -1) {
106#endif
107		switch (ch) {
108
109#ifdef KERN_DRIVERS
110		case 'l':
111			l_flag = 1;
112			break;
113#endif
114
115		case 'r':
116			r_flag = 1;
117			break;
118
119		case 'R':
120			r_flag = 2;
121			break;
122
123		case 'F':
124			pack = pack_find(optarg);
125			if (pack == NULL)
126				errx(1, "invalid format: %s", optarg);
127			hasformat++;
128			break;
129
130		case 'g':
131			if (optarg[0] == '#') {
132				gid = strtol(optarg + 1, &p, 10);
133				if (*p == 0)
134					break;
135			}
136			if (gid_name(optarg, &gid) == 0)
137				break;
138			gid = strtol(optarg, &p, 10);
139			if (*p == 0)
140				break;
141			errx(1, "%s: invalid group name", optarg);
142
143		case 'm':
144			modes = setmode(optarg);
145			if (modes == NULL)
146				err(1, "Cannot set file mode `%s'", optarg);
147			break;
148
149		case 'u':
150			if (optarg[0] == '#') {
151				uid = strtol(optarg + 1, &p, 10);
152				if (*p == 0)
153					break;
154			}
155			if (uid_from_user(optarg, &uid) == 0)
156				break;
157			uid = strtol(optarg, &p, 10);
158			if (*p == 0)
159				break;
160			errx(1, "%s: invalid user name", optarg);
161
162		default:
163		case '?':
164			usage();
165		}
166	}
167	argc -= optind;
168	argv += optind;
169
170#ifdef KERN_DRIVERS
171	if (l_flag) {
172		print_device_info(argv);
173		return 0;
174	}
175#endif
176
177	if (argc < 2 || argc > 10)
178		usage();
179
180	name = *argv;
181	argc--;
182	argv++;
183
184	umask(mode = umask(0));
185	mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) & ~mode;
186
187	if (argv[0][1] != '\0')
188		goto badtype;
189	switch (*argv[0]) {
190	case 'c':
191		mode |= S_IFCHR;
192		break;
193
194	case 'b':
195		mode |= S_IFBLK;
196		break;
197
198	case 'p':
199		if (hasformat)
200			errx(1, "format is meaningless for fifos");
201		mode |= S_IFIFO;
202		fifo = 1;
203		break;
204
205	default:
206 badtype:
207		errx(1, "node type must be 'b', 'c' or 'p'.");
208	}
209	argc--;
210	argv++;
211
212	if (fifo) {
213		if (argc != 0)
214			usage();
215	} else {
216		if (argc < 1 || argc > MAXARGS)
217			usage();
218	}
219
220	for (n = 0; n < argc; n++) {
221		errno = 0;
222		numbers[n] = strtoul(argv[n], &p, 0);
223		if (*p == 0 && errno == 0)
224			continue;
225#ifdef KERN_DRIVERS
226		if (n == 0) {
227			major = major_from_name(argv[0], mode);
228			if (major != -1) {
229				numbers[0] = major;
230				continue;
231			}
232			if (!isdigit(*(unsigned char *)argv[0]))
233				errx(1, "unknown driver: %s", argv[0]);
234		}
235#endif
236		errx(1, "invalid number: %s", argv[n]);
237	}
238
239	switch (argc) {
240	case 0:
241		dev = 0;
242		break;
243
244	case 1:
245		dev = numbers[0];
246		break;
247
248	default:
249		dev = callPack(pack, argc, numbers);
250		break;
251	}
252
253	if (modes != NULL)
254		mode = getmode(modes, mode);
255	umask(0);
256	rval = fifo ? mkfifo(name, mode) : mknod(name, mode, dev);
257	if (rval < 0 && errno == EEXIST && r_flag) {
258		struct stat sb;
259		if (lstat(name, &sb) != 0 || (!fifo && sb.st_rdev != dev))
260			sb.st_mode = 0;
261
262		if ((sb.st_mode & S_IFMT) == (mode & S_IFMT)) {
263			if (r_flag == 1)
264				/* Ignore permissions and user/group */
265				return 0;
266			if (sb.st_mode != mode)
267				rval = chmod(name, mode);
268			else
269				rval = 0;
270		} else {
271			unlink(name);
272			rval = fifo ? mkfifo(name, mode)
273				    : mknod(name, mode, dev);
274		}
275	}
276	if (rval < 0)
277		err(1, "%s", name);
278	if ((uid != (uid_t)-1 || gid != (uid_t)-1) && chown(name, uid, gid) == -1)
279		/* XXX Should we unlink the files here? */
280		warn("%s: uid/gid not changed", name);
281
282	return 0;
283}
284
285static void
286usage(void)
287{
288	const char *progname = getprogname();
289
290	(void)fprintf(stderr,
291	    "usage: %s [-rR] [-F format] [-m mode] [-u user] [-g group]\n",
292	    progname);
293	(void)fprintf(stderr,
294#ifdef KERN_DRIVERS
295	    "                   [ name [b | c] [major | driver] minor\n"
296#else
297	    "                   [ name [b | c] major minor\n"
298#endif
299	    "                   | name [b | c] major unit subunit\n"
300	    "                   | name [b | c] number\n"
301	    "                   | name p ]\n");
302#ifdef KERN_DRIVERS
303	(void)fprintf(stderr, "       %s -l [driver] ...\n", progname);
304#endif
305	exit(1);
306}
307
308static int
309gid_name(const char *name, gid_t *gid)
310{
311	struct group *g;
312
313	g = getgrnam(name);
314	if (!g)
315		return -1;
316	*gid = g->gr_gid;
317	return 0;
318}
319
320static portdev_t
321callPack(pack_t *f, int n, u_long *numbers)
322{
323	portdev_t d;
324	const char *error = NULL;
325
326	d = (*f)(n, numbers, &error);
327	if (error != NULL)
328		errx(1, "%s", error);
329	return d;
330}
331
332#ifdef KERN_DRIVERS
333static void
334get_device_info(void)
335{
336	static int mib[2] = {CTL_KERN, KERN_DRIVERS};
337	size_t len;
338
339	if (sysctl(mib, 2, NULL, &len, NULL, 0) != 0)
340		err(1, "kern.drivers" );
341	kern_drivers = malloc(len);
342	if (kern_drivers == NULL)
343		err(1, "malloc");
344	if (sysctl(mib, 2, kern_drivers, &len, NULL, 0) != 0)
345		err(1, "kern.drivers" );
346
347	num_drivers = len / sizeof *kern_drivers;
348}
349
350static void
351print_device_info(char **names)
352{
353	int i;
354	struct kinfo_drivers *kd;
355
356	if (kern_drivers == NULL)
357		get_device_info();
358
359	do {
360		kd = kern_drivers;
361		for (i = 0; i < num_drivers; kd++, i++) {
362			if (*names && strcmp(*names, kd->d_name))
363				continue;
364			printf("%s", kd->d_name);
365			if (kd->d_cmajor != -1)
366				printf(" character major %d", kd->d_cmajor);
367			if (kd->d_bmajor != -1)
368				printf(" block major %d", kd->d_bmajor);
369			printf("\n");
370		}
371	} while (*names && *++names);
372}
373
374static int
375major_from_name(const char *name, mode_t mode)
376{
377	int i;
378	struct kinfo_drivers *kd;
379
380	if (kern_drivers == NULL)
381		get_device_info();
382
383	kd = kern_drivers;
384	for (i = 0; i < num_drivers; kd++, i++) {
385		if (strcmp(name, kd->d_name))
386			continue;
387		if (S_ISCHR(mode))
388			return kd->d_cmajor;
389		return kd->d_bmajor;
390	}
391	return -1;
392}
393#endif
394