xref: /dragonfly/usr.sbin/vnconfig/vnconfig.c (revision 6fb88001)
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.10 2005/12/05 01:23:23 swildner 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, *rdev, *oarg;
247 	FILE *f;
248 	struct vn_ioctl vnio;
249 	int flags, pgsize, rv, status;
250 	u_long l;
251 
252 	pgsize = getpagesize();
253 
254 	status = rv = 0;
255 
256 	/*
257 	 * Prepend "/dev/" to the specified device name, if necessary.
258 	 * Operate on vnp->dev because it is used later.
259 	 */
260 	if (vnp->dev[0] != '/' && vnp->dev[0] != '.')
261 		asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev);
262 	dev = vnp->dev;
263 	file = vnp->file;
264 	flags = vnp->flags;
265 	oarg = vnp->oarg;
266 
267 	if (flags & VN_IGNORE)
268 		return(0);
269 
270 	/*
271 	 * When a regular file has been specified, do any requested setup
272 	 * of the file.  Truncation (also creates the file if necessary),
273 	 * sizing, and zeroing.
274 	 */
275 
276 	if (file && vnp->size != 0 && (flags & VN_CONFIG)) {
277 		int  fd;
278 		struct stat st;
279 
280 		if (flags & VN_TRUNCATE)
281 			fd = open(file, O_RDWR|O_CREAT|O_TRUNC);
282 		else
283 			fd = open(file, O_RDWR);
284 		if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
285 			if (st.st_size < (off_t)vnp->size * pgsize)
286 				ftruncate(fd, (off_t)vnp->size * pgsize);
287 			if (vnp->size != 0)
288 				st.st_size = (off_t)vnp->size * pgsize;
289 
290 			if (flags & VN_ZERO) {
291 				char *buf = malloc(ZBUFSIZE);
292 				bzero(buf, ZBUFSIZE);
293 				while (st.st_size > 0) {
294 					int n = (st.st_size > ZBUFSIZE) ?
295 					    ZBUFSIZE : (int)st.st_size;
296 					if (write(fd, buf, n) != n) {
297 						ftruncate(fd, 0);
298 						printf("Unable to ZERO file %s\n", file);
299 						return(0);
300 					}
301 					st.st_size -= (off_t)n;
302 				}
303 			}
304 			close(fd);
305 		} else {
306 			printf("Unable to open file %s\n", file);
307 			return(0);
308 		}
309 	}
310 
311 	rdev = rawdevice(dev);
312 	f = fopen(rdev, "rw");
313 	if (f == NULL) {
314 		warn("%s", dev);
315 		return(1);
316 	}
317 	vnio.vn_file = file;
318 	vnio.vn_size = vnp->size;	/* non-zero only if swap backed */
319 
320 	/*
321 	 * Disable the device
322 	 */
323 	if (flags & VN_DISABLE) {
324 		if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
325 			rv = unmount(oarg, 0);
326 			if (rv) {
327 				status--;
328 				if (errno == EBUSY)
329 					flags &= ~VN_UNCONFIG;
330 				if ((flags & VN_UNCONFIG) == 0)
331 					warn("umount");
332 			} else if (verbose)
333 				printf("%s: unmounted\n", dev);
334 		}
335 	}
336 	/*
337 	 * Clear (un-configure) the device
338 	 */
339 	if (flags & VN_UNCONFIG) {
340 		rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
341 		if (rv) {
342 			if (errno == ENODEV) {
343 				if (verbose)
344 					printf("%s: not configured\n", dev);
345 				rv = 0;
346 			} else {
347 				status--;
348 				warn("VNIOCDETACH");
349 			}
350 		} else if (verbose)
351 			printf("%s: cleared\n", dev);
352 	}
353 	/*
354 	 * Set specified options
355 	 */
356 	if (flags & VN_SET) {
357 		l = setopt;
358 		if (global)
359 			rv = ioctl(fileno(f), VNIOCGSET, &l);
360 		else
361 			rv = ioctl(fileno(f), VNIOCUSET, &l);
362 		if (rv) {
363 			status--;
364 			warn("VNIO[GU]SET");
365 		} else if (verbose)
366 			printf("%s: flags now=%08x\n",dev,l);
367 	}
368 	/*
369 	 * Reset specified options
370 	 */
371 	if (flags & VN_RESET) {
372 		l = resetopt;
373 		if (global)
374 			rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
375 		else
376 			rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
377 		if (rv) {
378 			status--;
379 			warn("VNIO[GU]CLEAR");
380 		} else if (verbose)
381 			printf("%s: flags now=%08x\n",dev,l);
382 	}
383 	/*
384 	 * Configure the device
385 	 */
386 	if (flags & VN_CONFIG) {
387 		rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
388 		if (rv) {
389 			status--;
390 			warn("VNIOCATTACH");
391 			flags &= ~VN_ENABLE;
392 		} else {
393 			if (verbose) {
394 				printf("%s: %s, ", dev, file);
395 				if (vnp->size != 0) {
396 				    printf("%d bytes mapped\n", vnio.vn_size);
397 				} else {
398 				    printf("complete file mapped\n");
399 				}
400 			}
401 			/*
402 			 * autolabel
403 			 */
404 			if (vnp->autolabel) {
405 				do_autolabel(vnp->dev, vnp->autolabel);
406 			}
407 		}
408 	}
409 	/*
410 	 * Set an option
411 	 */
412 	if (flags & VN_SET) {
413 		l = setopt;
414 		if (global)
415 			rv = ioctl(fileno(f), VNIOCGSET, &l);
416 		else
417 			rv = ioctl(fileno(f), VNIOCUSET, &l);
418 		if (rv) {
419 			status--;
420 			warn("VNIO[GU]SET");
421 		} else if (verbose)
422 			printf("%s: flags now=%08lx\n",dev,l);
423 	}
424 	/*
425 	 * Reset an option
426 	 */
427 	if (flags & VN_RESET) {
428 		l = resetopt;
429 		if (global)
430 			rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
431 		else
432 			rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
433 		if (rv) {
434 			status--;
435 			warn("VNIO[GU]CLEAR");
436 		} else if (verbose)
437 			printf("%s: flags now=%08lx\n",dev,l);
438 	}
439 
440 	/*
441 	 * Close the device now, as we may want to mount it.
442 	 */
443 	fclose(f);
444 
445 	/*
446 	 * Enable special functions on the device
447 	 */
448 	if (flags & VN_ENABLE) {
449 		if (flags & VN_SWAP) {
450 			rv = swapon(dev);
451 			if (rv) {
452 				status--;
453 				warn("swapon");
454 			}
455 			else if (verbose)
456 				printf("%s: swapping enabled\n", dev);
457 		}
458 		if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
459 			struct ufs_args args;
460 			int mflags;
461 
462 			args.fspec = dev;
463 			mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
464 			rv = mount("ufs", oarg, mflags, &args);
465 			if (rv) {
466 				status--;
467 				warn("mount");
468 			}
469 			else if (verbose)
470 				printf("%s: mounted on %s\n", dev, oarg);
471 		}
472 	}
473 /* done: */
474 	fflush(stdout);
475 	return(status < 0);
476 }
477 
478 #define EOL(c)		((c) == '\0' || (c) == '\n')
479 #define WHITE(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
480 
481 void
482 readconfig(int flags)
483 {
484 	char buf[LINESIZE];
485 	FILE *f;
486 	char *cp, *sp;
487 	int ix;
488 	int ax;
489 
490 	f = fopen(configfile, "r");
491 	if (f == NULL)
492 		err(1, "%s", configfile);
493 	ix = 0;		/* number of elements */
494 	ax = 0;		/* allocated elements */
495 	while (fgets(buf, LINESIZE, f) != NULL) {
496 		cp = buf;
497 		if (*cp == '#')
498 			continue;
499 		while (!EOL(*cp) && WHITE(*cp))
500 			cp++;
501 		if (EOL(*cp))
502 			continue;
503 		sp = cp;
504 		while (!EOL(*cp) && !WHITE(*cp))
505 			cp++;
506 		if (EOL(*cp))
507 			continue;
508 		*cp++ = '\0';
509 
510 		if (ix == ax) {
511 			ax = ax + 16;
512 			vndisks = realloc(vndisks, ax * sizeof(struct vndisk));
513 			bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk));
514 		}
515 		vndisks[ix].dev = malloc(cp - sp);
516 		strcpy(vndisks[ix].dev, sp);
517 		while (!EOL(*cp) && WHITE(*cp))
518 			cp++;
519 		if (EOL(*cp))
520 			continue;
521 		sp = cp;
522 		while (!EOL(*cp) && !WHITE(*cp))
523 			cp++;
524 		*cp++ = '\0';
525 
526 		if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) {
527 			vndisks[ix].size = getsize(sp + 1);
528 		} else {
529 			vndisks[ix].file = malloc(cp - sp);
530 			strcpy(vndisks[ix].file, sp);
531 		}
532 
533 		while (!EOL(*cp) && WHITE(*cp))
534 			cp++;
535 		vndisks[ix].flags = flags;
536 		if (!EOL(*cp)) {
537 			sp = cp;
538 			while (!EOL(*cp) && !WHITE(*cp))
539 				cp++;
540 			*cp++ = '\0';
541 			getoptions(&vndisks[ix], sp);
542 		}
543 		nvndisks++;
544 		ix++;
545 	}
546 }
547 
548 void
549 getoptions(struct vndisk *vnp, char *fstr)
550 {
551 	int flags = 0;
552 	char *oarg = NULL;
553 
554 	if (strcmp(fstr, "swap") == 0)
555 		flags |= VN_SWAP;
556 	else if (strncmp(fstr, "mount=", 6) == 0) {
557 		flags |= VN_MOUNTRW;
558 		oarg = &fstr[6];
559 	} else if (strncmp(fstr, "mountrw=", 8) == 0) {
560 		flags |= VN_MOUNTRW;
561 		oarg = &fstr[8];
562 	} else if (strncmp(fstr, "mountro=", 8) == 0) {
563 		flags |= VN_MOUNTRO;
564 		oarg = &fstr[8];
565 	} else if (strcmp(fstr, "ignore") == 0)
566 		flags |= VN_IGNORE;
567 	vnp->flags |= flags;
568 	if (oarg) {
569 		vnp->oarg = malloc(strlen(oarg) + 1);
570 		strcpy(vnp->oarg, oarg);
571 	} else
572 		vnp->oarg = NULL;
573 }
574 
575 char *
576 rawdevice(char *dev)
577 {
578 	char *rawbuf, *dp, *ep;
579 	struct stat sb;
580 	int len;
581 
582 	len = strlen(dev);
583 	rawbuf = malloc(len + 2);
584 	strcpy(rawbuf, dev);
585 	if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
586 		dp = strrchr(rawbuf, '/');
587 		if (dp) {
588 			for (ep = &rawbuf[len]; ep > dp; --ep)
589 				*(ep+1) = *ep;
590 			*++ep = 'r';
591 		}
592 	}
593 	return (rawbuf);
594 }
595 
596 static void
597 usage(void)
598 {
599 	fprintf(stderr, "%s\n%s\n%s\n",
600 		"usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file",
601 		"                [regular_file] [feature]",
602 		"       vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]");
603 	exit(1);
604 }
605 
606 static int
607 getsize(const char *arg)
608 {
609 	char *ptr;
610 	int pgsize = getpagesize();
611 	quad_t size = strtoq(arg, &ptr, 0);
612 
613 	switch(tolower(*ptr)) {
614 	case 't':
615 		/*
616 		 * GULP!  Terrabytes.  It's actually possible to create
617 		 * a 7.9 TB VN device, though newfs can't handle any single
618 		 * filesystem larger then 1 TB.
619 		 */
620 		size *= 1024;
621 		/* fall through */
622 	case 'g':
623 		size *= 1024;
624 		/* fall through */
625 	default:
626 	case 'm':
627 		size *= 1024;
628 		/* fall through */
629 	case 'k':
630 		size *= 1024;
631 		/* fall through */
632 	case 'c':
633 		break;
634 	}
635 	size = (size + pgsize - 1) / pgsize;
636 	return((int)size);
637 }
638 
639 /*
640  * DO_AUTOLABEL
641  *
642  *	Automatically label the device.  This will wipe any preexisting
643  *	label.
644  */
645 
646 static void
647 do_autolabel(const char *dev, const char *label)
648 {
649 	/* XXX not yet implemented */
650 	fprintf(stderr, "autolabel not yet implemented, sorry\n");
651 	exit(1);
652 }
653 
654