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