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