/*      $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;
}