/*
 * Copyright (c) 1987, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Copyright (c) 1994 Jason R. Thorpe.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Symmetric Computer Systems.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and it's contributors.
 *	This product includes software developed by Jason R. Thorpe.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

static char rcsid[] =
	"$Id: makedisk.c 1.3 1994/11/08 20:32:27 thorpej Exp $";

#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define	DKTYPENAMES
#include "disklabel.h"

#ifndef	BUFSIZ
#undef	BUFSIZ
#endif
#define	BUFSIZ		1024
#define	BIGBUF		65536

#define	BBSIZE		8192		/* size of boot area, with label */
#define	SBSIZE		8192		/* FFS superblock size */
#define	streq(a,b)	(strcmp(a,b) == 0)
#define	progname	"makedisk"
#define	path_label	"/tmp/NetBSD.disklabel"

char bootarea[BBSIZE];
char *xxboot = 0;	/* hp300 has a one-part bootblock */
char *bootbuf;		/* pointer to buffer with remainder of bootblock */
int bootsize;		/* size of remaining bootblock */
struct disklabel lab;

int eval = 0;		/* our exit value */
int e_boundary = 0;	/* did we violate a boundary? */

extern int errno;

#ifdef	__P
#undef	__P
#endif
#if defined(__STDC__) || defined(__cplus_plus)
#define	__P(protos)	protos
#else
#define	__P(protos)	()
#endif

struct disklabel *makebootarea __P((char *, struct disklabel *, int));
int checklabel __P((struct disklabel *));
int writelabel __P((int, char *, struct disklabel *));
u_short dkcksum __P((struct disklabel *));
int dumpdisk __P((char *, char *, struct disklabel *));

usage()
{
	fprintf(stderr, "usage: %s disk protofile bootblock rootimage\n",
	        progname);
}

Perror(str)
	char *str;
{
	fprintf(stderr, "%s: ", progname);
	perror(str);
}

Warning(fmt, a1, a2, a3, a4, a5)
	char *fmt;
{
	fprintf(stderr, "%s: warning: ", progname);
	fprintf(stderr, fmt, a1, a2, a3, a4, a5);
	fprintf(stderr, "\n");
}

char *
skip(cp)
	char *cp;
{
	while (*cp != '\0' && isspace(*cp))
		++cp;
	if (*cp == '\0' || *cp == '#')
		return ((char *) NULL);
	return (cp);
}

char *
word(cp)
	char *cp;
{
	char c;

	while (*cp != '\0' && !isspace(*cp) && *cp != '#')
		++cp;
	if ((c = *cp) != '\0') {
		*cp++ = '\0';
		if (c != '#')
			return(skip(cp));
	}
	return ((char *) NULL);
}

main(argc, argv)
	int argc;
	char **argv;
{
	struct disklabel *lp;
	char *disk = 0, *file = 0, *img = 0;
	FILE *proto;
	int askme, dfd;
	struct stat st;

	disk = argv[1];
	file = argv[2];
	xxboot = argv[3];
	img = argv[4];

	if (!disk || !file || !xxboot || !img || argc > 5) {
		usage();
		exit(1);
	}

	if (stat(disk, &st)) {
		Perror(disk);
		exit(1);
	}
	if (!S_ISCHR(st.st_mode)) {
		fprintf(stderr, "%s: %s: not a character device\n",
		        progname, disk);
		exit(1);
	}
	if (stat(file, &st)) {
		Perror(file);
		exit(1);
	}
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "%s: %s: not a regular file\n",
		        progname, file);
		exit(1);
	}

	if (stat(xxboot, &st)) {
		Perror(xxboot);
		exit(1);
	}
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "%s: %s: not a regular file\n",
		        progname, xxboot);
		exit(1);
	}
	if (stat(img, &st)) {
		Perror(img);
		exit(1);
	}
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "%s: %s: not a regular file\n",
		        progname, img);
		exit(1);
	}

	dfd = open(path_label, O_RDWR | O_CREAT, 0644);
	if (dfd < 0) {
		Perror(path_label);
		exit(1);
	}

	printf("\n");
	lp = makebootarea(bootarea, &lab, dfd);
	if (eval)
		goto done;
	if (!(proto = fopen(file, "r"))) {
		Perror(file);
		++eval;
		goto done;
	}
	if (getasciilabel(proto, lp))
		if (writelabel(dfd, bootarea, lp)) {
			fprintf(stderr, "%s: can't write disklabel\n",
				progname);
			++eval;
			goto done;
		}
	if (close(dfd)) {
		Perror(path_label);
		++eval;
		goto done;
	}
	/*
	 * Provide a hook for exiting in case checklabel() found
	 * problems with cylinder boundaries.
	 */
	if (e_boundary) {
		printf("\nIf you see either of the following above:\n");
		printf("\tsize %% cylinder-size != 0\n");
		printf("\toffset %% cylinder-size != 0\n");
		printf("then you should not proceed, but rather go back and\n");
		printf("edit your disklabel such that the partitions\n");
		printf("are cylinder-aligned.\n\n");
		printf("Hit <Y><return> to proceed, anything else exits: ");
		askme = getchar();
		if (askme != 'Y') {
			printf("\nExiting.\n", askme);
			eval = 254;
			goto done;
		}
	}
 ok:
	if (dumpdisk(disk, img, lp))
		++eval;

 done:
	(void) unlink(path_label);
	exit(eval);
}

struct disklabel *
makebootarea(boot, dp, f)
	char *boot;
	struct disklabel *dp;
	int f;
{
	struct disklabel *lp;
	struct stat sb;
	char *p;
	int b;

	dp->d_secsize = DEV_BSIZE;	/* XXX: hardwire */
	dp->d_bbsize = BBSIZE;

	lp = (struct disklabel *)
		(boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET);
	memset((char *)lp, 0, sizeof *lp);	/* for HP-UX 7 */

	/*
	 * We're not going to worry about clobbering any existing
	 * boot area here, since, hopefully, the disk has been
	 * prepared with mediainit(1M).
	 *
	 * We have a strange rule to contend with.  On the hp300, up to
	 * d_bbsize bytes of ``xxboot'' go into the bootarea, and the
	 * rest is remembered and written later following the bootarea.
	 */
	b = open(xxboot, O_RDONLY);
	if (b < 0) {
		Perror(xxboot);
		++eval;
		goto finis;
	}

	if (read(b, boot, (size_t) dp->d_bbsize) < 0) {
		Perror(xxboot);
		++eval;
		goto finis;
	}
	if (fstat(b, &sb)) {
		Perror(xxboot);
		++eval;
		goto finis;
	}
	bootsize = (int)sb.st_size - dp->d_bbsize;
	if (bootsize > 0) {
		/* XXX: assume d_sectsize is power of 2 */
		bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1);
		bootbuf = (char *)malloc((size_t) bootsize);
		if (bootbuf == 0) {
			Perror(xxboot);
			++eval;
			goto finis;
		}
		if (read(b, bootbuf, bootsize) < 0) {
			free(bootbuf);
			Perror(xxboot);
			++eval;
			goto finis;
		}
	}
	
	/*
	 * Ensure that no part of the bootblock has spilled into the
	 * area reserved for the label.
	 */
	for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++)
		if (*p) {
			fprintf(stderr,
				"Bootblock spills into label area.\n");
			++eval;
		}

 finis:
	if (close(b)) {
		Perror("Closing bootblock");
		++eval;
	}
	return (lp);
}

/*
 * Read an ascii label in from descriptor f,
 * in regular ``prototype'' format, and fill
 * in lp.
 */
int
getasciilabel(f, lp)
	FILE *f;
	struct disklabel *lp;
{
	char **cpp, *cp;
	struct partition *pp;
	char *tp, *s, line[BUFSIZ];
	int v, lineno = 0, errors = 0;

	lp->d_bbsize = BBSIZE;		/* XXX */
	lp->d_sbsize = SBSIZE;		/* XXX */

	while (fgets(line, sizeof(line) - 1, f)) {
		++lineno;
		if (cp = strchr(line, '\n'))
			*cp = '\0';
		cp = skip(line);
		if (cp == (char *) NULL)
			continue;
		tp = strchr(cp, ':');
		if (tp == (char *) NULL) {
			fprintf(stderr, "line %d: syntax error\n", lineno);
			++errors;
			continue;
		}
		*tp++ = '\0', tp = skip(tp);
		if (streq(cp, "type")) {
			if (tp == (char *) NULL)
				tp = "unknown";
			cpp = dktypenames;
			for (; cpp < &dktypenames[DKMAXTYPES]; cpp++)
				if ((s = *cpp) && streq(s, tp)) {
					lp->d_type = cpp - dktypenames;
					goto next;
				}
			v = atoi(tp);
			if ((unsigned) v >= DKMAXTYPES)
				fprintf(stderr, "ling %d:%s %d\n", lineno,
					"Warning, unknown disk type", v);
			lp->d_type = v;
			continue;
		}
		if (streq(cp, "flags")) {
			for (v = 0; (cp = tp) && *cp != '\0';) {
				tp = word(cp);
				if (streq(cp, "removeable"))
					v |= D_REMOVABLE;
				else if (streq(cp, "ecc"))
					v |= D_ECC;
				else if (streq(cp, "badsect"))
					v |= D_BADSECT;
				else {
					fprintf(stderr,
						"line %d: %s: bad flag\n",
						lineno, cp);
					++errors;
				}
			}
			lp->d_flags = v;
			continue;
		}
		if (streq(cp, "drivedata")) {
			int i;

			for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) {
				lp->d_drivedata[i++] = atoi(cp);
				tp = word(cp);
			}
			continue;
		}
		if (sscanf(cp, "%d partitions", &v) == 1) {
			if (v == 0 || (unsigned) v > MAXPARTITIONS) {
				fprintf(stderr,
					"line %d: bad # of partitions\n",
					lineno);
				++errors;
			} else
				lp->d_npartitions = v;
			continue;
		}
		if (tp == (char *) NULL)
			tp = "";
		if (streq(cp, "disk")) {
			strncpy(lp->d_typename, tp, sizeof(lp->d_typename));
			continue;
		}
		if (streq(cp, "label")) {
			strncpy(lp->d_packname, tp, sizeof(lp->d_packname));
			continue;
		}
		if (streq(cp, "bytes/sector")) {
			v = atoi(tp);
			if (v <= 0 || (v % 512) != 0) {
				fprintf(stderr,
					"line %d: %s: bad sector size\n",
					lineno, tp);
				++errors;
			} else
				lp->d_secsize = v;
			continue;
		}
		if (streq(cp, "sectors/track")) {
			v = atoi(tp);
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_nsectors = v;
			continue;
		}
		if (streq(cp, "sectors/cylinder")) {
			v = atoi(tp);
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_secpercyl = v;
			continue;
		}
		if (streq(cp, "tracks/cylinder")) {
			v = atoi(tp);
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_ntracks = v;
			continue;
		}
		if (streq(cp, "cylinders")) {
			v = atoi(tp);
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_ncylinders = v;
			continue;
		}
		if (streq(cp, "rpm")) { 
			v = atoi(tp); 
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_rpm = v;
			continue;
		}
		if (streq(cp, "interleave")) {
			v = atoi(tp);
			if (v <= 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_interleave = v;
			continue;
		}
		if (streq(cp, "trackskew")) {
			v = atoi(tp);
			if (v < 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_trackskew = v;
			continue;
		}
		if (streq(cp, "cylinderskew")) {
			v = atoi(tp);
			if (v < 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_cylskew = v;
			continue;
		}
		if (streq(cp, "headswitch")) {
			v = atoi(tp);
			if (v < 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_headswitch = v;
			continue;
		}
		if (streq(cp, "track-to-track seek")) {
			v = atoi(tp);
			if (v < 0) {
				fprintf(stderr, "line %d: %s: bad %s\n",
					lineno, tp, cp);
				++errors;
			} else
				lp->d_trkseek = v;
			continue;
		}
		if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') {
			unsigned part = *cp - 'a';

			if (part > lp->d_npartitions) {
				fprintf(stderr,
				        "line %d: bad partition name\n",
				        lineno);
				++errors;
				continue;
			}
			pp = &lp->d_partitions[part];
#define	NXTNUM(n) { \
	cp = tp, tp = word(cp); \
	if (tp == (char *) NULL) \
		tp = cp; \
	(n) = atoi(cp); \
     }

			NXTNUM(v);
			if (v < 0) {
				fprintf(stderr,
					"line %d: %s: bad partition size\n",
					lineno, cp);
				++errors;
			} else
				pp->p_size = v;
			NXTNUM(v);
			if (v < 0) {
				fprintf(stderr,
					"line %d: %s: bad partition offset\n",
					lineno, cp);
				++errors;
			} else
				pp->p_offset = v;
			cp = tp, tp = word(cp);
			cpp = fstypenames;
			for (; cpp < &fstypenames[FSMAXTYPES]; cpp++)
				if ((s = *cpp) && streq(s, cp)) {
					pp->p_fstype = cpp - fstypenames;
					goto gottype;
				}
			if (isdigit(*cp))
				v = atoi(cp);
			else
				v = FSMAXTYPES;
			if ((unsigned) v >= FSMAXTYPES) {
				fprintf(stderr, "line %d: %s %s\n", lineno,
					"Warning, unknown filesystem type", cp);
				v = FS_UNUSED;
			}
			pp->p_fstype = v;
 gottype:
			switch (pp->p_fstype) {

			case FS_UNUSED:				/* XXX */
				NXTNUM(pp->p_fsize);
				if (pp->p_fsize == 0)
					break;
				NXTNUM(v);
				pp->p_frag = v / pp->p_fsize;
				break;

			case FS_BSDFFS:
				NXTNUM(pp->p_fsize);
				if (pp->p_fsize == 0)
					break;
				NXTNUM(v);
				pp->p_frag = v / pp->p_fsize;
				NXTNUM(pp->p_cpg);
				break;

			default:
				break;
			}
			continue;
		}
		fprintf(stderr, "line %d: %s: Unknown disklabel field\n",
			lineno, cp);
		++errors;
 next:
		;
	}
	errors += checklabel(lp);
	return (errors == 0);
}

/*
 * Check disklabel for errors and fill in
 * derived fields according to supplied values.
 */
int
checklabel(lp)
	struct disklabel *lp;
{
	struct partition *pp;
	int i, errors = 0;
	char part;

	if (lp->d_secsize == 0) {
		fprintf(stderr, "sector size %d\n", lp->d_secsize);
		return (1);
	}
	if (lp->d_nsectors == 0) {
		fprintf(stderr, "sectors/track %d\n", lp->d_nsectors);
		return (1);
	}
	if (lp->d_ntracks == 0) {
		fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks);
		return (1);
	}
	if (lp->d_ncylinders == 0) {
		fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders);
		++errors;
	}
	if (lp->d_rpm == 0)
		Warning("revolutions/minute %d\n", lp->d_rpm);
	if (lp->d_secpercyl == 0) {
		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
		fprintf(stderr, "Using calculated value %d for %s.\n",
			lp->d_secpercyl, "sectors/cylinder");
	}
	if (lp->d_secperunit == 0) {
		lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
		fprintf(stderr, "Using calculated value %d for %s.\n",
			lp->d_secperunit, "sectors/unit");
	}
	if (lp->d_bbsize == 0) {
		fprintf(stderr, "boot block size %d\n", lp->d_bbsize);
		++errors;
	} else if (lp->d_bbsize % lp->d_secsize)
		Warning("boot block size %% sector-size != 0\n");
	if (lp->d_sbsize == 0) {
		fprintf(stderr, "superblock size %d\n", lp->d_sbsize);
		++errors;
	} else if (lp->d_sbsize % lp->d_secsize)
		Warning("superblock size %% sector-size != 0\n");
	if (lp->d_npartitions > MAXPARTITIONS)
		Warning("number of partitions (%d) > MAXPARTITIONS (%d)\n",
			lp->d_npartitions, MAXPARTITIONS);
	for (i = 0; i < lp->d_npartitions; i++) {
		part = 'a' + i;
		pp = &lp->d_partitions[i];
		if (pp->p_size == 0 && pp->p_offset != 0)
			Warning("partition %c: size 0, but offset %d\n",
				part, pp->p_offset);

		if (pp->p_size % lp->d_secpercyl) {
			Warning("partition %c: size %% cylinder-size != 0\n",
				part);
			++e_boundary;
		}
		if (pp->p_offset % lp->d_secpercyl) {
			Warning("partition %c: offset %% cylinder-size != 0\n",
				part);
			++e_boundary;
		}

		if (pp->p_offset > lp->d_secperunit) {
			fprintf(stderr,
				"partition %c: offset past end of unit\n",
				part);
			++errors;
		}
		if (pp->p_offset + pp->p_size > lp->d_secperunit) {
			fprintf(stderr,
			   "partition %c: partition extends past end of unit\n",
				part);
			++errors;
		}
	}
	for (; i< MAXPARTITIONS; i++) {
		part = 'a' + i;
		pp = &lp->d_partitions[i];
		if (pp->p_size || pp->p_offset)
			Warning("unused partition %c: size %d offset %d\n",
				'a' + i, pp->p_size, pp->p_offset);
	}
	return (errors);
}

/*
 * Actually put the label on the disk.
 */
int
writelabel(f, boot, lp)
	int f;
	char *boot;
	struct disklabel *lp;
{
	int i;
	int flag;
	off_t sectoroffset = 0;			/* XXX */
	size_t rval;
	FILE *fp;

	setbootflag(lp);

	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_checksum = 0;
	lp->d_checksum = dkcksum(lp);

	/*
	 * XXX: Drop the label into a file.  We can't dump
	 * it right onto the disk, as HP-UX doesn't
	 * seem to lseek() correctly on raw devices.
	 * *sigh*
	 */
	rval = lseek(f, (off_t) sectoroffset, SEEK_SET);
	if (rval < 0 ) {
		Perror("lseek");
		return (1);
	}

	/* first part of bootblock */
	printf("Generating bootblock: %d+", lp->d_bbsize);
	if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) {
		Perror("write");
		return (1);
	}
	/* second part of bootblock */
	printf("%d\n", bootsize);
	if (bootbuf && write(f, bootbuf, bootsize) != bootsize) {
		Perror("write");
		return(1);
	}
	/* Emulate the DIOCWLABEL ioctl. */
	printf("Generating label at offset: %d+",
	       (LABELSECTOR * DEV_BSIZE) + LABELOFFSET);
	rval = lseek(f, (off_t) ((LABELSECTOR * DEV_BSIZE) + LABELOFFSET),
		     SEEK_SET);
	if (rval < 0) {
		Perror("lseek");
		return (1);
	}
	rval = write(f, lp, (size_t) sizeof(*lp));
	printf("%d\n", rval);
	if (rval != sizeof(*lp)) {
		Perror("write");
		fprintf(stderr, "%s: can't write label structure.\n", progname);
		return (1);
	}
	if (fsync(f)) {
		Perror("fsync");
		fprintf(stderr, "%s: can't flush cache.\n", progname);
		return (1);
	}

	/* Whew!  Made it! */
	return (0);
}

/*
 * We're probably going to spill out of d_bbsize with the
 * bootblock, so it's necessary to mark whichever partition
 * it spills into as ``boot''.  On the hp300, this is always the
 * ``c'' partition (on a bootable disk, that is...), but we'll
 * run through the algorithm anyway...
 */
setbootflag(lp)
	struct disklabel *lp;
{
	struct partition *pp;
	int i, errors = 0;
	char part;
	u_long boffset;

	if (bootbuf == 0)
		return;
	boffset = bootsize / lp->d_secsize;
	for (i = 0; i < lp->d_npartitions; i++) {
		part = 'a' + i;
		pp = &lp->d_partitions[i];
		if (pp->p_size == 0)
			continue;
		if (boffset <= pp->p_offset) {
			if (pp->p_fstype == FS_BOOT)
				pp->p_fstype = FS_UNUSED;
		} else if (pp->p_fstype != FS_BOOT) {
			if (pp->p_fstype != FS_UNUSED) {
				fprintf(stderr,
					"boot overlaps used partition %c\n",
					part);
				++errors;
			} else {
				pp->p_fstype = FS_BOOT;
				Warning("boot overlaps partition %c: %s",
					part, "markes as FS_BOOT");
			}
		}
	}
	if (errors) {
		fprintf(stderr, "%s: Cannot install bootblock.\n", progname);
		exit(4);
	}
}

u_short
dkcksum(lp)
	struct disklabel *lp;
{
	u_short *start, *end;
	u_short sum = 0;

	start = (u_short *)lp;
	end = (u_short *)&lp->d_partitions[lp->d_npartitions];
	while (start < end)
		sum ^= *start++;
	return (sum);
}

int
dumpdisk(disk, img, lp)
	char *disk;
	char *img;
	struct disklabel *lp;
{
	char *blank = 0, buf[BUFSIZ], bigbuf[BIGBUF], cmd[BUFSIZ], *ext;
	FILE *in, *out;
	struct partition *pp;
	size_t accum = 0, size;
	int dfd, fd;

	/*
	 * XXX: This is stupid, but a necessary evil, I guess.
	 * HP-UX is really picky about writing to raw
	 * devices, but dd(1) seems to do it ok - we'll use
	 * dd.
	 */
	sprintf(cmd, "dd of=%s bs=%d", disk, BIGBUF);
	out = popen(cmd, "w");
	if (out == (FILE *) NULL) {
		Perror("popen");
		return (1);
	}
	dfd = fileno(out);
	if (dfd < 0) {
		Perror("fileno");
		return (1);
	}

	fd = open(path_label, O_RDONLY);
	if (fd < 0) {
		Perror(path_label);
		return (1);
	}
	printf("Writing bootblock/disklabel.\n");
	while ((size = read(fd, buf, BUFSIZ)) > 0) {
		if (write(dfd, buf, size) != size) {
			printf("\n");
			Perror("write");
			return (1);
		}
		accum += size;
	}
	if (close(fd)) {
		Perror("close");
		return (1);
	}

	printf("Jumping to partition a.\n");
	size = (lp->d_partitions[0].p_offset * DEV_BSIZE) - accum;
	blank = (char *) malloc((size_t) size);
	if (blank == (char *) NULL) {
		fprintf(stderr, "malloc failed\n");
		return (1);
	}
	memset(blank, '\0', (size_t) size);
	if (write(dfd, blank, size) != size) {
		Perror("write");
		free(blank);
		return (1);
	}
	free(blank);
	fflush(out);
	fflush(stdout);

	ext = strrchr(img, '.');
	if (!ext)
		sprintf(cmd, "cat %s", img);
	else if (streq(ext, ".Z"))
		sprintf(cmd, "zcat %s", img);
	else if (streq(ext, ".gz"))
		sprintf(cmd, "gzip -d -c %s", img);
	else
		sprintf(cmd, "cat %s", img);
	in = popen(cmd, "r");
	fd = fileno(in);
	if (fd < 0) {
		Perror(img);
		return (1);
	}
	printf("Writing root image: ");
	while ((size = read(fd, bigbuf, BIGBUF)) > 0) {
		printf(".");
		if (write(dfd, bigbuf, size) != size) {
			printf("\n");
			Perror("write");
			return (1);
		}
		fflush(out);
		fflush(stdout);
	}
	printf("\n");

	pclose(in);
	pclose(out);
	
	return (0);
}
