xref: /dragonfly/usr.sbin/vnconfig/vnconfig.c (revision 6e285212)
1 /*
2  * Copyright (c) 1993 University of Utah.
3  * Copyright (c) 1990, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * the Systems Programming Group of the University of Utah Computer
8  * Science Department.
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 University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$
39  *
40  * @(#)vnconfig.c	8.1 (Berkeley) 12/15/93
41  * $FreeBSD: src/usr.sbin/vnconfig/vnconfig.c,v 1.13.2.7 2003/06/02 09:10:27 maxim Exp $
42  * $DragonFly: src/usr.sbin/vnconfig/vnconfig.c,v 1.2 2003/06/17 04:30:04 dillon Exp $
43  */
44 
45 #include <ctype.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <paths.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <fcntl.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <sys/param.h>
55 #include <sys/ioctl.h>
56 #include <sys/linker.h>
57 #include <sys/mount.h>
58 #include <sys/module.h>
59 #include <sys/stat.h>
60 #include <sys/vnioctl.h>
61 #include <ufs/ufs/ufsmount.h>
62 
63 #define LINESIZE	1024
64 #define ZBUFSIZE	32768
65 
66 struct vndisk {
67 	char	*dev;
68 	char	*file;
69 	char	*autolabel;
70 	int	flags;
71 	int	size;
72 	char	*oarg;
73 } *vndisks;
74 
75 #define VN_CONFIG	0x01
76 #define VN_UNCONFIG	0x02
77 #define VN_ENABLE	0x04
78 #define VN_DISABLE	0x08
79 #define	VN_SWAP		0x10
80 #define VN_MOUNTRO	0x20
81 #define VN_MOUNTRW	0x40
82 #define VN_IGNORE	0x80
83 #define VN_SET		0x100
84 #define VN_RESET	0x200
85 #define VN_TRUNCATE	0x400
86 #define VN_ZERO		0x800
87 
88 int nvndisks;
89 
90 int all = 0;
91 int verbose = 0;
92 int global = 0;
93 u_long setopt = 0;
94 u_long resetopt = 0;
95 char *configfile;
96 
97 int config __P((struct vndisk *));
98 void getoptions __P((struct vndisk *, char *));
99 char *rawdevice __P((char *));
100 void readconfig __P((int));
101 static void usage __P((void));
102 static int getsize(const char *arg);
103 static void do_autolabel(const char *dev, const char *label);
104 int what_opt __P((char *, u_long *));
105 
106 int
107 main(argc, argv)
108 	char **argv;
109 {
110 	register int i, rv;
111 	int flags = 0;
112 	int size = 0;
113 	char *autolabel = NULL;
114 	char *s;
115 
116 	configfile = _PATH_VNTAB;
117 	while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1)
118 		switch (i) {
119 
120 		/* all -- use config file */
121 		case 'a':
122 			all++;
123 			break;
124 
125 		/* configure */
126 		case 'c':
127 			flags |= VN_CONFIG;
128 			flags &= ~VN_UNCONFIG;
129 			break;
130 
131 		/* disable */
132 		case 'd':
133 			flags |= VN_DISABLE;
134 			flags &= ~VN_ENABLE;
135 			break;
136 
137 		/* enable */
138 		case 'e':
139 			flags |= (VN_ENABLE|VN_CONFIG);
140 			flags &= ~(VN_DISABLE|VN_UNCONFIG);
141 			break;
142 
143 		/* alternate config file */
144 		case 'f':
145 			configfile = optarg;
146 			break;
147 
148 		/* fiddle global options */
149 		case 'g':
150 			global = 1 - global;
151 			break;
152 
153 		/* reset options */
154 		case 'r':
155 			for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
156 				if (what_opt(s, &resetopt))
157 					errx(1, "invalid options '%s'", s);
158 			}
159 			flags |= VN_RESET;
160 			break;
161 
162 		/* set options */
163 		case 's':
164 			for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) {
165 				if (what_opt(s, &setopt))
166 					errx(1, "invalid options '%s'", s);
167 			}
168 			flags |= VN_SET;
169 			break;
170 
171 		/* unconfigure */
172 		case 'u':
173 			flags |= (VN_DISABLE|VN_UNCONFIG);
174 			flags &= ~(VN_ENABLE|VN_CONFIG);
175 			break;
176 
177 		/* verbose */
178 		case 'v':
179 			verbose++;
180 			break;
181 
182 		case 'S':
183 			size = getsize(optarg);
184 			flags |= VN_CONFIG;
185 			flags &= ~VN_UNCONFIG;
186 			break;
187 
188 		case 'T':
189 			flags |= VN_TRUNCATE;
190 			break;
191 
192 		case 'Z':
193 			flags |= VN_ZERO;
194 			break;
195 
196 		case 'L':
197 			autolabel = optarg;
198 			break;
199 
200 		default:
201 			usage();
202 		}
203 
204 	if (modfind("vn") < 0)
205 		if (kldload("vn") < 0 || modfind("vn") < 0)
206 			warnx( "cannot find or load \"vn\" kernel module");
207 
208 	if (flags == 0)
209 		flags = VN_CONFIG;
210 	if (all) {
211 		readconfig(flags);
212 	} else {
213 		vndisks = calloc(sizeof(struct vndisk), 1);
214 		if (argc < optind + 1)
215 			usage();
216 		vndisks[0].dev = argv[optind++];
217 		vndisks[0].file = argv[optind++];	/* may be NULL */
218 		vndisks[0].flags = flags;
219 		vndisks[0].size = size;
220 		vndisks[0].autolabel = autolabel;
221 		if (optind < argc)
222 			getoptions(&vndisks[0], argv[optind]);
223 		nvndisks = 1;
224 	}
225 	rv = 0;
226 	for (i = 0; i < nvndisks; i++)
227 		rv += config(&vndisks[i]);
228 	exit(rv);
229 }
230 
231 int
232 what_opt(str,p)
233 	char *str;
234 	u_long *p;
235 {
236 	if (!strcmp(str,"reserve")) { *p |= VN_RESERVE; return 0; }
237 	if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
238 	if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
239 	if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
240 	if (!strcmp(str,"io")) { *p |= VN_IO; return 0; }
241 	if (!strcmp(str,"all")) { *p |= ~0; return 0; }
242 	if (!strcmp(str,"none")) { *p |= 0; return 0; }
243 	return 1;
244 }
245 
246 int
247 config(vnp)
248 	struct vndisk *vnp;
249 {
250 	char *dev, *file, *oarg;
251 	int flags, status;
252 	struct vn_ioctl vnio;
253 	register int rv;
254 	char *rdev;
255 	FILE *f;
256 	u_long l;
257 	int pgsize = getpagesize();
258 
259 	status = rv = 0;
260 
261 	/*
262 	 * Prepend "/dev/" to the specified device name, if necessary.
263 	 * Operate on vnp->dev because it is used later.
264 	 */
265 	if (vnp->dev[0] != '/' && vnp->dev[0] != '.')
266 		(void)asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev);
267 	dev = vnp->dev;
268 	file = vnp->file;
269 	flags = vnp->flags;
270 	oarg = vnp->oarg;
271 
272 	if (flags & VN_IGNORE)
273 		return(0);
274 
275 	/*
276 	 * When a regular file has been specified, do any requested setup
277 	 * of the file.  Truncation (also creates the file if necessary),
278 	 * sizing, and zeroing.
279 	 */
280 
281 	if (file && vnp->size != 0 && (flags & VN_CONFIG)) {
282 		int  fd;
283 		struct stat st;
284 
285 		if (flags & VN_TRUNCATE)
286 			fd = open(file, O_RDWR|O_CREAT|O_TRUNC);
287 		else
288 			fd = open(file, O_RDWR);
289 		if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
290 			if (st.st_size < (off_t)vnp->size * pgsize)
291 				ftruncate(fd, (off_t)vnp->size * pgsize);
292 			if (vnp->size != 0)
293 				st.st_size = (off_t)vnp->size * pgsize;
294 
295 			if (flags & VN_ZERO) {
296 				char *buf = malloc(ZBUFSIZE);
297 				bzero(buf, ZBUFSIZE);
298 				while (st.st_size > 0) {
299 					int n = (st.st_size > ZBUFSIZE) ?
300 					    ZBUFSIZE : (int)st.st_size;
301 					if (write(fd, buf, n) != n) {
302 						ftruncate(fd, 0);
303 						printf("Unable to ZERO file %s\n", file);
304 						return(0);
305 					}
306 					st.st_size -= (off_t)n;
307 				}
308 			}
309 			close(fd);
310 		} else {
311 			printf("Unable to open file %s\n", file);
312 			return(0);
313 		}
314 	}
315 
316 	rdev = rawdevice(dev);
317 	f = fopen(rdev, "rw");
318 	if (f == NULL) {
319 		warn("%s", dev);
320 		return(1);
321 	}
322 	vnio.vn_file = file;
323 	vnio.vn_size = vnp->size;	/* non-zero only if swap backed */
324 
325 	/*
326 	 * Disable the device
327 	 */
328 	if (flags & VN_DISABLE) {
329 		if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
330 			rv = unmount(oarg, 0);
331 			if (rv) {
332 				status--;
333 				if (errno == EBUSY)
334 					flags &= ~VN_UNCONFIG;
335 				if ((flags & VN_UNCONFIG) == 0)
336 					warn("umount");
337 			} else if (verbose)
338 				printf("%s: unmounted\n", dev);
339 		}
340 	}
341 	/*
342 	 * Clear (un-configure) the device
343 	 */
344 	if (flags & VN_UNCONFIG) {
345 		rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
346 		if (rv) {
347 			if (errno == ENODEV) {
348 				if (verbose)
349 					printf("%s: not configured\n", dev);
350 				rv = 0;
351 			} else {
352 				status--;
353 				warn("VNIOCDETACH");
354 			}
355 		} else if (verbose)
356 			printf("%s: cleared\n", dev);
357 	}
358 	/*
359 	 * Set specified options
360 	 */
361 	if (flags & VN_SET) {
362 		l = setopt;
363 		if (global)
364 			rv = ioctl(fileno(f), VNIOCGSET, &l);
365 		else
366 			rv = ioctl(fileno(f), VNIOCUSET, &l);
367 		if (rv) {
368 			status--;
369 			warn("VNIO[GU]SET");
370 		} else if (verbose)
371 			printf("%s: flags now=%08x\n",dev,l);
372 	}
373 	/*
374 	 * Reset specified options
375 	 */
376 	if (flags & VN_RESET) {
377 		l = resetopt;
378 		if (global)
379 			rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
380 		else
381 			rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
382 		if (rv) {
383 			status--;
384 			warn("VNIO[GU]CLEAR");
385 		} else if (verbose)
386 			printf("%s: flags now=%08x\n",dev,l);
387 	}
388 	/*
389 	 * Configure the device
390 	 */
391 	if (flags & VN_CONFIG) {
392 		rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
393 		if (rv) {
394 			status--;
395 			warn("VNIOCATTACH");
396 			flags &= ~VN_ENABLE;
397 		} else {
398 			if (verbose) {
399 				printf(
400 				    "%s: %d bytes on %s\n",
401 				    dev, vnio.vn_size, file
402 				);
403 			}
404 			/*
405 			 * autolabel
406 			 */
407 			if (vnp->autolabel) {
408 				do_autolabel(vnp->dev, vnp->autolabel);
409 			}
410 		}
411 	}
412 	/*
413 	 * Set an option
414 	 */
415 	if (flags & VN_SET) {
416 		l = setopt;
417 		if (global)
418 			rv = ioctl(fileno(f), VNIOCGSET, &l);
419 		else
420 			rv = ioctl(fileno(f), VNIOCUSET, &l);
421 		if (rv) {
422 			status--;
423 			warn("VNIO[GU]SET");
424 		} else if (verbose)
425 			printf("%s: flags now=%08lx\n",dev,l);
426 	}
427 	/*
428 	 * Reset an option
429 	 */
430 	if (flags & VN_RESET) {
431 		l = resetopt;
432 		if (global)
433 			rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
434 		else
435 			rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
436 		if (rv) {
437 			status--;
438 			warn("VNIO[GU]CLEAR");
439 		} else if (verbose)
440 			printf("%s: flags now=%08lx\n",dev,l);
441 	}
442 
443 	/*
444 	 * Close the device now, as we may want to mount it.
445 	 */
446 	fclose(f);
447 
448 	/*
449 	 * Enable special functions on the device
450 	 */
451 	if (flags & VN_ENABLE) {
452 		if (flags & VN_SWAP) {
453 			rv = swapon(dev);
454 			if (rv) {
455 				status--;
456 				warn("swapon");
457 			}
458 			else if (verbose)
459 				printf("%s: swapping enabled\n", dev);
460 		}
461 		if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
462 			struct ufs_args args;
463 			int mflags;
464 
465 			args.fspec = dev;
466 			mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
467 			rv = mount("ufs", oarg, mflags, &args);
468 			if (rv) {
469 				status--;
470 				warn("mount");
471 			}
472 			else if (verbose)
473 				printf("%s: mounted on %s\n", dev, oarg);
474 		}
475 	}
476 /* done: */
477 	fflush(stdout);
478 	return(status < 0);
479 }
480 
481 #define EOL(c)		((c) == '\0' || (c) == '\n')
482 #define WHITE(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
483 
484 void
485 readconfig(flags)
486 	int flags;
487 {
488 	char buf[LINESIZE];
489 	FILE *f;
490 	register char *cp, *sp;
491 	register int ix;
492 	int ax;
493 
494 	f = fopen(configfile, "r");
495 	if (f == NULL)
496 		err(1, "%s", configfile);
497 	ix = 0;		/* number of elements */
498 	ax = 0;		/* allocated elements */
499 	while (fgets(buf, LINESIZE, f) != NULL) {
500 		cp = buf;
501 		if (*cp == '#')
502 			continue;
503 		while (!EOL(*cp) && WHITE(*cp))
504 			cp++;
505 		if (EOL(*cp))
506 			continue;
507 		sp = cp;
508 		while (!EOL(*cp) && !WHITE(*cp))
509 			cp++;
510 		if (EOL(*cp))
511 			continue;
512 		*cp++ = '\0';
513 
514 		if (ix == ax) {
515 			ax = ax + 16;
516 			vndisks = realloc(vndisks, ax * sizeof(struct vndisk));
517 			bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk));
518 		}
519 		vndisks[ix].dev = malloc(cp - sp);
520 		strcpy(vndisks[ix].dev, sp);
521 		while (!EOL(*cp) && WHITE(*cp))
522 			cp++;
523 		if (EOL(*cp))
524 			continue;
525 		sp = cp;
526 		while (!EOL(*cp) && !WHITE(*cp))
527 			cp++;
528 		*cp++ = '\0';
529 
530 		if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) {
531 			vndisks[ix].size = getsize(sp + 1);
532 		} else {
533 			vndisks[ix].file = malloc(cp - sp);
534 			strcpy(vndisks[ix].file, sp);
535 		}
536 
537 		while (!EOL(*cp) && WHITE(*cp))
538 			cp++;
539 		vndisks[ix].flags = flags;
540 		if (!EOL(*cp)) {
541 			sp = cp;
542 			while (!EOL(*cp) && !WHITE(*cp))
543 				cp++;
544 			*cp++ = '\0';
545 			getoptions(&vndisks[ix], sp);
546 		}
547 		nvndisks++;
548 		ix++;
549 	}
550 }
551 
552 void
553 getoptions(vnp, fstr)
554 	struct vndisk *vnp;
555 	char *fstr;
556 {
557 	int flags = 0;
558 	char *oarg = NULL;
559 
560 	if (strcmp(fstr, "swap") == 0)
561 		flags |= VN_SWAP;
562 	else if (strncmp(fstr, "mount=", 6) == 0) {
563 		flags |= VN_MOUNTRW;
564 		oarg = &fstr[6];
565 	} else if (strncmp(fstr, "mountrw=", 8) == 0) {
566 		flags |= VN_MOUNTRW;
567 		oarg = &fstr[8];
568 	} else if (strncmp(fstr, "mountro=", 8) == 0) {
569 		flags |= VN_MOUNTRO;
570 		oarg = &fstr[8];
571 	} else if (strcmp(fstr, "ignore") == 0)
572 		flags |= VN_IGNORE;
573 	vnp->flags |= flags;
574 	if (oarg) {
575 		vnp->oarg = malloc(strlen(oarg) + 1);
576 		strcpy(vnp->oarg, oarg);
577 	} else
578 		vnp->oarg = NULL;
579 }
580 
581 char *
582 rawdevice(dev)
583 	char *dev;
584 {
585 	register char *rawbuf, *dp, *ep;
586 	struct stat sb;
587 	int len;
588 
589 	len = strlen(dev);
590 	rawbuf = malloc(len + 2);
591 	strcpy(rawbuf, dev);
592 	if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
593 		dp = rindex(rawbuf, '/');
594 		if (dp) {
595 			for (ep = &rawbuf[len]; ep > dp; --ep)
596 				*(ep+1) = *ep;
597 			*++ep = 'r';
598 		}
599 	}
600 	return (rawbuf);
601 }
602 
603 static void
604 usage()
605 {
606 	fprintf(stderr, "%s\n%s\n%s\n",
607 		"usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file",
608 		"                [regular_file] [feature]",
609 		"       vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]");
610 	exit(1);
611 }
612 
613 static int
614 getsize(const char *arg)
615 {
616 	char *ptr;
617 	int pgsize = getpagesize();
618 	quad_t size = strtoq(arg, &ptr, 0);
619 
620 	switch(tolower(*ptr)) {
621 	case 't':
622 		/*
623 		 * GULP!  Terrabytes.  It's actually possible to create
624 		 * a 7.9 TB VN device, though newfs can't handle any single
625 		 * filesystem larger then 1 TB.
626 		 */
627 		size *= 1024;
628 		/* fall through */
629 	case 'g':
630 		size *= 1024;
631 		/* fall through */
632 	default:
633 	case 'm':
634 		size *= 1024;
635 		/* fall through */
636 	case 'k':
637 		size *= 1024;
638 		/* fall through */
639 	case 'c':
640 		break;
641 	}
642 	size = (size + pgsize - 1) / pgsize;
643 	return((int)size);
644 }
645 
646 /*
647  * DO_AUTOLABEL
648  *
649  *	Automatically label the device.  This will wipe any preexisting
650  *	label.
651  */
652 
653 static void
654 do_autolabel(const char *dev, const char *label)
655 {
656 	/* XXX not yet implemented */
657 	fprintf(stderr, "autolabel not yet implemented, sorry\n");
658 	exit(1);
659 }
660 
661