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