usr/src/lib/liborchestrator/disk_parts.c
author Alasdair Lumsden <al@everycity.co.uk>
Sat, 10 Sep 2011 02:50:50 +0000
branchoi_151a
changeset 1430 398513b48d4c
parent 836 0707c84bc450
permissions -rw-r--r--
1490 oi_151a installer Territory dropdown contains blank entries

/*
 * 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 (c) 2010, Oracle and/or its affiliates.  All rights reserved.
 */

#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>

#include "orchestrator_private.h"
#ifdef	__sparc
#define	fdisk_is_dos_extended(p) (B_FALSE)
#else
#include <libfdisk.h>
#endif

/*
 * fdisk(1m) rsect - must be space before logical partition
 */
#define	LOGICAL_PARTITION_PAD (63)

/*
 * shorthand method for finding end of ith partition
 */
#define	PARTITION_END(i) \
	(sorted_parts[(i)].partition_offset_sec + \
	sorted_parts[(i)].partition_size_sec)

static struct free_region {
	uint64_t free_offset;
	uint64_t free_size;
};

boolean_t	whole_disk = B_FALSE; /* assume existing partition */

static void mark_for_deletion_by_index(int);
static void delete_all_logical_partitions(void);

/* free space management */
static struct free_region free_space_table[OM_NUMPART + 1];
static int n_fragments = 0;
static struct {
	int		partition_id;
	uint64_t	partition_offset_sec;
	uint64_t	partition_size_sec;
} sorted_parts[OM_NUMPART];
static int n_sorted_parts = 0;
/*
 * sorted_parts and n_sorted_parts are used only for a temporary sort of the
 * partitions according to starting offset when building the free space table
 */
static struct free_region *find_unused_region_of_size(uint64_t, boolean_t);
static void insertion_sort_partition_info(partition_info_t *);
static void sort_used_regions(boolean_t);
static boolean_t build_free_space_table(boolean_t);
static void append_free_space_table(uint64_t, uint64_t);
static struct free_region *find_largest_free_region(boolean_t);
static struct free_region *find_free_region_best_fit(uint64_t, boolean_t);

/* logging */
static void log_partition_map(void);
static void log_used_regions(void);
static void log_free_space_table(void);

static partition_info_t *get_extended_partition_info(disk_parts_t *);

/* ----------------- definition of private functions ----------------- */

/*
 * is_resized_partition
 * This function checks if partition size changed
 *
 * Input:	pold, pnew - pointer to the old and new partition entries
 *
 * Return:	B_TRUE - partition was resized
 *		B_FALSE - partition size not changed
 */

static boolean_t
is_resized_partition(partition_info_t *pold, partition_info_t *pnew)
{
	return (pold->partition_size != pnew->partition_size);
}


/*
 * is_changed_partition
 * This function checks if partition changed. It means that
 * [1] size changed OR
 * [2] type changed for used partition (size is not 0)
 *
 * Input:	pold, pnew - pointer to the old and new partition entries
 *
 * Return:	B_TRUE - partition was resized
 *		B_FALSE - partition size not changed
 */

static boolean_t
is_changed_partition(partition_info_t *pold, partition_info_t *pnew)
{
	return (is_resized_partition(pold, pnew) ||
	    (pold->partition_type != pnew->partition_type &&
	    pnew->partition_size != 0));
}


/*
 * is_deleted_partition
 * This function checks if partition was deleted
 *
 * Input:	pold, pnew - pointer to the old and new partition entries
 *
 * Return:	B_TRUE - partition was deleted
 *		B_FALSE - partition was not deleted
 */

static boolean_t
is_deleted_partition(partition_info_t *pold, partition_info_t *pnew)
{
	return (pold->partition_size != 0 && pnew->partition_size == 0);
}


/*
 * is_created_partition
 * This function checks if partition was created
 *
 * Input:	pold, pnew - pointer to the old and new partition entries
 *
 * Return:	B_TRUE - partition was created
 *		B_FALSE - partition already existed
 */

static boolean_t
is_created_partition(partition_info_t *pold, partition_info_t *pnew)
{
	return (pold->partition_size == 0 && pnew->partition_size != 0);
}


/*
 * is_used_partition
 * This function checks if partition_info_t structure
 * describes used partition entry.
 *
 * Entry is considered to be used if
 * [1] partition size is greater than 0
 * [2] type is not set to unused - id 0 or 100
 *
 * Input:	pentry - pointer to the partition entry
 *
 * Return:	B_TRUE - partition entry is in use
 *		B_FALSE - partition entry is empty
 */

boolean_t
is_used_partition(partition_info_t *pentry)
{
	return (pentry->partition_type != 0 &&
	    pentry->partition_type != UNUSED);
}

/*
 * partition_index_by_id
 * This function checks if partition number (1-4) is in use
 *
 * Input:	ipno - partition number
 *
 * Return:	index to partition table (0-3)
 *		-1 not found or not in use
 */

static int
partition_index_by_id(int ipno)
{
	partition_info_t *pinfo;
	int ipart;

	assert(committed_disk_target != NULL);
	assert(committed_disk_target->dparts != NULL);

	pinfo = committed_disk_target->dparts->pinfo;
	for (ipart = 0; ipart < OM_NUMPART; ipart++, pinfo++) {
		if (pinfo->partition_id == ipno && is_used_partition(pinfo))
			return (ipart);
	}
	return (-1);
}

/*
 * set_partition_unused
 * This function sets partition entry as unused
 *
 * Input:	pentry - pointer to the partition entry
 */

static void
set_partition_unused(partition_info_t *pentry)
{
	bzero(pentry, sizeof (*pentry));
	pentry->partition_type = UNUSED;
	pentry->partition_size = 0;
	pentry->partition_size_sec = 0;
}

/*
 * get_first_used_partition
 * This function will search array of partition_info_t structures
 * and will find the first used entry
 *
 * see is_used_partition() for how empty partition is defined
 *
 * Input:	pentry - pointer to the array of partition entries
 *
 * Output:	None
 *
 * Return:	>=0	- index of first used partition entry
 *		-1	- array contains only empty entries
 */

static int
get_first_used_partition(partition_info_t *pentry)
{
	int i;

	for (i = 0; i < OM_NUMPART; i++) {
		if (is_used_partition(&pentry[i]))
			return (i);
	}

	return (-1);
}

/*
 * get_last_used_partition
 * This function will search array of partition_info_t structures
 * and will find the last used entry
 *
 * see is_used_partition() for how empty partition is defined
 *
 * Input:	pentry - pointer to the array of partition entries
 *
 * Output:	None
 *
 * Return:	>=0	- index of first used partition entry
 *		-1	- array contains only empty entries
 */

static int
get_last_used_partition(partition_info_t *pentry, int index)
{
	int i;
	int highest = -1;
	int ret = -1;

	if (IS_LOG_PAR(index + 1)) {
		for (i = OM_NUMPART - 1; i >= FD_NUMPART; i--) {
			if (is_used_partition(&pentry[i]) &&
			    pentry[i].partition_order > highest) {
				ret = i;
				highest = pentry[i].partition_order;
			}
		}
	} else {
		for (i = FD_NUMPART - 1; i >= 0; i--) {
			if (is_used_partition(&pentry[i]) &&
			    pentry[i].partition_order > highest) {
				ret = i;
				highest = pentry[i].partition_order;
			}
		}
	}

	return (ret);
}

/*
 * get_next_used_partition
 * This function will search array of partition_info_t structures
 * and will find the next used entry
 *
 * Input:	pentry - pointer to the array of partition entries
 *		current - current index
 *
 * Output:	None
 *
 * Return:	>=0	- index of next used partition entry
 *		-1	- no more used entries
 */

static int
get_next_used_partition(partition_info_t *pentry, int current)
{
	int i;
	int next_order;

	/* Starting at current partition_order + 1 */
	/* cycle through all partitions to find */
	/* first used partition after this one */
	for (next_order = pentry[current].partition_order + 1;
	    next_order <= OM_NUMPART;
	    next_order++) {
		for (i = 0; i < OM_NUMPART; i++) {
			if (is_used_partition(&pentry[i]) &&
			    pentry[i].partition_order == next_order) {
				return (i);
			}
		}
	}

	return (-1);
}


/*
 * get_previous_used_partition
 * This function will search array of partition_info_t structures
 * and will find the previous used entry in terms of order on disk
 *
 * Input:	pentry - pointer to the array of partition entries
 *		current - current index
 *
 * Output:	None
 *
 * Return:	>=0	- index of next used partition entry
 *		-1	- no more used entries
 */

static int
get_previous_used_partition(partition_info_t *pentry, int current)
{
	int i;

	assert(pentry[current].partition_order > 1);

	if (IS_LOG_PAR(current + 1)) {
		for (i = FD_NUMPART; i < OM_NUMPART; i++)
			if (pentry[current].partition_order - 1 ==
			    pentry[i].partition_order)
				return (i);
	} else {
		for (i = 0; i < FD_NUMPART; i++)
			if (pentry[current].partition_order - 1 ==
			    pentry[i].partition_order)
				return (i);
	}
	return (-1);
}


/*
 * is_first_used_partition
 * This function checks if index points
 * to the first used partition entry.
 *
 * Input:	pentry	- pointer to the array of partition entries
 *		index	- index of partition to be checked
 *
 * Return:	B_TRUE - partition entry is in use
 *		B_FALSE - partition entry is empty
 */

static boolean_t
is_first_used_partition(partition_info_t *pentry, int index)
{
	return (pentry[index].partition_order ==
	    (IS_LOG_PAR(index + 1) ? FD_NUMPART + 1 : 1));
}

/*
 * is_last_used_partition
 * This function checks if index points to
 * the last used partition entry.
 *
 * Input:	pentry	- pointer to the array of partition entries
 *		index	- index of partition to be checked
 *
 * Return:	B_TRUE - partition entry is in use
 *		B_FALSE - partition entry is empty
 */

static boolean_t
is_last_used_partition(partition_info_t *pentry, int index)
{
	return (get_last_used_partition(pentry, index) == index ?
	    B_TRUE : B_FALSE);
}

static void
log_partition_map()
{
	int ipar;
	partition_info_t *pinfo;

	pinfo = committed_disk_target->dparts->pinfo;
	om_debug_print(OM_DBGLVL_INFO,
	    "id\ttype\torder\tsector offset\tsize in sectors\n");
	for (ipar = 0; ipar < OM_NUMPART; ipar++) {
		om_debug_print(OM_DBGLVL_INFO, "%d\t%02X\t%2d\t%lld\t%lld\n",
		    pinfo[ipar].partition_id,
		    pinfo[ipar].partition_type,
		    pinfo[ipar].partition_order,
		    pinfo[ipar].partition_offset_sec,
		    pinfo[ipar].partition_size_sec);
	}
}

/*
 * from install target disk, find extended partition struct pointer
 * returns NULL if no extended partition defined
 */
partition_info_t *
get_extended_partition_info(disk_parts_t *ipinfo)
{
	int ipar;
	partition_info_t *pinfo;

	if (ipinfo == NULL) {
		if (committed_disk_target == NULL ||
		    committed_disk_target->dparts == NULL ||
		    committed_disk_target->dparts->pinfo == NULL)
			return (B_FALSE);
		/*
		 * extended partition could be of several different types
		 */
		pinfo = committed_disk_target->dparts->pinfo;
	} else
		pinfo = ipinfo->pinfo;
	for (ipar = 0; ipar < OM_NUMPART; ipar++, pinfo++)
		if (is_used_partition(pinfo) &&
		    fdisk_is_dos_extended(pinfo->partition_type))
			return (pinfo);
	return (NULL);	/* no extended partition found */
}

/*
 * resize partition using all critical struct elements
 */
static void
resize_partition(partition_info_t *p_new, int offset)
{
	/* push starting offset ahead */
	p_new->partition_offset_sec += offset;
	p_new->partition_offset += (offset/BLOCKS_TO_MB);
	/* subtract from region size */
	p_new->partition_size_sec -= offset;
	p_new->partition_size -= (offset/BLOCKS_TO_MB);
}

/*
 * adjust start of logical partition to make 63 unused sectors before it.
 *
 * p_new - pointer to list of partitions for the new configuration
 * offset - offset into p_new to represent partition to adjust
 *	assumed to be partition number - 1 per convention
 * extpinfo - partition information for the extended partition
 *
 * assumes that partition_order element is set to indicate order of partitions
 */
static void
logical_start_adjust(partition_info_t *p_new, int offset,
    partition_info_t *extpinfo)
{
	partition_info_t	*p_prev;
	int			previous;
	uint64_t		first_free_sector;
	int64_t			diff;

	if (offset < FD_NUMPART)
		return;	/* consider logical partitions only */

	if (is_first_used_partition(p_new, offset)) {
		/*
		 * start counting from start of extended partition
		 */
		assert(extpinfo != NULL);
		first_free_sector = extpinfo->partition_offset_sec;
	} else {
		/*
		 * start counting from end of previous partition
		 */
		previous = get_previous_used_partition(p_new, offset);
		assert(previous != -1);
		p_prev = &p_new[previous];

		first_free_sector =
		    p_prev->partition_offset_sec +
		    p_prev->partition_size_sec;
	}

	diff = p_new[offset].partition_offset_sec - first_free_sector;
	if (diff < LOGICAL_PARTITION_PAD) {
		int padsize = LOGICAL_PARTITION_PAD - diff;

		resize_partition(&p_new[offset], padsize);
		om_debug_print(OM_DBGLVL_INFO,
		    "Logical partition %d (%02X) starting sector moved forward "
		    "%ld sectors. (new starting sector %lld)\n",
		    offset + 1, p_new[offset].partition_type, padsize,
		    p_new[offset].partition_offset_sec);
	}
}

/*
 * given order in partition table and the table,
 * return index for partition
 */
static int
map_order_to_index(int order, partition_info_t *pentry)
{
	int i;

	for (i = 0; i < OM_NUMPART; i++)
		if (is_used_partition(&pentry[i]) &&
		    pentry[i].partition_order == order)
			return (i);
	return (-1);
}

/*
 * trim end of partition within allowable limits.
 *
 * given partition table, index into it, extended partition info,
 *	and disk target info:
 *	- primary - limit by disk size
 *	- logical - limit by extended partition size
 *
 * For a primary partition, the limit is the end of the disk
 * For a logical partition, the limit is the end of the extended partition
 */
static void
partition_end_adjust(partition_info_t *p_new, int offset,
    partition_info_t *extpinfo, disk_target_t *dt)
{
	uint64_t	first_sector_after;
	int64_t		diff;

	/*
	 * find offset of first sector beyond end of disk or extended partition.
	 * Trim last partition on disk/extended partition so that
	 * it doesn't exceed this limit
	 */
	first_sector_after = (offset < FD_NUMPART ? dt->dinfo.disk_size_sec :
	    extpinfo->partition_offset_sec + extpinfo->partition_size_sec);

	/*
	 * calculate difference between maximum and actual end
	 */
	diff = p_new->partition_offset_sec +
	    p_new->partition_size_sec - first_sector_after;

	if (diff <= 0)
		return;
	/*
	 * if overextended, trim size to fit
	 */
	p_new->partition_size_sec -= diff;
	om_set_part_mb_size_from_sec(p_new);
	om_debug_print(OM_DBGLVL_INFO,
	    "Partition %d (%02X) size trimmed by %lld sectors "
	    "(new size %lld)\n",
	    offset + 1, p_new->partition_type, diff,
	    p_new->partition_size_sec);
}

/*
 * from install target disk, find Solaris partition
 * returns B_TRUE if Solaris partition is in logical partition
 * returns B_FALSE if not or if Solaris partition was not found
 */
boolean_t
om_install_partition_is_logical()
{
	int ipar;
	partition_info_t *pinfo;

	if (committed_disk_target == NULL ||
	    committed_disk_target->dparts == NULL ||
	    committed_disk_target->dparts->pinfo == NULL)
		return (B_FALSE);
	/*
	 * If the partition is SOLARIS2 or SOLARIS and the content has
	 * been confirmed not to be Linux swap,
	 * if the fdisk partition number is > FD_NUMPART,
	 * the partition is a logical partition
	 */
	pinfo = committed_disk_target->dparts->pinfo;
	for (ipar = 0; ipar < OM_NUMPART; ipar++, pinfo++) {
		if (!is_used_partition(pinfo))
			continue;
		if (pinfo->partition_type == SUNIXOS2 ||
		    (pinfo->partition_type == SUNIXOS &&
		    pinfo->content_type != OM_CTYPE_LINUXSWAP))
			return (pinfo->partition_id > FD_NUMPART);
	}
	return (B_FALSE);	/* no Solaris partition found */
}

/* ----------------- definition of public functions ----------------- */

/*
 * om_get_disk_partition_info
 * This function will return the partition information of the specified disk.
 * Input:	om_handle_t handle - The handle returned by
 *		om_initiate_target_discovery()
 * 		char *diskname - The name of the disk
 * Output:	None
 * Return:	disk_parts_t * - The fdisk partition information for
 *		the disk with diskname will be 	returned. The space will be
 *		allocated here linked and returned to the caller.
 *		NULL - if the partition data can't be returned.
 */
/*ARGSUSED*/
disk_parts_t *
om_get_disk_partition_info(om_handle_t handle, char *diskname)
{
	disk_parts_t	*dp;

	/*
	 * Validate diskname
	 */
	if (diskname == NULL || diskname[0] == '\0') {
		om_set_error(OM_BAD_DISK_NAME);
		return (NULL);
	}

	/*
	 * If the discovery is not yet completed or not started,
	 * return error
	 */
	if (!disk_discovery_done) {
		om_set_error(OM_DISCOVERY_NEEDED);
		return (NULL);
	}

	if (system_disks == NULL) {
		om_set_error(OM_NO_DISKS_FOUND);
		return (NULL);
	}

	/*
	 * Find the disk from the cache using the diskname
	 */
	dp = find_partitions_by_disk(diskname);
	if (dp == NULL) {
		return (NULL);
	}

	/*
	 * copy the data
	 */
	return (om_duplicate_disk_partition_info(handle, dp));
}

/*
 * om_free_disk_partition_info
 * This function will free up the disk information data allocated during
 * om_get_disk_partition_info().
 * Input:	om_handle_t handle - The handle returned by
 *		om_initiate_target_discovery()
 *		disk_parts_t *dinfo - The pointer to disk_parts_t. Usually
 *		returned by om_get_disk_partition_info().
 * Output:	None.
 * Return:	None.
 */
/*ARGSUSED*/
void
om_free_disk_partition_info(om_handle_t handle, disk_parts_t *dpinfo)
{
	if (dpinfo == NULL) {
		return;
	}

	local_free_part_info(dpinfo);
}

/*
 * om_validate_and_resize_disk_partitions
 * This function will check whether the the partition information of the
 * specified disk is valid. If the reuqested space can't be allocated, then
 * suggested partition allocation will be returned.
 * Input:	disk_parts_t *dpart
 *		partition_allocation_scheme
 *			if not set for AI, assume allocation for GUI
 * Output:	None
 * Return:	disk_parts_t *, with the corrected values. If the sizes
 *		are valid, the return disk_parts_t structure will have same
 *		data as the the	disk_parts_t structure passed as the input.
 *		NULL, if the partition data is invalid.
 * Note:	If the partition is not valid, the om_errno will be set to
 *		the actual error condition. The error information can be
 *		obtained by calling om_get_errno().
 *
 *		This function checks:
 *		- Whether the total partition space is < disk space
 *		- There is enough space to create a new partition
 *		- for the GUI allocation, if there are holes between partitions,
 *		can the new partition fit into any of the holes?
 *		GUI_allocation should serve the use case of the original
 *		GUI in which only partition sizes are user-specified and
 *		starting offsets are unset.
 *		For GUI only, dpart element partition_order must contain
 *			order of the partitions by disk sector:
 *			- primary partitions are ordered by starting offset 1:4
 *			- logical partitions are ordered by starting offset 5:36
 *
 *		The first cylinder will be reserved for Solaris
 */
/*ARGSUSED*/
disk_parts_t *
om_validate_and_resize_disk_partitions(om_handle_t handle, disk_parts_t *dpart,
	partition_allocation_scheme_t partition_allocation_scheme)
{
	boolean_t	changed = B_FALSE;
	disk_target_t	*dt;
	disk_parts_t	*dp, *new_dp;
	int		i, j;
	partition_info_t *extpinfo;
	int		nparts;

	/*
	 * validate the input
	 */
	if (dpart == NULL) {
		om_set_error(OM_INVALID_DISK_PARTITION);
		return (NULL);
	}

	/*
	 * Is the target discovery completed?
	 */
	if (!disk_discovery_done) {
		om_set_error(OM_DISCOVERY_NEEDED);
		return (NULL);
	}

	if (system_disks  == NULL) {
		om_set_error(OM_NO_DISKS_FOUND);
		return (NULL);
	}

	if (dpart->disk_name == NULL) {
		om_set_error(OM_INVALID_DISK_PARTITION);
		return (NULL);
	}

	/*
	 * Find the disk from the cache using the diskname
	 */
	dt = find_disk_by_name(dpart->disk_name);
	if (dt == NULL) {
		om_set_error(OM_BAD_DISK_NAME);
		return (NULL);
	}

	/*
	 * Create the disk_parts_t structure to be returned
	 */
	new_dp = om_duplicate_disk_partition_info(handle, dpart);
	if (new_dp == NULL) {
		return (NULL);
	}

	/*
	 * find an extended partition if it exists
	 */
	extpinfo = get_extended_partition_info(new_dp);
	nparts = (extpinfo == NULL ? FD_NUMPART:OM_NUMPART);

	/*
	 * check if "whole disk" path was selected. It is true if
	 * both following conditions are met:
	 * [1] Only first partition is defined. Rest are left unused
	 *	(size ==0 &&, type == 100)
	 * [2] First partition is Solaris2 and occupies all available space
	 */

	whole_disk = B_TRUE;

	if ((new_dp->pinfo[0].partition_size != dt->dinfo.disk_size) ||
	    (new_dp->pinfo[0].partition_type != SUNIXOS2)) {
		om_debug_print(OM_DBGLVL_INFO, "entire disk not used\n");
		whole_disk = B_FALSE;
	}

	for (i = 1; i < nparts && whole_disk == B_TRUE; i++) {
		if ((new_dp->pinfo[i].partition_size != 0) ||
		    is_used_partition(&new_dp->pinfo[i])) {
			om_debug_print(OM_DBGLVL_INFO,
			    "Entire disk not used\n");
			whole_disk = B_FALSE;
		}
	}

	if (whole_disk) {
		return (new_dp);
	}

	/*
	 * if target disk is empty (there are no partitions defined),
	 * create dummy partition configuration. This allows using
	 * unified code for dealing with partition changes.
	 */

	if (dt->dparts == NULL) {
		om_log_print("disk currently doesn't contain any partition\n");

		dp = om_duplicate_disk_partition_info(handle, dpart);

		if (dp == NULL) {
			om_log_print("Couldn't duplicate partition info\n");
			return (NULL);
		}

		(void) memset(dp->pinfo, 0, sizeof (partition_info_t) *
		    OM_NUMPART);
	} else
		dp = dt->dparts;

	/*
	 * Compare the size and partition type (for used partition)
	 * of each partition to decide whether any of them was changed.
	 */

	for (i = 0; i < nparts; i++) {
		if (is_changed_partition(&dp->pinfo[i], &new_dp->pinfo[i])) {
			om_log_print("disk partition info changed\n");
			changed = B_TRUE;
			break;
		}
	}

	if (!changed) {
		/*
		 * No change in the partition table.
		 */
		om_log_print("disk partition info not changed\n");

		/* release partition info if allocated if this function */
		if (dt->dparts == NULL)
			local_free_part_info(dp);

		return (new_dp);
	}

	/*
	 * Finally calculate sector geometry information for changed
	 * partitions
	 */

	om_debug_print(OM_DBGLVL_INFO,
	    "Partition LBA information before recalculation\n");

	for (i = 0; i < nparts; i++) {
		om_debug_print(OM_DBGLVL_INFO,
		    "[%d] pid=%d ord=%d, type=%02X, beg=%llu(%lu MiB), "
		    "size=%llu(%lu MiB)\n", i,
		    new_dp->pinfo[i].partition_id,
		    new_dp->pinfo[i].partition_order,
		    new_dp->pinfo[i].partition_type,
		    new_dp->pinfo[i].partition_offset_sec,
		    new_dp->pinfo[i].partition_offset,
		    new_dp->pinfo[i].partition_size_sec,
		    new_dp->pinfo[i].partition_size);
	}

	for (j = 0; j < nparts; j++) {
		partition_info_t	*p_orig;
		partition_info_t	*p_new;

		if (partition_allocation_scheme == GUI_allocation) {
			/*
			 * for GUI, look at all partitions by order on disk
			 */
			i = map_order_to_index(j + 1, &new_dp->pinfo[0]);
			if (i == -1)
				continue;
			/*
			 * compare partitions by partition number
			 */
			p_new = &new_dp->pinfo[i];
			p_orig = &dp->pinfo[p_new->partition_id - 1];
		} else {
			/*
			 * for AI, look at all partitions by index
			 * regardless of order on disk
			 */
			i = j;
			p_orig = &dp->pinfo[i];
			p_new = &new_dp->pinfo[i];
		}

		if (p_orig->partition_type != 0 || p_new->partition_type != 0) {
			om_debug_print(OM_DBGLVL_INFO,
			    "examining orig partition [%d] pid=%d ord=%d, type"
			    "=%02X, beg=%llu(%lu MiB), size=%llu(%lu MiB)\n",
			    p_orig->partition_id - 1,
			    p_orig->partition_id,
			    p_orig->partition_order,
			    p_orig->partition_type,
			    p_orig->partition_offset_sec,
			    p_orig->partition_offset,
			    p_orig->partition_size_sec,
			    p_orig->partition_size);
			om_debug_print(OM_DBGLVL_INFO,
			    "examining new  partition [%d] pid=%d ord=%d, type"
			    "=%02X, beg=%llu(%lu MiB), size=%llu(%lu MiB)\n",
			    p_new->partition_id - 1,
			    p_new->partition_id,
			    p_new->partition_order,
			    p_new->partition_type,
			    p_new->partition_offset_sec,
			    p_new->partition_offset,
			    p_new->partition_size_sec,
			    p_new->partition_size);
		}

		/*
		 * If the partition was not resized, skip it, since
		 * other modifications (change of type) don't require
		 * offset & size recalculation
		 */

		if (!is_resized_partition(p_orig, p_new)) {
			/*
			 * retain existing data for later calculations
			 */
			om_debug_print(OM_DBGLVL_INFO,
			    "Partition pid=%d, ord=%d, type=%02X "
			    "was NOT resized\n",
			    p_orig->partition_id,
			    p_orig->partition_order,
			    p_orig->partition_type);
			p_new->partition_size_sec = p_orig->partition_size_sec;
			p_new->partition_offset_sec =
			    p_orig->partition_offset_sec;
			p_new->partition_offset = p_orig->partition_offset;
			continue;
		}

		om_debug_print(OM_DBGLVL_INFO,
		    "Partition pid=%d, ord=%d, type=%02X was resized: "
		    "old = %d, new = %d\n",
		    p_orig->partition_id,
		    p_orig->partition_order,
		    p_orig->partition_type,
		    p_orig->partition_size,
		    p_new->partition_size);

		/*
		 * If partition is deleted (marked as "UNUSED"),
		 * clear offset and size
		 */

		if (is_deleted_partition(p_orig, p_new)) {
			om_debug_print(OM_DBGLVL_INFO,
			    "Partition pid=%d, ord=%d, type=%02X "
			    "is to be deleted\n",
			    p_orig->partition_id,
			    p_orig->partition_order,
			    p_orig->partition_type);

			p_new->partition_offset = 0;
			p_new->partition_offset_sec =
			    p_new->partition_size_sec = 0;

			/*
			 * don't clear partition_id - it is "read only"
			 * from orchestrator point of view - modified by GUI
			 */
			continue;
		}

		if (is_created_partition(p_orig, p_new)) {
			om_debug_print(OM_DBGLVL_INFO,
			    "Partition pid=%d, ord=%d, type=%02X "
			    "is to be created\n",
			    p_new->partition_id,
			    p_new->partition_order,
			    p_new->partition_type);

			if (partition_allocation_scheme != GUI_allocation)
				om_debug_print(OM_DBGLVL_INFO,
				    "Partition offset=%lld, "
				    "size=%lld is created\n",
				    p_new->partition_offset_sec,
				    p_new->partition_size_sec);
		}

		if (partition_allocation_scheme == GUI_allocation) {
			/*
			 * Assume initial GUI support
			 *
			 * Calculate sector offset information
			 *
			 * Gaps are not allowed for now - partition starts
			 * right after previous used partition
			 *
			 * If this is the first partition, it starts at the
			 * second cylinder - adjust size accordingly
			 */

			if (is_first_used_partition(new_dp->pinfo, i)) {
				/*
				 * place first partition as close to the front
				 * as possible while allowing the 1st cylinder
				 * to be used for the boot partition
				 */
				if (i < FD_NUMPART) {
					/*
					 * set offset of 1st primary partition
					 * to 2nd cylinder
					 */
					p_new->partition_offset_sec =
					    dt->dinfo.disk_cyl_size;
					p_new->partition_offset =
					    p_new->partition_offset_sec /
					    BLOCKS_TO_MB;
					/*
					 * reduce size by 1 cylinder
					 */
					p_new->partition_size -=
					    dt->dinfo.disk_cyl_size /
					    BLOCKS_TO_MB;
					om_set_part_sec_size_from_mb(p_new);
					om_debug_print(OM_DBGLVL_INFO,
					    "%d (%02X) is the first primary "
					    "partition - will start at the 2nd "
					    "cylinder (sector %lld)\n",
					    i, p_new->partition_type,
					    p_new->partition_offset_sec);
				} else {
					/*
					 * set offset for first logical
					 * partition to start of extended
					 * partition
					 */
					p_new->partition_offset_sec =
					    extpinfo->partition_offset_sec;
					p_new->partition_offset =
					    p_new->partition_offset_sec /
					    BLOCKS_TO_MB;
					om_debug_print(OM_DBGLVL_INFO,
					    "[%d] (%02X) is the first logical "
					    "partition\n",
					    i, p_new->partition_type);
				}
			} else {
				partition_info_t	*p_prev;
				int			previous;

				previous = get_previous_used_partition(
				    new_dp->pinfo, i);

				/*
				 * previous should be always found, since check
				 * for "first used" was done in if() statement
				 * above
				 */

				assert(previous != -1);
				p_prev = &new_dp->pinfo[previous];

				p_new->partition_offset_sec =
				    p_prev->partition_offset_sec +
				    p_prev->partition_size_sec;
				p_new->partition_offset =
				    p_new->partition_offset_sec/BLOCKS_TO_MB;
			}
			/*
			 * user changed partition size in GUI or size
			 * was adjusted above.
			 * Calculate new sector size information from megabytes
			 */

			om_set_part_sec_size_from_mb(p_new);

			/*
			 * logical partitions only, must be preceded by
			 * 63 unused sectors (see man page for fdisk).
			 * push starting offset forward if 63 sectors not unused
			 * before the starting offset
			 */

			logical_start_adjust(new_dp->pinfo, i, extpinfo);
		}

		if (partition_allocation_scheme == AI_allocation) {
			/*
			 * adjust for boot partition being in 1st cylinder
			 * special allocation for GUI not needed
			 */
			if (p_new->partition_offset_sec == 0 ||
			    p_new->partition_offset_sec ==
			    LOGICAL_PARTITION_PAD) {
				/* move from 1st to 2nd cylinder */
				resize_partition(p_new,
				    dt->dinfo.disk_cyl_size);
				om_debug_print(OM_DBGLVL_INFO,
				    "%d (%02X) is the first %spartition - will "
				    "start at the 2nd cylinder (sector %lld)\n",
				    i, p_new->partition_type,
				    IS_LOG_PAR(p_new->partition_id) ?
				    "logical ":"",
				    p_new->partition_offset_sec);
			} else if (extpinfo != NULL &&
			    IS_LOG_PAR(p_new->partition_id) &&
			    extpinfo->partition_offset_sec ==
			    p_new->partition_offset_sec) {
				/* pad for logical partition */
				resize_partition(p_new, LOGICAL_PARTITION_PAD);
				om_debug_print(OM_DBGLVL_INFO,
				    "%d (%02X) is the first partition of the "
				    "extended partition - will "
				    "start 63 sectors into extended partition "
				    "(sector %lld)\n",
				    i, p_new->partition_type,
				    p_new->partition_offset_sec);
			}
		}

		/*
		 * For the GUI:
		 *
		 * If the partition overlaps with subsequent one
		 * which is in use and that partition was not changed,
		 * adjust size accordingly.
		 *
		 * If subsequent used partition was resized as well, its
		 * offset and size will be adjusted in next step, so
		 * don't modify size of current partition.
		 *
		 * If this is the last used partition, adjust its size
		 * so that it fits into available disk space
		 */

		if (partition_allocation_scheme == GUI_allocation) {
			if (!is_last_used_partition(new_dp->pinfo, i)) {
				partition_info_t *p_next_orig, *p_next_new;
				int next;

				next = get_next_used_partition(new_dp->pinfo,
				    i);

				/*
				 * next should be always found, since check for
				 * "last used" was done in if() statement above
				 */

				assert(next != -1);
				p_next_orig = &dp->pinfo[next];
				p_next_new = &new_dp->pinfo[next];

				/*
				 * next partition was resized, adjust rather
				 * that one, leave current one as is
				 */

				if (is_resized_partition(p_next_orig,
				    p_next_new))
					continue;

				if ((p_new->partition_offset_sec +
				    p_new->partition_size_sec) >
				    p_next_new->partition_offset_sec) {
					p_new->partition_size_sec =
					    p_next_new->partition_offset_sec -
					    p_new->partition_offset_sec;
					/*
					 * ensure 63 block pad before next
					 * logical partition
					 */
					if (IS_LOG_PAR(p_new->partition_id)) {
						assert(p_new->partition_size_sec
						    > LOGICAL_PARTITION_PAD);
						p_new->partition_size_sec -=
						    LOGICAL_PARTITION_PAD;
					}
					/*
					 * partition sector size was adjusted.
					 * Recalculate size in MiB as well
					 */

					om_set_part_mb_size_from_sec(p_new);

					om_debug_print(OM_DBGLVL_INFO,
					    "Partition %d (ID=%02X) overlaps "
					    "subsequent partition, "
					    "size will be adjusted to %d MB\n",
					    i, p_new->partition_type,
					    p_new->partition_size);
				}
			}
			/*
			 * if this is the last primary or logical partition,
			 * adjust the end so that it fits onto the disk or
			 * extended partition respectively
			 */
			partition_end_adjust(p_new, i, extpinfo, dt);
		}
	}

	om_debug_print(OM_DBGLVL_INFO,
	    "Adjusted partition LBA information\n");

	for (i = 0; i < nparts; i++) {
		om_debug_print(OM_DBGLVL_INFO,
		    "[%d] pid=%d ord=%d, type=%02X, beg=%llu(%lu MiB), "
		    "size=%llu(%lu MiB)\n", i,
		    new_dp->pinfo[i].partition_id,
		    new_dp->pinfo[i].partition_order,
		    new_dp->pinfo[i].partition_type,
		    new_dp->pinfo[i].partition_offset_sec,
		    new_dp->pinfo[i].partition_offset,
		    new_dp->pinfo[i].partition_size_sec,
		    new_dp->pinfo[i].partition_size);
	}

	/* release partition info if allocated if this function */

	if (dt->dparts == NULL)
		local_free_part_info(dp);

	return (new_dp);
}


/*
 * om_duplicate_disk_partition_info
 * This function will allocate space and copy the disk_parts_t structure
 * passed as a parameter.
 * Input:	om_handle_t handle - The handle returned by
 *              om_initiate_target_discovery()
 *		disk_parts_t * - Pointer to disk_parts_t. Usually the return
 *		value from get_disk_partition_info().
 * Return:	disk_parts_t * - Pointer to disk_parts_t. Space will be
 *		allocated and the data is copied and returned.
 *		NULL, if space cannot be allocated.
 */
/*ARGSUSED*/
disk_parts_t *
om_duplicate_disk_partition_info(om_handle_t handle, disk_parts_t *dparts)
{
	disk_parts_t	*dp;

	if (dparts == NULL) {
		om_set_error(OM_BAD_INPUT);
		return (NULL);
	}

	/*
	 * Allocate space for partitions and copy data
	 */
	dp = (disk_parts_t *)calloc(1, sizeof (disk_parts_t));

	if (dp == NULL) {
		om_set_error(OM_NO_SPACE);
		return (NULL);
	}

	(void) memcpy(dp, dparts, sizeof (disk_parts_t));
	dp->disk_name = strdup(dparts->disk_name);

	return (dp);
}

/*
 * om_set_disk_partition_info
 * This function will save the disk partition information passed by the
 * caller and use it for creating disk partitions during install.
 * This function should be used in conjunction with om_perform_install
 * If om_perform_install is not called, no changes in the disk will be made.
 *
 * Input:	om_handle_t handle - The handle returned by
 *		om_initiate_target_discovery()
 * 		disk_parts_t *dp - The modified disk partitions
 * Output:	None
 * Return:	OM_SUCCESS - If the disk partition information is saved
 *		OM_FAILURE - If the data cannot be saved.
 * Note:	If the partition information can't be saved, the om_errno
 *		will be set to the actual error condition. The error
 *		information can be obtained by calling om_get_errno().
 */
/*ARGSUSED*/
int
om_set_disk_partition_info(om_handle_t handle, disk_parts_t *dp)
{
	disk_target_t	*dt;

	/*
	 * Validate the input
	 */
	if (dp == NULL || dp->disk_name == NULL) {
		om_set_error(OM_BAD_INPUT);
		return (OM_FAILURE);
	}

	/*
	 * Find the disk from the cache using the diskname
	 */
	dt = find_disk_by_name(dp->disk_name);

	if (dt == NULL) {
		return (OM_FAILURE);
	}

	if (dt->dparts == NULL) {
		/*
		 * Log the information that the disk partitions are not defined
		 * before the install started and GUI has defined the partitions
		 * and saving it with orchestrator to be used during install
		 */
		om_log_print("No disk partitions defined prior to install\n");
	}

	if (allocate_target_disk_info(&dt->dinfo) != OM_SUCCESS)
		return (OM_FAILURE);

	if (committed_disk_target->dinfo.disk_name == NULL ||
	    committed_disk_target->dinfo.vendor == NULL ||
	    committed_disk_target->dinfo.serial_number == NULL) {
		goto sdpi_return;
	}
	/*
	 * Copy the partition data from the input
	 */
	committed_disk_target->dparts =
	    om_duplicate_disk_partition_info(handle, dp);
	if (committed_disk_target->dparts == NULL) {
		goto sdpi_return;
	}
	/*
	 * finishing set partition info
	 */
	log_partition_map();
	return (OM_SUCCESS);
sdpi_return:
	free_target_disk_info();
	return (OM_FAILURE);
}

/*
 * Partition editing suite
 *
 * These functions start with a description of existing partitions
 * To find partitions for a disk:
 *	-perform Target Discovery, finding disks and partitions for the disk
 *	-get partition table for disk with om_get_disk_partition_info()
 *	-if partitions exist (not NULL), set target disk information with
 *		om_set_disk_partition_info()
 *	-if no partitions exist (NULL) , create empty partition table with
 *		om_init_disk_partition_info()
 * The partition descriptions can then be edited with:
 *	om_create_partition(), om_delete_partition()
 * When new partition configuration is complete, finalize it for TI with:
 *	om_finalize_fdisk_info_for_TI()
 * Set attribute list for TI with:
 *	om_set_fdisk_target_attrs()
 *
 * om_create_partition() - create a new partition
 * partition_size_sec - size of partition in sectors
 * partition_offset_sec - offset of beginning sector
 * use_entire_disk - if true, ignore size, offset and commit entire disk
 * only Solaris partitions are created
 * returns B_TRUE if success, B_FALSE otherwise
 */
boolean_t
om_create_partition(uint8_t partition_type, uint64_t partition_offset_sec,
    uint64_t partition_size_sec, boolean_t use_entire_disk,
    boolean_t is_log_part)
{
	partition_info_t *pinfo;
	int ipart;
	char *ptype_text = "";

	assert(committed_disk_target != NULL);
	assert(committed_disk_target->dparts != NULL);

	pinfo = committed_disk_target->dparts->pinfo;
	for (ipart = 0; ipart < OM_NUMPART; ipart++, pinfo++) {
		if (partition_offset_sec != 0 &&
		    partition_offset_sec == pinfo->partition_offset_sec &&
		    pinfo->partition_size_sec != 0) { /* part already exists */
			om_debug_print(OM_DBGLVL_ERR, "Attempting to "
			    "create partition that already exists\n");
			om_set_error(OM_ALREADY_EXISTS);
			return (B_FALSE);
		}
	}
	/* find free entry */
	if (is_log_part) { /* logical partition numbering 5-36 */
		pinfo = &committed_disk_target->dparts->pinfo[4]; /* reset */
		for (ipart = FD_NUMPART; ipart < OM_NUMPART; ipart++, pinfo++)
			if (!is_used_partition(pinfo))
				break;
		if (ipart >= OM_NUMPART) {
			om_debug_print(OM_DBGLVL_ERR, "The maximum number "
			    "of logical partitions (%d) already exist\n",
			    MAX_EXT_PARTS);
			om_debug_print(OM_DBGLVL_ERR, "No more logical "
			    "partitions may be created until some are "
			    "deleted.\n");
			om_set_error(OM_BAD_INPUT);
			return (B_FALSE);
		}
	} else { /* primary partitions number 1-4 */
		pinfo = committed_disk_target->dparts->pinfo; /* reset */
		for (ipart = 0; ipart < FD_NUMPART; ipart++, pinfo++)
			if (!is_used_partition(pinfo))
				break;
		if (ipart >= FD_NUMPART) {
			om_debug_print(OM_DBGLVL_ERR, "No more primary "
			    "partitions may be created until some are "
			    "deleted.\n");
			om_set_error(OM_BAD_INPUT);
			return (B_FALSE);
		}
	}
	/* not more than one extended partition is allowed */
	if (fdisk_is_dos_extended(partition_type) &&
	    get_extended_partition_info(NULL) != NULL) {
		om_debug_print(OM_DBGLVL_ERR,
		    "Attempt to create more than one extended partition\n");
		om_set_error(OM_ALREADY_EXISTS);
		return (B_FALSE);
	}
	if (use_entire_disk) {
		partition_offset_sec = 0;
		partition_size_sec =
		    (uint64_t)committed_disk_target->dinfo.disk_size *
		    BLOCKS_TO_MB;
	}
	/* if no starting offset, select location */
	if (partition_offset_sec == (uint64_t)-1LL) {
		struct free_region *pfree_region;

		om_debug_print(OM_DBGLVL_INFO,
		    "finding unused region of size=%s\n",
		    part_size_or_max(partition_size_sec));
		pfree_region = find_unused_region_of_size(partition_size_sec,
		    is_log_part);
		if (pfree_region == NULL) {
			om_debug_print(OM_DBGLVL_ERR,
			    "failure to find unused region of size %s\n",
			    part_size_or_max(partition_size_sec));
			om_set_error(OM_ALREADY_EXISTS);
			return (B_FALSE);
		}
		pinfo->partition_offset_sec = pfree_region->free_offset;
		if (partition_size_sec == OM_MAX_SIZE)
			partition_size_sec = pfree_region->free_size;
	} else {
		/* if size set to OM_MAX_SIZE in manifest, take entire disk */
		if (partition_size_sec == OM_MAX_SIZE)
			if (is_log_part) {
				partition_info_t *extpinfo;

				om_debug_print(OM_DBGLVL_INFO,
				    "allocating entire extended partition "
				    "for logical partition\n");
				extpinfo = get_extended_partition_info(NULL);
				if (extpinfo == NULL) {
					om_debug_print(OM_DBGLVL_ERR,
					    "system error: failed to find "
					    "extended partition definition\n");
					om_set_error(OM_NO_PARTITION_FOUND);
					return (B_FALSE);
				}
				partition_size_sec =
				    (uint64_t)extpinfo->partition_size *
				    BLOCKS_TO_MB;
			} else
				partition_size_sec =
				    (uint64_t)
				    committed_disk_target->dinfo.disk_size *
				    BLOCKS_TO_MB;
		pinfo->partition_offset_sec = partition_offset_sec;
	}
	/* check type of partition to create - fdisk supported? */
	switch (partition_type) {
	case SUNIXOS2:		/* Solaris */
		ptype_text = "Solaris2 ";
		pinfo->content_type = OM_CTYPE_SOLARIS;
		break;
	case SUNIXOS:		/* Solaris UNIX partition */
		ptype_text = "Solaris ";
		pinfo->content_type = OM_CTYPE_SOLARIS;
		break;
	case FDISK_LINUX:	/* Linux */
	case FDISK_LINUXDNAT:	/* Linux native (sharing disk with DRDOS) */
	case FDISK_LINUXNAT:	/* Linux native */
		pinfo->content_type = OM_CTYPE_UNKNOWN; /* content unknown */
		ptype_text = "Linux ";
		break;
	case FDISK_LINUXDSWAP:	/* Linux swap (sharing disk w/ DRDOS) */
		pinfo->content_type = OM_CTYPE_UNKNOWN; /* contnet unknown */
		ptype_text = "Linux swap ";
		break;
	/* any other types that fdisk supports */
	case DOSOS12:		/* DOS partition, 12-bit FAT */
	case DOSOS16:		/* DOS partition, 16-bit FAT */
	case EXTDOS:		/* EXT-DOS partition */
	case DOSHUGE:		/* Huge DOS partition  > 32MB */
	case FDISK_IFS:		/* Installable File System (IFS): HPFS & NTFS */
	case FDISK_AIXBOOT:	/* AIX Boot */
	case FDISK_AIXDATA:	/* AIX Data */
	case FDISK_OS2BOOT:	/* OS/2 Boot Manager */
	case FDISK_WINDOWS:	/* Windows 95 FAT32 (up to 2047GB) */
	case FDISK_EXT_WIN:	/* Windows 95 FAT32 (extended-INT13) */
	case FDISK_FAT95:	/* DOS 16-bit FAT, LBA-mapped */
	case FDISK_EXTLBA:	/* Extended partition, LBA-mapped */
	case DIAGPART:		/* Diagnostic boot partition (OS independent) */
	case FDISK_CPM:		/* CP/M */
	case DOSDATA:		/* DOS data partition */
	case OTHEROS:
	case UNIXOS:		/* UNIX V.x partition */
	case FDISK_NOVELL3:	/* Novell Netware 3.x and later */
	case FDISK_QNX4:	/* QNX 4.x */
	case FDISK_QNX42:	/* QNX 4.x 2nd part */
	case FDISK_QNX43:	/* QNX 4.x 3rd part */
	case FDISK_NTFSVOL1:	/* NTFS volume set 1 */
	case FDISK_NTFSVOL2:	/* NTFS volume set 2 */
	case FDISK_BSD:		/* BSD/386, 386BSD, NetBSD, FreeBSD, OpenBSD */
	case FDISK_NEXTSTEP:	/* NeXTSTEP */
	case FDISK_BSDIFS:	/* BSDI file system */
	case FDISK_BSDISWAP:	/* BSDI swap */
		pinfo->content_type = OM_CTYPE_UNKNOWN; /* content unknown */
		break;
	default:
		om_debug_print(OM_DBGLVL_ERR,
		    "Unsupported partition type %d requested.\n",
		    partition_type);
		return (B_FALSE);
	}
	if (!build_free_space_table(is_log_part)) /* checks for overlap */
		return (B_FALSE);

	pinfo->partition_type = partition_type;
	pinfo->partition_size_sec = partition_size_sec;
	pinfo->partition_offset = pinfo->partition_offset_sec / BLOCKS_TO_MB;
	pinfo->partition_size = pinfo->partition_size_sec / BLOCKS_TO_MB;
	pinfo->partition_id = ipart + 1;

	om_debug_print(OM_DBGLVL_INFO,
	    "will create partition of type %s(%d) size=%lld offset=%lld\n",
	    ptype_text, partition_type, pinfo->partition_size_sec,
	    pinfo->partition_offset_sec);
	om_debug_print(OM_DBGLVL_INFO, "adding partition in slot %d\n", ipart);
	log_partition_map();
	return (B_TRUE);
}

/*
 * om_delete_partition() - delete an existing partition
 *
 * partition_id - number of partition to delete
 *	- or if zero -
 * partition_size_sec - size of partition in sectors
 * partition_offset_sec - offset of begininng sector
 *
 * returns B_TRUE if success, B_FALSE otherwise
 */
boolean_t
om_delete_partition(uint8_t partition_id, uint64_t partition_offset_sec,
    uint64_t partition_size_sec)
{
	partition_info_t *pinfo;
	int ipart;

	assert(committed_disk_target != NULL);
	assert(committed_disk_target->dparts != NULL);

	/*
	 * delete by ID (1-36) if provided
	 */
	if (partition_id != 0) {
		om_debug_print(OM_DBGLVL_INFO,
		    "to delete partition %d\n", (int)partition_id);
		ipart = partition_index_by_id(partition_id);
		if (ipart == -1) {
			om_debug_print(OM_DBGLVL_ERR,
			    "Could not find partition %d to delete. "
			    "Assumed deleted\n", partition_id);
			return (B_TRUE);
		}
		/*
		 * if partition to delete is extended partition,
		 *	also delete any logical partitions from target table
		 */
		pinfo = committed_disk_target->dparts->pinfo;
		if (fdisk_is_dos_extended(pinfo[ipart].partition_type))
			delete_all_logical_partitions();
		/*
		 * delete indicated partition
		 */
		mark_for_deletion_by_index(ipart);
		om_log_print("partition ID=%d marked for deletion\n",
		    (int)partition_id);
		return (B_TRUE);
	}
	/*
	 * otherwise, delete by sector location
	 */
	om_debug_print(OM_DBGLVL_INFO,
	    "to delete partition by location: offset=%lld size=%lld\n",
	    partition_offset_sec, partition_size_sec);
	pinfo = committed_disk_target->dparts->pinfo;
	for (ipart = 0; ipart < OM_NUMPART; ipart++) {
		if (partition_offset_sec == pinfo[ipart].partition_offset_sec &&
		    (partition_size_sec == 0LL || /* if length specified */
		    partition_size_sec == pinfo[ipart].partition_size_sec)) {
			/*
			 * if partition to delete is extended partition,
			 *	also delete any logical partitions from target
			 */
			if (fdisk_is_dos_extended(pinfo[ipart].partition_type))
				delete_all_logical_partitions();
			mark_for_deletion_by_index(ipart);
			return (B_TRUE);
		}
	}
	om_debug_print(OM_DBGLVL_ERR,
	    "Failed to locate specified partition to delete at starting sector "
	    "%lld with size in sectors=%lld\n",
	    partition_offset_sec, partition_size_sec);
	om_set_error(OM_BAD_INPUT);
	return (B_FALSE);
}

/*
 * given partition index (0-35), mark the partition for deletion
 *	by removing it from the disk target table
 * It is assumed that the index is valid and points to a used partition
 * NOTE: this function deletes an array element by index, not by partition
 *	number, which is the index + 1
 */
static void
mark_for_deletion_by_index(int ipart)
{
	partition_info_t *pinfo = committed_disk_target->dparts->pinfo;

	om_debug_print(OM_DBGLVL_INFO, "marking partition with index "
	    "%d (0-%d) for deletion-type=%d\n", ipart, OM_NUMPART - 1,
	    pinfo[ipart].partition_type);
	if (pinfo[ipart].partition_type == SUNIXOS2)
		om_invalidate_slice_info();
	/*
	 * clear entry
	 */
	set_partition_unused(&pinfo[ipart]);
}

static void
delete_all_logical_partitions()
{
	int ipart;
	partition_info_t *pinfo = committed_disk_target->dparts->pinfo;

	for (ipart = 0; ipart < OM_NUMPART; ipart++, pinfo++)
		if (IS_LOG_PAR(pinfo->partition_id))
			mark_for_deletion_by_index(ipart);
}
/*
 * om_finalize_fdisk_info_for_TI() - write out partition table containing edits
 * performs adjustments to layout:
 *	-eliminate use of first cylinder for x86
 *	-eliminate overlapping
 *	-allow gaps between partitions
 * returns B_TRUE if success, B_FALSE otherwise
 * side effect: may modify target disk partition info
 */
boolean_t
om_finalize_fdisk_info_for_TI()
{
	disk_parts_t *dparts = committed_disk_target->dparts;
	disk_parts_t *newdparts;

	assert(committed_disk_target->dinfo.disk_name != NULL);

	newdparts = om_validate_and_resize_disk_partitions(0, dparts,
	    AI_allocation);
	if (newdparts == NULL) {
		om_debug_print(OM_DBGLVL_ERR,
		    "Partition information is invalid\n");
		return (B_FALSE);
	}
	committed_disk_target->dparts = newdparts;
	om_debug_print(OM_DBGLVL_INFO,
	    "om_finalize_fdisk_info_for_TI:%s partition 0 %ld MB disk "
	    "%ld MB %lld sectors\n", whole_disk ? "entire disk":"",
	    dparts->pinfo[0].partition_size,
	    committed_disk_target->dinfo.disk_size,
	    committed_disk_target->dinfo.disk_size_sec);
	log_partition_map();
	return (B_TRUE);
}

/*
 * set disk partition info initially for no partitions
 * allocate on heap
 * given disk name, set all partitions empty
 * return pointer to disk partition info
 * return NULL if memory allocation failure
 */
disk_parts_t *
om_init_disk_partition_info(disk_info_t *di)
{
	char *disk_name;
	disk_parts_t *dp;
	int i;

	disk_name = di->disk_name;
	assert(disk_name != NULL);

	dp = calloc(1, sizeof (disk_parts_t));
	if (dp == NULL) {
		om_set_error(OM_NO_SPACE);
		return (NULL);
	}
	dp->disk_name = strdup(disk_name);
	if (dp->disk_name == NULL) {
		free(dp);
		om_set_error(OM_NO_SPACE);
		return (NULL);
	}
	/* mark all unused */
	for (i = 0; i < OM_NUMPART; i++)
		set_partition_unused(&dp->pinfo[i]);
	return (dp);
}

/*
 * om_create_target_partition_info_if_absent(): initialize a target disk
 *	partition struct if not yet initialized
 * This was designed for the case of no partition table on disk and no
 *	customizations were provided by the user
 * side effect: if no target disk partitions have been found or specified,
 *	initialize the target disk information to use the entire target disk
 *	for a single partition
 */
void
om_create_target_partition_info_if_absent()
{
	partition_info_t *pinfo;
	disk_info_t *di;

	assert(committed_disk_target != NULL);
	assert(committed_disk_target->dparts != NULL);

	pinfo = committed_disk_target->dparts->pinfo;
	if (pinfo != NULL && get_first_used_partition(pinfo) != -1)
		return;	/* target partition table has already initialized */
	om_debug_print(OM_DBGLVL_INFO,
	    "No partition info - Creating target disk partition table - "
	    "use entire target disk\n");
	/* mark first partition to be Solaris2 */
	di = &committed_disk_target->dinfo;
	pinfo->partition_id = 1;
	pinfo->content_type = OM_CTYPE_SOLARIS;
	pinfo->partition_type = SUNIXOS2;
	pinfo->partition_size = di->disk_size;
	pinfo->partition_size_sec = (uint64_t)di->disk_size * BLOCKS_TO_MB;
}

/*
 * om_set_fdisk_target_attrs
 * This function sets the appropriate fdisk attributes for target instantiation.
 * Input:	nvlist_t *target_attrs - list to add attributes to
 * Output:	None
 * Return:	errno - see orchestrator_api.h
 */
int
om_set_fdisk_target_attrs(nvlist_t *list, char *diskname)
{
	disk_target_t	*dt;
	disk_parts_t	*cdp;
	int		i;
	boolean_t	preserve_all;

	uint8_t		part_ids[OM_NUMPART], part_active_flags[OM_NUMPART];
	uint64_t	part_offsets[OM_NUMPART], part_sizes[OM_NUMPART];
	boolean_t	preserve_array[OM_NUMPART];
	partition_info_t	*install_partition = NULL;
	int		nparts = (get_extended_partition_info(NULL) == NULL ?
	    FD_NUMPART:OM_NUMPART);

	om_set_error(OM_SUCCESS);

	/*
	 * We have all the data from the GUI committed at this point.
	 * Gather it, and set the attributes.
	 */

	for (dt = system_disks; dt != NULL; dt = dt->next) {
		if (streq(dt->dinfo.disk_name, diskname)) {
			break;
		}
	}

	if (dt == NULL) {
		om_log_print("Bad target disk name\n");
		om_set_error(OM_BAD_DISK_NAME);
		return (-1);
	}

	if (committed_disk_target == NULL) {
		om_log_print("Disk is not changed\n");
		preserve_all = B_TRUE;

		/*
		 * No existing partitions and No new partitions.
		 * we can't proceed with install
		 */
		if (dt->dparts == NULL) {
			om_log_print("Disk is empty - doesn't contain "
			    "partitions\n");

			om_set_error(OM_NO_PARTITION_FOUND);
			return (-1);
		}

		cdp = dt->dparts;
	} else {
		om_log_print("Disk was changed\n");
		preserve_all = B_FALSE;

		if (committed_disk_target->dparts == NULL) {
			om_log_print("Configuration of new partitions not "
			    "available\n");

			om_set_error(OM_NO_PARTITION_FOUND);
			return (-1);
		}

		cdp = committed_disk_target->dparts;
	}

	/*
	 * Make sure that there is a Solaris or Solaris 2 partition.
	 */

	for (i = 0; i < nparts; i++) {
		if (cdp->pinfo[i].partition_type == SUNIXOS2 ||
		    (cdp->pinfo[i].partition_type == SUNIXOS &&
		    cdp->pinfo[i].content_type != OM_CTYPE_LINUXSWAP)) {
			om_log_print("Disk contains valid Solaris partition\n");

			/*
			 * Check whether the partition type is legacy Solaris
			 * (SUNIXOS) or new Solaris2 (SUNIXOS2). If it is
			 * SUNIXOS, convert it to SUNIXOS2.
			 */
			if (cdp->pinfo[i].partition_type == SUNIXOS) {
				om_log_print(
				    "Disk contains legacy Solaris partition. "
				    "It will be converted to Solaris2\n");

				/*
				 * If user didn't make any changes to the
				 * original partition configuration
				 * (committed_disk_target == NULL), it
				 * is necessary to create a copy of the
				 * original partition configuration and
				 * change the partition type.
				 */

				if (committed_disk_target == NULL) {
					om_debug_print(OM_DBGLVL_INFO,
					    "committed_disk_target == NULL, "
					    "copy of original partition "
					    "configuration will be created\n");

					/*
					 * First parameter (handle) is
					 * currently unused, set to '0'
					 */
					if (om_set_disk_partition_info(0,
					    cdp) != OM_SUCCESS) {
						om_debug_print(OM_DBGLVL_ERR,
						    "Couldn't duplicate "
						    "partition "
						    "configuration\n");

						return (-1);
					}

					cdp = committed_disk_target->dparts;
				}

				cdp->pinfo[i].partition_type = SUNIXOS2;
				preserve_all = B_FALSE;
			}
			install_partition = &cdp->pinfo[i];
			break;
		}
	}

	/*
	 * No Solaris partition. Do not proceed
	 */

	if (i == nparts) {
		om_log_print("Disk doesn't contain valid Solaris partition\n");

		om_set_error(OM_NO_PARTITION_FOUND);
		return (-1);
	}

	/*
	 * Solaris partition found - take a look at the partition size
	 * and decide, if there is enough space to create swap and
	 * dump devices
	 */

	om_debug_print(OM_DBGLVL_INFO,
	    "Recommended disk size is %llu MiB\n",
	    om_get_recommended_size(NULL, NULL));

	om_debug_print(OM_DBGLVL_INFO,
	    "Install partition size = %lu MiB\n",
	    install_partition != NULL ?
	    install_partition->partition_size : 0);

	if (install_partition == NULL) {
		om_debug_print(OM_DBGLVL_WARN,
		    "Couldn't obtain size of install partition, swap&dump "
		    "won't be created\n");

		create_swap_and_dump = B_FALSE;
	} else if (install_partition->partition_size <
	    om_get_recommended_size(NULL, NULL) - OVERHEAD_MB) {

		om_debug_print(OM_DBGLVL_INFO,
		    "Install partition is too small, swap&dump won't "
		    "be created\n");

		create_swap_and_dump = B_FALSE;
	} else {
		om_debug_print(OM_DBGLVL_INFO,
		    "Size of install partition is sufficient for creating "
		    "swap&dump\n");

		create_swap_and_dump = B_TRUE;
	}

	/*
	 * set target type
	 */

	if (nvlist_add_uint32(list, TI_ATTR_TARGET_TYPE,
	    TI_TARGET_TYPE_FDISK) != 0) {
		(void) om_log_print("Couldn't add TI_ATTR_TARGET_TYPE to"
		    "nvlist\n");

		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	if (nvlist_add_string(list, TI_ATTR_FDISK_DISK_NAME,
	    diskname) != 0) {
		om_log_print("Couldn't add FDISK_DISK_NAME attr\n");

		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	if (nvlist_add_boolean_value(list, TI_ATTR_FDISK_WDISK_FL,
	    whole_disk) != 0) {
		om_log_print("Couldn't add WDISK_FL attr\n");

		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	om_log_print("whole_disk = %d\n", whole_disk);
	om_log_print("diskname set = %s\n", diskname);

	/*
	 * If "whole disk", no more attributes need to be set
	 */

	if (whole_disk)
		return (0);

	/* add number of partitions to be created */

	if (nvlist_add_uint16(list, TI_ATTR_FDISK_PART_NUM,
	    nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_NAME attr\n");

		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	/*
	 * if no changes should be done to fdisk partition table
	 * set "preserve" flag for all partitions
	 */

	if (preserve_all) {
		om_log_print("No changes will be done to the partition "
		    "table\n");

		for (i = 0; i < nparts; i++)
			preserve_array[i] = B_TRUE;

		/* preserve flags */

		if (nvlist_add_boolean_array(list, TI_ATTR_FDISK_PART_PRESERVE,
		    preserve_array, nparts) != 0) {
			om_log_print("Couldn't add FDISK_PART_PRESERVE attr\n");
			om_set_error(OM_NO_SPACE);
			return (-1);
		}

		return (0);
	}

	/*
	 * check whether disk partitions are changed for this install
	 * The caller should have called to commit the changes
	 */

	if (committed_disk_target->dparts == NULL) {
		return (-1);
	}

	/*
	 * The disk we got for install is different
	 * from the disk information committed before.
	 * So return error.
	 */

	if (!streq(diskname, committed_disk_target->dinfo.disk_name)) {
		return (-1);
	}

	/*
	 * Now find out the changed partitions
	 */

	cdp = committed_disk_target->dparts;

	om_debug_print(OM_DBGLVL_INFO, "Committed partition LBA information\n");
	for (i = 0; i < nparts; i++) {
		om_debug_print(OM_DBGLVL_INFO,
		    "[%d] pos=%d, id=%02X, beg=%llu(%lu MiB), "
		    "size=%llu(%lu MiB)\n", i,
		    cdp->pinfo[i].partition_id,
		    cdp->pinfo[i].partition_type,
		    cdp->pinfo[i].partition_offset_sec,
		    cdp->pinfo[i].partition_offset,
		    cdp->pinfo[i].partition_size_sec,
		    cdp->pinfo[i].partition_size);
	}

	/*
	 * Partitions are sorted by offset for now.
	 *
	 * For TI purposes sort partitions according to position
	 * in fdisk partition table.
	 */

	/*
	 * Mark all entries as unused and then fill in used positions
	 *
	 * Set ID to UNUSED for unused entries. Otherwise fdisk(1M)
	 * refuses to create partition table.
	 *
	 * Initially assume that nothing will be preserved.
	 */

	for (i = 0; i < nparts; i++) {
		part_ids[i] = UNUSED;
		part_active_flags[i] =
		    part_offsets[i] = part_sizes[i] = 0;

		preserve_array[i] = B_FALSE;
	}

	for (i = 0; i < nparts; i++) {
		uint64_t	size_new = cdp->pinfo[i].partition_size;
		uint8_t		type_new = cdp->pinfo[i].partition_type;
		int		pos = i;

		/* Skip unused entries */

		if (pos == -1 || size_new == 0)
			continue;

		/*
		 * If size and type didn't change, preserve the partition.
		 * "move" operation (only offset changed) is not supported.
		 */

		/*
		 * If disk had empty partition table, don't compare,
		 * just create the partition
		 */

		if ((dt->dparts != NULL) &&
		    (dt->dparts->pinfo[i].partition_size == size_new) &&
		    (dt->dparts->pinfo[i].partition_type == type_new)) {
			preserve_array[pos] = B_TRUE;
		}

		part_ids[pos] = type_new;
		part_active_flags[pos] = 0;
		part_offsets[pos] = cdp->pinfo[i].partition_offset_sec;
		part_sizes[pos] = cdp->pinfo[i].partition_size_sec;
	}

	/*
	 * Add partition geometry to the list of attributes
	 */

	/* ID */

	if (nvlist_add_uint8_array(list, TI_ATTR_FDISK_PART_IDS,
	    part_ids, nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_IDS attr\n");
		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	/* ACTIVE */

	if (nvlist_add_uint8_array(list, TI_ATTR_FDISK_PART_ACTIVE,
	    part_active_flags, nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_ACTIVE attr\n");
		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	/* offset */

	if (nvlist_add_uint64_array(list, TI_ATTR_FDISK_PART_RSECTS,
	    part_offsets, nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_RSECTS attr\n");
		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	/* size */

	if (nvlist_add_uint64_array(list, TI_ATTR_FDISK_PART_NUMSECTS,
	    part_sizes, nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_NUMSECTS attr\n");
		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	/* preserve flags */

	if (nvlist_add_boolean_array(list, TI_ATTR_FDISK_PART_PRESERVE,
	    preserve_array, nparts) != 0) {
		om_log_print("Couldn't add FDISK_PART_PRESERVE attr\n");
		om_set_error(OM_NO_SPACE);
		return (-1);
	}

	om_set_error(OM_SUCCESS);
	return (0);
}

/*
 * find best fit among blocks of unused space that has at least partition_size
 *	sectors unallocated
 * if partition_size is 0, return largest free region
 */
static struct free_region *
find_unused_region_of_size(uint64_t partition_size, boolean_t is_log_part)
{
	assert(committed_disk_target != NULL);

	if (!build_free_space_table(is_log_part))
		return (NULL);
	/*
	 * if partition size unspecified (signaled when zero)
	 * find largest free space
	 * otherwise find best fit for specified size
	 */
	return (partition_size == OM_MAX_SIZE ?
	    find_largest_free_region(is_log_part) :
	    find_free_region_best_fit(partition_size, is_log_part));
}

/*
 * do a sorted insertion of used space in partition
 */
static void
insertion_sort_partition_info(partition_info_t *psinfo)
{
	int isl;

	for (isl = 0; isl < n_sorted_parts; isl++)
		if (sorted_parts[isl].partition_offset_sec >
		    psinfo->partition_offset_sec)
			break;
	/* safe push downward */
	(void) memmove(&sorted_parts[isl + 1], &sorted_parts[isl],
	    (n_sorted_parts - isl) * sizeof (sorted_parts[0]));
	/* move in new partition info entry */
	sorted_parts[isl].partition_id = psinfo->partition_id;
	sorted_parts[isl].partition_offset_sec = psinfo->partition_offset_sec;
	sorted_parts[isl].partition_size_sec = psinfo->partition_size_sec;
	n_sorted_parts++;
}

/*
 * make table of used partition space taken from disk target
 */
static void
sort_used_regions(boolean_t is_log_part)
{
	partition_info_t *psinfo;
	int isl;

	n_sorted_parts = 0;
	for (psinfo = &committed_disk_target->dparts->pinfo[0],
	    isl = 0; isl < OM_NUMPART; isl++, psinfo++) {
		if (psinfo->partition_size == 0)
			continue;
		/*
		 * if logical partition is to be created,
		 *	do not include non-logical partitions in table
		 */
		if (is_log_part && !IS_LOG_PAR(psinfo->partition_id))
			continue;
		/*
		 * if primary partition is to be created,
		 *	do not include logical partitions in table
		 */
		if (!is_log_part && IS_LOG_PAR(psinfo->partition_id))
			continue;
		insertion_sort_partition_info(psinfo);
	}
}

/*
 * populate a table with entries for each region of free space in the
 *	target disk partition table
 * returns B_FALSE if any problems
 *	-overlapping in the partitions was detected
 *	-disk size unknown
 * returns B_TRUE if no problems were detected
 * side effects:
 *	sets n_fragments: number of free space fragments
 *	calls append_free_space_table() to add free regions
 */
static boolean_t
build_free_space_table(boolean_t is_log_part)
{
	int isl;
	uint64_t free_size;
	uint64_t max_size_sec;
	uint64_t starting_sec = 0;
	partition_info_t *extpinfo;

	bzero(&sorted_parts, sizeof (sorted_parts));
	if (is_log_part) {
		extpinfo = get_extended_partition_info(NULL);
		if (extpinfo == NULL) {
			om_debug_print(OM_DBGLVL_ERR,
			    "system error: failed to find "
			    "extended partition definition\n");
			return (B_FALSE);
		}
		starting_sec = extpinfo->partition_offset_sec;
		max_size_sec = extpinfo->partition_size_sec;
		om_debug_print(OM_DBGLVL_INFO,
		    "extended partition: start %lld size %lld\n",
		    starting_sec, max_size_sec);
		assert(max_size_sec != 0);
	} else {
		uint64_t disk_size_sec;

		disk_size_sec = committed_disk_target->dinfo.disk_size_sec;
		if (disk_size_sec == 0) /* sometimes sectors field is blank */
			disk_size_sec =		/* take from MB field */
			    (uint64_t)committed_disk_target->dinfo.disk_size *
			    BLOCKS_TO_MB;
		if (disk_size_sec == 0) {
			om_debug_print(OM_DBGLVL_ERR, "User is requesting "
			    "partition changes, requiring a known disk size, "
			    "but the target disk size (%s) is unknown. "
			    "Cannot continue installation.\n",
			    committed_disk_target->dinfo.disk_name);
			return (B_FALSE);
		}
		max_size_sec = disk_size_sec;
	}

	/* sort partition table by starting offset */
	sort_used_regions(is_log_part);
	n_fragments = 0; /* reset number of free regions */

	/* if no partitions used, set entire partition as being free */
	if (n_sorted_parts == 0) {
		append_free_space_table(starting_sec, max_size_sec);
		return (B_TRUE);
	}
	/* check for space before first partition */
	if (is_log_part) {
		if (sorted_parts[0].partition_offset_sec > starting_sec +
		    LOGICAL_PARTITION_PAD)
			append_free_space_table(starting_sec,
			    sorted_parts[0].partition_offset_sec);
	} else if (sorted_parts[0].partition_offset_sec > starting_sec) {
		append_free_space_table(starting_sec,
		    sorted_parts[0].partition_offset_sec);
	}
	for (isl = 0; isl < n_sorted_parts - 1; isl++) {
		/* does end of current partition overlap start of next one? */
		if (PARTITION_END(isl) >
		    sorted_parts[isl + 1].partition_offset_sec) {
			om_debug_print(OM_DBGLVL_ERR, "User is requesting "
			    "overlapping partitions, which is illegal.\n");
			return (B_FALSE);
		}
		/* compute space between partitions */
		free_size = sorted_parts[isl + 1].partition_offset_sec -
		    PARTITION_END(isl);
		if (free_size > 0)
			append_free_space_table(PARTITION_END(isl), free_size);
	}
	/* check for any free space between last partition and end of disk */
	free_size = max_size_sec - PARTITION_END(n_sorted_parts - 1);
	if (free_size > 0)
		append_free_space_table(
		    PARTITION_END(n_sorted_parts - 1), free_size);
	return (B_TRUE);
}

/*
 * append offset and length of a block of free space
 */
static void
append_free_space_table(uint64_t free_offset, uint64_t free_size)
{
	/* should never exceed table size */
	if (n_fragments >=
	    sizeof (free_space_table) / sizeof (free_space_table[0]))
		return;

	free_space_table[n_fragments].free_offset = free_offset;
	free_space_table[n_fragments].free_size = free_size;
	om_debug_print(OM_DBGLVL_INFO,
	    "free space table %d offset %lld size %lld\n",
	    n_fragments, free_offset, free_size);
	n_fragments++;
}

/*
 * find largest contiguous space not in other partitions in free space table
 * must have previous call to build_free_space_table()
 * return size + offset of region or NULL if none found
 */
static struct free_region *
find_largest_free_region(boolean_t is_log_part)
{
	struct free_region *pregion;
	struct free_region *largest_region = NULL;
	int ireg;

	for (pregion = free_space_table, ireg = 0;
	    ireg < n_fragments; ireg++, pregion++) {
		if (largest_region == NULL ||
		    pregion->free_size > largest_region->free_size)
			largest_region = pregion;
	}
	if (largest_region != NULL && is_log_part) {
		largest_region->free_offset += LOGICAL_PARTITION_PAD;
		largest_region->free_size -= LOGICAL_PARTITION_PAD;
	}
	return (largest_region);
}

/*
 * find contiguous space that fits requested size most closely
 * must have previous call to build_free_space_table()
 * return size + offset of region or NULL if none found
 */
static struct free_region *
find_free_region_best_fit(uint64_t partition_size, boolean_t is_log_part)
{
	struct free_region *pregion;
	struct free_region *best_fit = NULL;
	int ireg;

	/*
	 * logical partitions require 63 sectors before each one
	 * request size 63 sectors
	 */
	if (is_log_part)
		partition_size += LOGICAL_PARTITION_PAD;
	for (pregion = free_space_table, ireg = 0;
	    ireg < n_fragments; ireg++, pregion++) {
		if (best_fit == NULL) { /* find first fit */
			if (pregion->free_size >= partition_size)
				best_fit = pregion;
			continue;
		}
		/* check if better fit */
		if (pregion->free_size > partition_size &&
		    pregion->free_size < best_fit->free_size)
			best_fit = pregion;
	}
	/*
	 * since logical partitions require padding,
	 * return the actual size of the block minus the padding
	 */
	if (best_fit != NULL && is_log_part) {
		best_fit->free_offset += LOGICAL_PARTITION_PAD;
		best_fit->free_size -= LOGICAL_PARTITION_PAD;
	}
	return (best_fit);
}

/*
 * dump from sorted non-exempt slice table
 */
static void
log_used_regions()
{
	int isl;

	om_debug_print(OM_DBGLVL_INFO, "Sorted partitions table:\n");
	if (n_sorted_parts == 0) {
		om_debug_print(OM_DBGLVL_INFO,
		    "\tno partitions in sorted table\n");
		return;
	}
	om_debug_print(OM_DBGLVL_INFO,
	    "\tpartition\toffset\tsize\toffset+size\n");
	for (isl = 0; isl < n_sorted_parts; isl++) {
		om_debug_print(OM_DBGLVL_INFO, "\t%d\t%lld\t%lld\t%lld\n",
		    sorted_parts[isl].partition_id,
		    sorted_parts[isl].partition_offset_sec,
		    sorted_parts[isl].partition_size_sec,
		    sorted_parts[isl].partition_offset_sec +
		    sorted_parts[isl].partition_size_sec);
	}
}

/*
 * dump free space entries from table
 */
static void
log_free_space_table()
{
	int i;

	om_debug_print(OM_DBGLVL_INFO,
	    "Free partition space fragments - count %d\n",
	    n_fragments);
	if (n_fragments == 0) {
		om_debug_print(OM_DBGLVL_INFO, "\tno free space\n");
		return;
	}
	om_debug_print(OM_DBGLVL_INFO, "\toffset\tsize\tnoffset+size\n");
	for (i = 0; i < n_fragments; i++)
		om_debug_print(OM_DBGLVL_INFO, "\t%lld\t%lld\t%lld\n",
		    free_space_table[i].free_offset,
		    free_space_table[i].free_size,
		    free_space_table[i].free_offset +
		    free_space_table[i].free_size);
}

/*
 * end of slice editing suite
 */