/*	$NetBSD: rump_allserver.c,v 1.39 2015/04/16 10:05:43 pooka Exp $	*/

/*-
 * Copyright (c) 2010, 2011 Antti Kantee.  All Rights Reserved.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

#include <rump/rumpuser_port.h>

#ifndef lint
__RCSID("$NetBSD: rump_allserver.c,v 1.39 2015/04/16 10:05:43 pooka Exp $");
#endif /* !lint */

#include <sys/types.h>
#include <sys/stat.h>

#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include <rump/rump.h>
#include <rump/rump_syscalls.h>
#include <rump/rumpdefs.h>
#include <rump/rumperr.h>

__dead static void
usage(void)
{

#ifndef HAVE_GETPROGNAME
#define getprogname() "rump_server"
#endif
	fprintf(stderr, "usage: %s [-s] [-c ncpu] [-d drivespec] [-l libs] "
	    "[-m modules] bindurl\n", getprogname());
	exit(1);
}

__dead static void
diedie(int sflag, const char *reason, int error, const char *errstr)
{

	if (reason != NULL)
		fputs(reason, stderr);
	if (errstr) {
		fprintf(stderr, ": %s", errstr);
	}
	fputc('\n', stderr);
	if (!sflag)
		rump_daemonize_done(error);
	exit(1);
}

__dead static void
die(int sflag, int error, const char *reason)
{

	diedie(sflag, reason, error, error == 0 ? NULL : strerror(error));
}

__dead static void
die_rumperr(int sflag, int error, const char *reason)
{

	diedie(sflag, reason, error, error == 0 ? NULL : rump_strerror(error));
}

static sem_t sigsem;
static void
sigreboot(int sig)
{

	sem_post(&sigsem);
}

static const char *const disktokens[] = {
#define DKEY 0
	"key",
#define DFILE 1
	"hostpath",
#define DSIZE 2
#define DSIZE_E -1
	"size",
#define DOFFSET 3
	"offset",
#define DLABEL 4
	"disklabel",
#define DTYPE 5
	"type",
	NULL
};

struct etfsreg {
	const char *key;
	const char *hostpath;
	off_t flen;
	off_t foffset;
	char partition;
	enum rump_etfs_type type;
};

struct etfstype {
	const char *name;
	enum rump_etfs_type type;
} etfstypes[] = {
	{ "blk", RUMP_ETFS_BLK },
	{ "chr", RUMP_ETFS_CHR },
	{ "reg", RUMP_ETFS_REG },
};

static void processlabel(int, int, int, off_t *, off_t *);

#define ALLOCCHUNK 32

int
main(int argc, char *argv[])
{
	const char *serverurl;
	struct etfsreg *etfs = NULL;
	unsigned netfs = 0, curetfs = 0;
	int error;
	int ch, sflag, onthepath;
	unsigned i;
	char **modarray = NULL, **libarray = NULL;
	unsigned nmods = 0, curmod = 0, nlibs = 0, curlib = 0, libidx;
	unsigned liblast = -1; /* XXXgcc */

	setprogname(argv[0]);
	sflag = 0;
	while ((ch = getopt(argc, argv, "c:d:l:m:r:sv")) != -1) {
		switch (ch) {
		case 'c':
			setenv("RUMP_NCPU", optarg, 1);
			break;
		case 'd': {
			char *options, *value;
			char *key, *hostpath;
			long long flen, foffset;
			char partition;
			int ftype;

			flen = foffset = 0;
			partition = 0;
			key = hostpath = NULL;
			ftype = -1;
			options = optarg;
			while (*options) {
				switch (getsubopt(&options,
				    __UNCONST(disktokens), &value)) {
				case DKEY:
					if (key != NULL) {
						fprintf(stderr,
						    "key already given\n");
						usage();
					}
					key = value;
					break;

				case DFILE:
					if (hostpath != NULL) {
						fprintf(stderr,
						    "hostpath already given\n");
						usage();
					}
					hostpath = value;
					break;

				case DSIZE:
					if (flen != 0) {
						fprintf(stderr,
						    "size already given\n");
						usage();
					}
					if (strcmp(value, "host") == 0) {
						if (foffset != 0) {
							fprintf(stderr,
							    "cannot specify "
							    "offset with "
							    "size=host\n");
							usage();
						}
						flen = DSIZE_E;
					} else {
#ifdef HAVE_STRSUFTOLL
						/* XXX: off_t max? */
						flen = strsuftoll("-d size",
						    value, 0, LLONG_MAX);
#else
						flen = strtoull(value,
						    NULL, 10);
#endif
					}
					break;
				case DOFFSET:
					if (foffset != 0) {
						fprintf(stderr,
						    "offset already given\n");
						usage();
					}
					if (flen == DSIZE_E) {
						fprintf(stderr, "cannot "
						    "specify offset with "
						    "size=host\n");
						usage();
					}
#ifdef HAVE_STRSUFTOLL
					/* XXX: off_t max? */
					foffset = strsuftoll("-d offset", value,
					    0, LLONG_MAX);
#else
					foffset = strtoull(value, NULL, 10);
#endif
					break;

				case DLABEL:
					if (foffset != 0 || flen != 0) {
						fprintf(stderr,
						    "disklabel needs to be "
						    "used alone\n");
						usage();
					}
					if (strlen(value) != 1 ||
					    *value < 'a' || *value > 'z') {
						fprintf(stderr,
						    "invalid label part\n");
						usage();
					}
					partition = *value;
					break;

				case DTYPE:
					if (ftype != -1) {
						fprintf(stderr,
						    "type already specified\n");
						usage();
					}

					for (i = 0;
					    i < __arraycount(etfstypes);
					    i++) {
						if (strcmp(etfstypes[i].name,
						    value) == 0)
							break;
					}
					if (i == __arraycount(etfstypes)) {
						fprintf(stderr,
						    "invalid type %s\n", value);
						usage();
					}
					ftype = etfstypes[i].type;
					break;

				default:
					fprintf(stderr, "invalid dtoken\n");
					usage();
					break;
				}
			}

			if (key == NULL || hostpath == NULL ||
			    (flen == 0
			      && partition == 0 && ftype != RUMP_ETFS_REG)) {
				fprintf(stderr, "incomplete drivespec\n");
				usage();
			}
			if (ftype == -1)
				ftype = RUMP_ETFS_BLK;

			if (netfs - curetfs == 0) {
				etfs = realloc(etfs,
				    (netfs+ALLOCCHUNK)*sizeof(*etfs));
				if (etfs == NULL)
					die(1, errno, "realloc etfs");
				netfs += ALLOCCHUNK;
			}

			etfs[curetfs].key = key;
			etfs[curetfs].hostpath = hostpath;
			etfs[curetfs].flen = flen;
			etfs[curetfs].foffset = foffset;
			etfs[curetfs].partition = partition;
			etfs[curetfs].type = ftype;
			curetfs++;

			break;
		}
		case 'l':
			if (nlibs - curlib == 0) {
				libarray = realloc(libarray,
				    (nlibs+ALLOCCHUNK) * sizeof(char *));
				if (libarray == NULL)
					die(1, errno, "realloc");
				nlibs += ALLOCCHUNK;
			}
			libarray[curlib++] = optarg;
			break;
		case 'm':
			if (nmods - curmod == 0) {
				modarray = realloc(modarray,
				    (nmods+ALLOCCHUNK) * sizeof(char *));
				if (modarray == NULL)
					die(1, errno, "realloc");
				nmods += ALLOCCHUNK;
			}
			modarray[curmod++] = optarg;
			break;
		case 'r':
			setenv("RUMP_MEMLIMIT", optarg, 1);
			break;
		case 's':
			sflag = 1;
			break;
		case 'v':
			setenv("RUMP_VERBOSE", "1", 1);
			break;
		default:
			usage();
			/*NOTREACHED*/
		}
	}

	argc -= optind;
	argv += optind;

	if (argc != 1)
		usage();

	/*
	 * Automatically "resolve" component dependencies, i.e.
	 * try to load libs in a loop until all are loaded or a
	 * full loop completes with no loads (latter case is an error).
	 */
	for (onthepath = 1, nlibs = curlib; onthepath && nlibs > 0;) {
		onthepath = 0;
		for (libidx = 0; libidx < curlib; libidx++) {
			/* loaded already? */
			if (libarray[libidx] == NULL)
				continue;

			/* try to load */
			liblast = libidx;
			if (dlopen(libarray[libidx],
			    RTLD_LAZY|RTLD_GLOBAL) == NULL) {
				char pb[MAXPATHLEN];
				/* try to mimic linker -l syntax */
				snprintf(pb, sizeof(pb),
				    "lib%s.so", libarray[libidx]);
				if (dlopen(pb, RTLD_LAZY|RTLD_GLOBAL) == NULL)
					continue;
			}

			/* managed to load that one */
			libarray[libidx] = NULL;
			nlibs--;
			onthepath = 1;
		}
	}
	if (nlibs > 0) {
		fprintf(stderr,
		    "failed to load -libraries, last error from \"%s\":\n",
		    libarray[liblast]);
		fprintf(stderr, "  %s", dlerror());
		die(1, 0, NULL);
	}
	free(libarray);

	serverurl = argv[0];

	if (!sflag) {
		error = rump_daemonize_begin();
		if (error)
			die_rumperr(1, error, "rump daemonize");
	}

	error = rump_init();
	if (error)
		die_rumperr(sflag, error, "rump init failed");

	/* load modules */
	for (i = 0; i < curmod; i++) {
		struct rump_modctl_load ml;

#define ETFSKEY "/module.mod"
		if ((error = rump_pub_etfs_register(ETFSKEY,
		    modarray[0], RUMP_ETFS_REG)) != 0)
			die_rumperr(sflag,
			    error, "module etfs register failed");
		memset(&ml, 0, sizeof(ml));
		ml.ml_filename = ETFSKEY;
		/*
		 * XXX: since this is a syscall, error namespace depends
		 * on loaded emulations.  revisit and fix.
		 */
		if (rump_sys_modctl(RUMP_MODCTL_LOAD, &ml) == -1)
			die(sflag, errno, "module load failed");
		rump_pub_etfs_remove(ETFSKEY);
#undef ETFSKEY
	}
	free(modarray);

	/* register host drives */
	for (i = 0; i < curetfs; i++) {
		struct stat sb;
		off_t foffset, flen, fendoff;
		int fd, oflags;

		oflags = etfs[i].flen == DSIZE_E ? 0 : O_CREAT;
		fd = open(etfs[i].hostpath, O_RDWR | oflags, 0644);
		if (fd == -1)
			die(sflag, errno, "etfs hostpath open");

		if (etfs[i].partition) {
			processlabel(sflag, fd, etfs[i].partition - 'a',
			    &foffset, &flen);
		} else {
			foffset = etfs[i].foffset;
			flen = etfs[i].flen;
		}

		if (fstat(fd, &sb) == -1)
			die(sflag, errno, "fstat etfs hostpath");
		if (flen == DSIZE_E) {
			if (sb.st_size == 0)
				die(sflag, EINVAL, "size=host, but cannot "
				    "query non-zero size");
			flen = sb.st_size;
		}
		fendoff = foffset + flen;
		if (S_ISREG(sb.st_mode) && sb.st_size < fendoff) {
			if (ftruncate(fd, fendoff) == -1)
				die(sflag, errno, "truncate");
		}
		close(fd);

		if ((error = rump_pub_etfs_register_withsize(etfs[i].key,
		    etfs[i].hostpath, etfs[i].type, foffset, flen)) != 0)
			die_rumperr(sflag, error, "etfs register");
	}

	error = rump_init_server(serverurl);
	if (error)
		die_rumperr(sflag, error, "rump server init failed");

	if (!sflag)
		rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS);

	sem_init(&sigsem, 0, 0);
	signal(SIGTERM, sigreboot);
	signal(SIGINT, sigreboot);
	sem_wait(&sigsem);

	rump_sys_reboot(0, NULL);
	/*NOTREACHED*/

	return 0;
}

/*
 * Copyright (c) 1987, 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may 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.
 *
 *	@(#)disklabel.h	8.2 (Berkeley) 7/10/94
 */

#define	RUMPSERVER_MAXPARTITIONS	22
#define	RUMPSERVER_DISKMAGIC		((uint32_t)0x82564557)	/* magic */
#define	RUMPSERVER_DEVSHIFT		9

struct rumpserver_disklabel {
	uint32_t d_magic;		/* the magic number */
	uint16_t d_type;		/* drive type */
	uint16_t d_subtype;		/* controller/d_type specific */
	char	 d_typename[16];	/* type name, e.g. "eagle" */

	/*
	 * d_packname contains the pack identifier and is returned when
	 * the disklabel is read off the disk or in-core copy.
	 * d_boot0 and d_boot1 are the (optional) names of the
	 * primary (block 0) and secondary (block 1-15) bootstraps
	 * as found in /usr/mdec.  These are returned when using
	 * getdiskbyname(3) to retrieve the values from /etc/disktab.
	 */
	union {
		char	un_d_packname[16];	/* pack identifier */
		struct {
			char *un_d_boot0;	/* primary bootstrap name */
			char *un_d_boot1;	/* secondary bootstrap name */
		} un_b;
	} d_un;
#define	d_packname	d_un.un_d_packname
#define	d_boot0		d_un.un_b.un_d_boot0
#define	d_boot1		d_un.un_b.un_d_boot1

			/* disk geometry: */
	uint32_t d_secsize;		/* # of bytes per sector */
	uint32_t d_nsectors;		/* # of data sectors per track */
	uint32_t d_ntracks;		/* # of tracks per cylinder */
	uint32_t d_ncylinders;		/* # of data cylinders per unit */
	uint32_t d_secpercyl;		/* # of data sectors per cylinder */
	uint32_t d_secperunit;		/* # of data sectors per unit */

	/*
	 * Spares (bad sector replacements) below are not counted in
	 * d_nsectors or d_secpercyl.  Spare sectors are assumed to
	 * be physical sectors which occupy space at the end of each
	 * track and/or cylinder.
	 */
	uint16_t d_sparespertrack;	/* # of spare sectors per track */
	uint16_t d_sparespercyl;	/* # of spare sectors per cylinder */
	/*
	 * Alternative cylinders include maintenance, replacement,
	 * configuration description areas, etc.
	 */
	uint32_t d_acylinders;		/* # of alt. cylinders per unit */

			/* hardware characteristics: */
	/*
	 * d_interleave, d_trackskew and d_cylskew describe perturbations
	 * in the media format used to compensate for a slow controller.
	 * Interleave is physical sector interleave, set up by the
	 * formatter or controller when formatting.  When interleaving is
	 * in use, logically adjacent sectors are not physically
	 * contiguous, but instead are separated by some number of
	 * sectors.  It is specified as the ratio of physical sectors
	 * traversed per logical sector.  Thus an interleave of 1:1
	 * implies contiguous layout, while 2:1 implies that logical
	 * sector 0 is separated by one sector from logical sector 1.
	 * d_trackskew is the offset of sector 0 on track N relative to
	 * sector 0 on track N-1 on the same cylinder.  Finally, d_cylskew
	 * is the offset of sector 0 on cylinder N relative to sector 0
	 * on cylinder N-1.
	 */
	uint16_t d_rpm;		/* rotational speed */
	uint16_t d_interleave;		/* hardware sector interleave */
	uint16_t d_trackskew;		/* sector 0 skew, per track */
	uint16_t d_cylskew;		/* sector 0 skew, per cylinder */
	uint32_t d_headswitch;		/* head switch time, usec */
	uint32_t d_trkseek;		/* track-to-track seek, usec */
	uint32_t d_flags;		/* generic flags */
#define	NDDATA 5
	uint32_t d_drivedata[NDDATA];	/* drive-type specific information */
#define	NSPARE 5
	uint32_t d_spare[NSPARE];	/* reserved for future use */
	uint32_t d_magic2;		/* the magic number (again) */
	uint16_t d_checksum;		/* xor of data incl. partitions */

			/* filesystem and partition information: */
	uint16_t d_npartitions;	/* number of partitions in following */
	uint32_t d_bbsize;		/* size of boot area at sn0, bytes */
	uint32_t d_sbsize;		/* max size of fs superblock, bytes */
	struct	rumpserver_partition {	/* the partition table */
		uint32_t p_size;	/* number of sectors in partition */
		uint32_t p_offset;	/* starting sector */
		union {
			uint32_t fsize; /* FFS, ADOS:
					    filesystem basic fragment size */
			uint32_t cdsession; /* ISO9660: session offset */
		} __partition_u2;
#define	p_fsize		__partition_u2.fsize
#define	p_cdsession	__partition_u2.cdsession
		uint8_t p_fstype;	/* filesystem type, see below */
		uint8_t p_frag;	/* filesystem fragments per block */
		union {
			uint16_t cpg;	/* UFS: FS cylinders per group */
			uint16_t sgs;	/* LFS: FS segment shift */
		} __partition_u1;
#define	p_cpg	__partition_u1.cpg
#define	p_sgs	__partition_u1.sgs
	} d_partitions[RUMPSERVER_MAXPARTITIONS];	/* actually may be more */
};


/* for swapping disklabel, so don't care about perf, just portability */
#define bs32(x) \
	((((x) & 0xff000000) >> 24)| \
	(((x) & 0x00ff0000) >>  8) | \
	(((x) & 0x0000ff00) <<  8) | \
	(((x) & 0x000000ff) << 24))
#define bs16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))

/*
 * From:
 *	$NetBSD: disklabel_dkcksum.c,v 1.4 2005/05/15 21:01:34 thorpej Exp
 */

/*-
 * Copyright (c) 1991, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may 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 uint16_t
rs_dl_dkcksum(struct rumpserver_disklabel *lp, int imswapped)
{
	uint16_t *start, *end;
	uint16_t sum;
	uint16_t npart;

	if (imswapped)
		npart = bs16(lp->d_npartitions);
	else
		npart = lp->d_npartitions;

	sum = 0;
	start = (uint16_t *)(void *)lp;
	end = (uint16_t *)(void *)&lp->d_partitions[npart];
	while (start < end) {
		if (imswapped)
			sum ^= bs16(*start);
		else
			sum ^= *start;
		start++;
	}
	return (sum);
}

/*
 * From:
 * NetBSD: disklabel_scan.c,v 1.3 2009/01/18 12:13:03 lukem Exp
 */

/*-
 * Copyright (c) 2002 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Roland C. Dowdeswell.
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 int
rs_dl_scan(struct rumpserver_disklabel *lp, int *isswapped,
	char *buf, size_t buflen)
{
	size_t i;
	int imswapped;
	uint16_t npart;

	/* scan for the correct magic numbers. */

	for (i=0; i <= buflen - sizeof(*lp); i += 4) {
		memcpy(lp, buf + i, sizeof(*lp));
		if (lp->d_magic == RUMPSERVER_DISKMAGIC &&
		    lp->d_magic2 == RUMPSERVER_DISKMAGIC) {
			imswapped = 0;
			goto sanity;
		}
		if (lp->d_magic == bs32(RUMPSERVER_DISKMAGIC) &&
		    lp->d_magic2 == bs32(RUMPSERVER_DISKMAGIC)) {
			imswapped = 1;
			goto sanity;
		}
	}

	return 1;

sanity:
	if (imswapped)
		npart = bs16(lp->d_npartitions);
	else
		npart = lp->d_npartitions;
	/* we've found something, let's sanity check it */
	if (npart > RUMPSERVER_MAXPARTITIONS
	    || rs_dl_dkcksum(lp, imswapped))
		return 1;

	*isswapped = imswapped;
	return 0;
}

static void
processlabel(int sflag, int fd, int partition, off_t *foffp, off_t *flenp)
{
	struct rumpserver_disklabel dl;
	char buf[1<<16];
	uint32_t foffset, flen;
	int imswapped;

	if (pread(fd, buf, sizeof(buf), 0) == -1)
		die(sflag, errno, "could not read disk device");
	if (rs_dl_scan(&dl, &imswapped, buf, sizeof(buf)))
		die(sflag, ENOENT, "disklabel not found");

	if (partition >= dl.d_npartitions)
		die(sflag, ENOENT, "partition not available");

	foffset = dl.d_partitions[partition].p_offset << RUMPSERVER_DEVSHIFT;
	flen = dl.d_partitions[partition].p_size << RUMPSERVER_DEVSHIFT;
	if (imswapped) {
		foffset = bs32(foffset);
		flen = bs32(flen);
	}

	*foffp = (off_t)foffset;
	*flenp = (off_t)flen;
}
