root/usr/src/cmd/rmformat/rmf_slice.c
/*
 * 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) != '\0')) {
                                (void) fprintf(stderr,
                                    gettext("Extra chars at the end\n"));
                                free(base);
                                return (-1);
                        }
                        break;
                } else {
                        *s2++ = *s1++;
                        *s2 = '\0';
                }
        }
        *s2 = '\0';

        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);
}


/*
 * write_default_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("/boot/pmbr", 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);

}