Merge from vendor branch GCC:
[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  * @(#)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