OpenSolaris_b135/cmd/rmformat/rmf_slice.c

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

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

/*
 * rmf_slice.c :
 * 	This file contains the functions for parsing a slice file
 * 	for rmformat.
 */

#include <sys/types.h>
#include <ctype.h>
#include <sys/vtoc.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <memory.h>
#include <dirent.h>
#include <sys/fcntl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/dkio.h>
#include <priv_utils.h>
#include "rmformat.h"

extern void my_perror(char *err_string);

static int32_t	last_token_type = 0;
#define	spc()	  (last_token_type)


/*
 * This global is used to store the current line # in the
 * data file. It must be global because the I/O routines
 * are allowed to side effect it to keep track of backslashed
 * newlines.
 */

static int32_t	data_lineno;		/* current line # in data file */

#define	CHG_MODE_UNDEFINED  (-1)	/* undefined value */
#define	CHG_MODE_SET	0		/* set bits by or'ing */
#define	CHG_MODE_CLR	1		/* clr bits by and'ing */
#define	CHG_MODE_ABS	2		/* set absolute value */


#define	TOKEN_SIZE	36		/* max length of a token */
typedef char TOKEN[TOKEN_SIZE+1];	/* token type */
#define	DATA_INPUT	0		/* 2 modes of input */
#define	CMD_INPUT	1
#define	WILD_STRING	"$"		/* wildcard character */
#define	COMMENT_CHAR	'#'		/* comment character */

/*
 * List of strings with arbitrary matching values
 */
typedef struct slist {
	char	*str;
	char	*help;
	int32_t	value;
} slist_t;

static slist_t ptag_choices[] = {
	{ "unassigned", "",	V_UNASSIGNED	},
	{ "boot",	"",	V_BOOT		},
	{ "root",	"",	V_ROOT		},
	{ "swap",	"",	V_SWAP		},
	{ "usr",	"",	V_USR		},
	{ "backup",	"",	V_BACKUP	},
	{ "stand",	"",	V_STAND		},
	{ "var",	"",	V_VAR		},
	{ "home",	"",	V_HOME		},
	{ "alternates", "",	V_ALTSCTR	},
	{ NULL }
};


/*
 * Choices for the p_flag vtoc field
 */
static slist_t pflag_choices[] = {
	{ "wm", "read-write, mountable",	0	},
	{ "wu", "read-write, unmountable",	V_UNMNT	},
	{ "rm", "read-only, mountable",		V_RONLY	},
	{ "ru", "read-only, unmountable",	V_RONLY|V_UNMNT },
	{ NULL }
};

/*
 * The definitions are the token types that the data file parser recognizes.
 */
#define	SUP_EOF			-1		/* eof token */
#define	SUP_STRING		0		/* string token */
#define	SUP_EQL			1		/* equals token */
#define	SUP_COMMA		2		/* comma token */
#define	SUP_COLON		3		/* colon token */
#define	SUP_EOL			4		/* newline token */
#define	SUP_OR			5		/* vertical bar */
#define	SUP_AND			6		/* ampersand */
#define	SUP_TILDE		7		/* tilde */


/*
 *	Prototypes for ANSI C compilers
 */
static int32_t	sup_prxfile(char *file_name, struct extvtoc *vt);
static int32_t	sup_setpart(struct extvtoc *vt);
static void	sup_pushchar(int32_t c);
static void	clean_token(char *cleantoken, char *token);
static void clean_token(char *cleantoken, char *token);
static int32_t sup_inputchar();
static int32_t sup_gettoken(char *buf);
static int32_t sup_get_token(char *buf);
static int32_t find_value(slist_t *slist, char *str, int32_t *value);
static int32_t check_vtoc_sanity(smedia_handle_t, int32_t fd,
		struct extvtoc *vt);
static uint64_t str2sector(char *str);
static int32_t strcnt(char *s1, char *s2);
static int32_t get_fdisk(smedia_handle_t, int32_t fd, int32_t offset,
		struct fdisk_info *fdisk);
static void erase(smedia_handle_t handle, diskaddr_t offset, diskaddr_t size);

extern char *myname;
extern uint64_t my_atoll(char *ptr);
extern smmedium_prop_t med_info;

static FILE *data_file;

static int32_t
sup_prxfile(char *file_name, struct extvtoc *vt)
{
	int32_t	status, ret_val;
	TOKEN	token;
	TOKEN	cleaned;

	/*
	 * Open the data file.  Return 0 if unable to do so.
	 */
	data_file = fopen(file_name, "r");
	if (data_file == NULL) {
		PERROR("Open failed");
		return (-1);
	}
	/*
	 * Step through the data file a meta-line at a time.  There are
	 * typically several backslashed newlines in each meta-line,
	 * so data_lineno will be getting side effected along the way.
	 */
	data_lineno = 1;
	for (;;) {

		/*
		 * Get the keyword.
		 */
		status = sup_gettoken(token);
		/*
		 * If we hit the end of the data file, we're done.
		 */
		if (status == SUP_EOF)
			break;
		/*
		 * If the line starts with some key character, it's an error.
		 */
		if (status != SUP_STRING) {
			(void) fprintf(stderr,
			    gettext("Expecting keyword, found '%s'"),
			    token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			continue;
		}
		/*
		 * Clean up the token and see which keyword it is.  Call
		 * the appropriate routine to process the rest of the line.
		 */
		clean_token(cleaned, token);
		if (strcmp(cleaned, "slices") == 0) {
			ret_val = sup_setpart(vt);
			(void) fclose(data_file);
			return (ret_val);
		} else {
			(void) fprintf(stderr, gettext("Unknown keyword '%s'"),
			    cleaned);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			(void) fclose(data_file);
			return (-1);
		}
	}
	/*
	 * Close the data file.
	 */
	(void) fclose(data_file);

	(void) fprintf(stderr,
	    gettext("Unexpected end of file (line no %d)\n"), data_lineno);
	return (-1);
}

static int32_t
sup_gettoken(char *buf)
{
	/*
	 * Skip end of lines and blank lines.
	 */
	while ((last_token_type = sup_get_token(buf)) == SUP_EOL)
			;
	return (last_token_type);
}

static int32_t
sup_get_token(char *buf)
{
	char	*ptr = buf;
	int32_t	c, quoted = 0;

	/*
	 * Was an end of file detected last try?
	 */

	if (feof(data_file)) {
		return (SUP_EOF);
	}

	/*
	 * Zero out the returned token buffer
	 */

	bzero(buf, TOKEN_SIZE + 1);

	/*
	 * Strip off leading white-space.
	 */
	while (isspace(c = sup_inputchar()))
		;

	/*
	 * Only white spaces and then end of file?
	 */

	if (feof(data_file)) {
		return (SUP_EOF);
	}

	/*
	 * Read in characters until we hit unquoted white-space.
	 */
	for (; !isspace(c) || quoted; c = sup_inputchar()) {

		/*
		 * If we hit eof, check if we have anything in buffer.
		 * if we have, return STRING, next time we will return EOF
		 * else, return EOF here...should not happen.
		 */
		if (feof(data_file)) {
			if (ptr - buf > 0) {
				return (SUP_STRING);
			} else {
				return (SUP_EOF);
			}
		}

		/*
		 * If we hit a double quote, change the state of quoting.
		 */
		if (c == '"') {
			quoted = !quoted;
			continue;
		}
		/*
		 * If we hit a newline, that delimits a token.
		 */
		if (c == '\n')
			break;
		/*
		 * If we hit any nonquoted special delimiters, that delimits
		 * a token.
		 */
		if (!quoted && (c == '=' || c == ',' || c == ':' ||
		    c == '#' || c == '|' || c == '&' || c == '~'))
				break;
		/*
		 * Store the character if there's room left.
		 */
		if (ptr - buf < TOKEN_SIZE)
			*ptr++ = (char)c;
	}
	/*
	 * If we stored characters in the buffer, then we inputted a string.
	 * Push the delimiter back into the pipe and return the string.
	 */
	if (ptr - buf > 0) {
		sup_pushchar(c);
		return (SUP_STRING);
	}
	/*
	 * We didn't input a string, so we must have inputted a known delimiter.
	 * store the delimiter in the buffer, so it will get returned.
	 */
	buf[0] = c;
	/*
	 * Switch on the delimiter.  Return the appropriate value for each one.
	 */
	switch (c) {
	case '=':
		return (SUP_EQL);
	case ':':
		return (SUP_COLON);
	case ',':
		return (SUP_COMMA);
	case '\n':
		return (SUP_EOL);
	case '|':
		return (SUP_OR);
	case '&':
		return (SUP_AND);
	case '~':
		return (SUP_TILDE);
	case '#':
		/*
		 * For comments, we flush out the rest of the line and return
		 * an eol.
		 */
		while ((c = sup_inputchar()) != '\n' && !feof(data_file))
			;
		if (feof(data_file))
			return (SUP_EOF);
		else
			return (SUP_EOL);
	/*
	 * Shouldn't ever get here.
	 */
	default:
		return (SUP_STRING);
	}
}
static int32_t
sup_inputchar()
{
	int32_t	c;

	/*
	 * Input the character.
	 */
	c = getc(data_file);
	/*
	 * If it's not a backslash, return it.
	 */

	/*
	 * It was a backslash.  Get the next character.
	 */

	if (c == '\\')
		c = getc(data_file);

	/*
	 * If it was a newline, update the line counter and get the next
	 * character.
	 */
	if (c == '\n') {
		data_lineno++;
	}
	/*
	 * Return the character.
	 */
	return (c);
}

static void
sup_pushchar(int32_t c)
{

	(void) ungetc(c, data_file);
	if (c == '\n')
		data_lineno--;
}

static void
clean_token(char *cleantoken, char *token)
{
	char	*ptr;

	/*
	 * Strip off leading white-space.
	 */
	for (ptr = token; isspace(*ptr) && (ptr <=
	    (token + strlen(token) - 1)); ptr++)
		;

	/*
	 * Copy it into the clean buffer.
	 */
	(void) strcpy(cleantoken, ptr);
	/*
	 * Strip off trailing white-space.
	 */
	for (ptr = cleantoken + strlen(cleantoken) - 1;
	    isspace(*ptr) && (ptr >= cleantoken); ptr--) {
		*ptr = '\0';
	}
}

static int32_t
sup_setpart(struct extvtoc *vt)
{
	TOKEN	token, cleaned, ident;
	int32_t	i, index, status;
	uint64_t	val1, val2;
	ushort_t	vtoc_tag = 0xFFFF;
	ushort_t	vtoc_flag = 0xFFFF;

	/*
	 * Pull in some grammar.
	 */

		status = sup_gettoken(token);

		if (status != SUP_COLON) {
			(void) fprintf(stderr,
			    gettext("Expecting ':', found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}

	for (;;) {
		status = sup_gettoken(token);
		if (status != SUP_STRING) {
			(void) fprintf(stderr,
			    gettext("Expecting string, found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}
		clean_token(ident, token);
		/*
		 * Here's the index of the partition we're dealing with
		 */
		index = (int32_t)my_atoll(ident);
		if ((index < 0) || (index >= NDKMAP)) {
			(void) fprintf(stderr,
			    gettext("Unknown partition %d"), index);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}
		/*
		 * Check for floppy and PCMCIA_MEM cards.
		 * for floppy, the partition no. can be 0 1 2.
		 * for PCMCIA, the partition no. can be 2
		 */
		if (med_info.sm_media_type == SM_FLOPPY) {
			if ((index < 0) || (index > 2)) {
				(void) fprintf(stderr, gettext(
			"Floppy can have partitions 0 1 and 2\n"));
				return (-1);
			}
		}
		if (med_info.sm_media_type == SM_PCMCIA_MEM) {
			if (index != 2) {
				(void) fprintf(stderr, gettext(
			"PCMCIA Memory cards can have partition 2 only.\n"));
				return (-1);
			}
		}

		DPRINTF1("\n Partition %d: ", index);

		status = sup_gettoken(token);
		if (status != SUP_EQL) {
			(void) fprintf(stderr,
			    gettext("Expecting '=', found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);

		}


		status = sup_gettoken(token);
		/*
		 * If we hit a key character, it's an error.
		 */
		if (status != SUP_STRING) {
			(void) fprintf(stderr,
			    gettext("Expecting value, found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}
		clean_token(cleaned, token);
		/*
		 * <tag> may be one of: boot, root, swap, etc.
		 * <flag> consists of two characters:
		 *	W (writable) or R (read-only)
		 *	M (mountable) or U (unmountable)
		 *
		 * Start with the defaults assigned above:
		 */

		/*
		 * All other attributes have a pair of numeric values.
		 * Convert the first value to a number.  This value
		 * is the starting cylinder number of the partition.
		 */

		/* Check for valid partition, e.g. > 8 or 16 */
		val1 = str2sector(cleaned);
		if (val1 == -1) {
			(void) fprintf(stderr,
			    gettext("Invalid partition beggining %s \n"),
			    cleaned);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
		}

		DPRINTF1(" begins %s", cleaned);
		/*
		 * Pull in some grammar.
		 */
		status = sup_gettoken(token);
		if (status != SUP_COMMA) {
			(void) fprintf(stderr,
			    gettext("Expecting ', ', found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}
		/*
		 * Pull in the second value.
		 */
		status = sup_gettoken(token);
		if (status != SUP_STRING) {
			(void) fprintf(stderr,
			    gettext("Expecting value, found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}
		clean_token(cleaned, token);

		val2 = str2sector(cleaned);
		if (val2 == -1) {
			(void) fprintf(stderr,
			    gettext("Invalid partition size %s \n"),
			    cleaned);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
		}
		DPRINTF1(" ends %s ", cleaned);

		/*
		 * Pull in some grammar.
		 */
		status = sup_gettoken(token);

		if (status == SUP_COMMA) {
			/* tags and flags  */
			status = sup_gettoken(token);
			if (status != SUP_STRING) {
				(void) fprintf(stderr,
				    gettext("Expecting value, found '%s'"),
				    token);
				(void) fprintf(stderr,
				    gettext("Line no %d\n"), data_lineno);
				return (-1);
			}
			clean_token(cleaned, token);
			if (find_value(pflag_choices, cleaned, &i) == 1) {
				/*
				 * Found valid tag. Use it and advance parser
				 */
				DPRINTF1(" flag = %s", cleaned);
				vtoc_flag = (ushort_t)i;
				status = sup_gettoken(token);
			} else if (find_value(ptag_choices, cleaned, &i) == 1) {
				DPRINTF1(" tag = %s", cleaned);
				vtoc_tag = (ushort_t)i;
				status = sup_gettoken(token);
				if (status == SUP_COMMA) {
					(void) fprintf(stderr,
					    gettext("Expecting : got %s\n"),
					    token);
					(void) fprintf(stderr,
					    gettext("Line no %d\n"),
					    data_lineno);
					return (-1);
				}
			} else {
				(void) fprintf(stderr,
				    gettext("Invalid flag or tag\n"));
				(void) fprintf(stderr,
				    gettext("Line no %d\n"), data_lineno);
				return (-1);
			}


			if (status == SUP_COMMA) {
					/* Can be tag only */

					status = sup_gettoken(token);
					if (status != SUP_STRING) {
						(void) fprintf(stderr,
						    gettext("Expecting value"
						    ", found '%s'"),
						    token);
						(void) fprintf(stderr,
						    gettext("Line no %d\n"),
						    data_lineno);
						return (-1);
					}

					clean_token(cleaned, token);
					if (find_value(ptag_choices,
					    cleaned, &i) == 1) {
						DPRINTF1(" tag = %s", cleaned);
						vtoc_tag = (ushort_t)i;
					}
			status = sup_gettoken(token);
			}
		}

		/*
		 * Fill in the appropriate map entry with the values.
		 */
		vt->v_part[index].p_start = val1;
		vt->v_part[index].p_size = val2;
		if (vtoc_tag != 0xFFFF) {
			vt->v_part[index].p_tag = vtoc_tag;
			vtoc_tag = 0xFFFF;
		}
		if (vtoc_flag != 0xFFFF) {
			vt->v_part[index].p_flag = vtoc_flag;
			vtoc_flag = 0xFFFF;
		}
		if (status == SUP_EOF) {
			DPRINTF("\nEnd of file\n");
			break;
		}
		if (status != SUP_COLON) {
			(void) fprintf(stderr,
			    gettext("Expecting ':', found '%s'"), token);
			(void) fprintf(stderr,
			    gettext("Line no %d\n"), data_lineno);
			return (-1);
		}

	}
	return (0);
}

static int32_t
find_value(slist_t *slist, char *match_str, int32_t *match_value)
{
	int32_t	i;
	int32_t	nmatches;
	int32_t	length;
	int32_t	match_length;

	nmatches = 0;
	length = 0;

	match_length = strlen(match_str);

	for (; slist->str != NULL; slist++) {
		/*
		 * See how many characters of the token match
		 */
		i = strcnt(match_str, slist->str);
		/*
		 * If it's not the whole token, then it's not a match.
		 */
		if (i  < match_length) {
			continue;
		}
		/*
		 * If it ties with another input, remember that.
		 */
		if (i == length)
			nmatches++;
		/*
		 * If it matches the most so far, record that.
		 */
		if (i > length) {
			*match_value = slist->value;
			nmatches = 1;
			length = i;
		}
	}

	return (nmatches);
}

static int32_t
strcnt(char	*s1, char *s2)
{
	int32_t	i = 0;

	while ((*s1 != '\0') && (*s1++ == *s2++))
		i++;
	return (i);
}

static uint64_t
str2sector(char *str)
{
	int32_t mul_factor = 1;
	char *s1, *s2, *base;
	uint64_t num_sectors;
	uint64_t size;

	base = s2 = (char *)malloc(strlen(str) + 1);
	if (s2 == NULL) {
		PERROR("Malloc failed");
		return (-1);
	}
	*s2 = '\0';



	s1 = str;
	while (*s1) {
		if ((*s1 != 'x') && ((*s1 < 'A') || (*s1 > 'F')) &&
		    ((*s1 < 'a') || (*s1 > 'f')) && ((*s1 < '0') ||
		    (*s1 > '9'))) {
			if (*s1 == 'G') {
					mul_factor = 1024*1024*1024;
					s1++;
			} else if (*s1 == 'M') {
					mul_factor = 1024*1024;
					s1++;
			} else if (*s1 == 'K') {
					mul_factor = 1024;
					s1++;
			}
			if ((*s1 != 'B') || (*(++s1) != NULL)) {
				(void) fprintf(stderr,
				    gettext("Extra chars at the end\n"));
				free(base);
				return (-1);
			}
			break;
		} else {
			*s2++ = *s1++;
			*s2 = '\0';
		}
	}
	*s2 = NULL;

	size = my_atoll(base);
	if ((!mul_factor) || (size == -1)) {
		free(base);
		return (-1);
	}
	num_sectors = size * (uint64_t)mul_factor /512;

	free(base);
	return (num_sectors);
}


int32_t
valid_slice_file(smedia_handle_t handle, int32_t fd, char *file_name,
	struct extvtoc *vt)
{
	struct stat status;
	int32_t ret_val;
	if (stat(file_name, &status)) {
		PERROR(file_name);
		return (-1);
	}
	(void) memset(vt, 0, sizeof (*vt));
	/* Set default tag and flag */
#ifdef sparc
	vt->v_part[0].p_tag = V_ROOT;
	vt->v_part[1].p_tag = V_SWAP;
	vt->v_part[2].p_tag = V_BACKUP;
	vt->v_part[6].p_tag = V_USR;

	vt->v_part[1].p_flag = V_UNMNT; /* Unmountable */
	vt->v_part[2].p_flag = V_UNMNT; /* Unmountable */
#endif

	ret_val = sup_prxfile(file_name, vt);
	if (ret_val < 0)
		return (-1);

#ifdef DEBUG
{
	int32_t i;
	for (i = 0; i < 8; i++) {
		DPRINTF1("\npart %d\n", i);
		DPRINTF1("\t start %llu",  vt->v_part[i].p_start);
		DPRINTF1("\t size %llu ", vt->v_part[i].p_size);
		DPRINTF1("\t tag %d", vt->v_part[i].p_tag);
		DPRINTF1("\t flag %d", vt->v_part[i].p_flag);
	}
}
#endif /* DEBUG */
	if (check_vtoc_sanity(handle, fd, vt) < 0) {
		return (-1);
	}
#ifdef DEBUG
{
	int32_t i;
	for (i = 0; i < 8; i++) {
		DPRINTF1("\npart %d\n", i);
		DPRINTF1("\t start %llu",  vt->v_part[i].p_start);
		DPRINTF1("\t size %llu ", vt->v_part[i].p_size);
		DPRINTF1("\t tag %d", vt->v_part[i].p_tag);
		DPRINTF1("\t flag %d", vt->v_part[i].p_flag);
	}
}
#endif /* DEBUG */
	return (0);
}

#define	SWAP(a, b)	{diskaddr_t tmp; tmp = (a); (a) = (b); (b) = tmp; }

/*
 * On x86 Solaris, the partitioning is done in two levels, fdisk and Solaris
 * VTOC. Where as, on sparc solaris, it is only VTOC. On floppy and PCMCIA
 * also it is assumed to be only VTOC, no fdisk.
 *
 * On sparc, the back up slice can cover the whole medium. But on x86
 * (SCSI/ATAPI disks), the backup slice can cover the solaris partition
 * in fdisk table.
 *	Following table describes how is it handled
 * SPARC:
 * 	SCSI/ATAPI, floppy, pcmcia : don't check for fdisk.
 *				DKIOCGGEOM is sufficient.
 * x86 : floppy, pcmcia : Don't check for fdisk. DKIOCGGEOM is sufficient.
 * 	SCSI/ATAPI : Check for fdisk.
 *			if not present, assume that the solaris
 *				partition covers 100% of the medium
 * 				(minus one cylinder).
 *
 * 		if present :
 *				check for active solaris partition.
 * 				if not found, take the first solaris
 *					partition.
 *			If there are no solaris partitions, its an error, stop.
 */

static int32_t
check_vtoc_sanity(smedia_handle_t handle, int32_t fd, struct extvtoc *vt)
{

	int32_t i, j;
	struct dk_geom dkg;
	int32_t num_backup = 0;
	diskaddr_t backup_size = 0;
	struct part_struct {
		diskaddr_t start;
		diskaddr_t end;
		int32_t num;
	} part[NDKMAP];
	diskaddr_t min_val;
	int32_t min_slice, num_slices;
	diskaddr_t media_size;
	uint32_t cyl_size;
	int sparc_style = 0;	/* sparc_style handling ? */
	struct fdisk_info fdisk;
	int sol_part;
	int total_parts = 0;

#ifdef sparc
	sparc_style = 1;
#endif /* sparc */

	if ((med_info.sm_media_type == SM_FLOPPY) ||
	    (med_info.sm_media_type == SM_PCMCIA_MEM) ||
	    (med_info.sm_media_type == SM_PCMCIA_ATA) ||
	    (med_info.sm_media_type == SM_SCSI_FLOPPY)) {
		sparc_style = 1;
	}

	if (sparc_style) {
		DPRINTF("sparc style true\n");
		if (ioctl(fd, DKIOCGGEOM, &dkg) < 0) {
			PERROR("DKIOCGGEOM Failed");
			return (-1);
		}
		media_size = (diskaddr_t)dkg.dkg_ncyl * dkg.dkg_nhead *
		    dkg.dkg_nsect;
		cyl_size = dkg.dkg_nhead * dkg.dkg_nsect;
	}

	if (!sparc_style) {
	/*
	 * Try to get the fdisk information if available.
	 */
		if (get_fdisk(handle, fd, 0, &fdisk) >= 0) {
			/* fdisk table on disk */
			sol_part = 0xFF;
			for (i = 0; i < FD_NUMPART; i++) {
				if (fdisk.part[i].systid == SUNIXOS ||
				    fdisk.part[i].systid == SUNIXOS2) {
					if (sol_part == 0xFF)
						sol_part = i;
					total_parts++;
					if (fdisk.part[i].bootid == ACTIVE)
						sol_part = i;
				}
			}
			if (sol_part == 0xFF) {
				/* No Solaris partition */

				(void) fprintf(stderr, gettext("No FDISK \
Solaris partition found!\n"));
				return (-1);
			}
			if (total_parts > 1)
				(void) fprintf(stderr, gettext("Multiple FDISK \
Solaris partitions found.\n"));
			media_size = (diskaddr_t)fdisk.part[sol_part].numsect;

			DPRINTF1("sol_part %d\n", sol_part);
			DPRINTF1("media_size %llu\n", media_size);
		} else {
			DPRINTF("Didn't get fdisk\n");
			/*
			 * No fdisk partition available. Assume a 100% Solaris.
			 * partition.
			 * Try getting disk geometry.
			 */
			if (ioctl(fd, DKIOCGGEOM, &dkg) < 0)
				if (ioctl(fd, DKIOCG_PHYGEOM, &dkg) < 0) {
					DPRINTF("DKIOCG_PHYGEOM ioctl failed");
					return (-1);
			}
			/* On x86 platform 1 cylinder is used for fdisk table */
			dkg.dkg_ncyl = dkg.dkg_ncyl - 1;
			media_size = (diskaddr_t)dkg.dkg_ncyl * dkg.dkg_nhead *
			    dkg.dkg_nsect;
		}
	}

#ifdef DEBUG
	DPRINTF1("Ncyl %d\n", dkg.dkg_ncyl);
	DPRINTF1("nhead %d\n", dkg.dkg_nhead);
	DPRINTF1("nsect %d\n", dkg.dkg_nsect);
#endif /* DEBUG */

	if (media_size == 0) {
		media_size = (uint32_t)med_info.sm_capacity;
	}

	(void) memset(&part, 0, sizeof (part));
	for (i = 0, j = 0; i < NDKMAP; i++) {
		if (vt->v_part[i].p_tag == V_BACKUP) {
			if (vt->v_part[i].p_start != 0) {
				(void) fprintf(stderr,
				    gettext(
			"Backup slice should start at sector 0\n"));
			return (-1);
			}
			backup_size = vt->v_part[i].p_size;
			num_backup++;
			continue;
		}
		if (vt->v_part[i].p_size) {

			if (sparc_style) {
				if (vt->v_part[i].p_start % cyl_size) {
					(void) fprintf(stderr,
					    gettext(
			"Slice %d does not start on cylinder boundary\n"), i);
					(void) fprintf(stderr,
					    gettext(
			"Cylinder size %d 512 byte sectors\n"), cyl_size);
					return (-1);
				}
			}
			part[j].start = vt->v_part[i].p_start;
			part[j].end = vt->v_part[i].p_start +
			    vt->v_part[i].p_size -1;
			part[j].num = i;
			j++;
		}
	}
	if (num_backup > 1) {
		(void) fprintf(stderr,
		    gettext("Maximum one backup slice is allowed\n"));
		(void) smedia_release_handle(handle);
		(void) close(fd);
		exit(1);
	}
	num_slices = j;

	for (i = 0; i < num_slices; i++) {
		min_val = part[i].start;
		min_slice = i;
		for (j = i+1; j < num_slices; j++) {
			if (part[j].start < min_val) {
				min_val = part[j].start;
				min_slice = j;
			}
		}
		if (min_slice != i) {
			SWAP(part[i].start, part[min_slice].start)
			SWAP(part[i].end, part[min_slice].end)
			SWAP(part[i].num, part[min_slice].num)
		}
	}

#ifdef DEBUG
	for (i = 0; i < num_slices; i++) {
		DPRINTF4("\n %d (%d) : %llu, %llu", i, part[i].num,
		    part[i].start, part[i].end);
	}
#endif /* DEBUG */

	if (backup_size > media_size) {
		if (sparc_style) {
			(void) fprintf(stderr,
			    gettext(
			"Backup slice extends beyond size of media\n"));
			(void) fprintf(stderr,
			    gettext("media size : %llu sectors \n"),
			    media_size);
		} else {

			(void) fprintf(stderr,
			    gettext("Backup slice extends beyond size of FDISK \
Solaris partition\n"));
			(void) fprintf(stderr,
			    gettext(
			"FDISK Solaris partition size : %llu sectors \n"),
			    media_size);
		}
		return (-1);
	}

	/*
	 * If we have only backup slice return success here.
	 */
	if (num_slices == 0)
		return (0);

	if (backup_size) {
		if (part[num_slices - 1].end > backup_size) {
			(void) fprintf(stderr,
			    gettext("Slice %d extends beyond backup slice.\n"),
			    part[num_slices -1].num);
			return (-1);
		}
	} else {
		if (part[num_slices - 1].end > media_size) {
			if (sparc_style) {
				(void) fprintf(stderr,
				    gettext(
				"Slice %d extends beyond media size\n"),
				    part[num_slices -1].num);
				(void) fprintf(stderr,
				    gettext("media size : %llu sectors \n"),
				    media_size);
			} else {
				(void) fprintf(stderr,
				    gettext("Slice %d extends beyond FDISK"
				    " Solaris partition size\n"),
				    part[num_slices -1].num);
				(void) fprintf(stderr, gettext(
				    "FDISK Solaris partition size : %llu "
				    "sectors \n"), media_size);
			}
			return (-1);
		}
	}



	for (i = 0; i < num_slices; i++) {
		if (i == 0)
			continue;
		if (part[i].start <= part[i-1].end) {
			(void) fprintf(stderr,
			    gettext("Overlap between slices %d and %d\n"),
			    part[i-1].num, part[i].num);
			(void) smedia_release_handle(handle);
			(void) close(fd);
			exit(1);
		}
	}

	return (0);
}


static int32_t
get_fdisk(smedia_handle_t handle, int32_t fd, int32_t offset,
	struct fdisk_info *fdisk)
{
	struct mboot *boot_sec;
	struct ipart *part;
	char *buf;
	int32_t i, ret;
	int	save_errno;

	/* Read the master boot program */

	buf = (char *)malloc(med_info.sm_blocksize);
	if (buf == NULL) {
		PERROR("malloc failed");
		exit(1);
	}
	errno = 0;
	ret = ioctl(fd, DKIOCGMBOOT, buf);
	if (ret < 0) {
		if (errno != ENOTTY) {
			PERROR("DKIOCGMBOOT ioctl failed");
			return (-1);
		}

		/* Turn on privileges. */
		(void) __priv_bracket(PRIV_ON);

		ret = smedia_raw_read(handle,
		    (diskaddr_t)offset/med_info.sm_blocksize,
		    buf, med_info.sm_blocksize);

		/* Turn off privileges. */
		(void) __priv_bracket(PRIV_OFF);

		save_errno = errno;
		errno = save_errno;
		if (ret != med_info.sm_blocksize) {
			if (errno == ENOTSUP) {
				errno = 0;
				if (lseek(fd, offset, SEEK_SET)) {
					PERROR("Seek failed:");
					free(buf);
					return (-1);
				}

				/* Turn on privileges. */
				(void) __priv_bracket(PRIV_ON);

				ret = read(fd, buf, sizeof (struct mboot));

				/* Turn off privileges. */
				(void) __priv_bracket(PRIV_OFF);

				if (ret != sizeof (struct mboot)) {
					PERROR("Could not read "
					    "master boot record");
					free(buf);
					return (-1);
				}
			} else {
				PERROR("Could not read master boot record");
				free(buf);
				return (-1);
			}
		}
	}
	/* LINTED pointer cast may result in improper alignment */
	boot_sec = (struct mboot *)buf;

	/* Is this really a master boot record? */
	if (les(boot_sec->signature) != MBB_MAGIC) {
		DPRINTF("fdisk: Invalid master boot file \n");
		DPRINTF2("Bad magic number: is %x, should be %x.\n",
		    les(boot_sec->signature), MBB_MAGIC);
		free(buf);
		return (-1);
	}

	for (i = 0; i < FD_NUMPART; i++) {
		DPRINTF1("part %d\n", i);
		/* LINTED pointer cast may result in improper alignment */
		part = (struct ipart *)&boot_sec->parts[i *
		    sizeof (struct ipart)];
		fdisk->part[i].bootid = part->bootid;
		if (part->bootid && (part->bootid != ACTIVE)) {
			/* Hmmm...not a valid fdisk! */
			return (-1);
		}
		fdisk->part[i].systid = part->systid;

		/* To avoid the misalign access in sparc */

		fdisk->part[i].relsect = lel(GET_32(&(part->relsect)));
		fdisk->part[i].numsect = lel(GET_32(&(part->numsect)));

		DPRINTF1("\tboot id 0x%x\n", part->bootid);
		DPRINTF1("\tsystem id 0x%x\n", part->systid);
		DPRINTF1("\trel sector 0x%x\n", fdisk->part[i].relsect);
		DPRINTF1("\tnum sector 0x%x\n", fdisk->part[i].numsect);
	}
	free(buf);
	return (0);
}


/*
 * wrrite_defualt_label(int32_t fd)
 * 	fd = file descriptor for the device.
 *
 * For sparc solaris
 *	Create a vtoc partition with
 *		slice 0 = slice 2 = medium capacity.
 *	The cyl, head, sect (CHS) values are computed as done in sd
 *	capacity <= 1GB,
 *		nhead = 64, nsect = 32
 *	capacity > 1gb,
 *		nhead = 255, nsect = 63
 *
 * For x86 solaris
 *	Create a fdisk partition,
 *		partition 0 covers the full medium, the partition
 *		type is set to Solaris.
 *	Then create solaris vtoc. The algorithm is same as sparc solaris.
 *	But the capacity is reduced by 1 cyl, to leave space for fdisk table.
 */

#ifdef sparc
/*ARGSUSED*/
void
write_default_label(smedia_handle_t handle, int32_t fd)
{

	struct extvtoc v_toc;
	uint32_t nhead, numcyl, nsect;
	diskaddr_t capacity;
	int32_t ret;
	char asciilabel[LEN_DKL_ASCII];
	char asciilabel2[LEN_DKL_ASCII] = "DEFAULT\0";
	uint32_t acyl = 2;


	DPRINTF("Writing default vtoc\n");
	(void) memset(&v_toc, 0, sizeof (v_toc));


	v_toc.v_nparts = V_NUMPAR;
	v_toc.v_sanity = VTOC_SANE;
	v_toc.v_version = V_VERSION;
	v_toc.v_sectorsz = DEV_BSIZE;

	/*
	 * For the head, cyl and number of sector per track,
	 * if the capacity <= 1GB, head = 64, sect = 32.
	 * else head = 255, sect 63
	 * NOTE: the capacity should be equal to C*H*S values.
	 * This will cause some truncation of size due to
	 * round off errors.
	 */
	if ((uint32_t)med_info.sm_capacity <= 0x200000) {
		nhead = 64;
		nsect = 32;
	} else {
		nhead = 255;
		nsect = 63;
	}

	numcyl = (uint32_t)med_info.sm_capacity / (nhead * nsect);
	capacity = (diskaddr_t)nhead * nsect * numcyl;

	v_toc.v_part[0].p_start = 0;
	v_toc.v_part[0].p_size = capacity;
	v_toc.v_part[0].p_tag  = V_ROOT;
	v_toc.v_part[0].p_flag = 0;	/* Mountable */

	v_toc.v_part[2].p_start = 0;
	v_toc.v_part[2].p_size = capacity;
	v_toc.v_part[2].p_tag  = V_BACKUP;
	v_toc.v_part[2].p_flag = V_UNMNT;

	/* Create asciilabel for compatibility with format utility */
	(void) snprintf(asciilabel, sizeof (asciilabel),
	    "%s cyl %d alt %d hd %d sec %d",
	    asciilabel2, numcyl, acyl, nhead, nsect);
	(void) memcpy(v_toc.v_asciilabel, asciilabel,
	    LEN_DKL_ASCII);

	errno = 0;

	/* Turn on privileges. */
	(void) __priv_bracket(PRIV_ON);

	ret = write_extvtoc(fd, &v_toc);

	/* Turn off privileges. */
	(void) __priv_bracket(PRIV_OFF);

	if (ret < 0) {
		PERROR("write VTOC failed");
		DPRINTF1("Errno = %d\n", errno);
	}
}

#else /* !sparc */
#ifdef i386

void
write_default_label(smedia_handle_t handle, int32_t fd)
{

	int32_t i, ret;
	struct dk_geom  dkg;
	struct extvtoc v_toc;
	int tmp_fd;
	char *fdisk_buf;
	struct mboot boot_code;		/* Buffer for master boot record */
	struct ipart parts[FD_NUMPART];
	uint32_t numcyl, nhead, nsect;
	uint32_t unixend;
	uint32_t blocksize;
	diskaddr_t capacity;
	int	save_errno;
	size_t	bytes_written;
	char asciilabel[LEN_DKL_ASCII];
	char asciilabel2[LEN_DKL_ASCII] = "DEFAULT\0";
	uint32_t acyl = 2;

	DPRINTF("Writing default fdisk table and vtoc\n");
	(void) memset(&v_toc, 0, sizeof (v_toc));
	/*
	 * Try getting disk geometry.
	 */
	if (ioctl(fd, DKIOCGGEOM, &dkg) < 0)
		if (ioctl(fd, DKIOCG_PHYGEOM, &dkg) < 0) {

			DPRINTF("DKIOCG_PHYGEOM ioctl failed");
			return;
	}

	tmp_fd = open("/usr/lib/fs/ufs/mboot", O_RDONLY);
	if (tmp_fd <= 0) {
		return;
	}

	if (read(tmp_fd, &boot_code, sizeof (struct mboot))
			!= sizeof (struct mboot)) {
		(void) close(tmp_fd);
		return;
	}

	blocksize = med_info.sm_blocksize;
	fdisk_buf = (char *)malloc(blocksize);
	if (fdisk_buf == NULL) {
		DPRINTF("malloc for fdisk_buf failed\n");
		return;
	}

	(void) memset(&parts, 0, sizeof (parts));

	for (i = 0; i < FD_NUMPART; i++) {
		parts[i].systid = UNUSED;
		parts[i].numsect = lel(UNUSED);
		parts[i].relsect = lel(UNUSED);
		parts[i].bootid = 0;
	}

	numcyl = dkg.dkg_ncyl;
	nhead = dkg.dkg_nhead;
	nsect = dkg.dkg_nsect;

	parts[0].bootid = ACTIVE;
	parts[0].begsect = 1;

	unixend = numcyl;

	parts[0].relsect = lel(nhead * nsect);
	parts[0].numsect = lel(((diskaddr_t)numcyl * nhead * nsect));
	parts[0].systid = SUNIXOS2;   /* Solaris */
	parts[0].beghead = 0;
	parts[0].begcyl = 1;
	parts[0].endhead = nhead - 1;
	parts[0].endsect = (nsect & 0x3f) |
	    (char)((unixend >> 2) & 0x00c0);
	parts[0].endcyl = (char)(unixend & 0x00ff);

	(void) memcpy(&(boot_code.parts), parts, sizeof (parts));
	(void) memcpy(fdisk_buf, &boot_code, sizeof (boot_code));

	/* Turn on privileges. */
	(void) __priv_bracket(PRIV_ON);

	ret = ioctl(fd, DKIOCSMBOOT, fdisk_buf);

	/* Turn off privileges. */
	(void) __priv_bracket(PRIV_OFF);

	if (ret == -1) {
		if (errno != ENOTTY) {
			PERROR("DKIOCSMBOOT ioctl Failed");
			return;
		}

		/* Turn on privileges. */
		(void) __priv_bracket(PRIV_ON);

		bytes_written = smedia_raw_write(handle, (diskaddr_t)0,
		    fdisk_buf, blocksize);

		/* Turn off privileges. */
		(void) __priv_bracket(PRIV_OFF);

		save_errno = errno;
		errno = save_errno;
		if (bytes_written != blocksize) {
			if (errno == ENOTSUP) {

			    /* Turn on privileges. */
				(void) __priv_bracket(PRIV_ON);

				ret = write(fd, fdisk_buf, blocksize);

			    /* Turn off privileges. */
				(void) __priv_bracket(PRIV_OFF);

				if (ret != blocksize) {
					return;
				}
			} else {
				return;
			}
		}
	}
	capacity = (diskaddr_t)(numcyl - 1) * nhead * nsect;

	v_toc.v_nparts = V_NUMPAR;
	v_toc.v_sanity = VTOC_SANE;
	v_toc.v_version = V_VERSION;
	v_toc.v_sectorsz = DEV_BSIZE;

	v_toc.v_part[0].p_start = 0;
	v_toc.v_part[0].p_size = capacity;
	v_toc.v_part[0].p_tag  = V_ROOT;
	v_toc.v_part[0].p_flag = 0;	/* Mountable */

	v_toc.v_part[2].p_start = 0;
	v_toc.v_part[2].p_size = capacity;
	v_toc.v_part[2].p_tag  = V_BACKUP;
	v_toc.v_part[2].p_flag = V_UNMNT;

	/* Create asciilabel for compatibility with format utility */
	(void) snprintf(asciilabel, sizeof (asciilabel),
	    "%s cyl %d alt %d hd %d sec %d",
	    asciilabel2, numcyl, acyl, nhead, nsect);
	(void) memcpy(v_toc.v_asciilabel, asciilabel,
	    LEN_DKL_ASCII);

	errno = 0;


	/* Turn on privileges. */
	(void) __priv_bracket(PRIV_ON);

	ret = write_extvtoc(fd, &v_toc);

	/* Turn off privileges. */
	(void) __priv_bracket(PRIV_OFF);

	if (ret < 0) {
		PERROR("write VTOC failed");
		DPRINTF1("Errno = %d\n", errno);
	}
}

#else	/* !i386 */

#error One of sparc or i386 must be defined!

#endif /* i386 */
#endif /* sparc */

/*
 * void overwrite_metadata(int32_t fd, smedia_handle_t handle)
 *
 * purpose : quick format does not erase the data on Iomega
 * zip/jaz media. So, the meta data on the disk should be erased.
 *
 * If there is a valid fdisk table,
 * 	erase first 64K of each partition.
 * If there is a valid vtoc,
 * 	erase first 64k of each slice.
 * Then erase the 0th sector (the home for vtoc and fdisk) of the disk.
 * Note that teh vtoc on x86 resides in one of the fdisk partition.
 * So delay the erasing of the solaris partition until the vtoc is read.
 */

void
overwrite_metadata(int32_t fd, smedia_handle_t handle)
{

	struct fdisk_info fdisk;
	diskaddr_t sol_offset = 0;
	int i, ret;
	struct extvtoc t_vtoc;
#ifdef i386
	diskaddr_t sol_size = 0;
	int32_t active = 0;
#endif /* i386 */

	/* Get fdisk info. */
	if (get_fdisk(handle, fd, 0, &fdisk) >= 0) {
		/* Got a valid fdisk */
		for (i = 0; i < FD_NUMPART; i++) {

			if (fdisk.part[i].numsect == 0)
				continue;
			if ((fdisk.part[i].systid == UNUSED) ||
			    (fdisk.part[i].systid == 0))
				continue;
#ifdef i386
			if (fdisk.part[i].systid == SUNIXOS ||
			    fdisk.part[i].systid == SUNIXOS2) {
				if (!sol_offset) {
					sol_offset = fdisk.part[i].relsect;
					sol_size = fdisk.part[i].numsect;
					if (fdisk.part[i].bootid == ACTIVE)
						active = 1;
					continue;
				} else if ((fdisk.part[i].bootid == ACTIVE) &&
				    (!active)) {
					erase(handle, sol_offset, sol_size);
					sol_offset = fdisk.part[i].relsect;
					sol_size = fdisk.part[i].numsect;
					active = 1;
					continue;
				}
			}
#endif /* i386 */
			erase(handle, (diskaddr_t)fdisk.part[i].relsect,
			    (diskaddr_t)fdisk.part[i].numsect);
		}
	}

	(void) memset(&t_vtoc, 0, sizeof (t_vtoc));

	if (sol_offset) {
		/* fdisk x86 Solaris partition */
		/* VTOC location in solaris partition is DK_LABEL_LOC */

		/* Turn on privileges. */
		(void) __priv_bracket(PRIV_ON);

		ret = read_extvtoc(fd, &t_vtoc);

		/* Turn off privileges. */
		(void) __priv_bracket(PRIV_OFF);

		if (ret < 0) {
			/* No valid vtoc, erase fdisk table. */
			erase(handle, (diskaddr_t)0, (diskaddr_t)1);
			return;
		}
	} else {
		/* Sparc Solaris or x86 solaris with faked fdisk */

		/* Turn on privileges */
		(void) __priv_bracket(PRIV_ON);

		ret = read_extvtoc(fd, &t_vtoc);

		/* Turn off privileges. */
		(void) __priv_bracket(PRIV_OFF);

		if (ret < 0) {
			/* No valid vtoc, erase from 0th sector */
			erase(handle, (diskaddr_t)0,
			    (uint32_t)med_info.sm_capacity);
			return;
		}
	}

	for (i = 0; i < V_NUMPAR; i++) {
		if (t_vtoc.v_part[i].p_size != 0) {
			erase(handle, sol_offset + t_vtoc.v_part[i].p_start,
			    t_vtoc.v_part[i].p_size);
			/*
			 * To make the udfs not recognise the partition we will
			 * erase sectors 256, (p_size-256) and psize.
			 */
			erase(handle,
			    sol_offset + t_vtoc.v_part[i].p_start + 256,
			    (diskaddr_t)1);
			erase(handle,
			    (sol_offset + t_vtoc.v_part[i].p_start +
			    t_vtoc.v_part[i].p_size - 256),
			    (diskaddr_t)1);
			erase(handle,
			    (sol_offset + t_vtoc.v_part[i].p_start +
			    t_vtoc.v_part[i].p_size - 1),
			    (diskaddr_t)1);
		}
	}

	/*
	 * If x86 fdisk solaris partition, erase the vtoc also.
	 * for sparc, the erasing 0the sector erases vtoc.
	 */
	if (sol_offset) {
		erase(handle, sol_offset, (diskaddr_t)DK_LABEL_LOC + 2);
	}

	/*
	 * erase the 0th sector, it is not guaranteed to be
	 * erased in the above sequence.
	 */

	erase(handle, (diskaddr_t)0, (diskaddr_t)1);
}

/*
 * void erase(smedia_handle_t handle, uint32_t offset, uint32_t size)
 *
 * Initialize the media with '0' from offset 'offset' upto 'size'
 * or 128 blocks(64k), whichever is smaller.
 */

static void
erase(smedia_handle_t handle, diskaddr_t offset, diskaddr_t size)
{
	char *buf;
	diskaddr_t nblocks = size;
	int32_t ret;


	nblocks = (nblocks < 128) ? nblocks : 128;
	buf = (char *)malloc(nblocks * med_info.sm_blocksize);
	if (buf == NULL) {
		PERROR("malloc failed");
		return;
	}
	(void) memset(buf, 0, (size_t)nblocks * med_info.sm_blocksize);

	/* Turn on privileges. */
	(void) __priv_bracket(PRIV_ON);

	ret = smedia_raw_write(handle, offset, buf,
	    (size_t)nblocks * med_info.sm_blocksize);

	/* Turn off privileges. */
	(void) __priv_bracket(PRIV_OFF);

	if (ret != (nblocks * med_info.sm_blocksize))
		PERROR("error in writing\n");

	free(buf);

}