diff options
Diffstat (limited to 'usr/src/lib/libtnf/ref.c')
-rw-r--r-- | usr/src/lib/libtnf/ref.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/usr/src/lib/libtnf/ref.c b/usr/src/lib/libtnf/ref.c new file mode 100644 index 0000000000..07e0ccccfb --- /dev/null +++ b/usr/src/lib/libtnf/ref.c @@ -0,0 +1,232 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "libtnf.h" + +/* + * Unoptimized versions, always dereference a cell through _GET_INT32() + * + */ + +#define LONG_SIGN_BIT 0x80000000 + +static tnf_ref32_t *vaddr_to_phys(TNF *, tnf_ref32_t *, tnf_ref32_t); + +/* + * Return target cell referred to via src_val from src_cell, after + * checking that target is valid (block was not reused). Return NULL + * otherwise. + * + * NOTE: We must check if the destination is within the valid_bytes + * range of its block, so as to correctly handle tnfxtract'ed files: + * the block containing the target cell may have been copied out + * before the block containing the source cell. + */ + +static tnf_ref32_t * +vaddr_to_phys(TNF *tnf, tnf_ref32_t *src_cell, tnf_ref32_t src_val) +{ + char *base; + unsigned shft, mask; + tnf_uint32_t src_gen, dst_gen, exp_gen; + tnf_int32_t gen_delta; + tnf_ref32_t src_off, exp_off, dst_off, *dst_blk, *dst_cell; + tnf_uint32_t bytes_valid; + + base = tnf->file_start; + shft = tnf->generation_shift; + mask = tnf->address_mask; + + /* Generation of source cell */ + /* LINTED pointer cast */ + src_gen = _GET_BLOCK_GENERATION(tnf, _GET_BLOCK(tnf, src_cell)); + /* Physical file offset of source cell */ + src_off = (tnf_ref32_t)((char *)src_cell - base); + /* Expected (unadjusted) file offset of destination cell */ + exp_off = src_off + src_val; + /* Generation delta */ + gen_delta = (tnf_int32_t)((unsigned)exp_off >> shft); + if ((exp_off & LONG_SIGN_BIT) == LONG_SIGN_BIT) { + /* sign bit was a 1 - so restore sign */ + gen_delta |= ((unsigned)mask << (32 - shft)); + } + /* Expected destination generation */ + exp_gen = src_gen + gen_delta; + /* Physical file offset of destination cell */ + dst_off = (tnf_ref32_t)((unsigned)exp_off & mask); + + /* Destination cell */ + /* LINTED pointer cast */ + dst_cell = (tnf_ref32_t *)(base + dst_off); + /* Destination block */ + /* LINTED pointer cast */ + dst_blk = _GET_BLOCK(tnf, dst_cell); + /* Generation of destination cell */ + /* LINTED pointer cast */ + dst_gen = _GET_BLOCK_GENERATION(tnf, dst_blk); + /* Bytes valid in destination block */ + /* LINTED pointer cast */ + bytes_valid = _GET_BLOCK_BYTES_VALID(tnf, dst_blk); + + if ((src_gen == (tnf_uint32_t)TNF_TAG_GENERATION_NUM) || + (dst_gen == (tnf_uint32_t)TNF_TAG_GENERATION_NUM) || + ((dst_gen == exp_gen) && + ((char *)dst_cell - (char *)dst_blk) < bytes_valid)) + return (dst_cell); + + return ((tnf_ref32_t *)NULL); +} + +/* + * Return the target referent of a cell, chasing forwarding references. + * Return TNF_NULL if cell is a TNF_NULL forwarding reference. + */ + +tnf_ref32_t * +_tnf_get_ref32(TNF *tnf, tnf_ref32_t *cell) +{ + tnf_ref32_t ref32, reftemp; + + ref32 = _GET_INT32(tnf, cell); + + if (TNF_REF32_IS_NULL(ref32)) + return (TNF_NULL); + + if (TNF_REF32_IS_RSVD(ref32)) { + _tnf_error(tnf, TNF_ERR_BADREFTYPE); + return (TNF_NULL); + } + + if (TNF_REF32_IS_PAIR(ref32)) { + /* We chase the high (tag) half */ + tnf_ref16_t tag16; + + tag16 = TNF_REF32_TAG16(ref32); + + if (TNF_TAG16_IS_ABS(tag16)) { + cell = (tnf_ref32_t *) + ((char *)tnf->file_start +/* LINTED pointer cast may result in improper alignment */ + + TNF_TAG16_ABS16(tag16)); + ref32 = _GET_INT32(tnf, cell); + + } else if (TNF_TAG16_IS_REL(tag16)) { + cell = vaddr_to_phys(tnf, cell, + (tnf_ref32_t) TNF_TAG16_REF16(tag16)); + if (cell == TNF_NULL) + return (TNF_NULL); + ref32 = _GET_INT32(tnf, cell); + + } else { + _tnf_error(tnf, TNF_ERR_BADREFTYPE); + return (TNF_NULL); + } + + } else if (TNF_REF32_IS_PERMANENT(ref32)) { + /* permanent space pointer */ + reftemp = TNF_REF32_VALUE(ref32); + reftemp = TNF_REF32_SIGN_EXTEND(reftemp); + /* LINTED pointer cast may result in improper alignment */ + cell = (tnf_ref32_t *) ((char *)tnf->file_start + reftemp); + ref32 = _GET_INT32(tnf, cell); + + } else { /* full/tag reclaimable space reference */ + cell = vaddr_to_phys(tnf, cell, TNF_REF32_VALUE(ref32)); + if (cell == TNF_NULL) + return (TNF_NULL); + ref32 = _GET_INT32(tnf, cell); + } + + /* chase intermediate forwarding references */ + while (ref32 && TNF_REF32_IS_FWD(ref32)) { + if (TNF_REF32_IS_PERMANENT(ref32)) { + reftemp = TNF_REF32_VALUE(ref32); + reftemp = TNF_REF32_SIGN_EXTEND(reftemp); + cell = (tnf_ref32_t *) ((char *)tnf->file_start + + /* LINTED pointer cast may result in improper alignment */ + reftemp); + + } else { + cell = vaddr_to_phys(tnf, cell, TNF_REF32_VALUE(ref32)); + if (cell == TNF_NULL) + return (TNF_NULL); + } + ref32 = _GET_INT32(tnf, cell); + } + + return (cell); +} + +/* + * Return the target referent of ref16 contained in cell. + * Return TNF_NULL if cell doesn't have a ref16. + */ + +tnf_ref32_t * +_tnf_get_ref16(TNF *tnf, tnf_ref32_t *cell) +{ + tnf_ref32_t ref32, reftemp; + + ref32 = _GET_INT32(tnf, cell); + + if (TNF_REF32_IS_PAIR(ref32)) { + tnf_ref16_t ref16; + + ref16 = TNF_REF32_REF16(ref32); + + if (TNF_REF16_VALUE(ref16) == TNF_NULL) + /* No ref16 was stored */ + return (TNF_NULL); + else { + cell = vaddr_to_phys(tnf, cell, + (tnf_ref32_t) TNF_REF16_VALUE(ref16)); + if (cell == TNF_NULL) + return (TNF_NULL); + ref32 = _GET_INT32(tnf, cell); + } + } else /* not a pair pointer */ + return (TNF_NULL); + + /* chase intermediate forwarding references */ + while (ref32 && TNF_REF32_IS_FWD(ref32)) { + if (TNF_REF32_IS_PERMANENT(ref32)) { + reftemp = TNF_REF32_VALUE(ref32); + reftemp = TNF_REF32_SIGN_EXTEND(reftemp); + cell = (tnf_ref32_t *) ((char *)tnf->file_start + + /* LINTED pointer cast may result in improper alignment */ + reftemp); + + } else { + cell = vaddr_to_phys(tnf, cell, TNF_REF32_VALUE(ref32)); + if (cell == TNF_NULL) + return (TNF_NULL); + } + ref32 = _GET_INT32(tnf, cell); + } + + return (cell); +} |