root/usr/src/cmd/fs.d/udfs/common/ud_lib.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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>
#include <libintl.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/int_types.h>
#include <sys/dkio.h>
#include <sys/cdio.h>
#include <sys/vtoc.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/fs/udf_volume.h>
#include "ud_lib.h"

extern char *getfullrawname(char *);

static int32_t ud_get_ecma_ver(ud_handle_t, uint32_t);
static int32_t ud_get_fs_bsize(ud_handle_t, uint32_t, uint32_t *);
static int32_t ud_parse_fill_vds(ud_handle_t, struct vds *, uint32_t, uint32_t);
static int32_t  ud_read_and_translate_lvd(ud_handle_t, uint32_t, uint32_t);
static int32_t ud_get_latest_lvid(ud_handle_t, uint32_t, uint32_t);
static int32_t  ud_get_latest_fsd(ud_handle_t, uint16_t, uint32_t, uint32_t);

static uint16_t ud_crc(uint8_t *, int32_t);
static int32_t UdfTxName(uint16_t *, int32_t);
static int32_t UncompressUnicode(int32_t, uint8_t *, uint16_t *);
static int32_t ud_compressunicode(int32_t, int32_t, uint16_t *, uint8_t *);
static int32_t ud_convert2utf8(uint8_t *, uint8_t *, int32_t);
static int32_t ud_convert2utf16(uint8_t *, uint8_t *, int32_t);


int
ud_init(int fd, ud_handle_t *hp)
{
        struct ud_handle *h;

        if ((h = calloc(1, sizeof (struct ud_handle))) == NULL) {
                return (ENOMEM);
        }
        h->fd = fd;
        *hp = h;
        return (0);
}

void
ud_fini(ud_handle_t h)
{
        free(h);
}

/* ARGSUSED */
int32_t
ud_open_dev(ud_handle_t h, char *special, uint32_t flags)
{
        char *temp;
        struct stat i_stat, r_stat;

        (void) bzero(&i_stat, sizeof (struct stat));
        (void) bzero(&r_stat, sizeof (struct stat));

        temp = special;
        /*
         * Get the stat structure
         */
        if (stat(special, &i_stat) == 0) {
                if ((i_stat.st_mode & S_IFMT) == S_IFBLK) {

                        /*
                         * Block device try to convert to raw device
                         */

                        temp = getfullrawname(special);

                        /*
                         * Stat the converted device name and verify
                         * both the raw and block device belong to
                         * the same device
                         */
                        if (stat(temp, &r_stat) < 0) {
                                temp = special;
                        } else {
                                if (((r_stat.st_mode & S_IFMT) == S_IFBLK) ||
                                        (r_stat.st_rdev != i_stat.st_rdev)) {
                                        temp = special;
                                }
                        }
                }
        }

        /*
         * Now finally open the device
         */
        h->fd = open(temp, flags);

        return (h->fd);
}

/* ARGSUSED */
void
ud_close_dev(ud_handle_t h)
{
        /*
         * Too simple Just close it
         */
        (void) close(h->fd);
}

int32_t
ud_read_dev(ud_handle_t h, uint64_t offset, uint8_t *buf, uint32_t count)
{
        /*
         * Seek to the given offset
         */
        if (lseek(h->fd, offset, SEEK_SET) == -1) {
                return (1);
        }

        /*
         * Read the required number of bytes
         */
        if (read(h->fd, buf, count) != count) {
                return (1);
        }
        return (0);
}

int32_t
ud_write_dev(ud_handle_t h, uint64_t offset, uint8_t *buf, uint32_t count)
{
        /*
         * Seek to the given offset
         */
        if (lseek(h->fd, offset, SEEK_SET) == -1) {
                return (1);
        }

        /*
         * Read the appropriate number of bytes
         */
        if (write(h->fd, buf, count) != count) {
                return (1);
        }
        return (0);
}

/* ----- BEGIN Read and translate the on disk VDS to IN CORE format -------- */

int32_t
ud_fill_udfs_info(ud_handle_t h)
{
        struct  anch_vol_desc_ptr       *avdp = NULL;
        uint32_t                        offset = 0;

        if (ioctl(h->fd, CDROMREADOFFSET, &offset) == -1) {
                offset = 0;
        }

        h->udfs.flags = INVALID_UDFS;

        h->udfs.ecma_version = ud_get_ecma_ver(h, offset);
        if (h->udfs.ecma_version == UD_ECMA_UNKN) {
                return (1);
        }

        h->udfs.lbsize = ud_get_fs_bsize(h, offset, &h->udfs.avdp_loc);
        if (h->udfs.lbsize == 0) {
                return (2);
        }

        h->udfs.avdp_len = lb_roundup(512, h->udfs.lbsize);


        if ((avdp = (struct anch_vol_desc_ptr *)
                        malloc(h->udfs.lbsize)) == NULL) {
                return (3);
        }
        if (ud_read_dev(h, h->udfs.avdp_loc * h->udfs.lbsize,
                        (uint8_t *)avdp, h->udfs.lbsize) != 0) {
                free(avdp);
                return (4);
        }
        if (ud_verify_tag(h, &avdp->avd_tag, UD_ANCH_VOL_DESC,
                        h->udfs.avdp_loc, 1, 0) != 0) {
                free(avdp);
                return (5);
        }

        h->udfs.mvds_loc = SWAP_32(avdp->avd_main_vdse.ext_loc);
        h->udfs.mvds_len = SWAP_32(avdp->avd_main_vdse.ext_len);

        h->udfs.rvds_loc = SWAP_32(avdp->avd_res_vdse.ext_loc);
        h->udfs.rvds_len = SWAP_32(avdp->avd_res_vdse.ext_len);

        free(avdp);

        /*
         * get information from mvds and rvds
         */
        if (ud_parse_fill_vds(h, &h->udfs.mvds,
                        h->udfs.mvds_loc, h->udfs.mvds_len) == 0) {
                h->udfs.flags |= VALID_MVDS;
        }
        if (ud_parse_fill_vds(h, &h->udfs.rvds,
                        h->udfs.rvds_loc, h->udfs.rvds_len) == 0) {
                h->udfs.flags |= VALID_RVDS;
        }

        if ((h->udfs.flags & (VALID_MVDS | VALID_RVDS)) == 0) {
                return (6);
        }

        /*
         * If we are here we have
         * a valid Volume Descriptor Seqence
         * Read and understand lvd
         */
        if (h->udfs.flags & VALID_MVDS) {
                if (ud_read_and_translate_lvd(h, h->udfs.mvds.lvd_loc,
                                h->udfs.mvds.lvd_len) != 0) {
                        return (7);
                }
        } else {
                if (ud_read_and_translate_lvd(h, h->udfs.rvds.lvd_loc,
                                h->udfs.rvds.lvd_len) != 0) {
                        return (8);
                }
        }

        h->udfs.flags |= VALID_UDFS;

        return (0);
}

static int32_t
ud_get_ecma_ver(ud_handle_t h, uint32_t offset)
{
        uint8_t *buf;
        uint64_t off;
        uint64_t end_off;
        struct nsr_desc *ndsc;
        uint32_t ecma_ver = UD_ECMA_UNKN;

        /*
         * Allocate a buffer of size UD_VOL_REC_BSZ
         */
        if ((buf = (uint8_t *)malloc(UD_VOL_REC_BSZ)) == NULL) {

                /*
                 * Uh could not even allocate this much
                 */
                goto end;
        }

        /*
         * Start from 32k and keep reading 2k blocks we
         * should be able to find NSR if we have one by 256 * 2k bytes
         */
        off = offset * 2048 + UD_VOL_REC_START;
        end_off = offset * 2048 + UD_VOL_REC_END;
        for (; off < end_off; off += UD_VOL_REC_BSZ) {

                if (ud_read_dev(h, off, buf, UD_VOL_REC_BSZ) == 0) {

                        ndsc = (struct nsr_desc *)buf;
                        /*
                         * Is this either NSR02 or NSR03
                         */
                        if ((ndsc->nsr_str_type == 0) &&
                                (ndsc->nsr_ver == 1) &&
                                (ndsc->nsr_id[0] == 'N') &&
                                (ndsc->nsr_id[1] == 'S') &&
                                (ndsc->nsr_id[2] == 'R') &&
                                (ndsc->nsr_id[3] == '0') &&
                                        ((ndsc->nsr_id[4] == '2') ||
                                        (ndsc->nsr_id[4] == '3'))) {

                                (void) strncpy((char *)h->udfs.ecma_id,
                                        (char *)ndsc->nsr_id, 5);

                                switch (ndsc->nsr_id[4]) {
                                case '2' :

                                        /*
                                         * ECMA 167/2
                                         */
                                        ecma_ver = UD_ECMA_VER2;
                                        goto end;
                                case '3' :

                                        /*
                                         * ECMA 167/3
                                         */
                                        ecma_ver = UD_ECMA_VER3;
                                        goto end;
                                }
                        }
                }
        }

end:
        /*
         * Cleanup
         */
        free(buf);
        return (ecma_ver);
}

static uint32_t last_block_index[] = {0, 0, 256, 2, 2 + 256,
                150, 150 + 256, 152, 152 + 256};

static int32_t
ud_get_fs_bsize(ud_handle_t h, uint32_t offset, uint32_t *avd_loc)
{
        uint64_t off;
        int32_t index, bsize, shift, end_index;
        uint32_t num_blocks, sub_blk;
        uint8_t *buf = NULL;
        struct anch_vol_desc_ptr *avdp;

        if ((buf = (uint8_t *)malloc(MAXBSIZE)) == NULL) {
                return (0);
        }

        /*
         * If we could figure out the last block
         * search at 256, N, N - 256 blocks
         * otherwise just check at 256
         */
        if (ud_get_num_blks(h, &num_blocks) != 0) {
                end_index = 1;
                num_blocks = 0;
        } else {
                end_index = sizeof (last_block_index) / 4;
        }

        for (index = 0; index < end_index; index++) {
                sub_blk = last_block_index[index];

                /*
                 * Start guessing from DEV_BSIZE to MAXBSIZE
                 */
                for (bsize = DEV_BSIZE, shift = 0;
                        bsize <= MAXBSIZE; bsize <<= 1, shift++) {

                        if (index == 0) {

                                /*
                                 * Check if we atleast have 256 of bsize
                                 * blocks on the device
                                 */
                                if ((end_index == 0) ||
                                        (num_blocks > (256 << shift))) {
                                        *avd_loc = 256;
                                        if (bsize <= 2048) {
                                                *avd_loc +=
                                                        offset * 2048 / bsize;
                                        } else {
                                                *avd_loc +=
                                                        offset / (bsize / 2048);
                                        }
                                } else {
                                        continue;
                                }
                        } else {
                                /*
                                 * Calculate the bsize avd block
                                 */
                                if ((num_blocks) &&
                                        (num_blocks > (sub_blk << shift))) {
                                        *avd_loc = (num_blocks >> shift) -
                                                sub_blk;
                                } else {
                                        continue;
                                }
                        }

                        off = (uint64_t)*avd_loc * bsize;

                        /*
                         * Read bsize bytes at off
                         */
                        if (ud_read_dev(h, off, buf, bsize) != 0) {
                                continue;
                        }

                        /*
                         * Check if we have a Anchor Volume Descriptor here
                         */

                        /* LINTED */
                        avdp = (struct anch_vol_desc_ptr *)buf;
                        if (ud_verify_tag(h, &avdp->avd_tag,
                                UD_ANCH_VOL_DESC, *avd_loc, 1, 0) != 0) {
                                continue;
                        }
                        goto end;
                }
        }

end:
        if (bsize > MAXBSIZE) {
                bsize = 0;
                *avd_loc = 0;
        }
        free(buf);
        return (bsize);
}

static int32_t
ud_parse_fill_vds(ud_handle_t h, struct vds *v,
        uint32_t vds_loc, uint32_t vds_len)
{
        uint8_t *addr, *taddr, *eaddr;
        uint16_t id;
        int32_t i;
        uint64_t off;
        struct tag *tag;
        struct pri_vol_desc *pvd;
        struct log_vol_desc *lvd;
        struct vol_desc_ptr *vds;
        struct unall_spc_desc *usd;

begin:
        if ((addr = (uint8_t *)malloc(vds_len)) == NULL) {
                return (1);
        }

        off = vds_loc * h->udfs.lbsize;
        if (ud_read_dev(h, off, addr, vds_len) != 0) {
                goto end;
        }

        for (taddr = addr, eaddr = addr + h->udfs.mvds_len; taddr < eaddr;
                        taddr += h->udfs.lbsize, vds_loc ++) {

                /* LINTED */
                tag = (struct tag *)taddr;
                id = SWAP_16(tag->tag_id);
                /*
                 * If you cannot verify the tag just skip it
                 * This is not a fatal error
                 */
                if (ud_verify_tag(h, tag, id, vds_loc, 1, 0) != 0) {
                        continue;
                }
                switch (id) {
                case UD_PRI_VOL_DESC :

                        /*
                         * Primary Volume Descriptor
                         */
                        /* LINTED */
                        pvd = (struct pri_vol_desc *)taddr;
                        if ((v->pvd_len == 0) ||
                                (SWAP_32(pvd->pvd_vdsn) > v->pvd_vdsn)) {
                                v->pvd_vdsn = SWAP_32(pvd->pvd_vdsn);
                                v->pvd_loc = vds_loc;
                                v->pvd_len = h->udfs.lbsize;
                        }
                        break;
                case UD_VOL_DESC_PTR :

                        /*
                         * Curent sequence is continued from
                         * the location pointed by vdp
                         */
                        /* LINTED */
                        vds = (struct vol_desc_ptr *)taddr;

                        if (SWAP_32(vds->vdp_nvdse.ext_len) != 0) {
                                vds_loc = SWAP_32(vds->vdp_nvdse.ext_loc);
                                vds_len = SWAP_32(vds->vdp_nvdse.ext_len);
                                free(addr);
                                goto begin;
                        }
                        break;
                case UD_IMPL_USE_DESC :

                        /*
                         * Implementation Use Volume Descriptor
                         */
                        v->iud_loc = vds_loc;
                        v->iud_len = lb_roundup(512, h->udfs.lbsize);
                        break;
                case UD_PART_DESC :
                        {
                                struct ud_part *p;
                                struct phdr_desc *ph;
                                struct part_desc *pd;

                                /*
                                 * Partition Descriptor
                                 */
                                /* LINTED */
                                pd = (struct part_desc *)taddr;

                                for (i = 0; i < h->n_parts; i++) {
                                        p = &h->part[i];

                                        if ((SWAP_16(pd->pd_pnum) ==
                                                        p->udp_number) &&
                                                (SWAP_32(pd->pd_vdsn) >
                                                        p->udp_seqno)) {
                                                break;
                                        }
                                }

                                v->part_loc[i] = vds_loc;
                                v->part_len[i] =
                                        lb_roundup(512, h->udfs.lbsize);

                                p = &h->part[i];
                                p->udp_number = SWAP_16(pd->pd_pnum);
                                p->udp_seqno = SWAP_32(pd->pd_vdsn);
                                p->udp_access = SWAP_32(pd->pd_acc_type);
                                p->udp_start = SWAP_32(pd->pd_part_start);
                                p->udp_length = SWAP_32(pd->pd_part_length);

                                /* LINTED */
                                ph = (struct phdr_desc *)pd->pd_pc_use;
                                if (ph->phdr_ust.sad_ext_len) {
                        p->udp_flags = UDP_SPACETBLS;
                        p->udp_unall_loc = SWAP_32(ph->phdr_ust.sad_ext_loc);
                        p->udp_unall_len = SWAP_32(ph->phdr_ust.sad_ext_len);
                        p->udp_freed_loc = SWAP_32(ph->phdr_fst.sad_ext_loc);
                        p->udp_freed_len = SWAP_32(ph->phdr_fst.sad_ext_len);
                                } else {
                        p->udp_flags = UDP_BITMAPS;
                        p->udp_unall_loc = SWAP_32(ph->phdr_usb.sad_ext_loc);
                        p->udp_unall_len = SWAP_32(ph->phdr_usb.sad_ext_len);
                        p->udp_freed_loc = SWAP_32(ph->phdr_fsb.sad_ext_loc);
                        p->udp_freed_len = SWAP_32(ph->phdr_fsb.sad_ext_len);
                                }

                                if (i == h->n_parts) {
                                        h->n_parts ++;
                                }
                        }
                        break;
                case UD_LOG_VOL_DESC :

                        /*
                         * Logical Volume Descriptor
                         */
                        /* LINTED */
                        lvd = (struct log_vol_desc *)taddr;
                        if ((v->lvd_len == 0) ||
                                (SWAP_32(lvd->lvd_vdsn) > v->lvd_vdsn)) {
                                v->lvd_vdsn = SWAP_32(lvd->lvd_vdsn);
                                v->lvd_loc = vds_loc;
                                v->lvd_len = ((uint32_t)
                                        &((struct log_vol_desc *)0)->lvd_pmaps);
                                v->lvd_len =
                                        lb_roundup(v->lvd_len, h->udfs.lbsize);
                        }
                        break;
                case UD_UNALL_SPA_DESC :

                        /*
                         * Unallocated Space Descriptor
                         */
                        /* LINTED */
                        usd = (struct unall_spc_desc *)taddr;
                        v->usd_loc = vds_loc;
                        v->usd_len = ((uint32_t)
                        &((unall_spc_desc_t *)0)->ua_al_dsc) +
                                SWAP_32(usd->ua_nad) *
                                sizeof (struct extent_ad);
                        v->usd_len = lb_roundup(v->usd_len, h->udfs.lbsize);
                        break;
                case UD_TERM_DESC :
                        /*
                         * Success fully completed
                         */
                        goto end;
                default :
                        /*
                         * If you donot undetstand any tag just skip
                         * it. This is not a fatal error
                         */
                        break;
                }
        }

end:
        free(addr);
        if ((v->pvd_len == 0) ||
                (v->part_len[0] == 0) ||
                (v->lvd_len == 0)) {
                return (1);
        }

        return (0);
}

static int32_t
ud_read_and_translate_lvd(ud_handle_t h, uint32_t lvd_loc, uint32_t lvd_len)
{
        caddr_t addr;
        uint16_t fsd_prn;
        uint32_t fsd_loc, fsd_len;
        uint32_t lvds_loc, lvds_len;
        uint64_t off;
        struct log_vol_desc *lvd = NULL;

        int32_t max_maps, i, mp_sz, index;
        struct ud_map *m;
        struct pmap_hdr *ph;
        struct pmap_typ1 *typ1;
        struct pmap_typ2 *typ2;

        if (lvd_len == 0) {
                return (1);
        }

        if ((lvd = (struct log_vol_desc *)
                        malloc(lvd_len)) == NULL) {
                return (1);
        }

        off = lvd_loc * h->udfs.lbsize;
        if (ud_read_dev(h, off, (uint8_t *)lvd, lvd_len) != 0) {
                free(lvd);
                return (1);
        }

        if (ud_verify_tag(h, &lvd->lvd_tag, UD_LOG_VOL_DESC,
                        lvd_loc, 1, 0) != 0) {
                free(lvd);
                return (1);
        }

        /*
         * Take care of maps
         */
        max_maps = SWAP_32(lvd->lvd_num_pmaps);
        ph = (struct pmap_hdr *)lvd->lvd_pmaps;
        for (h->n_maps = index = 0; index < max_maps; index++) {
                m = &h->maps[h->n_maps];
                switch (ph->maph_type) {
                case MAP_TYPE1 :

                        /* LINTED */
                        typ1 = (struct pmap_typ1 *)ph;

                        m->udm_flags = UDM_MAP_NORM;
                        m->udm_vsn = SWAP_16(typ1->map1_vsn);
                        m->udm_pn = SWAP_16(typ1->map1_pn);
                        h->n_maps++;
                        break;

                case MAP_TYPE2 :

                        /* LINTED */
                        typ2 = (struct pmap_typ2 *)ph;

                        if (strncmp(typ2->map2_pti.reg_id,
                                        UDF_VIRT_PART, 23) == 0) {

                                m->udm_flags = UDM_MAP_VPM;
                                m->udm_vsn = SWAP_16(typ2->map2_vsn);
                                m->udm_pn = SWAP_16(typ2->map2_pn);
                        } else if (strncmp(typ2->map2_pti.reg_id,
                                        UDF_SPAR_PART, 23) == 0) {

                                if ((SWAP_16(typ2->map2_pl) != 32) ||
                                                (typ2->map2_nst < 1) ||
                                                (typ2->map2_nst > 4)) {
                                        break;
                                }
                                m->udm_flags = UDM_MAP_SPM;
                                m->udm_vsn = SWAP_16(typ2->map2_vsn);
                                m->udm_pn = SWAP_16(typ2->map2_pn);

                                m->udm_plen = SWAP_16(typ2->map2_pl);
                                m->udm_nspm = typ2->map2_nst;
                                m->udm_spsz = SWAP_32(typ2->map2_sest);

                                mp_sz = lb_roundup(m->udm_spsz, h->udfs.lbsize);

                                if ((addr = malloc(mp_sz * m->udm_nspm)) ==
                                                NULL) {
                                        break;
                                }

                                for (i = 0; i < m->udm_nspm; i++) {
                                        m->udm_loc[i] =
                                                SWAP_32(typ2->map2_st[index]);
                                        m->udm_spaddr[i] = addr + i * mp_sz;

                                        off = m->udm_loc[i] * h->udfs.lbsize;
                                        if (ud_read_dev(h, off,
                                                (uint8_t *)m->udm_spaddr[i],
                                                        mp_sz) != 0) {
                                                m->udm_spaddr[i] = NULL;
                                                continue;
                                        }
                                }
                        }
                        h->n_maps++;
                default :
                        break;
                }
                ph = (struct pmap_hdr *)(((uint8_t *)h) + ph->maph_length);
        }

        lvds_loc = SWAP_32(lvd->lvd_int_seq_ext.ext_loc);
        lvds_len = SWAP_32(lvd->lvd_int_seq_ext.ext_len);

        fsd_prn = SWAP_16(lvd->lvd_lvcu.lad_ext_prn);
        fsd_loc = SWAP_32(lvd->lvd_lvcu.lad_ext_loc);
        fsd_len = SWAP_32(lvd->lvd_lvcu.lad_ext_len);

        free(lvd);

        /*
         * Get the latest LVID
         */
        if (ud_get_latest_lvid(h, lvds_loc, lvds_len) != 0) {
                return (1);
        }

        if (ud_get_latest_fsd(h, fsd_prn, fsd_loc, fsd_len) != 0) {
                return (1);
        }

        return (0);
}

static int32_t
ud_get_latest_lvid(ud_handle_t h, uint32_t lvds_loc, uint32_t lvds_len)
{
        uint8_t *addr, *taddr, *eaddr;
        uint16_t id;
        uint64_t off;
        struct tag *tag;
        struct log_vol_int_desc *lvid;

begin:
        if ((addr = (uint8_t *)malloc(lvds_len)) == NULL) {
                return (1);
        }

        off = lvds_loc * h->udfs.lbsize;
        if (ud_read_dev(h, off, addr, lvds_len) != 0) {
                goto end;
        }

        for (taddr = addr, eaddr = addr + h->udfs.mvds_len; taddr < eaddr;
                        taddr += h->udfs.lbsize, lvds_loc ++) {

                /* LINTED */
                tag = (struct tag *)taddr;
                id = SWAP_16(tag->tag_id);
                /*
                 * If you cannot verify the tag just skip it
                 * This is not a fatal error
                 */
                if (ud_verify_tag(h, tag, id, lvds_loc, 1, 0) != 0) {
                        continue;
                }
                switch (id) {
                case UD_LOG_VOL_INT :

                        /*
                         * Logical Volume Integrity Descriptor
                         */
                        /* LINTED */
                        lvid = (struct log_vol_int_desc *)taddr;
                        h->udfs.lvid_loc = lvds_loc;
                        h->udfs.lvid_len = ((uint32_t)
                        &((struct log_vol_int_desc *)0)->lvid_fst) +
                                SWAP_32(lvid->lvid_npart) * 8 +
                                SWAP_32(lvid->lvid_liu);
                        h->udfs.lvid_len = lb_roundup(h->udfs.lvid_len,
                                h->udfs.lbsize);

                        /*
                         * It seems we have a next integrity
                         * sequence
                         */
                        if (SWAP_32(lvid->lvid_nie.ext_len) != 0) {
                                free(addr);
                                lvds_loc = SWAP_32(lvid->lvid_nie.ext_loc);
                                lvds_len = SWAP_32(lvid->lvid_nie.ext_len);
                                goto begin;
                        }
                        goto end;
                case UD_TERM_DESC :

                        /*
                         * Success fully completed
                         */
                                goto end;
                default :
                        /*
                         * If you donot undetstand any tag just skip
                         * it. This is not a fatal error
                         */
                        break;
                }
        }
end:
        free(addr);
        if (h->udfs.lvid_len == 0) {
                return (1);
        }
        return (0);
}

static int32_t
ud_get_latest_fsd(ud_handle_t h, uint16_t fsd_prn,
        uint32_t fsd_loc, uint32_t fsd_len)
{
        uint8_t *addr, *taddr, *eaddr;
        uint16_t id;
        uint64_t off;
        uint32_t fsds_loc, fsds_len;
        struct tag *tag;
        struct file_set_desc *fsd;
        uint32_t old_fsn = 0;

begin:
        h->udfs.fsds_prn = fsd_prn;
        h->udfs.fsds_loc = fsd_loc;
        h->udfs.fsds_len = fsd_len;

        fsds_loc = ud_xlate_to_daddr(h, fsd_prn, fsd_loc);
        fsds_len = lb_roundup(fsd_len, h->udfs.lbsize);

        if ((addr = (uint8_t *)malloc(fsds_len)) == NULL) {
                return (1);
        }

        off = fsds_loc * h->udfs.lbsize;
        if (ud_read_dev(h, off, addr, fsds_len) != 0) {
                goto end;
        }

        for (taddr = addr, eaddr = addr + h->udfs.mvds_len; taddr < eaddr;
                        taddr += h->udfs.lbsize, fsds_loc ++) {

                /* LINTED */
                tag = (struct tag *)taddr;
                id = SWAP_16(tag->tag_id);
                /*
                 * If you cannot verify the tag just skip it
                 * This is not a fatal error
                 */
                if (ud_verify_tag(h, tag, id, fsds_loc, 1, 0) != 0) {
                        continue;
                }
                switch (id) {
                case UD_FILE_SET_DESC :
                        /* LINTED */
                        fsd = (struct file_set_desc *)taddr;
                        if ((h->udfs.fsd_len == 0) ||
                                (SWAP_32(fsd->fsd_fs_no) > old_fsn)) {
                                old_fsn = SWAP_32(fsd->fsd_fs_no);
                                h->udfs.fsd_loc = fsds_loc;
                                h->udfs.fsd_len = lb_roundup(512,
                                        h->udfs.lbsize);
                                h->udfs.ricb_prn =
                                        SWAP_16(fsd->fsd_root_icb.lad_ext_prn);
                                h->udfs.ricb_loc =
                                        SWAP_32(fsd->fsd_root_icb.lad_ext_loc);
                                h->udfs.ricb_len =
                                        SWAP_32(fsd->fsd_root_icb.lad_ext_len);
                        }
                        if (SWAP_32(fsd->fsd_next.lad_ext_len) != 0) {
                                fsd_prn = SWAP_16(fsd->fsd_next.lad_ext_prn);
                                fsd_loc = SWAP_32(fsd->fsd_next.lad_ext_loc);
                                fsd_len = SWAP_32(fsd->fsd_next.lad_ext_len);
                                goto begin;
                        }
                        break;
                case UD_TERM_DESC :

                        /*
                         * Success fully completed
                         */
                        goto end;
                default :
                        /*
                         * If you donot undetstand any tag just skip
                         * it. This is not a fatal error
                         */
                        break;
                }
        }

end:
        free(addr);
        if (h->udfs.fsd_len == 0) {
                return (1);
        }
        return (0);
}

int32_t
ud_get_num_blks(ud_handle_t h, uint32_t *blkno)
{
        struct vtoc vtoc;
        struct dk_cinfo dki_info;
        int32_t error;

        /*
         * Get VTOC from driver
         */
        if ((error = ioctl(h->fd, DKIOCGVTOC, (intptr_t)&vtoc)) != 0) {
                return (error);
        }

        /*
         * Verify if is proper
         */
        if (vtoc.v_sanity != VTOC_SANE) {
                return (EINVAL);
        }

        /*
         * Get dk_cinfo from driver
         */
        if ((error = ioctl(h->fd, DKIOCINFO, (intptr_t)&dki_info)) != 0) {
                return (error);
        }

        if (dki_info.dki_partition >= V_NUMPAR) {
                return (EINVAL);
        }

        /*
         * Return the size of the partition
         */
        *blkno = vtoc.v_part[dki_info.dki_partition].p_size;

        return (0);
}

uint32_t
ud_xlate_to_daddr(ud_handle_t h, uint16_t prn, uint32_t blkno)
{
        int32_t i;
        struct ud_map *m;
        struct ud_part *p;


        if (prn < h->n_maps) {
                m = &h->maps[prn];
                for (i = 0; i < h->n_parts; i++) {
                        p = &h->part[i];
                        if (m->udm_pn == p->udp_number) {
                                return (p->udp_start + blkno);
                        }
                }
        }
        return (0);
}

/* ------ END Read and translate the on disk VDS to IN CORE format -------- */

int32_t
ud_verify_tag(ud_handle_t h, struct tag *tag, uint16_t id,
        uint32_t blockno, int32_t do_crc, int32_t print_msg)
{
        int32_t i;
        uint8_t *addr, cksum = 0;
        uint16_t crc;


        /*
         * Verify Tag Identifier
         */
        if (tag->tag_id != SWAP_16(id)) {
                if (print_msg != 0) {
                        (void) fprintf(stderr,
                                gettext("tag does not verify tag %x req %x\n"),
                                SWAP_16(tag->tag_id), id);
                }
                return (1);
        }

        /*
         * Verify Tag Descriptor Version
         */
        if (SWAP_16(tag->tag_desc_ver) != h->udfs.ecma_version) {
                if (print_msg != 0) {
                        (void) fprintf(stderr,
                                gettext("tag version does not match with "
                                "NSR descriptor version TAG %x NSR %x\n"),
                                SWAP_16(tag->tag_desc_ver),
                                h->udfs.ecma_version);
                }
                return (1);
        }

        /*
         * Caliculate Tag Checksum
         */
        addr = (uint8_t *)tag;
        for (i = 0; i <= 15; i++) {
                if (i != 4) {
                        cksum += addr[i];
                }
        }

        /*
         * Verify Tag Checksum
         */
        if (cksum != tag->tag_cksum) {
                if (print_msg != 0) {
                        (void) fprintf(stderr,
                                gettext("Checksum Does not Verify TAG"
                                " %x CALC %x\n"), tag->tag_cksum, cksum);
                }
                return (1);
        }


        /*
         * Do we want to do crc
         */
        if (do_crc) {
                if (tag->tag_crc_len) {

                        /*
                         * Caliculate CRC for the descriptor
                         */
                        crc = ud_crc(addr + 0x10, SWAP_16(tag->tag_crc_len));

                        /*
                         * Verify CRC
                         */
                        if (crc != SWAP_16(tag->tag_crc)) {
                                if (print_msg != 0) {
                                        (void) fprintf(stderr,
                                                gettext("CRC Does not verify"
                                                " TAG %x CALC %x %x\n"),
                                                SWAP_16(tag->tag_crc),
                                                crc, addr);
                                }
                        }
                }

                /*
                 * Verify Tag Location
                 */
                if (SWAP_32(blockno) != tag->tag_loc) {
                        if (print_msg != 0) {
                                (void) fprintf(stderr,
                                        gettext("Tag Location Does not verify"
                                        " blockno %x tag_blockno %x\n"),
                                        blockno, SWAP_32(tag->tag_loc));
                        }
                }
        }

        return (0);
}


/* ARGSUSED1 */
void
ud_make_tag(ud_handle_t h, struct tag *tag, uint16_t tag_id,
        uint32_t blkno, uint16_t crc_len)
{
        int32_t i;
        uint16_t crc;
        uint8_t *addr, cksum = 0;

        tag->tag_id = SWAP_16(tag_id);
        tag->tag_desc_ver = SWAP_16(h->udfs.ecma_version);
        tag->tag_cksum = 0;
        tag->tag_res = 0;

        /*
         * Calicualte and assign CRC, CRC_LEN
         */
        addr = (uint8_t *)tag;
        crc = ud_crc(addr + 0x10, crc_len);
        tag->tag_crc = SWAP_16(crc);
        tag->tag_crc_len = SWAP_16(crc_len);
        tag->tag_loc = SWAP_32(blkno);

        /*
         * Caliculate Checksum
         */
        for (i = 0; i <= 15; i++) {
                cksum += addr[i];
        }

        /*
         * Assign Checksum
         */
        tag->tag_cksum = cksum;
}

/* **************** udf specific subroutines *********************** */

static uint16_t ud_crc_table[256] = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
        0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
        0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
        0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
        0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
        0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
        0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
        0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
        0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
        0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
        0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
        0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
        0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
        0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
        0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
        0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
        0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
        0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
        0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
        0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
        0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
        0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
        0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
        0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
        0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
        0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
        0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
        0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
        0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
        0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
        0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
        0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
};

static uint16_t
ud_crc(uint8_t *addr, int32_t len)
{
        uint16_t crc = 0;

        while (len-- > 0) {
                crc = ud_crc_table[(crc >> 8 ^ *addr++) & 0xff] ^ (crc<<8);
        }

        return (crc);
}

#define MAXNAMLEN       0x200


#define POUND           0x0023
#define DOT             0x002E
#define SLASH           0x002F
#define UNDERBAR        0x005F


static uint16_t htoc[16] = {'0', '1', '2', '3',
        '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/*
 * unicode is the string of 16-bot characters
 * length is the number of 16-bit characters
 */
static int32_t
UdfTxName(uint16_t *unicode, int32_t count)
{
        int32_t i, j, k, lic, make_crc, dot_loc;
        uint16_t crc;

        if ((unicode[0] == DOT) &&
                ((count == 1) || ((count == 2) && (unicode[1] == DOT)))) {
                crc = DOT;
                if (count == 2) {
                        crc += DOT;
                }
                unicode[0] = UNDERBAR;
                unicode[1] = POUND;
                unicode[2] = htoc[(uint16_t)(crc & 0xf000) >> 12];
                unicode[3] = htoc[(uint16_t)(crc & 0xf00) >> 8];
                unicode[4] = htoc[(uint16_t)(crc & 0xf0) >> 4];
                unicode[5] = htoc[crc & 0xf];
                return (6);
        }
        crc = 0;
        j = make_crc = 0;
        lic = dot_loc = -1;
        for (i = 0; i < count; i++) {
                if (make_crc) {
                        crc += unicode[i];
                }
                if (unicode[i] == DOT) {
                        dot_loc = j;
                }
                if ((unicode[i] == SLASH) ||
                        (unicode[i] == 0)) {
                        if (make_crc == 0) {
                                for (k = 0; k <= i; k++) {
                                        crc += unicode[k];
                                }
                                make_crc = 1;
                        }
                        if (lic != (i - 1)) {
                                unicode[j++] = UNDERBAR;
                        }
                        lic = i;
                } else {
                        unicode[j++] = unicode[i];
                }
        }

        if (make_crc) {
                if (dot_loc != -1) {
                        if ((j + 5) > MAXNAMLEN) {
                                if ((j - dot_loc + 5) > MAXNAMLEN) {
                                        j = MAXNAMLEN - 5 + dot_loc;
                                        for (k = MAXNAMLEN;
                                                j >= dot_loc; k --, j--) {
                                                unicode[k] = unicode[j];
                                        }
                                        k = 0;
                                } else {
                                        for (k = MAXNAMLEN;
                                                j >= dot_loc; k--, j--) {
                                                unicode[k] = unicode[j];
                                        }
                                        k -= 4;
                                }
                                j = MAXNAMLEN;
                        } else {
                                for (k = j; k >= dot_loc; k--) {
                                        unicode[k + 5] = unicode[k];
                                }
                                k = dot_loc;
                                j += 5;
                        }
                } else {
                        if ((j + 5) > MAXNAMLEN) {
                                j = MAXNAMLEN;
                                k = MAXNAMLEN - 5;
                        } else {
                                k = j;
                                j += 5;
                        }
                }
                unicode[k++] = POUND;
                unicode[k++] = htoc[(uint16_t)(crc & 0xf000) >> 12];
                unicode[k++] = htoc[(uint16_t)(crc & 0xf00) >> 8];
                unicode[k++] = htoc[(uint16_t)(crc & 0xf0) >> 4];
                unicode[k++] = htoc[crc & 0xf];
        }
        return (j);
}

/*
 * Assumes the output buffer is large
 * enough to hold the uncompressed
 * code
 */
static int32_t
UncompressUnicode(
        int32_t numberOfBytes,  /* (Input) number of bytes read from media. */
        uint8_t *UDFCompressed, /* (Input) bytes read from media. */
        uint16_t *unicode)      /* (Output) uncompressed unicode characters. */
{
        int32_t compID;
        int32_t returnValue, unicodeIndex, byteIndex;


        /*
         * Use UDFCompressed to store current byte being read.
         */
        compID = UDFCompressed[0];

        /* First check for valid compID. */
        if (compID != 8 && compID != 16) {
                returnValue = -1;
        } else {
                unicodeIndex = 0;
                byteIndex = 1;

                /* Loop through all the bytes. */
                while (byteIndex < numberOfBytes) {
                        if (compID == 16) {
                                /*
                                 * Move the first byte to the
                                 * high bits of the unicode char.
                                 */
                                unicode[unicodeIndex] =
                                        UDFCompressed[byteIndex++] << 8;
                        } else {
                                unicode[unicodeIndex] = 0;
                        }
                        if (byteIndex < numberOfBytes) {
                                /*
                                 * Then the next byte to the low bits.
                                 */
                                unicode[unicodeIndex] |=
                                        UDFCompressed[byteIndex++];
                        }
                        unicodeIndex++;
                }
                returnValue = unicodeIndex;
        }
        return (returnValue);
}





static int32_t
ud_compressunicode(
        int32_t numberOfChars,  /* (Input) number of unicode characters. */
        int32_t compID,         /* (Input) compression ID to be used. */
        uint16_t *unicode,      /* (Input) unicode characters to compress. */
        uint8_t *UDFCompressed) /* (Output) compressed string, as bytes. */
{
        int32_t byteIndex;

        if (compID != 8 && compID != 16) {
                /*
                 * Unsupported compression ID !
                 */
                byteIndex = -1;
        } else {
                /*
                 * Place compression code in first byte.
                 */
                UDFCompressed[0] = (uint8_t)compID;
                (void) strncpy((caddr_t)&UDFCompressed[1],
                        (caddr_t)unicode, numberOfChars);
                byteIndex = numberOfChars + 1;
        }
        return (byteIndex);
}


static int32_t
ud_convert2utf8(uint8_t *ibuf, uint8_t *obuf, int32_t length)
{
        int i, size;
        uint16_t *buf;

        /* LINTED */
        buf = (uint16_t *)obuf;

        size = UncompressUnicode(length, ibuf, buf);

        size = UdfTxName(buf, size);

        for (i = 0; i < size; i++) {
                obuf[i] = (uint8_t)buf[i];
        }
        obuf[i] = '\0';

        return (size);
}

static int32_t
ud_convert2utf16(uint8_t *ibuf, uint8_t *obuf, int32_t length)
{
        int32_t comp_len;
        uint16_t *ptr;

        /* LINTED */
        ptr = (uint16_t *)ibuf;
        comp_len = ud_compressunicode(length, 8, ptr, obuf);

        return (comp_len);
}

/*
 * Assumption code set is zero in udfs
 */
void
ud_convert2local(int8_t *ibuf, int8_t *obuf, int32_t length)
{
        wchar_t buf4c[128];
        int32_t i, comp, index;

        /*
         * Special uncompress code
         * written to accomodate solaris wchar_t
         */
        comp = ibuf[0];
        for (i = 0, index = 1; i < length; i++) {
                if (comp == 16) {
                        buf4c[i] = ibuf[index++] << 8;
                } else {
                        buf4c[i] = 0;
                }
                if (index < length) {
                        buf4c[i] |= ibuf[index++];
                }
        }
        (void) wcstombs((char *)obuf, buf4c, 128);
}


/* ------------ Routines to print basic structures Part 1 ---------------- */



void
print_charspec(FILE *fout, char *name, struct charspec *cspec)
{
        int i = 0;

        (void) fprintf(fout,
                "%s : %x - \"", name, cspec->cs_type);
        for (i = 0; i < 63; i++) {
                (void) fprintf(fout,
                        "%c", cspec->cs_info[i]);
        }
        (void) fprintf(fout, "\n");
}

/* ARGSUSED */
void
print_dstring(FILE *fout, char *name, uint16_t cset, char *bufc, uint8_t length)
{
        int8_t bufmb[1024];

        ud_convert2local(bufc, bufmb, length);

        (void) fprintf(fout,
                "%s %s\n", name, bufmb);
}

void
set_dstring(dstring_t *dp, char *cp, int32_t len)
{
        int32_t length;

        bzero(dp, len);
        length = strlen(cp);
        if (length > len - 1) {
                length = len - 1;
        }
        (void) strncpy(dp, cp, length);
        dp[len - 1] = length;
}

void
print_tstamp(FILE *fout, char *name, tstamp_t *ts)
{
        (void) fprintf(fout, "%s tz : %d yr : %d mo : %d da : %d "
                "Time : %d : %d : %d : %d : %d : %d\n", name,
                SWAP_16(ts->ts_tzone), SWAP_16(ts->ts_year), ts->ts_month,
                ts->ts_day, ts->ts_hour, ts->ts_min, ts->ts_sec, ts->ts_csec,
                ts->ts_husec, ts->ts_usec);
}



void
make_regid(ud_handle_t h, struct regid *reg, char *id, int32_t type)
{
        reg->reg_flags = 0;
        (void) strncpy(reg->reg_id, id, 23);

        if (type == REG_DOM_ID) {
                struct dom_id_suffix *dis;

                /* LINTED */
                dis = (struct dom_id_suffix *)reg->reg_ids;
                dis->dis_udf_revison = SWAP_16(h->udfs.ma_write);
                dis->dis_domain_flags = 0;

        } else if (type == REG_UDF_ID) {
                struct udf_id_suffix *uis;

                /* LINTED */
                uis = (struct udf_id_suffix *)reg->reg_ids;
                uis->uis_udf_revision = SWAP_16(h->udfs.ma_write);
                uis->uis_os_class = OS_CLASS_UNIX;
                uis->uis_os_identifier = OS_IDENTIFIER_SOLARIS;
        } else if (type == REG_UDF_II) {
                struct impl_id_suffix *iis;

                iis = (struct impl_id_suffix *)reg->reg_ids;
                iis->iis_os_class = OS_CLASS_UNIX;
                iis->iis_os_identifier = OS_IDENTIFIER_SOLARIS;
        }
}

void
print_regid(FILE *fout, char *name, struct regid *reg, int32_t type)
{
        (void) fprintf(fout, "%s : 0x%x : \"%s\" :",
                name, reg->reg_flags, reg->reg_id);

        if (type == REG_DOM_ID) {
                struct dom_id_suffix *dis;

                /* LINTED */
                dis = (struct dom_id_suffix *)reg->reg_ids;
                (void) fprintf(fout, " 0x%x : %s : %s\n",
                        SWAP_16(dis->dis_udf_revison),
                        (dis->dis_domain_flags & PROTECT_SOFT_WRITE) ?
                                "HW Protect" : "No HW Write Protect",
                        (dis->dis_domain_flags & PROTECT_HARD_WRITE) ?
                                "SW Protect" : "No SW Protect");
        } else if (type == REG_UDF_ID) {
                struct udf_id_suffix *uis;

                /* LINTED */
                uis = (struct udf_id_suffix *)reg->reg_ids;
                (void) fprintf(fout,
                        " 0x%x : OS Class 0x%x : OS Identifier 0x%x\n",
                        SWAP_16(uis->uis_udf_revision),
                        uis->uis_os_class, uis->uis_os_identifier);
        } else {
                struct impl_id_suffix *iis;

                iis = (struct impl_id_suffix *)reg->reg_ids;
                (void) fprintf(fout,
                        " OS Class 0x%x : OS Identifier 0x%x\n",
                        iis->iis_os_class, iis->iis_os_identifier);
        }
}

#ifdef  OLD
void
print_regid(FILE *fout, char *name, struct regid *reg)
{
        (void) fprintf(fout, "%s : 0x%x : \"%s\" :",
                name, reg->reg_flags, reg->reg_id);

        if (strncmp(reg->reg_id, "*OSTA UDF Compliant", 19) == 0) {
                (void) fprintf(fout, " 0x%x : %s : %s\n",
                        reg->reg_ids[0] | (reg->reg_ids[1] << 8),
                        (reg->reg_ids[2] & 1) ?
                                "HW Protect" : "No HW Write Protect",
                        (reg->reg_ids[2] & 2) ?
                                "SW Protect" : "No SW Protect");
        } else if ((strncmp(reg->reg_id, "*UDF Virtual Partition", 22) == 0) ||
                (strncmp(reg->reg_id, "*UDF Sparable Partition", 23) == 0) ||
                (strncmp(reg->reg_id, "*UDF Virtual Alloc Tbl", 22) == 0) ||
                (strncmp(reg->reg_id, "*UDF Sparing Table", 18) == 0)) {
                (void) fprintf(fout,
                        " 0x%x : OS Class 0x%x : OS Identifier 0x%x\n",
                        reg->reg_ids[0] | (reg->reg_ids[1] << 8),
                        reg->reg_ids[2], reg->reg_ids[3]);
        } else {
                (void) fprintf(fout,
                        " OS Class 0x%x : OS Identifier 0x%x\n",
                        reg->reg_ids[0], reg->reg_ids[1]);
        }
}
#endif


/* ------------ Routines to print basic structures Part 2 ---------------- */
/*
 * Part 2
 * This part is OS specific and is currently
 * not supported
 */

/* ------------ Routines to print basic structures Part 3 ---------------- */

void
print_ext_ad(FILE *fout, char *name, struct extent_ad *ead)
{
        (void) fprintf(fout,
                "%s EAD Len %x Loc %x\n",
                name, SWAP_32(ead->ext_len), SWAP_32(ead->ext_loc));
}

void
print_tag(FILE *fout, struct tag *tag)
{
        (void) fprintf(fout,
                "tag_id : %x ver : %x cksum : %x "
                "sno : %x crc : %x crc_len : %x loc : %x\n",
                SWAP_16(tag->tag_id), SWAP_16(tag->tag_desc_ver),
                tag->tag_cksum, SWAP_16(tag->tag_sno),
                SWAP_16(tag->tag_crc), SWAP_16(tag->tag_crc_len),
                SWAP_32(tag->tag_loc));
}


void
print_pvd(FILE *fout, struct pri_vol_desc *pvd)
{
        (void) fprintf(fout,
                "\n\t\t\tPrimary Volume Descriptor\n");
        print_tag(fout, &pvd->pvd_tag);
        (void) fprintf(fout, "vdsn : %x vdn : %x\n",
                SWAP_32(pvd->pvd_vdsn), SWAP_32(pvd->pvd_pvdn));
        print_dstring(fout, "volid : ", pvd->pvd_desc_cs.cs_type,
                        pvd->pvd_vol_id, 32);
        (void) fprintf(fout,
                "vsn : %x mvsn : %x il : %x mil :"
                " %x csl : %x mcsl %x\n",
                SWAP_16(pvd->pvd_vsn), SWAP_16(pvd->pvd_mvsn),
                SWAP_16(pvd->pvd_il), SWAP_16(pvd->pvd_mil),
                SWAP_32(pvd->pvd_csl), SWAP_32(pvd->pvd_mcsl));
        print_dstring(fout, "vsid :", pvd->pvd_desc_cs.cs_type,
                        pvd->pvd_vsi, 128);
        print_charspec(fout, "desc_cs", &pvd->pvd_desc_cs);
        print_charspec(fout, "exp_cs", &pvd->pvd_exp_cs);
        print_ext_ad(fout, "val ", &pvd->pvd_vol_abs);
        print_ext_ad(fout, "vcnl ", &pvd->pvd_vcn);
        print_regid(fout, "ai", &pvd->pvd_appl_id, REG_UDF_II);
        print_regid(fout, "ii", &pvd->pvd_ii, REG_UDF_II);
        (void) fprintf(fout, "pvdsl : %x flags : %x\n",
                SWAP_32(pvd->pvd_pvdsl),
                SWAP_16(pvd->pvd_flags));
}

void
print_avd(FILE *fout, struct anch_vol_desc_ptr *avdp)
{
        (void) fprintf(fout,
                "\n\t\t\tAnchor Volume Descriptor\n");
        print_tag(fout, &avdp->avd_tag);
        print_ext_ad(fout, "Main Volume Descriptor Sequence : ",
                        &avdp->avd_main_vdse);
        print_ext_ad(fout, "Reserve Volume Descriptor Sequence : ",
                        &avdp->avd_res_vdse);
}

void
print_vdp(FILE *fout, struct vol_desc_ptr *vdp)
{
        (void) fprintf(fout,
                "\n\t\t\tVolume Descriptor Pointer\n");
        print_tag(fout, &vdp->vdp_tag);
        (void) fprintf(fout, "vdsn : %x ",
                SWAP_32(vdp->vdp_vdsn));
        print_ext_ad(fout, "vdse ", &vdp->vdp_nvdse);
}

void
print_iuvd(FILE *fout, struct iuvd_desc *iuvd)
{
        (void) fprintf(fout,
                "\n\t\t\tImplementation Use Volume Descriptor\n");
        print_tag(fout, &iuvd->iuvd_tag);
        (void) fprintf(fout,
                "vdsn : %x ", SWAP_32(iuvd->iuvd_vdsn));
        print_regid(fout, "Impl Id : ", &iuvd->iuvd_ii, REG_UDF_ID);
        print_charspec(fout, "cset ", &iuvd->iuvd_cset);
        print_dstring(fout, "lvi : ", iuvd->iuvd_cset.cs_type,
                        iuvd->iuvd_lvi, 128);
        print_dstring(fout, "ifo1 : ", iuvd->iuvd_cset.cs_type,
                        iuvd->iuvd_ifo1, 36);
        print_dstring(fout, "ifo2 : ", iuvd->iuvd_cset.cs_type,
                        iuvd->iuvd_ifo2, 36);
        print_dstring(fout, "ifo3 : ", iuvd->iuvd_cset.cs_type,
                        iuvd->iuvd_ifo3, 36);

        print_regid(fout, "iid ", &iuvd->iuvd_iid, REG_UDF_II);
}

void
print_part(FILE *fout, struct part_desc *pd)
{
        (void) fprintf(fout,
                "\n\t\t\tPartition Descriptor\n");
        print_tag(fout, &pd->pd_tag);
        (void) fprintf(fout,
                "vdsn : %x flags : %x num : %x ",
                SWAP_32(pd->pd_vdsn),
                SWAP_16(pd->pd_pflags),
                SWAP_16(pd->pd_pnum));
        print_regid(fout, "contents ", &pd->pd_pcontents, REG_UDF_II);
        /* LINTED */
        print_phdr(fout, (struct phdr_desc *)(&pd->pd_pc_use));
        (void) fprintf(fout,
                "acc : %x start : %x length : %x ",
                SWAP_32(pd->pd_acc_type),
                SWAP_32(pd->pd_part_start),
                SWAP_32(pd->pd_part_length));
        print_regid(fout, "Impl Id : ", &pd->pd_ii, REG_UDF_II);
}

void
print_lvd(FILE *fout, struct log_vol_desc *lvd)
{
        (void) fprintf(fout,
                "\n\t\t\tLogical Volume Descriptor\n");
        print_tag(fout, &lvd->lvd_tag);
        (void) fprintf(fout,
                "vdsn : %x ", SWAP_32(lvd->lvd_vdsn));
        print_charspec(fout, "Desc Char Set ", &lvd->lvd_desc_cs);
        print_dstring(fout, "lvid : ", lvd->lvd_desc_cs.cs_type,
                        lvd->lvd_lvid, 28);
        (void) fprintf(fout,
                "lbsize : %x ",
                SWAP_32(lvd->lvd_log_bsize));
        print_regid(fout, "Dom Id", &lvd->lvd_dom_id, REG_DOM_ID);
        print_long_ad(fout, "lvcu", &lvd->lvd_lvcu);
        (void) fprintf(fout,
                "mtlen : %x nmaps : %x ",
                SWAP_32(lvd->lvd_mtbl_len),
                SWAP_32(lvd->lvd_num_pmaps));
        print_regid(fout, "Impl Id : ", &lvd->lvd_ii, REG_UDF_II);
        print_ext_ad(fout, "Int Seq", &lvd->lvd_int_seq_ext);
        print_pmaps(fout, lvd->lvd_pmaps, SWAP_32(lvd->lvd_num_pmaps));
}

void
print_usd(FILE *fout, struct unall_spc_desc *ua)
{
        int32_t i, count;

        (void) fprintf(fout,
                "\n\t\t\tUnallocated Space Descriptor\n");
        print_tag(fout, &ua->ua_tag);
        count = SWAP_32(ua->ua_nad);
        (void) fprintf(fout,
                "vdsn : %x nad : %x\n",
                SWAP_32(ua->ua_vdsn), count);
        for (i = 0; i < count; i++) {
                (void) fprintf(fout,
                        "loc : %x len : %x\n",
                        SWAP_32(ua->ua_al_dsc[i * 2]),
                        SWAP_32(ua->ua_al_dsc[i * 2 + 1]));
        }
}

void
print_lvid(FILE *fout, struct log_vol_int_desc *lvid)
{
        int32_t i, count;
        caddr_t addr;
        struct lvid_iu *liu;

        (void) fprintf(fout,
                "\n\t\t\tLogical Volume Integrity Descriptor\n");
        print_tag(fout, &lvid->lvid_tag);
        print_tstamp(fout, "Rec TM ", &lvid->lvid_tstamp);
        if (SWAP_32(lvid->lvid_int_type) == 0) {
                (void) fprintf(fout,
                        "int_typ : Open\n");
        } else if (SWAP_32(lvid->lvid_int_type) == 1) {
                (void) fprintf(fout, "int_typ : Closed\n");
        } else {
                (void) fprintf(fout, "int_typ : Unknown\n");
        }
        print_ext_ad(fout, "Nie ", &lvid->lvid_nie);
        count = SWAP_32(lvid->lvid_npart);
        (void) fprintf(fout,
                "Uniq : %llx npart : %x liu : %x\n",
                SWAP_64(lvid->lvid_lvcu.lvhd_uniqid),
                count, SWAP_32(lvid->lvid_liu));
        for (i = 0; i < count; i++) {
                (void) fprintf(fout,
                        "Part : %x Free : %x Size : %x\n",
                        i, SWAP_32(lvid->lvid_fst[i]),
                        SWAP_32(lvid->lvid_fst[count + i]));
        }

        addr = (caddr_t)lvid->lvid_fst;
        /* LINTED */
        liu = (struct lvid_iu *)(addr + 2 * count * 4);
        print_regid(fout, "Impl Id :", &liu->lvidiu_regid, REG_UDF_II);
        (void) fprintf(fout,
                "nfiles : %x ndirs : %x miread : %x"
                " miwrite : %x mawrite : %x\n",
                SWAP_32(liu->lvidiu_nfiles), SWAP_32(liu->lvidiu_ndirs),
                SWAP_16(liu->lvidiu_mread), SWAP_16(liu->lvidiu_mwrite),
                SWAP_16(liu->lvidiu_maxwr));
}


/* ------------ Routines to print basic structures Part 4 ---------------- */

void
print_fsd(FILE *fout, ud_handle_t h, struct file_set_desc *fsd)
{
        (void) fprintf(fout,
                "\n\t\t\tFile Set Descriptor\n");

        print_tag(fout, &fsd->fsd_tag);
        print_tstamp(fout, "Rec TM ", &fsd->fsd_time);
        (void) fprintf(fout,
                "ilvl : %x milvl : %x csl : %x"
                " mcsl : %x fsn : %x fsdn : %x\n",
                SWAP_16(fsd->fsd_ilevel), SWAP_16(fsd->fsd_mi_level),
                SWAP_32(fsd->fsd_cs_list), SWAP_32(fsd->fsd_mcs_list),
                SWAP_32(fsd->fsd_fs_no), SWAP_32(fsd->fsd_fsd_no));
        print_charspec(fout, "ID CS ", &fsd->fsd_lvidcs);
        print_dstring(fout, "lvi : ", fsd->fsd_lvidcs.cs_type,
                        fsd->fsd_lvid, 128);
        print_charspec(fout, "ID CS ", &fsd->fsd_fscs);
        print_dstring(fout, "fsi : ", fsd->fsd_lvidcs.cs_type,
                        fsd->fsd_fsi, 32);
        print_dstring(fout, "cfi : ", fsd->fsd_lvidcs.cs_type,
                        fsd->fsd_cfi, 32);
        print_dstring(fout, "afi : ", fsd->fsd_lvidcs.cs_type,
                        fsd->fsd_afi, 32);
        print_long_ad(fout, "Ricb ", &fsd->fsd_root_icb);
        print_regid(fout, "DI ", &fsd->fsd_did, REG_DOM_ID);
        print_long_ad(fout, "Next Fsd ", &fsd->fsd_next);
        if (h->udfs.ecma_version == UD_ECMA_VER3) {
                print_long_ad(fout, "System Stream Directory ICB ",
                                &fsd->fsd_next);
        }
}

void
print_phdr(FILE *fout, struct phdr_desc *ph)
{
        print_short_ad(fout, "ust ", &ph->phdr_ust);
        print_short_ad(fout, "usb ", &ph->phdr_usb);
        print_short_ad(fout, "int ", &ph->phdr_it);
        print_short_ad(fout, "fst ", &ph->phdr_fst);
        print_short_ad(fout, "fsh ", &ph->phdr_fsb);
}

void
print_fid(FILE *fout, struct file_id *fid)
{
        int32_t i;
        uint8_t *addr;

        (void) fprintf(fout,
                "File Identifier Descriptor\n");
        print_tag(fout, &fid->fid_tag);
        (void) fprintf(fout, "fvn : %x fc : %x length : %x ",
                fid->fid_ver, fid->fid_flags, fid->fid_idlen);
        print_long_ad(fout, "ICB", &fid->fid_icb);
        addr = &fid->fid_spec[SWAP_16(fid->fid_iulen)];
        (void) fprintf(fout, "iulen : %x comp : %x name : ",
                SWAP_16(fid->fid_iulen), *addr);
        addr++;
        for (i = 0; i < fid->fid_idlen; i++) {
                (void) fprintf(fout, "%c", *addr++);
        }
        (void) fprintf(fout, "\n");
}

void
print_aed(FILE *fout, struct alloc_ext_desc *aed)
{
        (void) fprintf(fout,
                "Allocation Extent Descriptor\n");
        print_tag(fout, &aed->aed_tag);
        (void) fprintf(fout, "prev ael loc : %x laed : %x\n",
                SWAP_32(aed->aed_rev_ael), SWAP_32(aed->aed_len_aed));
}

static char *ftype[] = {
        "NON",  "USE",  "PIE",  "IE",
        "DIR",  "REG",  "BDEV", "CDEV",
        "EATT", "FIFO", "SOCK", "TERM",
        "SYML", "SDIR"
};

void
print_icb_tag(FILE *fout, struct icb_tag *itag)
{
        (void) fprintf(fout,
                "prnde : %x strat : %x param : %x max_ent %x\n",
                SWAP_32(itag->itag_prnde), SWAP_16(itag->itag_strategy),
                SWAP_16(itag->itag_param), SWAP_16(itag->itag_max_ent));
        (void) fprintf(fout,
                "ftype : %s prn : %x loc : %x flags : %x\n",
                (itag->itag_ftype >= 14) ? ftype[0] : ftype[itag->itag_ftype],
                SWAP_16(itag->itag_lb_prn),
                SWAP_32(itag->itag_lb_loc), SWAP_16(itag->itag_flags));
}


void
print_ie(FILE *fout, struct indirect_entry *ie)
{
        (void) fprintf(fout,
                "Indirect Entry\n");
        print_tag(fout, &ie->ie_tag);
        print_icb_tag(fout, &ie->ie_icb_tag);
        print_long_ad(fout, "ICB", &ie->ie_indirecticb);
}

void
print_td(FILE *fout, struct term_desc *td)
{
        (void) fprintf(fout,
                "Terminating Descriptor\n");
        print_tag(fout, &td->td_tag);
}

void
print_fe(FILE *fout, struct file_entry *fe)
{
        (void) fprintf(fout,
                "File Entry\n");
        print_tag(fout, &fe->fe_tag);
        print_icb_tag(fout, &fe->fe_icb_tag);
        (void) fprintf(fout,
                "uid : %x gid : %x perms : %x nlnk : %x\n",
                SWAP_32(fe->fe_uid), SWAP_32(fe->fe_gid),
                SWAP_32(fe->fe_perms), SWAP_16(fe->fe_lcount));
        (void) fprintf(fout,
                "rec_for : %x rec_dis : %x rec_len : %x "
                "sz : %llx blks : %llx\n",
                fe->fe_rec_for, fe->fe_rec_dis, SWAP_32(fe->fe_rec_len),
                SWAP_64(fe->fe_info_len), SWAP_64(fe->fe_lbr));
        print_tstamp(fout, "ctime ", &fe->fe_acc_time);
        print_tstamp(fout, "mtime ", &fe->fe_mod_time);
        print_tstamp(fout, "atime ", &fe->fe_attr_time);
        (void) fprintf(fout,
                "ckpoint : %x ", SWAP_32(fe->fe_ckpoint));
        print_long_ad(fout, "ICB", &fe->fe_ea_icb);
        print_regid(fout, "impl", &fe->fe_impl_id, REG_UDF_II);
        (void) fprintf(fout,
                "uniq_id : %llx len_ear : %x len_adesc %x\n",
                SWAP_64(fe->fe_uniq_id), SWAP_32(fe->fe_len_ear),
                SWAP_32(fe->fe_len_adesc));
}

void
print_pmaps(FILE *fout, uint8_t *addr, int32_t count)
{
        struct pmap_hdr *hdr;
        struct pmap_typ1 *map1;
        struct pmap_typ2 *map2;

        while (count--) {
                hdr = (struct pmap_hdr *)addr;
                switch (hdr->maph_type) {
                case 1 :
                        /* LINTED */
                        map1 = (struct pmap_typ1 *)hdr;
                        (void) fprintf(fout, "Map type 1 ");
                        (void) fprintf(fout, "VSN %x prn %x\n",
                                        SWAP_16(map1->map1_vsn),
                                        SWAP_16(map1->map1_pn));
                        break;
                case 2 :
                        /* LINTED */
                        map2 = (struct pmap_typ2 *)hdr;
                        (void) fprintf(fout, "Map type 2 ");
                        (void) fprintf(fout, "VSN %x prn %x\n",
                                        SWAP_16(map2->map2_vsn),
                                        SWAP_16(map2->map2_pn));
                        print_regid(fout, "Partition Type Identifier",
                                        &map2->map2_pti, REG_UDF_ID);
                        break;
                default :
                        (void) fprintf(fout, "unknown map type\n");
                }
                addr += hdr->maph_length;
        }
}



void
print_short_ad(FILE *fout, char *name, struct short_ad *sad)
{
        (void) fprintf(fout,
                "%s loc : %x len : %x\n", name,
                SWAP_32(sad->sad_ext_loc), SWAP_32(sad->sad_ext_len));
}

void
print_long_ad(FILE *fout, char *name, struct long_ad *lad)
{
        (void) fprintf(fout,
                "%s prn : %x loc : %x len : %x\n", name,
                SWAP_16(lad->lad_ext_prn), SWAP_32(lad->lad_ext_loc),
                SWAP_32(lad->lad_ext_len));
}