summaryrefslogtreecommitdiff
path: root/usr/src/cmd/format/label.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/format/label.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/format/label.c')
-rw-r--r--usr/src/cmd/format/label.c1124
1 files changed, 1124 insertions, 0 deletions
diff --git a/usr/src/cmd/format/label.c b/usr/src/cmd/format/label.c
new file mode 100644
index 0000000000..d4dd4fef6a
--- /dev/null
+++ b/usr/src/cmd/format/label.c
@@ -0,0 +1,1124 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * 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 <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>
+
+#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 vtoc *vtoc,
+ struct dk_geom *geom);
+extern int read_vtoc(int, struct vtoc *);
+extern int write_vtoc(int, struct vtoc *);
+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_vtoc();
+extern int write_vtoc();
+static int vtoc64_to_label();
+
+#endif /* __STDC__ */
+
+/*
+ * 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);
+}
+
+/*
+ * expand the internal 32-bit SMI VTOC into the 64-bit EFI version
+ * for writing it out to the disk
+ */
+int
+SMI_vtoc_to_EFI(int fd, struct dk_gpt **new_vtoc, struct vtoc *old_vtoc)
+{
+ int i, j;
+ int vtoc_part_count = 0;
+ int highest_assigned_part = 0;
+ int last_par = 0;
+ int compact = 0;
+ struct dk_gpt *vtoc;
+ struct vtoc old_vtoc_copy;
+ diskaddr_t start, size;
+
+ if (efi_alloc_and_init(fd, EFI_NUMPAR, new_vtoc) != 0) {
+ err_print("SMI vtoc to EFI failed\n");
+ return (-1);
+ }
+ vtoc = *new_vtoc;
+ old_vtoc_copy = *old_vtoc;
+
+ /*
+ * Prepare old VTOC table for transfer by ensuring the
+ * tags are set correctly. Also collect information
+ * about the old VTOC table including the number of
+ * valid VTOC partitions and the highest VTOC partition
+ * in use. An EFI label provides fewer partitions, so
+ * it is possible that the VTOC partitions cannot all be
+ * transferred.
+ */
+ for (i = 0; i < V_NUMPAR; i++) {
+ /*
+ * we may be carrying around old tags from the
+ * default partition table. If the partition
+ * is really unassigned, set the tag correctly
+ */
+ if ((old_vtoc->v_part[i].p_start == 0) &&
+ (old_vtoc->v_part[i].p_size == 0))
+ old_vtoc->v_part[i].p_tag = V_UNASSIGNED;
+ /*
+ * Likewise, if the partition is not empty, don't
+ * write it out as "unassigned."
+ */
+ if ((old_vtoc->v_part[i].p_size != 0) &&
+ (old_vtoc->v_part[i].p_tag == V_UNASSIGNED))
+ old_vtoc->v_part[i].p_tag = V_ROOT;
+ if (old_vtoc->v_part[i].p_tag != V_BACKUP &&
+ old_vtoc->v_part[i].p_tag != V_UNASSIGNED &&
+ old_vtoc->v_part[i].p_start < vtoc->efi_first_u_lba) {
+ old_vtoc->v_part[i].p_start = vtoc->efi_first_u_lba;
+ old_vtoc->v_part[i].p_size -= vtoc->efi_first_u_lba;
+ }
+
+ if (old_vtoc->v_part[i].p_tag == V_BACKUP ||
+ old_vtoc->v_part[i].p_tag == V_BOOT) {
+ old_vtoc->v_part[i].p_tag = V_UNASSIGNED;
+ old_vtoc->v_part[i].p_start =
+ old_vtoc->v_part[i].p_size = 0;
+ last_par = i;
+ }
+ if ((i == WD_NODE) && (old_vtoc->v_part[i].p_tag !=
+ V_UNASSIGNED)) {
+ old_vtoc->v_part[i].p_tag = V_UNASSIGNED;
+ old_vtoc->v_part[i].p_start = 0;
+ old_vtoc->v_part[i].p_size = 0;
+ }
+
+ if (old_vtoc->v_part[i].p_tag != V_UNASSIGNED) {
+ /* Update count of valid VTOC partitions */
+ vtoc_part_count++;
+
+ /* Note the highest valid VTOC slice */
+ highest_assigned_part = i;
+ }
+ }
+ if (vtoc_part_count > vtoc->efi_nparts - 1) {
+ /* Too many partitions to convert the VTOC label */
+ err_print("There are %d defined VTOC ", vtoc_part_count);
+ err_print("partitions and an EFI label\n");
+ err_print("can only accept %d.\n", vtoc->efi_nparts - 1);
+ if (check("Continue anyway") != 0) {
+ /* If no, restore VTOC and return an error */
+ *old_vtoc = old_vtoc_copy;
+ efi_free(vtoc);
+ return (-1);
+ }
+ }
+
+ if (highest_assigned_part > vtoc->efi_nparts - 2) {
+ /*
+ * Partition indexes cannot be transferred directly since
+ * the highest valid VTOC index is higher than the highest
+ * available EFI partition. Ask the user if it is OK to
+ * move the partitions to available slots
+ */
+ err_print("VTOC partition %d is defined ",
+ highest_assigned_part);
+ err_print("and the highest available EFI\n");
+ err_print("partition is %d. The partitions\n",
+ vtoc->efi_nparts - 2);
+ err_print("will fit if they are re-numbered\n");
+ if (check("OK to renumber") != 0) {
+ /* If no, restore VTOC and return an error */
+ *old_vtoc = old_vtoc_copy;
+ efi_free(vtoc);
+ return (-1);
+ } else {
+ compact = 1;
+ }
+ }
+
+ /*
+ * Now copy the VTOC partitions, remapping the indices if
+ * necessary.
+ */
+ j = 0;
+ for (i = 0; i < V_NUMPAR; i++) {
+ if (old_vtoc->v_part[i].p_tag != V_UNASSIGNED) {
+ /* Copy partition info */
+ vtoc->efi_parts[j].p_tag = old_vtoc->v_part[i].p_tag;
+ vtoc->efi_parts[j].p_flag = old_vtoc->v_part[i].p_flag;
+ vtoc->efi_parts[j].p_start =
+ old_vtoc->v_part[i].p_start;
+ vtoc->efi_parts[j].p_size =
+ old_vtoc->v_part[i].p_size;
+ if (vtoc->efi_parts[j].p_size != 0)
+ last_par = j;
+ j++; /* Increment EFI index */
+ } else {
+ if (!compact) {
+ j++; /* Increment EFI index */
+ }
+ }
+ }
+
+ start = vtoc->efi_parts[last_par].p_start;
+ size = vtoc->efi_parts[last_par].p_size;
+ if ((start + size) > (vtoc->efi_last_u_lba - EFI_MIN_RESV_SIZE)) {
+ size = (start + size) - (vtoc->efi_last_u_lba -
+ EFI_MIN_RESV_SIZE);
+ vtoc->efi_parts[last_par].p_size -= size;
+ }
+ vtoc->efi_parts[vtoc->efi_nparts - 1].p_tag = V_RESERVED;
+ vtoc->efi_parts[vtoc->efi_nparts - 1].p_start =
+ vtoc->efi_last_u_lba - EFI_MIN_RESV_SIZE;
+ vtoc->efi_parts[vtoc->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 dk_label new_label;
+ struct vtoc vtoc;
+ struct dk_geom geom;
+ struct dk_gpt *vtoc64;
+ int nbackups;
+
+#if defined(_SUNOS_VTOC_8)
+ int i;
+#endif /* defined(_SUNOS_VTOC_8) */
+
+ /*
+ * 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));
+ (void) memset((char *)&new_label, 0, sizeof (struct dk_label));
+ 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. Everyone
+ * else is assuming DEV_BSIZE, so we do the same.
+ */
+ label.dkl_vtoc.v_sectorsz = DEV_BSIZE;
+#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) {
+ 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) {
+ 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_vtoc(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, (caddr_t)&new_label, F_NORMAL, NULL)) {
+ err_print("Warning: error reading backup label.\n");
+ error = -1;
+ } else {
+ if (bcmp((char *)&label, (char *)&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();
+ return (error);
+}
+
+
+/*
+ * Read the label from the disk.
+ * Do this via the read_vtoc() 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 vtoc vtoc;
+ struct dk_geom geom;
+
+ if (read_vtoc(fd, &vtoc) < 0 || ioctl(fd, DKIOCGGEOM, &geom) == -1) {
+ return (-1);
+ }
+ return (vtoc_to_label(label, &vtoc, &geom));
+}
+
+/*
+ * 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;
+
+ if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) {
+ err_print("Inquiry failed on %d\n", fd);
+ return (-1);
+ }
+ if (uscsi_read_capacity(fd, &capacity)) {
+ err_print("Read Capacity failed for %d\n", fd);
+ return (-1);
+ }
+
+ (void) strlcpy(label->vendor, inquiry.inq_vid, 9);
+ (void) strlcpy(label->product, inquiry.inq_pid, 17);
+ (void) strlcpy(label->revision, inquiry.inq_revision, 5);
+ 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_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 vtoc *vtoc, struct dk_geom *geom)
+{
+#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 partition *vpart;
+ long nblks;
+ int i;
+
+ (void) memset((char *)label, 0, sizeof (struct dk_label));
+
+ /*
+ * Sanity-check the vtoc
+ */
+ if (vtoc->v_sanity != VTOC_SANE || vtoc->v_sectorsz != DEV_BSIZE ||
+ 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;
+ }
+ (void) memcpy((char *)label->dkl_vtoc.v_bootinfo,
+ (char *)vtoc->v_bootinfo, sizeof (vtoc->v_bootinfo));
+ (void) memcpy((char *)label->dkl_vtoc.v_reserved,
+ (char *)vtoc->v_reserved, sizeof (vtoc->v_reserved));
+ (void) memcpy((char *)label->dkl_vtoc.v_timestamp,
+ (char *)vtoc->timestamp, sizeof (vtoc->timestamp));
+
+ (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.
+ */
+#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 = (int)label->dkl_nsect * (int)label->dkl_nhead;
+
+ for (i = 0; i < NDKMAP; i++, lmap++, vpart++) {
+ if ((vpart->p_start % nblks) != 0 ||
+ (vpart->p_size % nblks) != 0) {
+ return (-1);
+ }
+#if defined(_SUNOS_VTOC_8)
+ lmap->dkl_cylno = vpart->p_start / nblks;
+ lmap->dkl_nblk = vpart->p_size;
+
+#elif defined(_SUNOS_VTOC_16)
+ lmap->p_start = vpart->p_start;
+ lmap->p_size = vpart->p_size;
+#else
+#error No VTOC format defined.
+#endif /* defined(_SUNOS_VTOC_8) */
+ }
+
+ /*
+ * Finally, make a checksum
+ */
+ (void) checksum(label, CK_MAKESUM);
+
+ return (0);
+}
+
+
+
+/*
+ * Extract a vtoc structure out of a valid label
+ */
+int
+label_to_vtoc(struct vtoc *vtoc, struct dk_label *label)
+{
+#if defined(_SUNOS_VTOC_8)
+ struct dk_map2 *lpart;
+ struct dk_map32 *lmap;
+ long nblks;
+
+#elif defined(_SUNOS_VTOC_16)
+ struct dkl_partition *lpart;
+#else
+#error No VTOC format defined.
+#endif /* defined(_SUNOS_VTOC_8) */
+
+ struct partition *vpart;
+ int i;
+
+ (void) memset((char *)vtoc, 0, sizeof (struct vtoc));
+
+ 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 = lpart->p_start;
+ vpart->p_size = lpart->p_size;
+#endif /* defined(_SUNOS_VTOC_16) */
+ }
+ (void) memcpy(vtoc->v_volume, label->dkl_vtoc.v_volume,
+ LEN_DKL_VVOL);
+ (void) memcpy((char *)vtoc->v_bootinfo,
+ (char *)label->dkl_vtoc.v_bootinfo,
+ sizeof (vtoc->v_bootinfo));
+ (void) memcpy((char *)vtoc->v_reserved,
+ (char *)label->dkl_vtoc.v_reserved,
+ sizeof (vtoc->v_reserved));
+ (void) memcpy((char *)vtoc->timestamp,
+ (char *)label->dkl_vtoc.v_timestamp,
+ sizeof (vtoc->timestamp));
+ 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 = DEV_BSIZE;
+ 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 = lmap->dkl_cylno * nblks;
+ vpart->p_size = lmap->dkl_nblk;
+ }
+#endif /* defined(_SUNOS_VTOC_8) */
+
+ return (0);
+}
+
+/*
+ * Input: File descriptor
+ * Output: 1 if disk is >1TB OR has an EFI label, 0 otherwise.
+ */
+
+int
+is_efi_type(int fd)
+{
+ struct vtoc vtoc;
+
+ if (ioctl(fd, DKIOCGVTOC, &vtoc) == -1) {
+ if (errno == ENOTSUP) {
+ 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 FOR_DEBUGGING_ONLY
+int
+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=%d, blocks=%d", 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 /* FOR_DEBUGGING_ONLY */