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