Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / vnconfig / vnconfig.c
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