diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/format/label.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/format/label.c')
-rw-r--r-- | usr/src/cmd/format/label.c | 1124 |
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 */ |