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