1/*	$NetBSD: sgivol.c,v 1.17 2008/08/03 16:09:20 rumble 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
60int	fd;
61int	opt_i;			/* Initialize volume header */
62int	opt_r;			/* Read a file from volume header */
63int	opt_w;			/* Write a file to volume header */
64int	opt_d;			/* Delete a file from volume header */
65int	opt_m;			/* Move (rename) a file in the volume header */
66int	opt_p;			/* Modify a partition */
67int	opt_q;			/* quiet mode */
68int	opt_f;			/* Don't ask, just do what you're told */
69int	partno, partfirst, partblocks, parttype;
70struct sgi_boot_block *volhdr;
71int32_t	checksum;
72u_int32_t	volhdr_size = SGI_BOOT_BLOCK_SIZE_VOLHDR;
73
74const char *vfilename = "";
75const char *ufilename = "";
76
77#if HAVE_NBTOOL_CONFIG_H
78struct stat st;
79#else
80struct disklabel lbl;
81#endif
82
83unsigned char buf[512];
84
85const char *sgi_types[] = {
86	"Volume Header",
87	"Repl Trks",
88	"Repl Secs",
89	"Raw",
90	"BSD4.2",
91	"SysV",
92	"Volume",
93	"EFS",
94	"LVol",
95	"RLVol",
96	"XFS",
97	"XSFLog",
98	"XLV",
99	"XVM"
100};
101
102int	main(int, char *[]);
103
104void	display_vol(void);
105void	init_volhdr(void);
106void	read_file(void);
107void	write_file(void);
108void	delete_file(void);
109void	move_file(void);
110void	modify_partition(void);
111void	write_volhdr(void);
112int	allocate_space(int);
113void	checksum_vol(void);
114int	names_match(int, const char *);
115void	usage(void);
116
117int
118main(int argc, char *argv[])
119{
120#define RESET_OPTS()	opt_i = opt_m = opt_r = opt_w = opt_d = opt_p = 0
121
122	int ch;
123	while ((ch = getopt(argc, argv, "qfih:rwdmp?")) != -1) {
124		switch (ch) {
125		/* -i, -r, -w, -d, -m and -p override each other */
126		/* -q implies -f */
127		case 'q':
128			++opt_q;
129			++opt_f;
130			break;
131		case 'f':
132			++opt_f;
133			break;
134		case 'i':
135			RESET_OPTS();
136			++opt_i;
137			break;
138		case 'h':
139			volhdr_size = atoi(optarg);
140			break;
141		case 'r':
142			RESET_OPTS();
143			++opt_r;
144			break;
145		case 'w':
146			RESET_OPTS();
147			++opt_w;
148			break;
149		case 'd':
150			RESET_OPTS();
151			++opt_d;
152			break;
153		case 'm':
154			RESET_OPTS();
155			++opt_m;
156			break;
157		case 'p':
158			RESET_OPTS();
159			++opt_p;
160			partno = atoi(argv[0]);
161			partfirst = atoi(argv[1]);
162			partblocks = atoi(argv[2]);
163			parttype = atoi(argv[3]);
164			break;
165		case '?':
166		default:
167			usage();
168		}
169	}
170	argc -= optind;
171	argv += optind;
172
173	if (opt_m || opt_r || opt_w) {
174		if (argc != 3)
175			usage();
176		vfilename = argv[0];
177		ufilename = argv[1];
178		argc -= 2;
179		argv += 2;
180	}
181	if (opt_d) {
182		if (argc != 2)
183			usage();
184		vfilename = argv[0];
185		argc--;
186		argv++;
187	}
188
189	if (opt_p) {
190		if (argc != 5)
191			usage();
192		partno = atoi(argv[0]);
193		partfirst = atoi(argv[1]);
194		partblocks = atoi(argv[2]);
195		parttype = atoi(argv[3]);
196		argc -= 4;
197		argv += 4;
198	}
199	if (argc != 1)
200		usage();
201
202	fd = open(argv[0],
203	    (opt_i | opt_m | opt_w | opt_d | opt_p) ? O_RDWR : O_RDONLY);
204	if (fd < 0) {
205#if HAVE_NBTOOL_CONFIG_H
206		perror("File open");
207		exit(1);
208#else
209		sprintf((char *)buf, "/dev/r%s%c", argv[0], 'a' + getrawpartition());
210		fd = open((char *)buf, (opt_i | opt_w | opt_d | opt_p)
211				? O_RDWR : O_RDONLY);
212		if (fd < 0) {
213			printf("Error opening device %s: %s\n",
214				argv[0], strerror(errno));
215			exit(1);
216		}
217#endif
218	}
219	if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
220		perror("read volhdr");
221		exit(1);
222	}
223#if HAVE_NBTOOL_CONFIG_H
224	if (fstat(fd, &st) < 0) {
225		perror("stat error");
226		exit(1);
227	}
228	if (!S_ISREG(st.st_mode)) {
229		printf("Must be regular file\n");
230		exit(1);
231	}
232	if (st.st_size % SGI_BOOT_BLOCK_BLOCKSIZE) {
233		printf("Size must be multiple of %d\n",
234		    SGI_BOOT_BLOCK_BLOCKSIZE);
235		exit(1);
236	}
237	if (st.st_size < (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS)) {
238		printf("Minimum size of %d required\n",
239		    SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS);
240		exit(1);
241	}
242#else
243	if (ioctl(fd, DIOCGDINFO, &lbl) < 0) {
244		perror("DIOCGDINFO");
245		exit(1);
246	}
247#endif
248	volhdr = (struct sgi_boot_block *) buf;
249	if (opt_i) {
250		init_volhdr();
251		exit(0);
252	}
253	if (be32toh(volhdr->magic) != SGI_BOOT_BLOCK_MAGIC) {
254		printf("No Volume Header found, magic=%x.  Use -i first.\n",
255		       be32toh(volhdr->magic));
256		exit(1);
257	}
258	if (opt_r) {
259		read_file();
260		exit(0);
261	}
262	if (opt_w) {
263		write_file();
264		exit(0);
265	}
266	if (opt_d) {
267		delete_file();
268		exit(0);
269	}
270	if (opt_m) {
271		move_file();
272		exit(0);
273	}
274	if (opt_p) {
275		modify_partition();
276		exit(0);
277	}
278
279	if (!opt_q)
280		display_vol();
281
282	return 0;
283}
284
285/*
286 * Compare the name in `slot' of voldir to `b'. Be careful, as the
287 * name in voldir need not be nul-terminated and `b' may be longer
288 * than the maximum (in which case it will never match).
289 *
290 * Returns non-0 if names are equal.
291 */
292int
293names_match(int slot, const char *b)
294{
295	int cmp;
296
297	if (slot < 0 || slot >= SGI_BOOT_BLOCK_MAXVOLDIRS) {
298		printf("Internal error: bad slot in %s()\n", __func__);
299		exit(1);
300	}
301
302	cmp = strncmp(volhdr->voldir[slot].name, b,
303	    sizeof(volhdr->voldir[slot].name));
304
305	return (cmp == 0 && strlen(b) <= sizeof(volhdr->voldir[slot].name));
306}
307
308void
309display_vol(void)
310{
311	int32_t *l;
312	int i;
313
314#if HAVE_NBTOOL_CONFIG_H
315	printf("disklabel shows %d sectors\n",
316	    st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
317#else
318	printf("disklabel shows %d sectors\n", lbl.d_secperunit);
319#endif
320	l = (int32_t *)buf;
321	checksum = 0;
322	for (i = 0; i < 512 / 4; ++i)
323		checksum += be32toh(l[i]);
324	printf("checksum: %08x%s\n", checksum, checksum == 0 ? "" : " *ERROR*");
325	printf("root part: %d\n", be16toh(volhdr->root));
326	printf("swap part: %d\n", be16toh(volhdr->swap));
327	printf("bootfile: %s\n", volhdr->bootfile);
328	/* volhdr->devparams[0..47] */
329	printf("\nVolume header files:\n");
330	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; ++i)
331		if (volhdr->voldir[i].name[0])
332			printf("%-8s offset %4d blocks, length %8d bytes "
333			    "(%d blocks)\n",
334			    volhdr->voldir[i].name,
335                            be32toh(volhdr->voldir[i].block),
336			    be32toh(volhdr->voldir[i].bytes),
337                            (be32toh(volhdr->voldir[i].bytes) + 511) / 512);
338	printf("\nSGI partitions:\n");
339	for (i = 0; i < SGI_BOOT_BLOCK_MAXPARTITIONS; ++i) {
340		if (be32toh(volhdr->partitions[i].blocks)) {
341			printf("%2d:%c blocks %8d first %8d type %2d (%s)\n",
342			  i, i + 'a', be32toh(volhdr->partitions[i].blocks),
343			       be32toh(volhdr->partitions[i].first),
344			       be32toh(volhdr->partitions[i].type),
345			  be32toh(volhdr->partitions[i].type) > 13 ? "???" :
346			    sgi_types[be32toh(volhdr->partitions[i].type)]);
347		}
348	}
349}
350
351void
352init_volhdr(void)
353{
354	memset(buf, 0, sizeof(buf));
355	volhdr->magic = htobe32(SGI_BOOT_BLOCK_MAGIC);
356	volhdr->root = htobe16(0);
357	volhdr->swap = htobe16(1);
358	strcpy(volhdr->bootfile, "/netbsd");
359#if HAVE_NBTOOL_CONFIG_H
360	volhdr->dp.dp_skew = 0;
361#else
362	volhdr->dp.dp_skew = lbl.d_trackskew;
363#endif
364	volhdr->dp.dp_gap1 = 1; /* XXX */
365	volhdr->dp.dp_gap2 = 1; /* XXX */
366#if HAVE_NBTOOL_CONFIG_H
367	volhdr->dp.dp_cyls =
368	    htobe16(st.st_size / (SGIVOL_NBTOOL_NSECS * SGIVOL_NBTOOL_NTRACKS));
369#else
370	volhdr->dp.dp_cyls = htobe16(lbl.d_ncylinders);
371#endif
372	volhdr->dp.dp_shd0 = 0;
373#if HAVE_NBTOOL_CONFIG_H
374	volhdr->dp.dp_trks0 = htobe16(SGIVOL_NBTOOL_NTRACKS);
375	volhdr->dp.dp_secs = htobe16(SGIVOL_NBTOOL_NSECS);
376	volhdr->dp.dp_secbytes = htobe16(SGI_BOOT_BLOCK_BLOCKSIZE);
377	volhdr->dp.dp_interleave = htobe16(1);
378#else
379	volhdr->dp.dp_trks0 = htobe16(lbl.d_ntracks);
380	volhdr->dp.dp_secs = htobe16(lbl.d_nsectors);
381	volhdr->dp.dp_secbytes = htobe16(lbl.d_secsize);
382	volhdr->dp.dp_interleave = htobe16(lbl.d_interleave);
383#endif
384	volhdr->dp.dp_nretries = htobe32(22);
385#if HAVE_NBTOOL_CONFIG_H
386	volhdr->partitions[10].blocks =
387	    htobe32(st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE);
388#else
389	volhdr->partitions[10].blocks = htobe32(lbl.d_secperunit);
390#endif
391	volhdr->partitions[10].first = 0;
392	volhdr->partitions[10].type = htobe32(SGI_PTYPE_VOLUME);
393	volhdr->partitions[8].blocks = htobe32(volhdr_size);
394	volhdr->partitions[8].first = 0;
395	volhdr->partitions[8].type = htobe32(SGI_PTYPE_VOLHDR);
396#if HAVE_NBTOOL_CONFIG_H
397	volhdr->partitions[0].blocks =
398	    htobe32((st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE) - volhdr_size);
399#else
400	volhdr->partitions[0].blocks = htobe32(lbl.d_secperunit - volhdr_size);
401#endif
402	volhdr->partitions[0].first = htobe32(volhdr_size);
403	volhdr->partitions[0].type = htobe32(SGI_PTYPE_BSD);
404	write_volhdr();
405}
406
407void
408read_file(void)
409{
410	FILE *fp;
411	int i;
412
413	if (!opt_q)
414		printf("Reading file %s\n", vfilename);
415	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; ++i) {
416		if (strncmp(vfilename, volhdr->voldir[i].name,
417			    strlen(volhdr->voldir[i].name)) == 0)
418			break;
419	}
420	if (i >= SGI_BOOT_BLOCK_MAXVOLDIRS) {
421		printf("File '%s' not found\n", vfilename);
422		exit(1);
423	}
424	/* XXX assumes volume header starts at 0? */
425	lseek(fd, be32toh(volhdr->voldir[i].block) * 512, SEEK_SET);
426	fp = fopen(ufilename, "w");
427	if (fp == NULL) {
428		perror("open write");
429		exit(1);
430	}
431	i = be32toh(volhdr->voldir[i].bytes);
432	while (i > 0) {
433		if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
434			perror("read file");
435			exit(1);
436		}
437		fwrite(buf, 1, i > sizeof(buf) ? sizeof(buf) : i, fp);
438		i -= i > sizeof(buf) ? sizeof(buf) : i;
439	}
440	fclose(fp);
441}
442
443void
444write_file(void)
445{
446	FILE *fp;
447	int slot;
448	size_t namelen;
449	int block, i;
450	struct stat st;
451	char fbuf[512];
452
453	if (!opt_q)
454		printf("Writing file %s\n", ufilename);
455	if (stat(ufilename, &st) < 0) {
456		perror("stat");
457		exit(1);
458	}
459	if (!opt_q)
460		printf("File %s has %lld bytes\n", ufilename, st.st_size);
461	slot = -1;
462	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; ++i) {
463		if (volhdr->voldir[i].name[0] == '\0' && slot < 0)
464			slot = i;
465		if (names_match(i, vfilename)) {
466			slot = i;
467			break;
468		}
469	}
470	if (slot == -1) {
471		printf("No directory space for file %s\n", vfilename);
472		exit(1);
473	}
474	/* -w can overwrite, -a won't overwrite */
475	if (be32toh(volhdr->voldir[slot].block) > 0) {
476		if (!opt_q)
477			printf("File %s exists, removing old file\n",
478				vfilename);
479		volhdr->voldir[slot].name[0] = 0;
480		volhdr->voldir[slot].block = volhdr->voldir[slot].bytes = 0;
481	}
482	if (st.st_size == 0) {
483		printf("bad file size\n");
484		exit(1);
485	}
486	/* XXX assumes volume header starts at 0? */
487	block = allocate_space((int)st.st_size);
488	if (block < 0) {
489		printf("No space for file\n");
490		exit(1);
491	}
492
493	/*
494	 * Make sure the name in the volume header is max. 8 chars,
495	 * NOT including NUL.
496	 */
497	namelen = strlen(vfilename);
498	if (namelen > sizeof(volhdr->voldir[slot].name)) {
499		printf("Warning: '%s' is too long for volume header, ",
500		       vfilename);
501		namelen = sizeof(volhdr->voldir[slot].name);
502		printf("truncating to '%.8s'\n", vfilename);
503	}
504
505	/* Populate it w/ NULs */
506	memset(volhdr->voldir[slot].name, 0,
507	    sizeof(volhdr->voldir[slot].name));
508	/* Then copy the name */
509	memcpy(volhdr->voldir[slot].name, vfilename, namelen);
510
511	volhdr->voldir[slot].block = htobe32(block);
512	volhdr->voldir[slot].bytes = htobe32(st.st_size);
513
514	write_volhdr();
515
516	/* write the file itself */
517	i = lseek(fd, block * 512, SEEK_SET);
518	if (i < 0) {
519		perror("lseek write");
520		exit(1);
521	}
522	i = st.st_size;
523	fp = fopen(ufilename, "r");
524	while (i > 0) {
525		fread(fbuf, 1, i > 512 ? 512 : i, fp);
526		if (write(fd, fbuf, 512) != 512) {
527			perror("write file");
528			exit(1);
529		}
530		i -= i > 512 ? 512 : i;
531	}
532}
533
534void
535delete_file(void)
536{
537	int i;
538
539	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; ++i) {
540		if (names_match(i, vfilename)) {
541			break;
542		}
543	}
544	if (i >= SGI_BOOT_BLOCK_MAXVOLDIRS) {
545		printf("File '%s' not found\n", vfilename);
546		exit(1);
547	}
548
549	/* XXX: we don't compact the file space, so get fragmentation */
550	volhdr->voldir[i].name[0] = '\0';
551	volhdr->voldir[i].block = volhdr->voldir[i].bytes = 0;
552	write_volhdr();
553}
554
555void
556move_file(void)
557{
558	char dstfile[sizeof(volhdr->voldir[0].name) + 1];
559	size_t namelen;
560	int i, slot = -1;
561
562	/*
563	 * Make sure the name in the volume header is max. 8 chars,
564	 * NOT including NUL.
565	 */
566	namelen = strlen(ufilename);
567	if (namelen > sizeof(volhdr->voldir[0].name)) {
568		printf("Warning: '%s' is too long for volume header, ",
569		       ufilename);
570		namelen = sizeof(volhdr->voldir[0].name);
571		printf("truncating to '%.8s'\n", ufilename);
572	}
573	memset(dstfile, 0, sizeof(dstfile));
574	memcpy(dstfile, ufilename, namelen);
575
576	for (i = 0; i < SGI_BOOT_BLOCK_MAXVOLDIRS; i++) {
577		if (names_match(i, vfilename)) {
578			if (slot != -1) {
579				printf("Error: Cannot move '%s' to '%s' - "
580				    "duplicate source files exist!\n",
581				    vfilename, dstfile);
582				exit(1);
583			}
584			slot = i;
585		}
586		if (names_match(i, dstfile)) {
587			printf("Error: Cannot move '%s' to '%s' - "
588			    "destination file already exists!\n",
589			    vfilename, dstfile);
590			exit(1);
591		}
592	}
593	if (slot == -1) {
594		printf("File '%s' not found\n", vfilename);
595		exit(1);
596	}
597
598	/* `dstfile' is already padded with NULs */
599	memcpy(volhdr->voldir[slot].name, dstfile,
600	    sizeof(volhdr->voldir[slot].name));
601
602	write_volhdr();
603}
604
605void
606modify_partition(void)
607{
608	if (!opt_q)
609		printf("Modify partition %d start %d length %d\n",
610			partno, partfirst, partblocks);
611	if (partno < 0 || partno >= SGI_BOOT_BLOCK_MAXPARTITIONS) {
612		printf("Invalid partition number: %d\n", partno);
613		exit(1);
614	}
615	volhdr->partitions[partno].blocks = htobe32(partblocks);
616	volhdr->partitions[partno].first = htobe32(partfirst);
617	volhdr->partitions[partno].type = htobe32(parttype);
618	write_volhdr();
619}
620
621void
622write_volhdr(void)
623{
624	int i;
625
626	checksum_vol();
627
628	if (!opt_q)
629		display_vol();
630	if (!opt_f) {
631		printf("\nDo you want to update volume (y/n)? ");
632		i = getchar();
633		if (i != 'Y' && i != 'y')
634			exit(1);
635	}
636	i = lseek(fd, 0, SEEK_SET);
637	if (i < 0) {
638		perror("lseek 0");
639		exit(1);
640	}
641	i = write(fd, buf, 512);
642	if (i < 0)
643		perror("write volhdr");
644}
645
646int
647allocate_space(int size)
648{
649	int n, blocks;
650	int first;
651
652	blocks = (size + 511) / 512;
653	first = 2;
654	n = 0;
655	while (n < SGI_BOOT_BLOCK_MAXVOLDIRS) {
656		if (volhdr->voldir[n].name[0]) {
657			if (first < (be32toh(volhdr->voldir[n].block) +
658			  (be32toh(volhdr->voldir[n].bytes) + 511) / 512) &&
659			    (first + blocks) > be32toh(volhdr->voldir[n].block)) {
660				first = be32toh(volhdr->voldir[n].block) +
661					(be32toh(volhdr->voldir[n].bytes) + 511) / 512;
662#if 0
663				printf("allocate: n=%d first=%d blocks=%d size=%d\n", n, first, blocks, size);
664				printf("%s %d %d\n", volhdr->voldir[n].name, volhdr->voldir[n].block, volhdr->voldir[n].bytes);
665				printf("first=%d block=%d last=%d end=%d\n", first, volhdr->voldir[n].block,
666				       first + blocks - 1, volhdr->voldir[n].block + (volhdr->voldir[n].bytes + 511) / 512);
667#endif
668				n = 0;
669				continue;
670			}
671		}
672		++n;
673	}
674#if HAVE_NBTOOL_CONFIG_H
675	if (first + blocks > (st.st_size / SGI_BOOT_BLOCK_BLOCKSIZE))
676#else
677	if (first + blocks > lbl.d_secperunit)
678#endif
679		first = -1;
680	/* XXX assumes volume header is partition 8 */
681	/* XXX assumes volume header starts at 0? */
682	if (first + blocks >= be32toh(volhdr->partitions[8].blocks))
683		first = -1;
684	return (first);
685}
686
687void
688checksum_vol(void)
689{
690	int32_t *l;
691	int i;
692
693	volhdr->checksum = checksum = 0;
694	l = (int32_t *)buf;
695	for (i = 0; i < 512 / 4; ++i)
696		checksum += be32toh(l[i]);
697	volhdr->checksum = htobe32(-checksum);
698}
699
700void
701usage(void)
702{
703	printf("Usage:	sgivol [-qf] -i [-h vhsize] device\n"
704	       "	sgivol [-qf] -r vhfilename diskfilename device\n"
705	       "	sgivol [-qf] -w vhfilename diskfilename device\n"
706	       "	sgivol [-qf] -d vhfilename device\n"
707	       "	sgivol [-qf] -m vhfilename vhfilename device\n"
708	       "	sgivol [-qf] -p partno partfirst partblocks "
709	       "parttype device\n"
710	       );
711	exit(0);
712}
713