OpenSolaris_b135/cmd/format/label.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This file contains the code relating to label manipulation.
 */

#include "global.h"
#include "label.h"
#include "misc.h"
#include "main.h"
#include "partition.h"
#include "ctlr_scsi.h"
#include "checkdev.h"
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/isa_defs.h>
#include <sys/efi_partition.h>
#include <sys/vtoc.h>
#include <sys/uuid.h>
#include <errno.h>
#include <devid.h>

#if defined(_FIRMWARE_NEEDS_FDISK)
#include <sys/dktp/fdisk.h>
#include "menu_fdisk.h"
#endif		/* defined(_FIRMWARE_NEEDS_FDISK) */

#ifndef	WD_NODE
#define	WD_NODE		7
#endif

#ifdef	__STDC__
/*
 * Prototypes for ANSI C compilers
 */
static int	do_geometry_sanity_check(void);
static int	vtoc_to_label(struct dk_label *label, struct extvtoc *vtoc,
		struct dk_geom *geom, struct dk_cinfo *cinfo);
extern int	read_extvtoc(int, struct extvtoc *);
extern int	write_extvtoc(int, struct extvtoc *);
static int	vtoc64_to_label(struct efi_info *, struct dk_gpt *);

#else	/* __STDC__ */

/*
 * Prototypes for non-ANSI C compilers
 */
static int	do_geometry_sanity_check();
static int	vtoc_to_label();
extern int	read_extvtoc();
extern int	write_extvtoc();
static int	vtoc64_to_label();

#endif	/* __STDC__ */

#ifdef	DEBUG
static void dump_label(struct dk_label *label);
#endif

/*
 * This routine checks the given label to see if it is valid.
 */
int
checklabel(label)
	register struct dk_label *label;
{

	/*
	 * Check the magic number.
	 */
	if (label->dkl_magic != DKL_MAGIC)
		return (0);
	/*
	 * Check the checksum.
	 */
	if (checksum(label, CK_CHECKSUM) != 0)
		return (0);
	return (1);
}

/*
 * This routine checks or calculates the label checksum, depending on
 * the mode it is called in.
 */
int
checksum(label, mode)
	struct	dk_label *label;
	int	mode;
{
	register short *sp, sum = 0;
	register short count = (sizeof (struct dk_label)) / (sizeof (short));

	/*
	 * If we are generating a checksum, don't include the checksum
	 * in the rolling xor.
	 */
	if (mode == CK_MAKESUM)
		count -= 1;
	sp = (short *)label;
	/*
	 * Take the xor of all the half-words in the label.
	 */
	while (count--) {
		sum ^= *sp++;
	}
	/*
	 * If we are checking the checksum, the total will be zero for
	 * a correct checksum, so we can just return the sum.
	 */
	if (mode == CK_CHECKSUM)
		return (sum);
	/*
	 * If we are generating the checksum, fill it in.
	 */
	else {
		label->dkl_cksum = sum;
		return (0);
	}
}

/*
 * This routine is used to extract the id string from the string stored
 * in a disk label.  The problem is that the string in the label has
 * the physical characteristics of the drive appended to it.  The approach
 * is to find the beginning of the physical attributes portion of the string
 * and truncate it there.
 */
int
trim_id(id)
	char	*id;
{
	register char *c;

	/*
	 * Start at the end of the string.  When we match the word ' cyl',
	 * we are at the beginning of the attributes.
	 */
	for (c = id + strlen(id); c >= id; c--) {
		if (strncmp(c, " cyl", strlen(" cyl")) == 0) {
			/*
			 * Remove any white space.
			 */
			for (; (((*(c - 1) == ' ') || (*(c - 1) == '\t')) &&
				(c >= id)); c--);
			break;
		}
	}
	/*
	 * If we ran off the beginning of the string, something is wrong.
	 */
	if (c < id)
		return (-1);
	/*
	 * Truncate the string.
	 */
	*c = '\0';
	return (0);
}

/*
 * This routine is used by write_label() to do a quick sanity check on the
 * supplied geometry. This is not a thorough check.
 *
 * The SCSI READ_CAPACITY command is used here to get the capacity of the
 * disk. But, the available area to store data on a disk is usually less
 * than this. So, if the specified geometry evaluates to a value which falls
 * in this margin, then such illegal geometries can slip through the cracks.
 */
static int
do_geometry_sanity_check()
{
	struct scsi_capacity_16	 capacity;

	if (uscsi_read_capacity(cur_file, &capacity)) {
		err_print("Warning: Unable to get capacity."
		    " Cannot check geometry\n");
		return (0);	/* Just ignore this problem */
	}

	if (capacity.sc_capacity < ncyl * nhead * nsect) {
		err_print("\nWarning: Current geometry overshoots "
		    "actual geometry of disk\n\n");
		if (check("Continue labelling disk") != 0)
			return (-1);
		return (0);	/* Just ignore this problem */
	}

	return (0);
}

/*
 * create a clear EFI partition table when format is used
 * to convert an SMI label to an EFI label
 */
int
SMI_vtoc_to_EFI(int fd, struct dk_gpt **new_vtoc)
{
	int i;
	struct dk_gpt	*efi;

	if (efi_alloc_and_init(fd, EFI_NUMPAR, new_vtoc) != 0) {
		err_print("SMI vtoc to EFI failed\n");
		return (-1);
	}
	efi = *new_vtoc;

	/*
	 * create a clear EFI partition table:
	 * s0 takes the whole disk except the primary EFI lable,
	 * backup EFI labels, and the reserved partition.
	 * s1-s6 are unassigned slices.
	 */
	efi->efi_parts[0].p_tag = V_USR;
	efi->efi_parts[0].p_start = efi->efi_first_u_lba;
	efi->efi_parts[0].p_size = efi->efi_last_u_lba - efi->efi_first_u_lba
	    - EFI_MIN_RESV_SIZE + 1;

	/*
	 * s1-s6 are unassigned slices
	 */
	for (i = 1; i < efi->efi_nparts - 2; i++) {
		efi->efi_parts[i].p_tag = V_UNASSIGNED;
		efi->efi_parts[i].p_start = 0;
		efi->efi_parts[i].p_size = 0;
	}

	/*
	 * the reserved slice
	 */
	efi->efi_parts[efi->efi_nparts - 1].p_tag = V_RESERVED;
	efi->efi_parts[efi->efi_nparts - 1].p_start =
	    efi->efi_last_u_lba - EFI_MIN_RESV_SIZE + 1;
	efi->efi_parts[efi->efi_nparts - 1].p_size = EFI_MIN_RESV_SIZE;

	return (0);
}

/*
 * This routine constructs and writes a label on the disk.  It writes both
 * the primary and backup labels.  It assumes that there is a current
 * partition map already defined.  It also notifies the SunOS kernel of
 * the label and partition information it has written on the disk.
 */
int
write_label()
{
	int	error = 0, head, sec;
	struct dk_label label;
	struct extvtoc	vtoc;
	struct dk_geom	geom;
	struct dk_gpt	*vtoc64;
	int		nbackups;
	char		*new_label;

#if defined(_SUNOS_VTOC_8)
	int i;
#endif		/* defined(_SUNOS_VTOC_8) */

	/*
	 * Check to see if any partitions used for svm, vxvm or live upgrade
	 * are on the disk. If so, refuse to label the disk, but only
	 * if we are trying to shrink a partition in use.
	 */
	if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1,
	    (diskaddr_t)-1, 0, 1)) {
		err_print("Cannot label disk when "
		    "partitions are in use as described.\n");
		return (-1);
	}

	/*
	 * If EFI label, then write it out to disk
	 */
	if (cur_label == L_TYPE_EFI) {
		enter_critical();
		vtoc64 = cur_parts->etoc;
		err_check(vtoc64);
		if (efi_write(cur_file, vtoc64) != 0) {
			err_print("Warning: error writing EFI.\n");
			error = -1;
			}

		cur_disk->disk_flags |= DSK_LABEL;
		exit_critical();
		return (error);
	}

	/*
	 * Fill in a label structure with the geometry information.
	 */
	(void) memset((char *)&label, 0, sizeof (struct dk_label));
	new_label = zalloc(cur_blksz);

	label.dkl_pcyl = pcyl;
	label.dkl_ncyl = ncyl;
	label.dkl_acyl = acyl;

#if defined(_SUNOS_VTOC_16)
	label.dkl_bcyl = bcyl;
#endif			/* defined(_SUNOC_VTOC_16) */

	label.dkl_nhead = nhead;
	label.dkl_nsect = nsect;
	label.dkl_apc = apc;
	label.dkl_intrlv = 1;
	label.dkl_rpm = cur_dtype->dtype_rpm;

#if defined(_SUNOS_VTOC_8)
	/*
	 * Also fill in the current partition information.
	 */
	for (i = 0; i < NDKMAP; i++) {
		label.dkl_map[i] = cur_parts->pinfo_map[i];
	}
#endif			/* defined(_SUNOS_VTOC_8) */

	label.dkl_magic = DKL_MAGIC;

	/*
	 * Fill in the vtoc information
	 */
	label.dkl_vtoc = cur_parts->vtoc;

	/*
	 * Use the current label
	 */
	bcopy(cur_disk->v_volume, label.dkl_vtoc.v_volume, LEN_DKL_VVOL);

	/*
	 * Put asciilabel in; on x86 it's in the vtoc, not the label.
	 */
	(void) snprintf(label.dkl_asciilabel, sizeof (label.dkl_asciilabel),
	    "%s cyl %d alt %d hd %d sec %d",
	    cur_dtype->dtype_asciilabel, ncyl, acyl, nhead, nsect);

#if defined(_SUNOS_VTOC_16)
	/*
	 * Also add in v_sectorsz, as the driver will.
	 */
	label.dkl_vtoc.v_sectorsz = cur_blksz;
#endif			/* defined(_SUNOS_VTOC_16) */

	/*
	 * Generate the correct checksum.
	 */
	(void) checksum(&label, CK_MAKESUM);
	/*
	 * Convert the label into a vtoc
	 */
	if (label_to_vtoc(&vtoc, &label) == -1) {
		free(new_label);
		return (-1);
	}
	/*
	 * Fill in the geometry info.  This is critical that
	 * we do this before writing the vtoc.
	 */
	bzero((caddr_t)&geom, sizeof (struct dk_geom));
	geom.dkg_ncyl = ncyl;
	geom.dkg_acyl = acyl;

#if defined(_SUNOS_VTOC_16)
	geom.dkg_bcyl = bcyl;
#endif			/* defined(_SUNOS_VTOC_16) */

	geom.dkg_nhead = nhead;
	geom.dkg_nsect = nsect;
	geom.dkg_intrlv = 1;
	geom.dkg_apc = apc;
	geom.dkg_rpm = cur_dtype->dtype_rpm;
	geom.dkg_pcyl = pcyl;

	/*
	 * Make a quick check to see that the geometry is being
	 * written now is not way off from the actual capacity
	 * of the disk. This is only an appoximate check and
	 * is only for SCSI disks.
	 */
	if (SCSI && do_geometry_sanity_check() != 0) {
		free(new_label);
		return (-1);
	}

	/*
	 * Lock out interrupts so we do things in sync.
	 */
	enter_critical();
	/*
	 * Do the ioctl to tell the kernel the geometry.
	 */
	if (ioctl(cur_file, DKIOCSGEOM, &geom) == -1) {
		err_print("Warning: error setting drive geometry.\n");
		error = -1;
	}
	/*
	 * Write the vtoc.  At the time of this writing, our
	 * drivers convert the vtoc back to a label, and
	 * then write both the primary and backup labels.
	 * This is not a requirement, however, as we
	 * always use an ioctl to read the vtoc from the
	 * driver, so it can do as it likes.
	 */
	if (write_extvtoc(cur_file, &vtoc) != 0) {
		err_print("Warning: error writing VTOC.\n");
		error = -1;
	}

	/*
	 * Calculate where the backup labels went.  They are always on
	 * the last alternate cylinder, but some older drives put them
	 * on head 2 instead of the last head.  They are always on the
	 * first 5 odd sectors of the appropriate track.
	 */
	if (cur_ctype->ctype_flags & CF_BLABEL)
		head  = 2;
	else
		head = nhead - 1;
	/*
	 * Read and verify the backup labels.
	 */
	nbackups = 0;
	for (sec = 1; ((sec < BAD_LISTCNT * 2 + 1) && (sec < nsect));
	    sec += 2) {
		if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, (diskaddr_t)
		    ((chs2bn(ncyl + acyl - 1, head, sec))
		    + solaris_offset), 1, new_label, F_NORMAL, NULL)) {
			err_print("Warning: error reading"
			    "backup label.\n");
			error = -1;
		} else {
			if (bcmp((char *)&label, new_label,
			    sizeof (struct dk_label)) == 0) {
				nbackups++;
			}
		}
	}
	if (nbackups != BAD_LISTCNT) {
		err_print("Warning: %s\n", nbackups == 0 ?
		    "no backup labels" : "some backup labels incorrect");
	}
	/*
	 * Mark the current disk as labelled and notify the kernel of what
	 * has happened.
	 */
	cur_disk->disk_flags |= DSK_LABEL;

	exit_critical();
	free(new_label);
	return (error);
}


/*
 * Read the label from the disk.
 * Do this via the read_extvtoc() library routine, then convert it to a label.
 * We also need a DKIOCGGEOM ioctl to get the disk's geometry.
 */
int
read_label(int fd, struct dk_label *label)
{
	struct extvtoc	vtoc;
	struct dk_geom	geom;
	struct dk_cinfo	dkinfo;

	if (read_extvtoc(fd, &vtoc) < 0		||
	    ioctl(fd, DKIOCGGEOM, &geom) == -1	||
	    ioctl(fd, DKIOCINFO, &dkinfo) == -1) {
		return (-1);
	}

	return (vtoc_to_label(label, &vtoc, &geom, &dkinfo));
}

int
get_disk_info_from_devid(int fd, struct efi_info *label)
{
	ddi_devid_t	devid;
	char		*s;
	int		n;
	char		*vid, *pid;
	int		nvid, npid;
	struct dk_minfo	minf;
	struct dk_cinfo	dkinfo;

	if (devid_get(fd, &devid)) {
		if (option_msg && diag_msg)
			err_print("devid_get failed\n");
		return (-1);
	}

	n = devid_sizeof(devid);
	s = (char *)devid;

	if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) {
		if (option_msg && diag_msg)
			err_print("DKIOCINFO failed\n");
		return (-1);
	}

	if (dkinfo.dki_ctype != DKC_DIRECT)
		return (-1);

	vid = s+12;
	if (!(pid = strchr(vid, '=')))
		return (-1);
	nvid = pid - vid;
	pid += 1;
	npid = n - nvid - 13;

	if (nvid > 9)
		nvid = 9;
	if (npid > 17) {
		pid = pid + npid - 17;
		npid = 17;
	}

	if (ioctl(fd, DKIOCGMEDIAINFO, &minf) == -1) {
		devid_free(devid);
		return (-1);
	}

	(void) strlcpy(label->vendor, vid, nvid);
	(void) strlcpy(label->product, pid, npid);
	(void) strlcpy(label->revision, "0001", 5);
	label->capacity = minf.dki_capacity * minf.dki_lbsize / 512;

	devid_free(devid);
	return (0);
}

/*
 * Issue uscsi_inquiry and read_capacity commands to
 * retrieve the disk's Vendor, Product, Revision and
 * Capacity information.
 */
int
get_disk_info(int fd, struct efi_info *label)
{
	struct scsi_inquiry	inquiry;
	struct scsi_capacity_16	capacity;
	struct dk_minfo		minf;

	if (!get_disk_info_from_devid(fd, label))
		return (0);

	if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) {
		(void) strlcpy(label->vendor, "Unknown", 8);
		(void) strlcpy(label->product, "Unknown", 8);
		(void) strlcpy(label->revision, "0001", 5);
	} else {
		(void) strlcpy(label->vendor, inquiry.inq_vid, 9);
		(void) strlcpy(label->product, inquiry.inq_pid, 17);
		(void) strlcpy(label->revision, inquiry.inq_revision, 5);
	}

	if (uscsi_read_capacity(fd, &capacity)) {
		if (ioctl(fd, DKIOCGMEDIAINFO, &minf) == -1) {
			err_print("Fetch Capacity failed\n");
			return (-1);
		}
		label->capacity =
		    minf.dki_capacity * minf.dki_lbsize / cur_blksz;
	} else {
		label->capacity = capacity.sc_capacity;

		/* Since we are counting from zero, add 1 to capacity */
		label->capacity++;
	}

	return (0);
}

int
read_efi_label(int fd, struct efi_info *label)
{
	struct dk_gpt	*vtoc64;

	/* This could fail if there is no label already */
	if (efi_alloc_and_read(fd, &vtoc64) < 0) {
		return (-1);
	}
	if (vtoc64_to_label(label, vtoc64) != 0) {
		err_print("vtoc64_to_label failed\n");
		return (-1);
	}
	efi_free(vtoc64);
	if (get_disk_info(fd, label) != 0) {
		return (-1);
	}
	return (0);
}


/*
 * We've read a 64-bit label which has no geometry information.  Use
 * some heuristics to fake up a geometry that would match the disk in
 * order to make the rest of format(1M) happy.
 */
static int
vtoc64_to_label(struct efi_info *label, struct dk_gpt *vtoc)
{
	int		i, nparts = 0;
	struct dk_gpt	*lmap;

	(void) memset((char *)label, 0, sizeof (struct efi_info));

	/* XXX do a sanity check here for nparts */
	nparts = vtoc->efi_nparts;
	lmap = (struct dk_gpt *) calloc(1, (sizeof (struct dk_part) *
	    nparts) + sizeof (struct dk_gpt));
	if (lmap == NULL) {
		err_print("vtoc64_to_label: unable to allocate lmap\n");
		fullabort();
	}
	label->e_parts = lmap;

	/*
	 * Copy necessary portions
	 * XXX Maybe we can use memcpy() ??
	 */
	lmap->efi_version = vtoc->efi_version;
	lmap->efi_nparts = vtoc->efi_nparts;
	lmap->efi_part_size = vtoc->efi_part_size;
	lmap->efi_lbasize = vtoc->efi_lbasize;
	lmap->efi_last_lba = vtoc->efi_last_lba;
	lmap->efi_first_u_lba = vtoc->efi_first_u_lba;
	lmap->efi_last_u_lba = vtoc->efi_last_u_lba;
	lmap->efi_altern_lba = vtoc->efi_altern_lba;
	lmap->efi_flags = vtoc->efi_flags;
	(void) memcpy((uchar_t *)&lmap->efi_disk_uguid,
	    (uchar_t *)&vtoc->efi_disk_uguid, sizeof (struct uuid));

	for (i = 0; i < nparts; i++) {
		lmap->efi_parts[i].p_tag = vtoc->efi_parts[i].p_tag;
		lmap->efi_parts[i].p_flag = vtoc->efi_parts[i].p_flag;
		lmap->efi_parts[i].p_start = vtoc->efi_parts[i].p_start;
		lmap->efi_parts[i].p_size = vtoc->efi_parts[i].p_size;
		(void) memcpy((uchar_t *)&lmap->efi_parts[i].p_uguid,
		    (uchar_t *)&vtoc->efi_parts[i].p_uguid,
		    sizeof (struct uuid));
		if (vtoc->efi_parts[i].p_tag == V_RESERVED) {
			bcopy(vtoc->efi_parts[i].p_name,
			    lmap->efi_parts[i].p_name, LEN_DKL_VVOL);
		}
	}
	return (0);
}

/*
 * Convert vtoc/geom to label.
 */
static int
vtoc_to_label(struct dk_label *label, struct extvtoc *vtoc,
    struct dk_geom *geom, struct dk_cinfo *cinfo)
{
#if defined(_SUNOS_VTOC_8)
	struct dk_map32		*lmap;
#elif defined(_SUNOS_VTOC_16)
	struct dkl_partition	*lmap;
#else
#error No VTOC format defined.
#endif			/* defined(_SUNOS_VTOC_8) */

	struct extpartition	*vpart;
	ulong_t			nblks;
	int			i;

	(void) memset((char *)label, 0, sizeof (struct dk_label));

	/*
	 * Sanity-check the vtoc
	 */
	if (vtoc->v_sanity != VTOC_SANE ||
	    vtoc->v_nparts != V_NUMPAR) {
		return (-1);
	}

	/*
	 * Sanity check of geometry
	 */
	if (geom->dkg_ncyl == 0 || geom->dkg_nhead == 0 ||
	    geom->dkg_nsect == 0) {
		return (-1);
	}

	label->dkl_magic = DKL_MAGIC;

	/*
	 * Copy necessary portions of the geometry information
	 */
	label->dkl_rpm = geom->dkg_rpm;
	label->dkl_pcyl = geom->dkg_pcyl;
	label->dkl_apc = geom->dkg_apc;
	label->dkl_intrlv = geom->dkg_intrlv;
	label->dkl_ncyl = geom->dkg_ncyl;
	label->dkl_acyl = geom->dkg_acyl;

#if defined(_SUNOS_VTOC_16)
	label->dkl_bcyl = geom->dkg_bcyl;
#endif			/* defined(_SUNOS_VTOC_16) */

	label->dkl_nhead = geom->dkg_nhead;
	label->dkl_nsect = geom->dkg_nsect;

#if defined(_SUNOS_VTOC_8)
	label->dkl_obs1 = geom->dkg_obs1;
	label->dkl_obs2 = geom->dkg_obs2;
	label->dkl_obs3 = geom->dkg_obs3;
#endif			/* defined(_SUNOS_VTOC_8) */

	label->dkl_write_reinstruct = geom->dkg_write_reinstruct;
	label->dkl_read_reinstruct = geom->dkg_read_reinstruct;

	/*
	 * Copy vtoc structure fields into the disk label dk_vtoc
	 */
	label->dkl_vtoc.v_sanity = vtoc->v_sanity;
	label->dkl_vtoc.v_nparts = vtoc->v_nparts;
	label->dkl_vtoc.v_version = vtoc->v_version;

	(void) memcpy(label->dkl_vtoc.v_volume, vtoc->v_volume,
	    LEN_DKL_VVOL);
	for (i = 0; i < V_NUMPAR; i++) {
		label->dkl_vtoc.v_part[i].p_tag = vtoc->v_part[i].p_tag;
		label->dkl_vtoc.v_part[i].p_flag = vtoc->v_part[i].p_flag;
		label->dkl_vtoc.v_timestamp[i] = vtoc->timestamp[i];
	}

	for (i = 0; i < 10; i++)
		label->dkl_vtoc.v_reserved[i] = vtoc->v_reserved[i];

	label->dkl_vtoc.v_bootinfo[0] = vtoc->v_bootinfo[0];
	label->dkl_vtoc.v_bootinfo[1] = vtoc->v_bootinfo[1];
	label->dkl_vtoc.v_bootinfo[2] = vtoc->v_bootinfo[2];

	(void) memcpy(label->dkl_asciilabel, vtoc->v_asciilabel,
	    LEN_DKL_ASCII);

	/*
	 * Note the conversion from starting sector number
	 * to starting cylinder number.
	 * Return error if division results in a remainder.
	 *
	 * Note: don't check, if probing virtual disk in Xen
	 * for that virtual disk will use fabricated # of headers
	 * and sectors per track which may cause the capacity
	 * not multiple of # of blocks per cylinder
	 */
#if defined(_SUNOS_VTOC_8)
	lmap = label->dkl_map;

#elif defined(_SUNOS_VTOC_16)
	lmap = label->dkl_vtoc.v_part;
#else
#error No VTOC format defined.
#endif			/* defined(_SUNOS_VTOC_8) */

	vpart = vtoc->v_part;

	nblks = label->dkl_nsect * label->dkl_nhead;

	for (i = 0; i < NDKMAP; i++, lmap++, vpart++) {
		if (cinfo->dki_ctype != DKC_VBD) {
			if ((vpart->p_start % nblks) != 0 ||
			    (vpart->p_size % nblks) != 0) {
				return (-1);
			}
		}
#if defined(_SUNOS_VTOC_8)
		lmap->dkl_cylno = (blkaddr32_t)(vpart->p_start / nblks);
		lmap->dkl_nblk = (blkaddr32_t)vpart->p_size;

#elif defined(_SUNOS_VTOC_16)
		lmap->p_start = (blkaddr32_t)vpart->p_start;
		lmap->p_size = (blkaddr32_t)vpart->p_size;
#else
#error No VTOC format defined.
#endif			/* defined(_SUNOS_VTOC_8) */
	}

	/*
	 * Finally, make a checksum
	 */
	(void) checksum(label, CK_MAKESUM);

#ifdef DEBUG
	if (option_msg && diag_msg)
		dump_label(label);
#endif
	return (0);
}



/*
 * Extract a vtoc structure out of a valid label
 */
int
label_to_vtoc(struct extvtoc *vtoc, struct dk_label *label)
{
#if defined(_SUNOS_VTOC_8)
	struct dk_map2		*lpart;
	struct dk_map32		*lmap;
	ulong_t			nblks;

#elif defined(_SUNOS_VTOC_16)
	struct dkl_partition	*lpart;
#else
#error No VTOC format defined.
#endif				/* defined(_SUNOS_VTOC_8) */

	struct extpartition	*vpart;
	int			i;

	(void) memset((char *)vtoc, 0, sizeof (struct extvtoc));

	switch (label->dkl_vtoc.v_version) {
	case 0:
		/*
		 * No valid vtoc information in the label.
		 * Construct default p_flags and p_tags.
		 */
		vpart = vtoc->v_part;
		for (i = 0; i < V_NUMPAR; i++, vpart++) {
			vpart->p_tag = default_vtoc_map[i].p_tag;
			vpart->p_flag = default_vtoc_map[i].p_flag;
		}
		break;

	case V_VERSION:
		vpart = vtoc->v_part;
		lpart = label->dkl_vtoc.v_part;
		for (i = 0; i < V_NUMPAR; i++, vpart++, lpart++) {
			vpart->p_tag = lpart->p_tag;
			vpart->p_flag = lpart->p_flag;

#if defined(_SUNOS_VTOC_16)
			vpart->p_start = (diskaddr_t)lpart->p_start;
			vpart->p_size = (diskaddr_t)lpart->p_size;
#endif	/* defined(_SUNOS_VTOC_16) */
			vtoc->timestamp[i] = label->dkl_vtoc.v_timestamp[i];
		}
		(void) memcpy(vtoc->v_volume, label->dkl_vtoc.v_volume,
		    LEN_DKL_VVOL);

		for (i = 0; i < 10; i++)
			vtoc->v_reserved[i] = label->dkl_vtoc.v_reserved[i];

		vtoc->v_bootinfo[0] = label->dkl_vtoc.v_bootinfo[0];
		vtoc->v_bootinfo[1] = label->dkl_vtoc.v_bootinfo[1];
		vtoc->v_bootinfo[2] = label->dkl_vtoc.v_bootinfo[2];
		break;

	default:
		return (-1);
	}

	/*
	 * XXX - this looks wrong to me....
	 * why are these values hardwired, rather than returned from
	 * the real disk label?
	 */
	vtoc->v_sanity = VTOC_SANE;
	vtoc->v_version = V_VERSION;
	vtoc->v_sectorsz = cur_blksz;
	vtoc->v_nparts = V_NUMPAR;

	(void) memcpy(vtoc->v_asciilabel, label->dkl_asciilabel,
	    LEN_DKL_ASCII);

#if defined(_SUNOS_VTOC_8)
	/*
	 * Convert partitioning information.
	 * Note the conversion from starting cylinder number
	 * to starting sector number.
	 */
	lmap = label->dkl_map;
	vpart = vtoc->v_part;
	nblks = label->dkl_nsect * label->dkl_nhead;
	for (i = 0; i < V_NUMPAR; i++, vpart++, lmap++) {
		vpart->p_start = (diskaddr_t)(lmap->dkl_cylno * nblks);
		vpart->p_size = (diskaddr_t)lmap->dkl_nblk;
	}
#endif			/* defined(_SUNOS_VTOC_8) */

	return (0);
}

/*
 * Input: File descriptor
 * Output: 1 if disk has an EFI label, 0 otherwise.
 */

int
is_efi_type(int fd)
{
	struct extvtoc vtoc;

	if (read_extvtoc(fd, &vtoc) == VT_ENOTSUP) {
		/* assume the disk has EFI label */
		return (1);
	}
	return (0);
}

/* make sure the user specified something reasonable */
void
err_check(struct dk_gpt *vtoc)
{
	int			resv_part = -1;
	int			i, j;
	diskaddr_t		istart, jstart, isize, jsize, endsect;
	int			overlap = 0;

	/*
	 * make sure no partitions overlap
	 */
	for (i = 0; i < vtoc->efi_nparts; i++) {
		/* It can't be unassigned and have an actual size */
		if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) &&
		    (vtoc->efi_parts[i].p_size != 0)) {
			(void) fprintf(stderr,
"partition %d is \"unassigned\" but has a size of %llu\n", i,
			    vtoc->efi_parts[i].p_size);
		}
		if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED) {
			continue;
		}
		if (vtoc->efi_parts[i].p_tag == V_RESERVED) {
			if (resv_part != -1) {
				(void) fprintf(stderr,
"found duplicate reserved partition at %d\n", i);
			}
			resv_part = i;
			if (vtoc->efi_parts[i].p_size != EFI_MIN_RESV_SIZE)
				(void) fprintf(stderr,
"Warning: reserved partition size must be %d sectors\n",
				    EFI_MIN_RESV_SIZE);
		}
		if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) ||
		    (vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) {
			(void) fprintf(stderr,
			    "Partition %d starts at %llu\n",
			    i,
			    vtoc->efi_parts[i].p_start);
			(void) fprintf(stderr,
			    "It must be between %llu and %llu.\n",
			    vtoc->efi_first_u_lba,
			    vtoc->efi_last_u_lba);
		}
		if ((vtoc->efi_parts[i].p_start +
		    vtoc->efi_parts[i].p_size <
		    vtoc->efi_first_u_lba) ||
		    (vtoc->efi_parts[i].p_start +
		    vtoc->efi_parts[i].p_size >
		    vtoc->efi_last_u_lba + 1)) {
			(void) fprintf(stderr,
			    "Partition %d ends at %llu\n",
			    i,
			    vtoc->efi_parts[i].p_start +
			    vtoc->efi_parts[i].p_size);
			(void) fprintf(stderr,
			    "It must be between %llu and %llu.\n",
			    vtoc->efi_first_u_lba,
			    vtoc->efi_last_u_lba);
		}

		for (j = 0; j < vtoc->efi_nparts; j++) {
			isize = vtoc->efi_parts[i].p_size;
			jsize = vtoc->efi_parts[j].p_size;
			istart = vtoc->efi_parts[i].p_start;
			jstart = vtoc->efi_parts[j].p_start;
			if ((i != j) && (isize != 0) && (jsize != 0)) {
				endsect = jstart + jsize -1;
				if ((jstart <= istart) &&
				    (istart <= endsect)) {
					if (!overlap) {
					(void) fprintf(stderr,
"label error: EFI Labels do not support overlapping partitions\n");
					}
					(void) fprintf(stderr,
"Partition %d overlaps partition %d.\n", i, j);
					overlap = 1;
				}
			}
		}
	}
	/* make sure there is a reserved partition */
	if (resv_part == -1) {
		(void) fprintf(stderr,
		    "no reserved partition found\n");
	}
}

#ifdef	DEBUG
static void
dump_label(label)
	struct dk_label	*label;
{
	int		i;

	fmt_print("%s\n", label->dkl_asciilabel);

	fmt_print("version:  %d\n", label->dkl_vtoc.v_version);
	fmt_print("volume:   ");
	for (i = 0; i < LEN_DKL_VVOL; i++) {
		if (label->dkl_vtoc.v_volume[i] == 0)
			break;
		fmt_print("%c", label->dkl_vtoc.v_volume[i]);
	}
	fmt_print("\n");
	fmt_print("v_nparts: %d\n", label->dkl_vtoc.v_nparts);
	fmt_print("v_sanity: %lx\n", label->dkl_vtoc.v_sanity);

#if defined(_SUNOS_VTOC_8)
	fmt_print("rpm:      %d\n", label->dkl_rpm);
	fmt_print("pcyl:     %d\n", label->dkl_pcyl);
	fmt_print("apc:      %d\n", label->dkl_apc);
	fmt_print("obs1:     %d\n", label->dkl_obs1);
	fmt_print("obs2:     %d\n", label->dkl_obs2);
	fmt_print("intrlv:   %d\n", label->dkl_intrlv);
	fmt_print("ncyl:     %d\n", label->dkl_ncyl);
	fmt_print("acyl:     %d\n", label->dkl_acyl);
	fmt_print("nhead:    %d\n", label->dkl_nhead);
	fmt_print("nsect:    %d\n", label->dkl_nsect);
	fmt_print("obs3:     %d\n", label->dkl_obs3);
	fmt_print("obs4:     %d\n", label->dkl_obs4);

#elif defined(_SUNOS_VTOC_16)
	fmt_print("rpm:      %d\n", label->dkl_rpm);
	fmt_print("pcyl:     %d\n", label->dkl_pcyl);
	fmt_print("apc:      %d\n", label->dkl_apc);
	fmt_print("intrlv:   %d\n", label->dkl_intrlv);
	fmt_print("ncyl:     %d\n", label->dkl_ncyl);
	fmt_print("acyl:     %d\n", label->dkl_acyl);
	fmt_print("nhead:    %d\n", label->dkl_nhead);
	fmt_print("nsect:    %d\n", label->dkl_nsect);
	fmt_print("bcyl:     %d\n", label->dkl_bcyl);
	fmt_print("skew:     %d\n", label->dkl_skew);
#else
#error No VTOC format defined.
#endif				/* defined(_SUNOS_VTOC_8) */
	fmt_print("magic:    %0x\n", label->dkl_magic);
	fmt_print("cksum:    %0x\n", label->dkl_cksum);

	for (i = 0; i < NDKMAP; i++) {

#if defined(_SUNOS_VTOC_8)
		fmt_print("%c:        cyl=%d, blocks=%d", i+'a',
			label->dkl_map[i].dkl_cylno,
			label->dkl_map[i].dkl_nblk);

#elif defined(_SUNOS_VTOC_16)
		fmt_print("%c:        start=%u, blocks=%u", i+'a',
		    label->dkl_vtoc.v_part[i].p_start,
		    label->dkl_vtoc.v_part[i].p_size);
#else
#error No VTOC format defined.
#endif				/* defined(_SUNOS_VTOC_8) */

		fmt_print(",  tag=%d,  flag=%d",
			label->dkl_vtoc.v_part[i].p_tag,
			label->dkl_vtoc.v_part[i].p_flag);
		fmt_print("\n");
	}

	fmt_print("read_reinstruct:  %d\n", label->dkl_read_reinstruct);
	fmt_print("write_reinstruct: %d\n", label->dkl_write_reinstruct);

	fmt_print("bootinfo: ");
	for (i = 0; i < 3; i++) {
		fmt_print("0x%x ", label->dkl_vtoc.v_bootinfo[i]);
	}
	fmt_print("\n");

	fmt_print("reserved: ");
	for (i = 0; i < 10; i++) {
		if ((i % 4) == 3)
			fmt_print("\n");
		fmt_print("0x%x ", label->dkl_vtoc.v_reserved[i]);
	}
	fmt_print("\n");

	fmt_print("timestamp:\n");
	for (i = 0; i < NDKMAP; i++) {
		if ((i % 4) == 3)
			fmt_print("\n");
		fmt_print("0x%x ", label->dkl_vtoc.v_timestamp[i]);
	}
	fmt_print("\n");

	fmt_print("pad:\n");
	dump("", label->dkl_pad, LEN_DKL_PAD, HEX_ONLY);

	fmt_print("\n\n");
}
#endif	/* DEBUG */