Home | History | Annotate | Line # | Download | only in sgivol
sgivol.c revision 1.15.46.1
      1 /*	$NetBSD: sgivol.c,v 1.15.46.1 2008/05/18 12:32:43 yamt Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Michael Hitch and Hubert Feyrer.
      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  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #if HAVE_NBTOOL_CONFIG_H
     33 #include "nbtool_config.h"
     34 #endif
     35 
     36 #include <sys/types.h>
     37 #include <sys/ioctl.h>
     38 #include <sys/stat.h>
     39 
     40 #if HAVE_NBTOOL_CONFIG_H
     41 #include "../../../../../sys/sys/bootblock.h"
     42 /* Ficticious geometry for cross tool usage against a file image */
     43 #define SGIVOL_NBTOOL_NSECS	32
     44 #define SGIVOL_NBTOOL_NTRACKS	64
     45 #else
     46 #include <sys/disklabel.h>
     47 #endif
     48 
     49 #include <errno.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <unistd.h>
     53 #include <string.h>
     54 #include <fcntl.h>
     55 #include <util.h>
     56 #ifndef HAVE_NBTOOL_CONFIG_H
     57 #include <sys/endian.h>
     58 #endif
     59 
     60 int	fd;
     61 int	opt_i;			/* Initialize volume header */
     62 int	opt_r;			/* Read a file from volume header */
     63 int	opt_w;			/* Write a file to volume header */
     64 int	opt_d;			/* Delete a file from volume header */
     65 int	opt_p;			/* Modify a partition */
     66 int	opt_q;			/* quiet mode */
     67 int	opt_f;			/* Don't ask, just do what you're told */
     68 int	partno, partfirst, partblocks, parttype;
     69 struct sgi_boot_block *volhdr;
     70 int32_t	checksum;
     71 u_int32_t	volhdr_size = SGI_BOOT_BLOCK_SIZE_VOLHDR;
     72 
     73 const char *vfilename = "";
     74 const char *ufilename = "";
     75 
     76 #if HAVE_NBTOOL_CONFIG_H
     77 struct stat st;
     78 #else
     79 struct disklabel lbl;
     80 #endif
     81 
     82 unsigned char buf[512];
     83 
     84 const char *sgi_types[] = {
     85 	"Volume Header",
     86 	"Repl Trks",
     87 	"Repl Secs",
     88 	"Raw",
     89 	"BSD4.2",
     90 	"SysV",
     91 	"Volume",
     92 	"EFS",
     93 	"LVol",
     94 	"RLVol",
     95 	"XFS",
     96 	"XSFLog",
     97 	"XLV",
     98 	"XVM"
     99 };
    100 
    101 int	main(int, char *[]);
    102 
    103 void	display_vol(void);
    104 void	init_volhdr(void);
    105 void	read_file(void);
    106 void	write_file(void);
    107 void	delete_file(void);
    108 void	modify_partition(void);
    109 void	write_volhdr(void);
    110 int	allocate_space(int);
    111 void	checksum_vol(void);
    112 void	usage(void);
    113 
    114 int
    115 main(int argc, char *argv[])
    116 {
    117 	int ch;
    118 	while ((ch = getopt(argc, argv, "irwpdqfh:")) != -1) {
    119 		switch (ch) {
    120 		/* -i, -r, -w, -d and -p override each other */
    121 		/* -q implies -f */
    122 		case 'q':
    123 			++opt_q;
    124 			++opt_f;
    125 			break;
    126 		case 'f':
    127 			++opt_f;
    128 			break;
    129 		case 'i':
    130 			++opt_i;
    131 			opt_r = opt_w = opt_d = opt_p = 0;
    132 			break;
    133 		case 'h':
    134 			volhdr_size = atoi(optarg);
    135 			break;
    136 		case 'r':
    137 			++opt_r;
    138 			opt_i = opt_w = opt_d = opt_p = 0;
    139 			break;
    140 		case 'w':
    141 			++opt_w;
    142 			opt_i = opt_r = opt_d = opt_p = 0;
    143 			break;
    144 		case 'd':
    145 			++opt_d;
    146 			opt_i = opt_r = opt_w = opt_p = 0;
    147 			break;
    148 		case 'p':
    149 			++opt_p;
    150 			opt_i = opt_r = opt_w = opt_d = 0;
    151 			partno = atoi(argv[0]);
    152 			partfirst = atoi(argv[1]);
    153 			partblocks = atoi(argv[2]);
    154 			parttype = atoi(argv[3]);
    155 			break;
    156 		case '?':
    157 		default:
    158 			usage();
    159 		}
    160 	}
    161 	argc -= optind;
    162 	argv += optind;
    163 
    164 	if (opt_r || opt_w) {
    165 		if (argc != 3)
    166 			usage();
    167 		vfilename = argv[0];
    168 		ufilename = argv[1];
    169 		argc -= 2;
    170 		argv += 2;
    171 	}
    172 	if (opt_d) {
    173 		if (argc != 2)
    174 			usage();
    175 		vfilename = argv[0];
    176 		argc--;
    177 		argv++;
    178 	}
    179 
    180 	if (opt_p) {
    181 		if (argc != 5)
    182 			usage();
    183 		partno = atoi(argv[0]);
    184 		partfirst = atoi(argv[1]);
    185 		partblocks = atoi(argv[2]);
    186 		parttype = atoi(argv[3]);
    187 		argc -= 4;
    188 		argv += 4;
    189 	}
    190 	if (argc != 1)
    191 		usage();
    192 
    193 	fd = open(argv[0], (opt_i | opt_w | opt_d | opt_p) ? O_RDWR : O_RDONLY);
    194 	if (fd < 0) {
    195 #if HAVE_NBTOOL_CONFIG_H
    196 		perror("File open");
    197 		exit(1);
    198 #else
    199 		sprintf((char *)buf, "/dev/r%s%c", argv[0], 'a' + getrawpartition());
    200 		fd = open((char *)buf, (opt_i | opt_w | opt_d | opt_p)
    201 				? O_RDWR : O_RDONLY);
    202 		if (fd < 0) {
    203 			printf("Error opening device %s: %s\n",
    204 				argv[0], strerror(errno));
    205 			exit(1);
    206 		}
    207 #endif
    208 	}
    209 	if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
    210 		perror("read volhdr");
    211 		exit(1);
    212 	}
    213 #if HAVE_NBTOOL_CONFIG_H
    214 	if (fstat(fd, &st) < 0) {
    215 		perror("stat error");
    216 		exit(1);
    217 	}
    218 	if (!S_ISREG(st.st_mode)) {
    219 		printf("Must be regular file\n");
    220 		exit(1);
    221 	}
    222 	if (st.st_size % SGI_BOOT_BLOCK_BLOCKSIZE) {
    223 		printf("Size must be multiple of %d\n",
    224 		    SGI_BOOT_BLOCK_BLOCKSIZE);
    225 		exit(1);
    226 	}
    227 	if (st.st_size < (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS)) {
    228 		printf("Minimum size of %d required\n",
    229 		    SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS);
    230 		exit(1);
    231 	}
    232 #else
    233 	if (ioctl(fd, DIOCGDINFO, &lbl) < 0) {
    234 		perror("DIOCGDINFO");
    235 		exit(1);
    236 	}
    237 #endif
    238 	volhdr = (struct sgi_boot_block *) buf;
    239 	if (opt_i) {
    240 		init_volhdr();
    241 		exit(0);
    242 	}
    243 	if (be32toh(volhdr->magic) != SGI_BOOT_BLOCK_MAGIC) {
    244 		printf("No Volume Header found, magic=%x.  Use -i first.\n",
    245 		       be32toh(volhdr->magic));
    246 		exit(1);
    247 	}
    248 	if (opt_r) {
    249 		read_file();
    250 		exit(0);
    251 	}
    252 	if (opt_w) {
    253 		write_file();
    254 		exit(0);
    255 	}
    256 	if (opt_d) {
    257 		delete_file();
    258 		exit(0);
    259 	}
    260 	if (opt_p) {
    261 		modify_partition();
    262 		exit(0);
    263 	}
    264 
    265 	if (!opt_q)
    266 		display_vol();
    267 
    268 	return 0;
    269 }
    270 
    271 void
    272 display_vol(void)
    273 {
    274 	int32_t *l;
    275 	int i;
    276 
    277 #if HAVE_NBTOOL_CONFIG_H
    278 	printf("disklabel shows %d sectors\n",
    279 	    st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
    280 #else
    281 	printf("disklabel shows %d sectors\n", lbl.d_secperunit);
    282 #endif
    283 	l = (int32_t *)buf;
    284 	checksum = 0;
    285 	for (i = 0; i < 512 / 4; ++i)
    286 		checksum += be32toh(l[i]);
    287 	printf("checksum: %08x%s\n", checksum, checksum == 0 ? "" : " *ERROR*");
    288 	printf("root part: %d\n", be16toh(volhdr->root));
    289 	printf("swap part: %d\n", be16toh(volhdr->swap));
    290 	printf("bootfile: %s\n", volhdr->bootfile);
    291 	/* volhdr->devparams[0..47] */
    292 	printf("\nVolume header files:\n");
    293 	for (i = 0; i < 15; ++i)
    294 		if (volhdr->voldir[i].name[0])
    295 			printf("%-8s offset %4d blocks, length %8d bytes "
    296 			    "(%d blocks)\n",
    297 			    volhdr->voldir[i].name,
    298                             be32toh(volhdr->voldir[i].block),
    299 			    be32toh(volhdr->voldir[i].bytes),
    300                             (be32toh(volhdr->voldir[i].bytes) + 511) / 512);
    301 	printf("\nSGI partitions:\n");
    302 	for (i = 0; i < SGI_BOOT_BLOCK_MAXPARTITIONS; ++i) {
    303 		if (be32toh(volhdr->partitions[i].blocks)) {
    304 			printf("%2d:%c blocks %8d first %8d type %2d (%s)\n",
    305 			  i, i + 'a', be32toh(volhdr->partitions[i].blocks),
    306 			       be32toh(volhdr->partitions[i].first),
    307 			       be32toh(volhdr->partitions[i].type),
    308 			  be32toh(volhdr->partitions[i].type) > 13 ? "???" :
    309 			    sgi_types[be32toh(volhdr->partitions[i].type)]);
    310 		}
    311 	}
    312 }
    313 
    314 void
    315 init_volhdr(void)
    316 {
    317 	memset(buf, 0, sizeof(buf));
    318 	volhdr->magic = htobe32(SGI_BOOT_BLOCK_MAGIC);
    319 	volhdr->root = htobe16(0);
    320 	volhdr->swap = htobe16(1);
    321 	strcpy(volhdr->bootfile, "/netbsd");
    322 #if HAVE_NBTOOL_CONFIG_H
    323 	volhdr->dp.dp_skew = 0;
    324 #else
    325 	volhdr->dp.dp_skew = lbl.d_trackskew;
    326 #endif
    327 	volhdr->dp.dp_gap1 = 1; /* XXX */
    328 	volhdr->dp.dp_gap2 = 1; /* XXX */
    329 #if HAVE_NBTOOL_CONFIG_H
    330 	volhdr->dp.dp_cyls =
    331 	    htobe16(st.st_size / (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS));
    332 #else
    333 	volhdr->dp.dp_cyls = htobe16(lbl.d_ncylinders);
    334 #endif
    335 	volhdr->dp.dp_shd0 = 0;
    336 #if HAVE_NBTOOL_CONFIG_H
    337 	volhdr->dp.dp_trks0 = htobe16(SGIVOL_NBTOOL_NTRACKS);
    338 	volhdr->dp.dp_secs = htobe16(SGIVOL_NBTOOL_NSECS);
    339 	volhdr->dp.dp_secbytes = htobe16(SGI_BOOT_BLOCK_BLOCKSIZE);
    340 	volhdr->dp.dp_interleave = htobe16(1);
    341 #else
    342 	volhdr->dp.dp_trks0 = htobe16(lbl.d_ntracks);
    343 	volhdr->dp.dp_secs = htobe16(lbl.d_nsectors);
    344 	volhdr->dp.dp_secbytes = htobe16(lbl.d_secsize);
    345 	volhdr->dp.dp_interleave = htobe16(lbl.d_interleave);
    346 #endif
    347 	volhdr->dp.dp_nretries = htobe32(22);
    348 #if HAVE_NBTOOL_CONFIG_H
    349 	volhdr->partitions[10].blocks =
    350 	    htobe32(st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
    351 #else
    352 	volhdr->partitions[10].blocks = htobe32(lbl.d_secperunit);
    353 #endif
    354 	volhdr->partitions[10].first = 0;
    355 	volhdr->partitions[10].type = htobe32(SGI_PTYPE_VOLUME);
    356 	volhdr->partitions[8].blocks = htobe32(volhdr_size);
    357 	volhdr->partitions[8].first = 0;
    358 	volhdr->partitions[8].type = htobe32(SGI_PTYPE_VOLHDR);
    359 #if HAVE_NBTOOL_CONFIG_H
    360 	volhdr->partitions[0].blocks =
    361 	    htobe32((st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE) - volhdr_size);
    362 #else
    363 	volhdr->partitions[0].blocks = htobe32(lbl.d_secperunit - volhdr_size);
    364 #endif
    365 	volhdr->partitions[0].first = htobe32(volhdr_size);
    366 	volhdr->partitions[0].type = htobe32(SGI_PTYPE_BSD);
    367 	write_volhdr();
    368 }
    369 
    370 void
    371 read_file(void)
    372 {
    373 	FILE *fp;
    374 	int i;
    375 
    376 	if (!opt_q)
    377 		printf("Reading file %s\n", vfilename);
    378 	for (i = 0; i < 15; ++i) {
    379 		if (strncmp(vfilename, volhdr->voldir[i].name,
    380 			    strlen(volhdr->voldir[i].name)) == 0)
    381 			break;
    382 	}
    383 	if (i >= 15) {
    384 		printf("file %s not found\n", vfilename);
    385 		exit(1);
    386 	}
    387 	/* XXX assumes volume header starts at 0? */
    388 	lseek(fd, be32toh(volhdr->voldir[i].block) * 512, SEEK_SET);
    389 	fp = fopen(ufilename, "w");
    390 	if (fp == NULL) {
    391 		perror("open write");
    392 		exit(1);
    393 	}
    394 	i = be32toh(volhdr->voldir[i].bytes);
    395 	while (i > 0) {
    396 		if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
    397 			perror("read file");
    398 			exit(1);
    399 		}
    400 		fwrite(buf, 1, i > sizeof(buf) ? sizeof(buf) : i, fp);
    401 		i -= i > sizeof(buf) ? sizeof(buf) : i;
    402 	}
    403 	fclose(fp);
    404 }
    405 
    406 void
    407 write_file(void)
    408 {
    409 	FILE *fp;
    410 	int slot;
    411 	size_t namelen;
    412 	int block, i;
    413 	struct stat st;
    414 	char fbuf[512];
    415 
    416 	if (!opt_q)
    417 		printf("Writing file %s\n", ufilename);
    418 	if (stat(ufilename, &st) < 0) {
    419 		perror("stat");
    420 		exit(1);
    421 	}
    422 	if (!opt_q)
    423 		printf("File %s has %lld bytes\n", ufilename, st.st_size);
    424 	slot = -1;
    425 	for (i = 0; i < 15; ++i) {
    426 		if (volhdr->voldir[i].name[0] == '\0' && slot < 0)
    427 			slot = i;
    428 		if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
    429 			slot = i;
    430 			break;
    431 		}
    432 	}
    433 	if (slot == -1) {
    434 		printf("No directory space for file %s\n", vfilename);
    435 		exit(1);
    436 	}
    437 	/* -w can overwrite, -a won't overwrite */
    438 	if (be32toh(volhdr->voldir[slot].block) > 0) {
    439 		if (!opt_q)
    440 			printf("File %s exists, removing old file\n",
    441 				vfilename);
    442 		volhdr->voldir[slot].name[0] = 0;
    443 		volhdr->voldir[slot].block = volhdr->voldir[slot].bytes = 0;
    444 	}
    445 	if (st.st_size == 0) {
    446 		printf("bad file size\n");
    447 		exit(1);
    448 	}
    449 	/* XXX assumes volume header starts at 0? */
    450 	block = allocate_space((int)st.st_size);
    451 	if (block < 0) {
    452 		printf("No space for file\n");
    453 		exit(1);
    454 	}
    455 
    456 	/*
    457 	 * Make sure the name in the volume header is max. 8 chars,
    458 	 * NOT including NUL.
    459 	 */
    460 	namelen = strlen(vfilename);
    461 	if (namelen > sizeof(volhdr->voldir[slot].name)) {
    462 		printf("Warning: '%s' is too long for volume header, ",
    463 		       vfilename);
    464 		namelen = sizeof(volhdr->voldir[slot].name);
    465 		printf("truncating to '%-8s'\n", vfilename);
    466 	}
    467 
    468 	/* Populate it w/ NULs */
    469 	memset(volhdr->voldir[slot].name, 0,
    470 	    sizeof(volhdr->voldir[slot].name));
    471 	/* Then copy the name */
    472 	memcpy(volhdr->voldir[slot].name, vfilename, namelen);
    473 
    474 	volhdr->voldir[slot].block = htobe32(block);
    475 	volhdr->voldir[slot].bytes = htobe32(st.st_size);
    476 
    477 	write_volhdr();
    478 
    479 	/* write the file itself */
    480 	i = lseek(fd, block * 512, SEEK_SET);
    481 	if (i < 0) {
    482 		perror("lseek write");
    483 		exit(1);
    484 	}
    485 	i = st.st_size;
    486 	fp = fopen(ufilename, "r");
    487 	while (i > 0) {
    488 		fread(fbuf, 1, i > 512 ? 512 : i, fp);
    489 		if (write(fd, fbuf, 512) != 512) {
    490 			perror("write file");
    491 			exit(1);
    492 		}
    493 		i -= i > 512 ? 512 : i;
    494 	}
    495 }
    496 
    497 void
    498 delete_file(void)
    499 {
    500 	int i;
    501 
    502 	for (i = 0; i < 15; ++i) {
    503 		if (strcmp(vfilename, volhdr->voldir[i].name) == 0) {
    504 			break;
    505 		}
    506 	}
    507 	if (i >= 15) {
    508 		printf("File %s not found\n", vfilename);
    509 		exit(1);
    510 	}
    511 
    512 	/* XXX: we don't compact the file space, so get fragmentation */
    513 	volhdr->voldir[i].name[0] = '\0';
    514 	volhdr->voldir[i].block = volhdr->voldir[i].bytes = 0;
    515 	write_volhdr();
    516 }
    517 
    518 void
    519 modify_partition(void)
    520 {
    521 	if (!opt_q)
    522 		printf("Modify partition %d start %d length %d\n",
    523 			partno, partfirst, partblocks);
    524 	if (partno < 0 || partno > 15) {
    525 		printf("Invalid partition number: %d\n", partno);
    526 		exit(1);
    527 	}
    528 	volhdr->partitions[partno].blocks = htobe32(partblocks);
    529 	volhdr->partitions[partno].first = htobe32(partfirst);
    530 	volhdr->partitions[partno].type = htobe32(parttype);
    531 	write_volhdr();
    532 }
    533 
    534 void
    535 write_volhdr(void)
    536 {
    537 	int i;
    538 
    539 	checksum_vol();
    540 
    541 	if (!opt_q)
    542 		display_vol();
    543 	if (!opt_f) {
    544 		printf("\nDo you want to update volume (y/n)? ");
    545 		i = getchar();
    546 		if (i != 'Y' && i != 'y')
    547 			exit(1);
    548 	}
    549 	i = lseek(fd, 0, SEEK_SET);
    550 	if (i < 0) {
    551 		perror("lseek 0");
    552 		exit(1);
    553 	}
    554 	i = write(fd, buf, 512);
    555 	if (i < 0)
    556 		perror("write volhdr");
    557 }
    558 
    559 int
    560 allocate_space(int size)
    561 {
    562 	int n, blocks;
    563 	int first;
    564 
    565 	blocks = (size + 511) / 512;
    566 	first = 2;
    567 	n = 0;
    568 	while (n < 15) {
    569 		if (volhdr->voldir[n].name[0]) {
    570 			if (first < (be32toh(volhdr->voldir[n].block) +
    571 			  (be32toh(volhdr->voldir[n].bytes) + 511) / 512) &&
    572 			    (first + blocks) > be32toh(volhdr->voldir[n].block)) {
    573 				first = be32toh(volhdr->voldir[n].block) +
    574 					(be32toh(volhdr->voldir[n].bytes) + 511) / 512;
    575 #if 0
    576 				printf("allocate: n=%d first=%d blocks=%d size=%d\n", n, first, blocks, size);
    577 				printf("%s %d %d\n", volhdr->voldir[n].name, volhdr->voldir[n].block, volhdr->voldir[n].bytes);
    578 				printf("first=%d block=%d last=%d end=%d\n", first, volhdr->voldir[n].block,
    579 				       first + blocks - 1, volhdr->voldir[n].block + (volhdr->voldir[n].bytes + 511) / 512);
    580 #endif
    581 				n = 0;
    582 				continue;
    583 			}
    584 		}
    585 		++n;
    586 	}
    587 #if HAVE_NBTOOL_CONFIG_H
    588 	if (first + blocks > (st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE))
    589 #else
    590 	if (first + blocks > lbl.d_secperunit)
    591 #endif
    592 		first = -1;
    593 	/* XXX assumes volume header is partition 8 */
    594 	/* XXX assumes volume header starts at 0? */
    595 	if (first + blocks >= be32toh(volhdr->partitions[8].blocks))
    596 		first = -1;
    597 	return (first);
    598 }
    599 
    600 void
    601 checksum_vol(void)
    602 {
    603 	int32_t *l;
    604 	int i;
    605 
    606 	volhdr->checksum = checksum = 0;
    607 	l = (int32_t *)buf;
    608 	for (i = 0; i < 512 / 4; ++i)
    609 		checksum += be32toh(l[i]);
    610 	volhdr->checksum = htobe32(-checksum);
    611 }
    612 
    613 void
    614 usage(void)
    615 {
    616 	printf("Usage:	sgivol [-qf] -i [-h vhsize] device\n"
    617 	       "	sgivol [-qf] -r vhfilename diskfilename device\n"
    618 	       "	sgivol [-qf] -w vhfilename diskfilename device\n"
    619 	       "	sgivol [-qf] -d vhfilename device\n"
    620 	       "	sgivol [-qf] -p partno partfirst partblocks "
    621 	       "parttype device\n"
    622 	       );
    623 	exit(0);
    624 }
    625