summaryrefslogtreecommitdiff
path: root/usr/src/lib/libfdisk
diff options
context:
space:
mode:
authorSheshadri Vasudevan <Sheshadri.Vasudevan@Sun.COM>2008-12-03 01:39:30 -0800
committerSheshadri Vasudevan <Sheshadri.Vasudevan@Sun.COM>2008-12-03 01:39:30 -0800
commitc26dc428cee0f025b14a5ad03a7722f2a52f8383 (patch)
tree8da849e326156c91dbd2a79ce00cce6a740a8cea /usr/src/lib/libfdisk
parent4ad13226f66b623d454251005ba7e1eeb218bbfb (diff)
downloadillumos-joyent-c26dc428cee0f025b14a5ad03a7722f2a52f8383.tar.gz
PSARC/2006/379 Solaris on Extended partition
6644364 Extended partitions need to be supported on Solaris 6713308 Macro UNUSED in fdisk.h needs to be changed since id 100 is Novell Netware 286's partition ID 6713318 Need to differentiate between solaris old partition and Linux swap 6745175 Partitions can be created using fdisk table with invalid partition line by "fdisk -F" 6745740 Multiple extended partition can be created by "fdisk -A"
Diffstat (limited to 'usr/src/lib/libfdisk')
-rw-r--r--usr/src/lib/libfdisk/Makefile64
-rw-r--r--usr/src/lib/libfdisk/i386/Makefile83
-rw-r--r--usr/src/lib/libfdisk/i386/libfdisk.c1372
-rw-r--r--usr/src/lib/libfdisk/i386/libfdisk.h304
-rw-r--r--usr/src/lib/libfdisk/i386/llib-lfdisk31
-rw-r--r--usr/src/lib/libfdisk/i386/mapfile-vers51
6 files changed, 1905 insertions, 0 deletions
diff --git a/usr/src/lib/libfdisk/Makefile b/usr/src/lib/libfdisk/Makefile
new file mode 100644
index 0000000000..2944b51753
--- /dev/null
+++ b/usr/src/lib/libfdisk/Makefile
@@ -0,0 +1,64 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.lib
+
+LIBRARY= libfdisk.a
+VERS= .1
+
+HDRS= libfdisk.h
+
+HDRDIR= $(MACH)
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+SUBDIRS= $(MACH)
+
+all install clean clobber lint: $(SUBDIRS)
+
+
+# install rule for install_h target
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+_msg: $(MSGSUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libfdisk/i386/Makefile b/usr/src/lib/libfdisk/i386/Makefile
new file mode 100644
index 0000000000..b695dddd06
--- /dev/null
+++ b/usr/src/lib/libfdisk/i386/Makefile
@@ -0,0 +1,83 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+HDRS = libfdisk.h
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+CHECKDIRS= $(HDRS:%.h=%.check)
+HDRDIR = ./
+
+$(ROOTHDRDIR)/%: %
+ $(INS.file)
+
+all := TARGET = all
+install_h:= TARGET = install_h
+
+install_h: $(ROOTHDRS)
+
+LIBRARY= libfdisk.a
+VERS= .1
+
+PICS= pics/libfdisk.o
+
+pics/%.o: %.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+OBJECTS= \
+libfdisk.o
+
+# include library definitions
+include ../../Makefile.lib
+
+# install this library in the root filesystem
+include ../../Makefile.rootfs
+
+SRCDIR = .
+
+C99MODE= $(C99_DISABLE)
+
+MAPFILES += mapfile-vers
+
+CPPFLAGS += -I.
+LDLIBS += -lc
+
+i386_CFLAGS += -D_LARGEFILE64_SOURCE
+i386_CFLAGS += -D_FILE_OFFSET_BITS=64
+
+.KEEP_STATE:
+
+LIBS= $(DYNLIB) $(LINTLIB)
+
+all: $(LIBS)
+
+lint: lintcheck
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
+
+
+# include library targets
+include ../../Makefile.targ
diff --git a/usr/src/lib/libfdisk/i386/libfdisk.c b/usr/src/lib/libfdisk/i386/libfdisk.c
new file mode 100644
index 0000000000..ff78432109
--- /dev/null
+++ b/usr/src/lib/libfdisk/i386/libfdisk.c
@@ -0,0 +1,1372 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systeminfo.h>
+#include <sys/efi_partition.h>
+#include <sys/byteorder.h>
+
+#include <sys/vtoc.h>
+#include <sys/tty.h>
+#include <sys/dktp/fdisk.h>
+#include <sys/dkio.h>
+#include <sys/mnttab.h>
+#include "libfdisk.h"
+
+#define DEFAULT_PATH_PREFIX "/dev/rdsk/"
+
+static void fdisk_free_ld_nodes(ext_part_t *epp);
+static void fdisk_ext_place_in_sorted_list(ext_part_t *epp,
+ logical_drive_t *newld);
+static void fdisk_ext_remove_from_sorted_list(ext_part_t *epp,
+ logical_drive_t *delld);
+static int fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec,
+ uint32_t endsec);
+static int fdisk_read_extpart(ext_part_t *epp);
+static void fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part);
+static int fdisk_init_master_part_table(ext_part_t *epp);
+static struct ipart *fdisk_alloc_part_table();
+static int fdisk_read_master_part_table(ext_part_t *epp);
+
+static int
+fdisk_init_disk_geom(ext_part_t *epp)
+{
+ struct dk_geom disk_geom;
+ struct dk_minfo disk_info;
+ int no_virtgeom_ioctl = 0, no_physgeom_ioctl = 0;
+
+ /* Get disk's HBA (virtual) geometry */
+ errno = 0;
+ if (ioctl(epp->dev_fd, DKIOCG_VIRTGEOM, &disk_geom)) {
+ if (errno == ENOTTY) {
+ no_virtgeom_ioctl = 1;
+ } else if (errno == EINVAL) {
+ /*
+ * This means that the ioctl exists, but
+ * is invalid for this disk, meaning the
+ * disk doesn't have an HBA geometry
+ * (like, say, it's larger than 8GB).
+ */
+ epp->disk_geom.virt_cyl = epp->disk_geom.virt_heads =
+ epp->disk_geom.virt_sec = 0;
+ } else {
+ return (FDISK_ENOVGEOM);
+ }
+ } else {
+ /* save virtual geometry values obtained by ioctl */
+ epp->disk_geom.virt_cyl = disk_geom.dkg_ncyl;
+ epp->disk_geom.virt_heads = disk_geom.dkg_nhead;
+ epp->disk_geom.virt_sec = disk_geom.dkg_nsect;
+ }
+
+ errno = 0;
+ if (ioctl(epp->dev_fd, DKIOCG_PHYGEOM, &disk_geom)) {
+ if (errno == ENOTTY) {
+ no_physgeom_ioctl = 1;
+ } else {
+ return (FDISK_ENOPGEOM);
+ }
+ }
+ /*
+ * Call DKIOCGGEOM if the ioctls for physical and virtual
+ * geometry fail. Get both from this generic call.
+ */
+ if (no_virtgeom_ioctl && no_physgeom_ioctl) {
+ errno = 0;
+ if (ioctl(epp->dev_fd, DKIOCGGEOM, &disk_geom)) {
+ return (FDISK_ENOLGEOM);
+ }
+ }
+
+ epp->disk_geom.phys_cyl = disk_geom.dkg_ncyl;
+ epp->disk_geom.phys_heads = disk_geom.dkg_nhead;
+ epp->disk_geom.phys_sec = disk_geom.dkg_nsect;
+ epp->disk_geom.alt_cyl = disk_geom.dkg_acyl;
+
+ /*
+ * If DKIOCGMEDIAINFO ioctl succeeds, set the dki_lbsize as the
+ * size of the sector, else default to 512
+ */
+ if (ioctl(epp->dev_fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) < 0) {
+ /* ioctl failed, falling back to default value of 512 bytes */
+ epp->disk_geom.sectsize = 512;
+ } else {
+ epp->disk_geom.sectsize = ((disk_info.dki_lbsize) ?
+ disk_info.dki_lbsize : 512);
+ }
+
+ /*
+ * if hba geometry was not set by DKIOC_VIRTGEOM
+ * or we got an invalid hba geometry
+ * then set hba geometry based on max values
+ */
+ if (no_virtgeom_ioctl || disk_geom.dkg_ncyl == 0 ||
+ disk_geom.dkg_nhead == 0 || disk_geom.dkg_nsect == 0 ||
+ disk_geom.dkg_ncyl > MAX_CYL || disk_geom.dkg_nhead > MAX_HEAD ||
+ disk_geom.dkg_nsect > MAX_SECT) {
+ epp->disk_geom.virt_sec = MAX_SECT;
+ epp->disk_geom.virt_heads = MAX_HEAD + 1;
+ epp->disk_geom.virt_cyl = (epp->disk_geom.phys_cyl *
+ epp->disk_geom.phys_heads * epp->disk_geom.phys_sec) /
+ (epp->disk_geom.virt_sec * epp->disk_geom.virt_heads);
+ }
+ return (FDISK_SUCCESS);
+}
+
+/*
+ * Initialise important members of the ext_part_t structure and
+ * other data structures vital to functionality of libfdisk
+ */
+int
+libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab, int opflag)
+{
+ ext_part_t *temp;
+ char *canonp;
+ struct stat sbuf;
+ int rval = FDISK_SUCCESS;
+
+ if ((temp = calloc(1, sizeof (ext_part_t))) == NULL) {
+ return (ENOMEM);
+ }
+ canonp = strstr(devstr, DEFAULT_PATH_PREFIX);
+ if (canonp == NULL) {
+ (void) snprintf(temp->device_name, sizeof (temp->device_name),
+ "%s%s", DEFAULT_PATH_PREFIX, devstr);
+ } else {
+ (void) strncpy(temp->device_name, devstr,
+ sizeof (temp->device_name));
+ }
+ /*
+ * In case of an EFI labeled disk, the device name could be cN[tN]dN.
+ * There is no pN. So we add "p0" at the end if we do not find it.
+ */
+ if (strrchr(temp->device_name, 'p') == NULL) {
+ (void) strcat(temp->device_name, "p0");
+ }
+
+ if (stat(temp->device_name, &sbuf) != 0) {
+ free(temp);
+ return (EINVAL);
+ }
+ temp->ld_head = NULL;
+ temp->sorted_ld_head = NULL;
+
+ if ((temp->dev_fd = open(temp->device_name, O_RDWR, 0666)) < 0) {
+ free(temp);
+ return (EINVAL);
+ }
+
+ if ((temp->mtable = parttab) == NULL) {
+ if ((rval = fdisk_init_master_part_table(temp)) !=
+ FDISK_SUCCESS) {
+ return (rval);
+ }
+ }
+
+ temp->op_flag = opflag;
+
+ if ((rval = fdisk_init_disk_geom(temp)) != FDISK_SUCCESS) {
+ return (rval);
+ }
+
+ *epp = temp;
+
+ if (opflag & FDISK_READ_DISK) {
+ rval = fdisk_read_extpart(*epp);
+ }
+ return (rval);
+}
+
+int
+libfdisk_reset(ext_part_t *epp)
+{
+ int rval = FDISK_SUCCESS;
+
+ fdisk_free_ld_nodes(epp);
+ epp->first_ebr_is_null = 1;
+ epp->corrupt_logical_drives = 0;
+ epp->logical_drive_count = 0;
+ epp->invalid_bb_sig[0] = 0;
+ if (epp->op_flag & FDISK_READ_DISK) {
+ rval = fdisk_read_extpart(epp);
+ }
+ return (rval);
+}
+
+void
+libfdisk_fini(ext_part_t **epp)
+{
+ fdisk_free_ld_nodes(*epp);
+ (void) close((*epp)->dev_fd);
+ free(*epp);
+ *epp = NULL;
+}
+
+int
+fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, off_t *lsm_offset)
+{
+ int i;
+ int rval = -1;
+ off_t seek_offset;
+ uint32_t linux_pg_size;
+ char *buf, *linux_swap_magic;
+ int sec_sz = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE);
+ /*
+ * Known linux kernel page sizes
+ * The linux swap magic is found as the last 10 bytes of a disk chunk
+ * at the beginning of the linux swap partition whose size is that of
+ * kernel page size.
+ */
+ uint32_t linux_pg_size_arr[] = {4096, };
+
+ if ((buf = calloc(1, sec_sz)) == NULL) {
+ return (ENOMEM);
+ }
+
+ linux_swap_magic = buf + sec_sz - LINUX_SWAP_MAGIC_LENGTH;
+
+ for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) {
+ linux_pg_size = linux_pg_size_arr[i];
+ seek_offset = linux_pg_size/sec_sz - 1;
+ seek_offset += part_start;
+ seek_offset *= sec_sz;
+
+ if ((rval = lseek(epp->dev_fd, seek_offset, SEEK_SET)) < 0) {
+ break;
+ }
+
+ if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) {
+ rval = EIO;
+ break;
+ }
+
+ if ((strncmp(linux_swap_magic, "SWAP-SPACE",
+ LINUX_SWAP_MAGIC_LENGTH) == 0) ||
+ (strncmp(linux_swap_magic, "SWAPSPACE2",
+ LINUX_SWAP_MAGIC_LENGTH) == 0)) {
+ /* Found a linux swap */
+ rval = 0;
+ *lsm_offset = seek_offset;
+ break;
+ }
+ }
+
+ free(buf);
+ return (rval);
+}
+
+int
+fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec,
+ uint32_t *numsec)
+{
+ logical_drive_t *temp = fdisk_get_ld_head(epp);
+ uint32_t part_start;
+ int pno;
+ int rval = -1;
+ off_t lsmo = 0;
+
+ for (pno = 5; temp != NULL; temp = temp->next, pno++) {
+ if (fdisk_is_solaris_part(LE_8(temp->parts[0].systid))) {
+ part_start = temp->abs_secnum + temp->logdrive_offset;
+ if (fdisk_is_linux_swap(epp, part_start, &lsmo) == 0) {
+ continue;
+ }
+ *pnum = pno;
+ *begsec = part_start;
+ *numsec = temp->numsect;
+ rval = FDISK_SUCCESS;
+ }
+ }
+ return (rval);
+}
+
+int
+fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid, uint32_t *begsec,
+ uint32_t *numsec)
+{
+ logical_drive_t *temp = fdisk_get_ld_head(epp);
+ int pno;
+
+ if ((pnum < 5) || (pnum >= MAX_EXT_PARTS + 5)) {
+ return (EINVAL);
+ }
+
+ for (pno = 5; (pno < pnum) && (temp != NULL); temp = temp->next, pno++)
+ ;
+
+ if (temp == NULL) {
+ return (EINVAL);
+ }
+
+ *sysid = LE_8(temp->parts[0].systid);
+ *begsec = temp->abs_secnum + temp->logdrive_offset;
+ *numsec = temp->numsect;
+ return (FDISK_SUCCESS);
+}
+
+/*
+ * Allocate a node of type logical_drive_t and return the pointer to it
+ */
+static logical_drive_t *
+fdisk_alloc_ld_node()
+{
+ logical_drive_t *temp;
+
+ if ((temp = calloc(1, sizeof (logical_drive_t))) == NULL) {
+ return (NULL);
+ }
+ temp->next = NULL;
+ return (temp);
+}
+
+/*
+ * Free all the logical_drive_t's allocated during the run
+ */
+static void
+fdisk_free_ld_nodes(ext_part_t *epp)
+{
+ logical_drive_t *temp;
+
+ for (temp = epp->ld_head; temp != NULL; ) {
+ temp = epp->ld_head -> next;
+ free(epp->ld_head);
+ epp->ld_head = temp;
+ }
+ epp->ld_head = NULL;
+ epp->sorted_ld_head = NULL;
+}
+
+/*
+ * Find the first free sector within the extended partition
+ */
+int
+fdisk_ext_find_first_free_sec(ext_part_t *epp, uint32_t *first_free_sec)
+{
+ logical_drive_t *temp;
+ uint32_t last_free_sec;
+
+ *first_free_sec = epp->ext_beg_sec;
+
+ if (epp->ld_head == NULL) {
+ return (FDISK_SUCCESS);
+ }
+
+ /*
+ * When the first logical drive is out of order, we need to adjust
+ * first_free_sec accordingly. In this case, the first extended
+ * partition sector is not free even though the actual logical drive
+ * does not occupy space from the beginning of the extended partition.
+ * The next free sector would be the second sector of the extended
+ * partition.
+ */
+ if (epp->ld_head->abs_secnum > epp->ext_beg_sec +
+ MAX_LOGDRIVE_OFFSET) {
+ (*first_free_sec)++;
+ }
+
+ while (*first_free_sec <= epp->ext_end_sec) {
+ for (temp = epp->sorted_ld_head; temp != NULL; temp =
+ temp->sorted_next) {
+ if (temp->abs_secnum == *first_free_sec) {
+ *first_free_sec = temp->abs_secnum +
+ temp->logdrive_offset + temp->numsect;
+ }
+ }
+
+ last_free_sec = fdisk_ext_find_last_free_sec(epp,
+ *first_free_sec);
+
+ if ((last_free_sec - *first_free_sec) < MAX_LOGDRIVE_OFFSET) {
+ /*
+ * Minimum size of a partition assumed to be atleast one
+ * sector.
+ */
+ *first_free_sec = last_free_sec + 1;
+ continue;
+ }
+
+ break;
+ }
+
+ if (*first_free_sec > epp->ext_end_sec) {
+ return (FDISK_EOOBOUND);
+ }
+
+ return (FDISK_SUCCESS);
+}
+
+/*
+ * Find the last free sector within the extended partition given, a beginning
+ * sector (so that the range - "begsec to last_free_sec" is contiguous)
+ */
+uint32_t
+fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec)
+{
+ logical_drive_t *temp;
+ uint32_t last_free_sec;
+
+ last_free_sec = epp->ext_end_sec;
+ for (temp = epp->sorted_ld_head; temp != NULL;
+ temp = temp->sorted_next) {
+ if (temp->abs_secnum > begsec) {
+ last_free_sec = temp->abs_secnum - 1;
+ break;
+ }
+ }
+ return (last_free_sec);
+}
+
+/*
+ * Place the given ext_part_t structure in a sorted list, sorted in the
+ * ascending order of their beginning sectors.
+ */
+static void
+fdisk_ext_place_in_sorted_list(ext_part_t *epp, logical_drive_t *newld)
+{
+ logical_drive_t *pre, *cur;
+
+ if (newld->abs_secnum < epp->sorted_ld_head->abs_secnum) {
+ newld->sorted_next = epp->sorted_ld_head;
+ epp->sorted_ld_head = newld;
+ return;
+ }
+ pre = cur = epp->sorted_ld_head;
+
+ for (; cur != NULL; pre = cur, cur = cur->sorted_next) {
+ if (newld->abs_secnum < cur->abs_secnum) {
+ break;
+ }
+ }
+
+ newld->sorted_next = cur;
+ pre->sorted_next = newld;
+}
+
+static void
+fdisk_ext_remove_from_sorted_list(ext_part_t *epp, logical_drive_t *delld)
+{
+ logical_drive_t *pre, *cur;
+
+ if (delld == epp->sorted_ld_head) {
+ epp->sorted_ld_head = delld->sorted_next;
+ return;
+ }
+
+ pre = cur = epp->sorted_ld_head;
+
+ for (; cur != NULL; pre = cur, cur = cur->sorted_next) {
+ if (cur->abs_secnum == delld->abs_secnum) {
+ /* Found */
+ break;
+ }
+ }
+
+ pre->sorted_next = cur->sorted_next;
+}
+
+static int
+fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, uint32_t endsec)
+{
+ logical_drive_t *temp;
+ uint32_t firstsec, lastsec, last_free_sec;
+
+ for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
+ firstsec = temp->abs_secnum;
+ lastsec = firstsec + temp->logdrive_offset + temp->numsect - 1;
+ if ((begsec >= firstsec) &&
+ (begsec <= lastsec)) {
+ return (1);
+ }
+ }
+
+ /*
+ * Find the maximum possible end sector value
+ * given a beginning sector value
+ */
+ last_free_sec = fdisk_ext_find_last_free_sec(epp, begsec);
+
+ if (endsec > last_free_sec) {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check if the logical drive boundaries are sane
+ */
+int
+fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec,
+ uint32_t offset, uint32_t numsec)
+{
+ uint32_t endsec;
+
+ endsec = begsec + offset + numsec - 1;
+ if (begsec < epp->ext_beg_sec ||
+ begsec > epp->ext_end_sec ||
+ endsec < epp->ext_beg_sec ||
+ endsec > epp->ext_end_sec ||
+ endsec < begsec ||
+ fdisk_ext_overlapping_parts(epp, begsec, endsec)) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Procedure to walk through the extended partitions and build a Singly
+ * Linked List out of the data.
+ */
+int
+fdisk_read_extpart(ext_part_t *epp)
+{
+ struct ipart *fdp, *ext_fdp;
+ int i = 0, j = 0, ext_part_found = 0, lpart = 5;
+ off_t secnum, offset;
+ logical_drive_t *temp, *ep_ptr;
+ unsigned char *ext_buf;
+ int sectsize = epp->disk_geom.sectsize;
+
+ if ((ext_buf = (uchar_t *)malloc(sectsize)) == NULL) {
+ return (ENOMEM);
+ }
+ fdp = epp->mtable;
+
+ for (i = 0; (i < FD_NUMPART) && (!ext_part_found); i++, fdp++) {
+ if (fdisk_is_dos_extended(LE_8(fdp->systid))) {
+ ext_part_found = 1;
+ secnum = LE_32(fdp->relsect);
+ offset = secnum * sectsize;
+ epp->ext_beg_sec = secnum;
+ epp->ext_end_sec = secnum + LE_32(fdp->numsect) - 1;
+ epp->ext_beg_cyl =
+ FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec);
+ epp->ext_end_cyl =
+ FDISK_SECT_TO_CYL(epp, epp->ext_end_sec);
+
+ /*LINTED*/
+ while (B_TRUE) {
+ if (lseek(epp->dev_fd, offset, SEEK_SET) < 0) {
+ return (EIO);
+ }
+ if (read(epp->dev_fd, ext_buf, sectsize) <
+ sectsize) {
+ return (EIO);
+ }
+ /*LINTED*/
+ ext_fdp = (struct ipart *)
+ (&ext_buf[FDISK_PART_TABLE_START]);
+ if ((LE_32(ext_fdp->relsect) == 0) &&
+ (epp->logical_drive_count == 0)) {
+ /* No logical drives defined */
+ epp->first_ebr_is_null = 0;
+ return (FDISK_ENOLOGDRIVE);
+ }
+
+ temp = fdisk_alloc_ld_node();
+ temp->abs_secnum = secnum;
+ temp->logdrive_offset =
+ LE_32(ext_fdp->relsect);
+ temp ->numsect = LE_32(ext_fdp->numsect);
+ if (epp->ld_head == NULL) {
+ /* adding first logical drive */
+ if (temp->logdrive_offset >
+ MAX_LOGDRIVE_OFFSET) {
+ /* out of order */
+ temp->abs_secnum +=
+ temp->logdrive_offset;
+ temp->logdrive_offset = 0;
+ }
+ }
+ temp->begcyl =
+ FDISK_SECT_TO_CYL(epp, temp->abs_secnum);
+ temp->endcyl = FDISK_SECT_TO_CYL(epp,
+ temp->abs_secnum +
+ temp->logdrive_offset +
+ temp->numsect - 1);
+
+ /*
+ * Check for sanity of logical drives
+ */
+ if (fdisk_validate_logical_drive(epp,
+ temp->abs_secnum, temp->logdrive_offset,
+ temp->numsect)) {
+ epp->corrupt_logical_drives = 1;
+ free(temp);
+ return (FDISK_EBADLOGDRIVE);
+ }
+
+ temp->parts[0] = *ext_fdp;
+ ext_fdp++;
+ temp->parts[1] = *ext_fdp;
+
+ if (epp->ld_head == NULL) {
+ epp->ld_head = temp;
+ epp->sorted_ld_head = temp;
+ ep_ptr = temp;
+ epp->logical_drive_count = 1;
+ } else {
+ ep_ptr->next = temp;
+ ep_ptr = temp;
+ fdisk_ext_place_in_sorted_list(epp,
+ temp);
+ epp->logical_drive_count++;
+ }
+
+ /*LINTED*/
+ if (LE_16((*(uint16_t *)&ext_buf[510])) !=
+ MBB_MAGIC) {
+ epp->invalid_bb_sig[j++] = lpart;
+ temp->modified = FDISK_MINOR_WRITE;
+ }
+
+ if (LE_32(ext_fdp->relsect) == 0)
+ break;
+ else {
+ secnum = LE_32(fdp->relsect) +
+ LE_32(ext_fdp->relsect);
+ offset = secnum * sectsize;
+ }
+ lpart++;
+ }
+ }
+ }
+ return (FDISK_SUCCESS);
+}
+
+static int
+fdisk_init_master_part_table(ext_part_t *epp)
+{
+ int rval;
+ if ((epp->mtable = fdisk_alloc_part_table()) == NULL) {
+ return (ENOMEM);
+ }
+ rval = fdisk_read_master_part_table(epp);
+ if (rval) {
+ return (rval);
+ }
+ return (FDISK_SUCCESS);
+}
+
+static struct ipart *
+fdisk_alloc_part_table()
+{
+ int size = sizeof (struct ipart);
+ struct ipart *table;
+
+ if ((table = calloc(4, size)) == NULL) {
+ return (NULL);
+ }
+
+ return (table);
+}
+
+/*
+ * Reads the master fdisk partition table from the device assuming that it has
+ * a valid table.
+ * MBR is supposed to be of 512 bytes no matter what the device block size is.
+ */
+static int
+fdisk_read_master_part_table(ext_part_t *epp)
+{
+ uchar_t buf[512];
+ int sectsize = 512;
+ int size = sizeof (struct ipart);
+ int cpcnt = FD_NUMPART * size;
+
+ if (lseek(epp->dev_fd, 0, SEEK_SET) < 0) {
+ return (EIO);
+ }
+ if (read(epp->dev_fd, buf, sectsize) < sectsize) {
+ return (EIO);
+ }
+ bcopy(&buf[FDISK_PART_TABLE_START], epp->mtable, cpcnt);
+
+ /*LINTED*/
+ if (LE_16((*(uint16_t *)&buf[510])) != MBB_MAGIC) {
+ return (FDISK_EBADMAGIC);
+ }
+
+ return (FDISK_SUCCESS);
+}
+
+int
+fdisk_ext_part_exists(ext_part_t *epp)
+{
+ int i;
+ struct ipart *part_table = epp->mtable;
+
+ if (part_table == NULL) {
+ /* No extended partition found */
+ return (0);
+ }
+
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (fdisk_is_dos_extended(LE_8(part_table[i].systid))) {
+ break;
+ }
+ }
+
+ if (i == FD_NUMPART) {
+ /* No extended partition found */
+ return (0);
+ }
+ return (1);
+}
+
+int
+fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl,
+ uint32_t *begsec)
+{
+ logical_drive_t *temp;
+ uint32_t first_free_sec;
+ uint32_t first_free_cyl;
+ int rval;
+
+ rval = fdisk_ext_find_first_free_sec(epp, &first_free_sec);
+ if (rval != FDISK_SUCCESS) {
+ return (rval);
+ }
+
+ first_free_cyl = FDISK_SECT_TO_CYL(epp, first_free_sec);
+ if (begcyl == first_free_cyl) {
+ *begsec = first_free_sec;
+ return (FDISK_SUCCESS);
+ }
+
+ /* Check if the cylinder number is beyond the extended partition */
+ if ((begcyl < epp->ext_beg_cyl) || (begcyl > epp->ext_end_cyl)) {
+ return (FDISK_EOOBOUND);
+ }
+
+ for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
+ if ((begcyl >= temp->begcyl) &&
+ (begcyl <= temp->endcyl)) {
+ return (FDISK_EOVERLAP);
+ }
+ }
+ *begsec = FDISK_CYL_TO_SECT(epp, begcyl);
+
+ return (FDISK_SUCCESS);
+}
+
+void
+fdisk_change_logical_drive_id(ext_part_t *epp, int pno, uchar_t partid)
+{
+ logical_drive_t *temp;
+ int i;
+
+ i = FD_NUMPART + 1;
+ for (temp = epp->ld_head; i < pno; temp = temp->next, i++)
+ ;
+
+ temp->parts[0].systid = LE_8(partid);
+ temp->modified = FDISK_MAJOR_WRITE;
+}
+
+/*
+ * A couple of special scenarios :
+ * 1. Since the first logical drive's EBR is always at the beginning of the
+ * extended partition, any specification that starts the first logical drive
+ * out of order will need to address the following issue :
+ * If the beginning of the drive is not coinciding with the beginning of the
+ * extended partition and :
+ * a) The start is within MAX_LOGDRIVE_OFFSET, the offset changes from the
+ * default of 63 to less than 63.
+ * logdrive_offset is updated to keep track of the space between
+ * the beginning of the logical drive and extended partition. abs_secnum
+ * points to the beginning of the extended partition.
+ * b) The start is greater than MAX_LOGDRIVE_OFFSET, the offset changes from
+ * the default of 63 to greater than 63.
+ * logdrive_offset is set to 0. abs_secnum points to the beginning of the
+ * logical drive, which is at an offset from the extended partition.
+ */
+void
+fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec, uint32_t endsec,
+ uchar_t partid)
+{
+ logical_drive_t *temp, *pre, *cur;
+ struct ipart *part;
+
+ temp = fdisk_alloc_ld_node();
+ temp->abs_secnum = begsec;
+ temp->logdrive_offset = MAX_LOGDRIVE_OFFSET;
+ temp->numsect = endsec - begsec + 1 - MAX_LOGDRIVE_OFFSET;
+ temp->begcyl = FDISK_SECT_TO_CYL(epp, begsec);
+ temp->endcyl = FDISK_SECT_TO_CYL(epp, endsec);
+ temp->modified = FDISK_MAJOR_WRITE;
+
+ part = &temp->parts[0];
+ part->bootid = 0;
+ part->systid = LE_8(partid);
+ part->relsect = MAX_LOGDRIVE_OFFSET;
+ part->numsect = LE_32(temp->numsect);
+
+ fdisk_set_CHS_values(epp, part);
+
+ if (epp->ld_head == NULL) {
+ epp->corrupt_logical_drives = 0;
+ if (begsec != epp->ext_beg_sec) {
+ part->relsect = LE_32(begsec - epp->ext_beg_sec);
+ temp->numsect = endsec - begsec + 1;
+ part->numsect = LE_32(temp->numsect);
+ if (LE_32(part->relsect) > MAX_LOGDRIVE_OFFSET) {
+ temp->logdrive_offset = 0;
+ } else {
+ temp->abs_secnum = epp->ext_beg_sec;
+ temp->logdrive_offset = LE_32(part->relsect);
+ }
+ }
+ epp->first_ebr_is_null = 0;
+ epp->ld_head = temp;
+ epp->sorted_ld_head = temp;
+ epp->logical_drive_count = 1;
+ return;
+ }
+
+ if (temp->abs_secnum == epp->ext_beg_sec) {
+ part->relsect = LE_32(LE_32(part->relsect) - 1);
+ temp->logdrive_offset--;
+ temp->abs_secnum++;
+ }
+
+ for (pre = cur = epp->ld_head; cur != NULL; pre = cur, cur = cur->next)
+ ;
+
+ part = &pre->parts[1];
+ part->bootid = 0;
+ part->systid = LE_8(EXTDOS);
+ part->relsect = LE_32(temp->abs_secnum - epp->ext_beg_sec);
+ part->numsect = LE_32(temp->numsect + temp->logdrive_offset);
+
+ fdisk_set_CHS_values(epp, part);
+
+ pre->next = temp;
+ pre->modified = FDISK_MAJOR_WRITE;
+ epp->logical_drive_count++;
+ fdisk_ext_place_in_sorted_list(epp, temp);
+}
+
+/*
+ * There are 2 cases that need to be handled.
+ * 1. Deleting the first extended partition :
+ * The peculiarity of this case is that the offset of the first extended
+ * partition is always indicated by the entry in the master boot record.
+ * (MBR). This never changes, unless the extended partition itself is
+ * deleted. Hence, the location of the first EBR is fixed.
+ * It is only the logical drive which is deleted. This first EBR now gives
+ * information of the next logical drive and the info about the subsequent
+ * extended partition. Hence the "relsect" of the first EBR is modified to
+ * point to the next logical drive.
+ *
+ * 2. Deleting an intermediate extended partition.
+ * This is quite normal and follows the semantics of a normal linked list
+ * delete operation. The node being deleted has the information about the
+ * logical drive that it houses and the location and the size of the next
+ * extended partition. This informationis transferred to the node previous
+ * to the node being deleted.
+ *
+ */
+
+void
+fdisk_delete_logical_drive(ext_part_t *epp, int pno)
+{
+ logical_drive_t *pre, *cur;
+ int i;
+
+ i = FD_NUMPART + 1;
+ pre = cur = epp->ld_head;
+ for (; i < pno; i++) {
+ pre = cur;
+ cur = cur->next;
+ }
+
+ if (cur == epp->ld_head) {
+ /* Deleting the first logical drive */
+ if (cur->next == NULL) {
+ /* Deleting the only logical drive left */
+ free(cur);
+ epp->ld_head = NULL;
+ epp->sorted_ld_head = NULL;
+ epp->logical_drive_count = 0;
+ epp->first_ebr_is_null = 1;
+ } else {
+ pre = epp->ld_head;
+ cur = pre->next;
+ cur->parts[0].relsect =
+ LE_32(LE_32(cur->parts[0].relsect) +
+ LE_32(pre->parts[1].relsect));
+ /* Corner case when partitions are out of order */
+ if ((pre->abs_secnum != epp->ext_beg_sec) &&
+ (cur->abs_secnum == epp->ext_beg_sec + 1)) {
+ cur->logdrive_offset++;
+ cur->abs_secnum = epp->ext_beg_sec;
+ } else {
+ cur->abs_secnum = LE_32(cur->parts[0].relsect) +
+ epp->ext_beg_sec;
+ cur->logdrive_offset = 0;
+ }
+ fdisk_ext_remove_from_sorted_list(epp, pre);
+ epp->ld_head = cur;
+ epp->ld_head->modified = FDISK_MAJOR_WRITE;
+ epp->logical_drive_count--;
+ free(pre);
+ }
+ } else {
+ pre->parts[1] = cur->parts[1];
+ pre->next = cur->next;
+ fdisk_ext_remove_from_sorted_list(epp, cur);
+ pre->modified = FDISK_MAJOR_WRITE;
+ free(cur);
+ epp->logical_drive_count--;
+ }
+}
+
+static void
+fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part)
+{
+ uint32_t lba, cy, hd, sc;
+ uint32_t sectors = epp->disk_geom.virt_sec;
+ uint32_t heads = epp->disk_geom.virt_heads;
+
+ lba = LE_32(part->relsect) + epp->ext_beg_sec;
+ if (lba >= heads * sectors * MAX_CYL) {
+ /*
+ * the lba address cannot be expressed in CHS value
+ * so store the maximum CHS field values in the CHS fields.
+ */
+ cy = MAX_CYL + 1;
+ hd = MAX_HEAD;
+ sc = MAX_SECT;
+ } else {
+ cy = lba / sectors / heads;
+ hd = lba / sectors % heads;
+ sc = lba % sectors + 1;
+ }
+
+ part->begcyl = cy & 0xff;
+ part->beghead = (uchar_t)hd;
+ part->begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
+
+ /*
+ * This code is identical to the code above
+ * except that it works on ending CHS values
+ */
+ lba += LE_32(part->numsect - 1);
+ if (lba >= heads * sectors * MAX_CYL) {
+ cy = MAX_CYL + 1;
+ hd = MAX_HEAD;
+ sc = MAX_SECT;
+ } else {
+ cy = lba / sectors / heads;
+ hd = lba / sectors % heads;
+ sc = lba % sectors + 1;
+ }
+ part->endcyl = cy & 0xff;
+ part->endhead = (uchar_t)hd;
+ part->endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
+}
+
+static int
+read_modify_write_ebr(ext_part_t *epp, unsigned char *ebr_buf,
+ struct ipart *ebr_tab, uint32_t sec_offset)
+{
+ off_t seek_offset;
+ int sectsize = epp->disk_geom.sectsize;
+
+ seek_offset = (off_t)sec_offset * sectsize;
+
+ if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) {
+ return (EIO);
+ }
+ if (read(epp->dev_fd, ebr_buf, sectsize) < sectsize) {
+ return (EIO);
+ }
+
+ bzero(&ebr_buf[FDISK_PART_TABLE_START], 4 * sizeof (struct ipart));
+ if (ebr_tab != NULL) {
+ bcopy(ebr_tab, &ebr_buf[FDISK_PART_TABLE_START],
+ 2 * sizeof (struct ipart));
+ }
+ ebr_buf[510] = 0x55;
+ ebr_buf[511] = 0xAA;
+ if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) {
+ return (EIO);
+ }
+ if (write(epp->dev_fd, ebr_buf, sectsize) < sectsize) {
+ return (EIO);
+ }
+ return (0);
+}
+
+/*
+ * XXX - ZFS mounts not detected. Needs to come in as a feature.
+ * Currently only /etc/mnttab entries are being checked
+ */
+int
+fdisk_mounted_logical_drives(ext_part_t *epp)
+{
+ char *part_str, *canonp;
+ char compare_pdev_str[PATH_MAX];
+ char compare_sdev_str[PATH_MAX];
+ FILE *fp;
+ struct mnttab mt;
+ int part;
+ int look_for_mounted_slices = 0;
+ uint32_t begsec, numsec;
+
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ return (ENOENT);
+ }
+
+ canonp = epp->device_name + strlen(DEFAULT_PATH_PREFIX);
+ (void) snprintf(compare_pdev_str, PATH_MAX, "%s%s", "/dev/dsk/",
+ canonp);
+ part_str = strrchr(compare_pdev_str, 'p');
+ *(part_str + 1) = '\0';
+ (void) strcpy(compare_sdev_str, compare_pdev_str);
+ part_str = strrchr(compare_sdev_str, 'p');
+ *part_str = 's';
+
+ if (fdisk_get_solaris_part(epp, &part, &begsec, &numsec) ==
+ FDISK_SUCCESS) {
+ if (part > FD_NUMPART) {
+ /*
+ * Solaris partition is on a logical drive. Look for
+ * mounted slices.
+ */
+ look_for_mounted_slices = 1;
+ }
+ }
+
+ while (getmntent(fp, &mt) == 0) {
+ if (strstr(mt.mnt_special, compare_pdev_str) == NULL) {
+ if (strstr(mt.mnt_special, compare_sdev_str) == NULL) {
+ continue;
+ } else {
+ if (look_for_mounted_slices) {
+ return (FDISK_EMOUNTED);
+ }
+ }
+ }
+
+ /*
+ * Get the partition number that is mounted, which would be
+ * found just beyond the last 'p' in the device string.
+ * For example, in /dev/dsk/c0t0d0p12, partition number 12
+ * is just beyond the last 'p'.
+ */
+ part_str = strrchr(mt.mnt_special, 'p');
+ if (part_str != NULL) {
+ part_str++;
+ part = atoi(part_str);
+ /* Extended partition numbers start from 5 */
+ if (part >= 5) {
+ return (FDISK_EMOUNTED);
+ }
+ }
+ }
+ return (0);
+}
+
+int
+fdisk_commit_ext_part(ext_part_t *epp)
+{
+ logical_drive_t *temp;
+ int wflag = 0; /* write flag */
+ int rval;
+ int sectsize = epp->disk_geom.sectsize;
+ unsigned char *ebr_buf;
+ int ld_count;
+ uint32_t abs_secnum;
+ int check_mounts = 0;
+ off_t lsmo;
+ char *lsm_buf;
+
+ if ((ebr_buf = (unsigned char *)malloc(sectsize)) == NULL) {
+ return (ENOMEM);
+ }
+ if ((lsm_buf = calloc(1, sectsize)) == NULL) {
+ return (ENOMEM);
+ }
+
+ if (epp->first_ebr_is_null) {
+ /*
+ * Indicator that the extended partition as a whole was
+ * modifies (either created or deleted. Must check for mounts
+ * and must commit
+ */
+ check_mounts = 1;
+ }
+
+ /*
+ * Pass1 through the logical drives to make sure that commit of minor
+ * written block dont get held up due to mounts.
+ */
+ for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
+ if (temp == epp->ld_head) {
+ abs_secnum = epp->ext_beg_sec;
+ } else {
+ abs_secnum = temp->abs_secnum;
+ }
+ if (temp->modified == FDISK_MINOR_WRITE) {
+ rval = read_modify_write_ebr(epp, ebr_buf,
+ temp->parts, abs_secnum);
+ if (rval) {
+ goto error;
+ }
+ temp->modified = 0;
+ } else if (temp->modified == FDISK_MAJOR_WRITE) {
+ check_mounts = 1;
+ }
+ }
+
+ if (!check_mounts) {
+ goto skip_check_mounts;
+ }
+
+ if ((rval = fdisk_mounted_logical_drives(epp)) != 0) {
+ /* One/more extended partitions are mounted */
+ if (ebr_buf) {
+ free(ebr_buf);
+ }
+ if (lsm_buf) {
+ free(lsm_buf);
+ }
+ return (rval);
+ }
+
+skip_check_mounts:
+
+ if (epp->first_ebr_is_null) {
+ rval = read_modify_write_ebr(epp, ebr_buf, NULL,
+ epp->ext_beg_sec);
+ if (rval) {
+ goto error;
+ }
+ wflag = 1;
+ ld_count = 0;
+ } else {
+ if (epp->logical_drive_count == 0) {
+ /*
+ * Can hit this case when there is just an extended
+ * partition with no logical drives, and the user
+ * committed without making any changes
+ * We dont have anything to commit. Return success
+ */
+ if (ebr_buf) {
+ free(ebr_buf);
+ }
+ if (lsm_buf) {
+ free(lsm_buf);
+ }
+ return (FDISK_SUCCESS);
+ }
+
+ /*
+ * Make sure that the first EBR is written with the first
+ * logical drive's data, which might not be the first in disk
+ * order.
+ */
+ for (temp = epp->ld_head, ld_count = 0; temp != NULL;
+ temp = temp->next, ld_count++) {
+ /*
+ * Check if the current partition is a solaris old
+ * partition. In that case, check if it was previously
+ * a linux swap. If so, overwrite the linux swap magic.
+ */
+ if (temp->parts[0].systid == SUNIXOS) {
+ uint32_t secnum = temp->abs_secnum +
+ temp->logdrive_offset;
+ if (fdisk_is_linux_swap(epp, secnum,
+ &lsmo) == 0) {
+ if ((rval = lseek(epp->dev_fd, lsmo,
+ SEEK_SET)) < 0) {
+ if (ld_count) {
+ break;
+ }
+ goto error;
+ }
+
+ if (read(epp->dev_fd, lsm_buf,
+ sectsize) < sectsize) {
+ rval = EIO;
+ if (ld_count) {
+ break;
+ }
+ goto error;
+ }
+
+ bzero(lsm_buf + sectsize -
+ LINUX_SWAP_MAGIC_LENGTH,
+ LINUX_SWAP_MAGIC_LENGTH);
+
+ if ((rval = lseek(epp->dev_fd, lsmo,
+ SEEK_SET)) < 0) {
+ if (ld_count) {
+ break;
+ }
+ goto error;
+ }
+
+ if ((rval = write(epp->dev_fd, lsm_buf,
+ sectsize)) < sectsize) {
+ rval = EIO;
+ if (ld_count) {
+ break;
+ }
+ goto error;
+ }
+ }
+ }
+
+ if (ld_count == 0) {
+ abs_secnum = epp->ext_beg_sec;
+ } else {
+ abs_secnum = temp->abs_secnum;
+ }
+ if (temp->modified) {
+ rval = read_modify_write_ebr(epp, ebr_buf,
+ temp->parts, abs_secnum);
+ if (rval) {
+ if (ld_count) {
+ /*
+ * There was atleast one
+ * write to the disk before
+ * this failure. Make sure that
+ * the kernel is notified.
+ * Issue the ioctl.
+ */
+ break;
+ }
+ goto error;
+ }
+ if ((!wflag) && (temp->modified ==
+ FDISK_MAJOR_WRITE)) {
+ wflag = 1;
+ }
+ }
+ }
+
+ if (wflag == 0) {
+ /* No changes made */
+ rval = FDISK_SUCCESS;
+ goto error;
+ }
+ }
+
+ /* Issue ioctl to the driver to update extended partition info */
+ rval = ioctl(epp->dev_fd, DKIOCSETEXTPART);
+error:
+ if (ebr_buf) {
+ free(ebr_buf);
+ }
+ if (lsm_buf) {
+ free(lsm_buf);
+ }
+ return (rval);
+}
+
+int
+fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect)
+{
+ epp->first_ebr_is_null = 1;
+ epp->corrupt_logical_drives = 0;
+ epp->logical_drive_count = 0;
+ epp->ext_beg_sec = rsect;
+ epp->ext_end_sec = rsect + nsect - 1;
+ epp->ext_beg_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec);
+ epp->ext_end_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_end_sec);
+ epp->invalid_bb_sig[0] = 0;
+ return (0);
+}
+
+int
+fdisk_delete_ext_part(ext_part_t *epp)
+{
+ epp->first_ebr_is_null = 1;
+ /* Clear the logical drive information */
+ fdisk_free_ld_nodes(epp);
+ epp->logical_drive_count = 0;
+ epp->corrupt_logical_drives = 0;
+ epp->invalid_bb_sig[0] = 0;
+ return (0);
+}
+
+int
+fdisk_get_disk_geom(ext_part_t *epp, int type, int what)
+{
+ switch (type) {
+ case PHYSGEOM:
+ switch (what) {
+ case NCYL:
+ return ((int)epp->disk_geom.phys_cyl);
+ case NHEADS:
+ return ((int)epp->disk_geom.phys_heads);
+ case NSECTPT:
+ return ((int)epp->disk_geom.phys_sec);
+ case SSIZE:
+ return ((int)epp->disk_geom.sectsize);
+ case ACYL:
+ return ((int)epp->disk_geom.alt_cyl);
+ default:
+ return (EINVAL);
+ }
+ case VIRTGEOM:
+ switch (what) {
+ case NCYL:
+ return ((int)epp->disk_geom.virt_cyl);
+ case NHEADS:
+ return ((int)epp->disk_geom.virt_heads);
+ case NSECTPT:
+ return ((int)epp->disk_geom.virt_sec);
+ case SSIZE:
+ return ((int)epp->disk_geom.sectsize);
+ case ACYL:
+ return ((int)epp->disk_geom.alt_cyl);
+ default:
+ return (EINVAL);
+ }
+ default:
+ return (EINVAL);
+ }
+}
+
+int
+fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr)
+{
+ *bbsig_arr = &(epp->invalid_bb_sig[0]);
+ return (epp->invalid_bb_sig[0]);
+}
diff --git a/usr/src/lib/libfdisk/i386/libfdisk.h b/usr/src/lib/libfdisk/i386/libfdisk.h
new file mode 100644
index 0000000000..ffba18dc29
--- /dev/null
+++ b/usr/src/lib/libfdisk/i386/libfdisk.h
@@ -0,0 +1,304 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef _LIBFDISK_H_
+#define _LIBFDISK_H_
+
+#include <limits.h>
+#include <sys/dktp/fdisk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_LOGDRIVE_OFFSET 63
+
+#define FDISK_ERRNO 200
+#define FDISK_ETOOLONG (FDISK_ERRNO + 0)
+#define FDISK_EOOBOUND (FDISK_ERRNO + 1)
+#define FDISK_EZERO (FDISK_ERRNO + 2)
+#define FDISK_EOVERLAP (FDISK_ERRNO + 3)
+#define FDISK_ENOVGEOM (FDISK_ERRNO + 4)
+#define FDISK_ENOPGEOM (FDISK_ERRNO + 5)
+#define FDISK_ENOLGEOM (FDISK_ERRNO + 6)
+#define FDISK_ENOLOGDRIVE (FDISK_ERRNO + 7)
+#define FDISK_EBADLOGDRIVE (FDISK_ERRNO + 8)
+#define FDISK_ENOEXTPART (FDISK_ERRNO + 9)
+#define FDISK_EBADMAGIC (FDISK_ERRNO + 10)
+#define FDISK_EMOUNTED (FDISK_ERRNO + 11)
+
+#define FDISK_SUCCESS 0
+
+#define FDISK_READ_DISK 0x00000001
+
+#define LINUX_SWAP_MAGIC_LENGTH 10
+enum {
+ PHYSGEOM = 0,
+ VIRTGEOM,
+ NCYL,
+ NHEADS,
+ NSECTPT,
+ SSIZE,
+ ACYL
+};
+
+enum {
+ FDISK_MINOR_WRITE = 1,
+ FDISK_MAJOR_WRITE
+};
+
+#define FDISK_SECTS_PER_CYL(epp) \
+ (epp->disk_geom.phys_heads * epp->disk_geom.phys_sec)
+#define FDISK_SECT_TO_CYL(epp, x) ((x) / (FDISK_SECTS_PER_CYL(epp)))
+#define FDISK_CYL_TO_SECT(epp, x) ((x) * (FDISK_SECTS_PER_CYL(epp)))
+#define FDISK_ABS_CYL_NUM(epp, x) (FDISK_SECT_TO_CYL(x) +\
+ epp->ext_beg_cyl)
+
+#define FDISK_CYL_BNDRY_ALIGN(epp, x) (((x) % (FDISK_SECTS_PER_CYL(epp))) ? \
+ (((x)/(FDISK_SECTS_PER_CYL(epp))) + 1) :\
+ ((x)/(FDISK_SECTS_PER_CYL(epp))))
+
+/*
+ * Extended partition structure :
+ * +--------------+
+ * |+--+ |
+ * || |----------+---> structure at the beginning of the extended partition
+ * ||--| | ( Lets call it the EBR - Extended Boot Record )
+ * || | +---+--->
+ * |+--+ | | Logical drive within the extended partition
+ * |+---------+--+| ( We will plainly call this a logical drive )
+ * || ||
+ * || ||
+ * || ||
+ * |+------------+|
+ * +--------------+
+ *
+ *
+ * EBR is effectively "struct ipart parts[2]".
+ * The picture below shows what the EBR contains. The EBR has
+ * two important pieces of information. The first is the offset and the size
+ * of the logical drive in this extended partition. The second is the offset
+ * and size of the next extended partition. The offsets are relative to
+ * beginning of the first extended partition. These extended partitions are
+ * arranged like a linked list.
+ * Note that (currently) only one extended partition can exist in the MBR.
+ * The system ID of a logical drive within the extended partition cannot be
+ * that of an extended partition.
+ *
+ * +------+
+ * | |
+ * +--------------+ | +-v------------+
+ * |+--+ | | |+--+ |
+ * || |---+ | | || | |
+ * ||--| | | | ||--| |
+ * || |---|------+-+ || | |
+ * |+--+ | | |+--+ |
+ * |+------v-----+| |+------------+|
+ * || || || ||
+ * || || || ||
+ * || || || ||
+ * |+------------+| |+------------+|
+ * +--------------+ +--------------+
+ *
+ */
+
+/*
+ * Main structure used to record changes to the partitions made.
+ * Changes are not written to disk everytime, but maintained in this structure.
+ * This information is used when the user chooses to commit the changes.
+ * A linked list of this structure represents the ondisk partitions.
+ */
+typedef struct logical_drive {
+
+ /* structure holding the EBR data */
+ struct ipart parts[2];
+
+ /*
+ * Absolute beginning sector of the extended partition, and hence an
+ * indicator of where the EBR for this logical drive would go on disk.
+ * NOTE : In case the first logical drive in this extended partition is
+ * out of (disk) order, this indicates the beginning of the logical
+ * drive. The EBR will anyway be at the first sector of the extended
+ * partition, for the first logical drive.
+ */
+ uint32_t abs_secnum;
+
+ /*
+ * Offset of the logical drive from the beginning of its extended
+ * partition
+ */
+ uint32_t logdrive_offset;
+
+ /* Size of the logical drive in sectors */
+ uint32_t numsect;
+
+ /* Beginning and ending cylinders of the extended partition */
+ uint32_t begcyl, endcyl;
+
+ /*
+ * Flag to indicate if this record is to be sync'ed to disk.
+ * It takes two values : FDISK_MAJOR_WRITE and FDISK_MINOR_WRITE
+ * If it is a minor write, there is no need to update the information
+ * in the kernel structures. Example of a minor write is correction of
+ * a corrupt boot signature.
+ */
+ int modified;
+
+ /*
+ * This pointer points to the next extended partition in the order
+ * found on disk.
+ */
+ struct logical_drive *next;
+
+ /*
+ * This pointer points to the next extended partition in a sorted list
+ * sorted in the ascending order of their beginning cylinders.
+ */
+ struct logical_drive *sorted_next;
+
+} logical_drive_t;
+
+typedef struct fdisk_disk_geom {
+ ushort_t phys_cyl;
+ ushort_t phys_sec;
+ ushort_t phys_heads;
+ ushort_t alt_cyl;
+ ushort_t virt_cyl;
+ ushort_t virt_sec;
+ ushort_t virt_heads;
+ ushort_t sectsize;
+} fdisk_disk_geom_t;
+
+typedef struct ext_part
+{
+ /* Structure holding geometry information about the device */
+ fdisk_disk_geom_t disk_geom;
+
+ struct ipart *mtable;
+
+ char device_name[PATH_MAX];
+
+ int dev_fd;
+
+ int op_flag;
+
+ /*
+ * Head of the in memory structure (singly linked list) of extended
+ * partition information.
+ */
+ logical_drive_t *ld_head;
+ logical_drive_t *sorted_ld_head;
+
+ /* Beginning cylinder of the extended partition */
+ uint32_t ext_beg_cyl;
+
+ /* Ending cylinder of the extended partition */
+ uint32_t ext_end_cyl;
+
+ /* Beginning sector of the extended partition */
+ uint32_t ext_beg_sec;
+
+ /* Ending sector of the extended partition */
+ uint32_t ext_end_sec;
+
+ /* Count of the number of logical drives in the extended partition */
+ int logical_drive_count;
+
+ /*
+ * Flag to keep track of the update to be made to the Extended Boot
+ * Record (EBR) when all logical drives are deleted. The EBR is filled
+ * with zeroes in such a case.
+ */
+ int first_ebr_is_null;
+
+ /*
+ * Flag to indicate corrupt logical drives. Can happen when a partition
+ * manager creates an extended partition and does not null the first EBR
+ * or when important ondisk structures are overwritten by a bad program
+ */
+ int corrupt_logical_drives;
+
+ /*
+ * The boot block signature 0xAA55 might not be found on some of the
+ * EBRs. ( Even though the rest of the data might be good )
+ * The following array is used to store the list of such logical drive
+ * numbers.
+ */
+ uchar_t invalid_bb_sig[MAX_EXT_PARTS];
+
+ /*
+ * Can add a "next" pointer here in case support for multiple
+ * extended partitions becomes the standard someday.
+ *
+ * struct ext_part *next;
+ */
+} ext_part_t;
+
+#define fdisk_get_logical_drive_count(epp) ((epp)->logical_drive_count)
+#define fdisk_corrupt_logical_drives(epp) ((epp)->corrupt_logical_drives)
+#define fdisk_get_ext_beg_cyl(epp) ((epp)->ext_beg_cyl)
+#define fdisk_get_ext_end_cyl(epp) ((epp)->ext_end_cyl)
+#define fdisk_get_ext_beg_sec(epp) ((epp)->ext_beg_sec)
+#define fdisk_get_ext_end_sec(epp) ((epp)->ext_end_sec)
+#define fdisk_get_ld_head(epp) ((epp)->ld_head)
+#define fdisk_is_solaris_part(id) (((id) == SUNIXOS) || ((id) == SUNIXOS2))
+#define fdisk_is_dos_extended(id) (((id) == EXTDOS) || ((id) == FDISK_EXTLBA))
+
+extern int fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start,
+ off_t *lsm_offset);
+extern int libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab,
+ int opflag);
+extern int libfdisk_reset(ext_part_t *epp);
+extern void libfdisk_fini(ext_part_t **epp);
+extern int fdisk_ext_find_first_free_sec(ext_part_t *epp,
+ uint32_t *first_free_sec);
+extern uint32_t fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec);
+extern int fdisk_ext_part_exists(ext_part_t *epp);
+extern int fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec,
+ uint32_t offset, uint32_t numsec);
+extern int fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl,
+ uint32_t *begsec);
+extern int fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec,
+ uint32_t *numsec);
+extern int fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid,
+ uint32_t *begsec, uint32_t *numsec);
+extern int fdisk_commit_ext_part(ext_part_t *epp);
+extern void fdisk_change_logical_drive_id(ext_part_t *epp, int pno,
+ uchar_t partid);
+extern void fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec,
+ uint32_t endsec, uchar_t partid);
+extern void fdisk_delete_logical_drive(ext_part_t *epp, int pno);
+extern int fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect);
+extern int fdisk_delete_ext_part(ext_part_t *epp);
+extern int fdisk_get_disk_geom(ext_part_t *epp, int type, int what);
+extern int fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr);
+extern int fdisk_mounted_logical_drives(ext_part_t *epp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H_ */
diff --git a/usr/src/lib/libfdisk/i386/llib-lfdisk b/usr/src/lib/libfdisk/i386/llib-lfdisk
new file mode 100644
index 0000000000..31d191b1a5
--- /dev/null
+++ b/usr/src/lib/libfdisk/i386/llib-lfdisk
@@ -0,0 +1,31 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * usr/src/lib/libfdisk/llib-lfdisk
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libfdisk.h>
diff --git a/usr/src/lib/libfdisk/i386/mapfile-vers b/usr/src/lib/libfdisk/i386/mapfile-vers
new file mode 100644
index 0000000000..8e769bb706
--- /dev/null
+++ b/usr/src/lib/libfdisk/i386/mapfile-vers
@@ -0,0 +1,51 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+SUNWprivate_1.1 {
+ global:
+ fdisk_ext_validate_part_start;
+ fdisk_commit_ext_part;
+ fdisk_ext_find_last_free_sec;
+ fdisk_ext_find_first_free_sec;
+ fdisk_delete_logical_drive;
+ fdisk_add_logical_drive;
+ fdisk_is_linux_swap;
+ fdisk_get_disk_geom;
+ fdisk_init_ext_part;
+ fdisk_get_solaris_part;
+ fdisk_get_part_info;
+ fdisk_invalid_bb_sig;
+ libfdisk_init;
+ libfdisk_reset;
+ libfdisk_fini;
+ fdisk_validate_logical_drive;
+ fdisk_change_logical_drive_id;
+ fdisk_delete_ext_part;
+ fdisk_ext_part_exists;
+ fdisk_mounted_logical_drives;
+ local:
+ *;
+};