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