root/usr/src/uts/common/fs/udfs/udf_inode.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 (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2017 by Delphix. All rights reserved.
 */

#include <sys/types.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#include <sys/mode.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/kmem.h>
#include <sys/uio.h>
#include <sys/dnlc.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/fbuf.h>
#include <sys/pathname.h>
#include <sys/debug.h>
#include <sys/vmsystm.h>
#include <sys/cmn_err.h>
#include <sys/dirent.h>
#include <sys/errno.h>
#include <sys/modctl.h>
#include <sys/statvfs.h>
#include <sys/mount.h>
#include <sys/sunddi.h>
#include <sys/bootconf.h>
#include <sys/policy.h>

#include <vm/hat.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/seg_kmem.h>
#include <vm/seg_vn.h>
#include <vm/rm.h>
#include <vm/page.h>
#include <sys/swap.h>


#include <fs/fs_subr.h>


#include <sys/fs/udf_volume.h>
#include <sys/fs/udf_inode.h>

extern struct vnodeops *udf_vnodeops;

kmutex_t ud_sync_busy;
/*
 * udf_vfs list manipulation routines
 */
kmutex_t udf_vfs_mutex;
struct udf_vfs *udf_vfs_instances;
#ifndef __lint
_NOTE(MUTEX_PROTECTS_DATA(udf_vfs_mutex, udf_vfs_instances))
#endif

union ihead ud_ihead[UD_HASH_SZ];
kmutex_t ud_icache_lock;

#define UD_BEGIN        0x0
#define UD_END          0x1
#define UD_UNKN         0x2
struct ud_inode *udf_ifreeh, *udf_ifreet;
kmutex_t udf_ifree_lock;
#ifndef __lint
_NOTE(MUTEX_PROTECTS_DATA(udf_ifree_lock, udf_ifreeh))
_NOTE(MUTEX_PROTECTS_DATA(udf_ifree_lock, udf_ifreet))
#endif

kmutex_t ud_nino_lock;
int32_t ud_max_inodes = 512;
int32_t ud_cur_inodes = 0;
#ifndef __lint
_NOTE(MUTEX_PROTECTS_DATA(ud_nino_lock, ud_cur_inodes))
#endif

uid_t ud_default_uid = 0;
gid_t ud_default_gid = 3;

int32_t ud_updat_ext4(struct ud_inode *, struct file_entry *);
int32_t ud_updat_ext4096(struct ud_inode *, struct file_entry *);
void ud_make_sad(struct icb_ext *, struct short_ad *, int32_t);
void ud_make_lad(struct icb_ext *, struct long_ad *, int32_t);
void ud_trunc_ext4(struct ud_inode *, u_offset_t);
void ud_trunc_ext4096(struct ud_inode *, u_offset_t);
void ud_add_to_free_list(struct ud_inode *, uint32_t);
void ud_remove_from_free_list(struct ud_inode *, uint32_t);


#ifdef  DEBUG
struct ud_inode *
ud_search_icache(struct vfs *vfsp, uint16_t prn, uint32_t ploc)
{
        int32_t hno;
        union ihead *ih;
        struct ud_inode *ip;
        struct udf_vfs *udf_vfsp;
        uint32_t loc, dummy;

        udf_vfsp = (struct udf_vfs *)vfsp->vfs_data;
        loc = ud_xlate_to_daddr(udf_vfsp, prn, ploc, 1, &dummy);

        mutex_enter(&ud_icache_lock);
        hno = UD_INOHASH(vfsp->vfs_dev, loc);
        ih = &ud_ihead[hno];
        for (ip = ih->ih_chain[0];
            ip != (struct ud_inode *)ih;
            ip = ip->i_forw) {
                if ((prn == ip->i_icb_prn) && (ploc == ip->i_icb_block) &&
                    (vfsp->vfs_dev == ip->i_dev)) {
                        mutex_exit(&ud_icache_lock);
                        return (ip);
                }
        }
        mutex_exit(&ud_icache_lock);
        return (0);
}
#endif

/* ARGSUSED */
int
ud_iget(struct vfs *vfsp, uint16_t prn, uint32_t ploc, struct ud_inode **ipp,
    struct buf *pbp, struct cred *cred)
{
        int32_t hno, nomem = 0, icb_tag_flags;
        union ihead *ih;
        struct ud_inode *ip;
        struct vnode *vp;
        struct buf *bp = NULL;
        struct file_entry *fe;
        struct udf_vfs *udf_vfsp;
        struct ext_attr_hdr *eah;
        struct attr_hdr *ah;
        int32_t ea_len, ea_off;
        daddr_t loc;
        uint64_t offset = 0;
        struct icb_ext *iext, *con;
        uint32_t length, dummy;
        int32_t ndesc, ftype;
        uint16_t old_prn;
        uint32_t old_block, old_lbano;

        ud_printf("ud_iget\n");
        udf_vfsp = (struct udf_vfs *)vfsp->vfs_data;
        old_prn = 0;
        old_block = old_lbano = 0;
        ftype = 0;
        loc = ud_xlate_to_daddr(udf_vfsp, prn, ploc, 1, &dummy);
loop:
        mutex_enter(&ud_icache_lock);
        hno = UD_INOHASH(vfsp->vfs_dev, loc);

        ih = &ud_ihead[hno];
        for (ip = ih->ih_chain[0];
            ip != (struct ud_inode *)ih;
            ip = ip->i_forw) {

                if ((prn == ip->i_icb_prn) &&
                    (ploc == ip->i_icb_block) &&
                    (vfsp->vfs_dev == ip->i_dev)) {

                        vp = ITOV(ip);
                        VN_HOLD(vp);
                        mutex_exit(&ud_icache_lock);

                        rw_enter(&ip->i_contents, RW_READER);
                        mutex_enter(&ip->i_tlock);
                        if ((ip->i_flag & IREF) == 0) {
                                mutex_enter(&udf_ifree_lock);
                                ud_remove_from_free_list(ip, UD_UNKN);
                                mutex_exit(&udf_ifree_lock);
                        }
                        ip->i_flag |= IREF;
                        mutex_exit(&ip->i_tlock);
                        rw_exit(&ip->i_contents);

                        *ipp = ip;

                        if (pbp != NULL) {
                                brelse(pbp);
                        }

                        return (0);
                }
        }

        /*
         * We don't have it in the cache
         * Allocate a new entry
         */
tryagain:
        mutex_enter(&udf_ifree_lock);
        mutex_enter(&ud_nino_lock);
        if (ud_cur_inodes > ud_max_inodes) {
                int32_t purged;

                mutex_exit(&ud_nino_lock);
                while (udf_ifreeh == NULL ||
                    vn_has_cached_data(ITOV(udf_ifreeh))) {
                        /*
                         * Try to put an inode on the freelist that's
                         * sitting in the dnlc.
                         */
                        mutex_exit(&udf_ifree_lock);
                        purged = dnlc_fs_purge1(udf_vnodeops);
                        mutex_enter(&udf_ifree_lock);
                        if (!purged) {
                                break;
                        }
                }
                mutex_enter(&ud_nino_lock);
        }

        /*
         * If there's a free one available and it has no pages attached
         * take it. If we're over the high water mark, take it even if
         * it has attached pages. Otherwise, make a new one.
         */
        if (udf_ifreeh &&
            (nomem || !vn_has_cached_data(ITOV(udf_ifreeh)) ||
            ud_cur_inodes >= ud_max_inodes)) {

                mutex_exit(&ud_nino_lock);
                ip = udf_ifreeh;
                vp = ITOV(ip);

                ud_remove_from_free_list(ip, UD_BEGIN);

                mutex_exit(&udf_ifree_lock);
                if (ip->i_flag & IREF) {
                        cmn_err(CE_WARN, "ud_iget: bad i_flag\n");
                        mutex_exit(&ud_icache_lock);
                        if (pbp != NULL) {
                                brelse(pbp);
                        }
                        return (EINVAL);
                }
                rw_enter(&ip->i_contents, RW_WRITER);

                /*
                 * We call udf_syncip() to synchronously destroy all pages
                 * associated with the vnode before re-using it. The pageout
                 * thread may have beat us to this page so our v_count can
                 * be > 0 at this point even though we are on the freelist.
                 */
                mutex_enter(&ip->i_tlock);
                ip->i_flag = (ip->i_flag & IMODTIME) | IREF;
                mutex_exit(&ip->i_tlock);

                VN_HOLD(vp);
                if (ud_syncip(ip, B_INVAL, I_SYNC) != 0) {
                        ud_idrop(ip);
                        rw_exit(&ip->i_contents);
                        mutex_exit(&ud_icache_lock);
                        goto loop;
                }

                mutex_enter(&ip->i_tlock);
                ip->i_flag &= ~IMODTIME;
                mutex_exit(&ip->i_tlock);

                if (ip->i_ext) {
                        kmem_free(ip->i_ext,
                            sizeof (struct icb_ext) * ip->i_ext_count);
                        ip->i_ext = 0;
                        ip->i_ext_count = ip->i_ext_used = 0;
                }

                if (ip->i_con) {
                        kmem_free(ip->i_con,
                            sizeof (struct icb_ext) * ip->i_con_count);
                        ip->i_con = 0;
                        ip->i_con_count = ip->i_con_used = ip->i_con_read = 0;
                }

                /*
                 * The pageout thread may not have had a chance to release
                 * its hold on the vnode (if it was active with this vp),
                 * but the pages should all be invalidated.
                 */
        } else {
                mutex_exit(&ud_nino_lock);
                mutex_exit(&udf_ifree_lock);
                /*
                 * Try to get memory for this inode without blocking.
                 * If we can't and there is something on the freelist,
                 * go ahead and use it, otherwise block waiting for
                 * memory holding the hash_lock. We expose a potential
                 * deadlock if all users of memory have to do a ud_iget()
                 * before releasing memory.
                 */
                ip = (struct ud_inode *)kmem_zalloc(sizeof (struct ud_inode),
                    KM_NOSLEEP);
                vp = vn_alloc(KM_NOSLEEP);
                if ((ip == NULL) || (vp == NULL)) {
                        mutex_enter(&udf_ifree_lock);
                        if (udf_ifreeh) {
                                mutex_exit(&udf_ifree_lock);
                                if (ip != NULL)
                                        kmem_free(ip, sizeof (struct ud_inode));
                                if (vp != NULL)
                                        vn_free(vp);
                                nomem = 1;
                                goto tryagain;
                        } else {
                                mutex_exit(&udf_ifree_lock);
                                if (ip == NULL)
                                        ip = (struct ud_inode *)
                                            kmem_zalloc(
                                            sizeof (struct ud_inode),
                                            KM_SLEEP);
                                if (vp == NULL)
                                        vp = vn_alloc(KM_SLEEP);
                        }
                }
                ip->i_vnode = vp;

                ip->i_marker1 = (uint32_t)0xAAAAAAAA;
                ip->i_marker2 = (uint32_t)0xBBBBBBBB;
                ip->i_marker3 = (uint32_t)0xCCCCCCCC;

                rw_init(&ip->i_rwlock, NULL, RW_DEFAULT, NULL);
                rw_init(&ip->i_contents, NULL, RW_DEFAULT, NULL);
                mutex_init(&ip->i_tlock, NULL, MUTEX_DEFAULT, NULL);

                ip->i_forw = ip;
                ip->i_back = ip;
                vp->v_data = (caddr_t)ip;
                vn_setops(vp, udf_vnodeops);
                ip->i_flag = IREF;
                cv_init(&ip->i_wrcv, NULL, CV_DRIVER, NULL);
                mutex_enter(&ud_nino_lock);
                ud_cur_inodes++;
                mutex_exit(&ud_nino_lock);

                rw_enter(&ip->i_contents, RW_WRITER);
        }

        if (vp->v_count < 1) {
                cmn_err(CE_WARN, "ud_iget: v_count < 1\n");
                mutex_exit(&ud_icache_lock);
                rw_exit(&ip->i_contents);
                if (pbp != NULL) {
                        brelse(pbp);
                }
                return (EINVAL);
        }
        if (vn_has_cached_data(vp)) {
                cmn_err(CE_WARN, "ud_iget: v_pages not NULL\n");
                mutex_exit(&ud_icache_lock);
                rw_exit(&ip->i_contents);
                if (pbp != NULL) {
                        brelse(pbp);
                }
                return (EINVAL);
        }

        /*
         * Move the inode on the chain for its new (ino, dev) pair
         */
        remque(ip);
        ip->i_forw = ip;
        ip->i_back = ip;
        insque(ip, ih);

        ip->i_dev = vfsp->vfs_dev;
        ip->i_udf = udf_vfsp;
        ip->i_diroff = 0;
        ip->i_devvp = ip->i_udf->udf_devvp;
        ip->i_icb_prn = prn;
        ip->i_icb_block = ploc;
        ip->i_icb_lbano = loc;
        ip->i_nextr = 0;
        ip->i_seq = 0;
        mutex_exit(&ud_icache_lock);

read_de:
        if (pbp != NULL) {
                /*
                 * assumption is that we will not
                 * create a 4096 file
                 */
                bp = pbp;
        } else {
                bp = ud_bread(ip->i_dev,
                    ip->i_icb_lbano << udf_vfsp->udf_l2d_shift,
                    udf_vfsp->udf_lbsize);
        }

        /*
         * Check I/O errors
         */
        fe = (struct file_entry *)bp->b_un.b_addr;
        if ((bp->b_flags & B_ERROR) ||
            (ud_verify_tag_and_desc(&fe->fe_tag, UD_FILE_ENTRY,
            ip->i_icb_block, 1, udf_vfsp->udf_lbsize) != 0)) {

                if (((bp->b_flags & B_ERROR) == 0) &&
                    (ftype == STRAT_TYPE4096)) {
                        if (ud_check_te_unrec(udf_vfsp,
                            bp->b_un.b_addr, ip->i_icb_block) == 0) {

                                brelse(bp);

                                /*
                                 * restore old file entry location
                                 */
                                ip->i_icb_prn = old_prn;
                                ip->i_icb_block = old_block;
                                ip->i_icb_lbano = old_lbano;

                                /*
                                 * reread old file entry
                                 */
                                bp = ud_bread(ip->i_dev,
                                    old_lbano << udf_vfsp->udf_l2d_shift,
                                    udf_vfsp->udf_lbsize);
                                if ((bp->b_flags & B_ERROR) == 0) {
                                        fe = (struct file_entry *)
                                            bp->b_un.b_addr;
                                        if (ud_verify_tag_and_desc(&fe->fe_tag,
                                            UD_FILE_ENTRY, ip->i_icb_block, 1,
                                            udf_vfsp->udf_lbsize) == 0) {
                                                goto end_4096;
                                        }
                                }
                        }
                }
error_ret:
                brelse(bp);
                /*
                 * The inode may not contain anything useful. Mark it as
                 * having an error and let anyone else who was waiting for
                 * this know there was an error. Callers waiting for
                 * access to this inode in ud_iget will find
                 * the i_icb_lbano == 0, so there won't be a match.
                 * It remains in the cache. Put it back on the freelist.
                 */
                mutex_enter(&vp->v_lock);
                VN_RELE_LOCKED(vp);
                mutex_exit(&vp->v_lock);
                ip->i_icb_lbano = 0;

                /*
                 * The folowing two lines make
                 * it impossible for any one do
                 * a VN_HOLD and then a VN_RELE
                 * so avoiding a ud_iinactive
                 */
                ip->i_icb_prn = 0xffff;
                ip->i_icb_block = 0;

                /*
                 * remove the bad inode from hash chains
                 * so that during unmount we will not
                 * go through this inode
                 */
                mutex_enter(&ud_icache_lock);
                remque(ip);
                ip->i_forw = ip;
                ip->i_back = ip;
                mutex_exit(&ud_icache_lock);

                /* Put the inode at the front of the freelist */
                mutex_enter(&ip->i_tlock);
                mutex_enter(&udf_ifree_lock);
                ud_add_to_free_list(ip, UD_BEGIN);
                mutex_exit(&udf_ifree_lock);
                ip->i_flag = 0;
                mutex_exit(&ip->i_tlock);
                rw_exit(&ip->i_contents);
                return (EIO);
        }

        if (fe->fe_icb_tag.itag_strategy == SWAP_16(STRAT_TYPE4096)) {
                struct buf *ibp = NULL;
                struct indirect_entry *ie;

                /*
                 * save old file_entry location
                 */
                old_prn = ip->i_icb_prn;
                old_block = ip->i_icb_block;
                old_lbano = ip->i_icb_lbano;

                ftype = STRAT_TYPE4096;

                /*
                 * If astrat is 4096 different versions
                 * of the file exist on the media.
                 * we are supposed to get to the latest
                 * version of the file
                 */

                /*
                 * IE is supposed to be in the next block
                 * of DE
                 */
                ibp = ud_bread(ip->i_dev,
                    (ip->i_icb_lbano + 1) << udf_vfsp->udf_l2d_shift,
                    udf_vfsp->udf_lbsize);
                if (ibp->b_flags & B_ERROR) {
                        /*
                         * Get rid of current ibp and
                         * then goto error on DE's bp
                         */
ie_error:
                        brelse(ibp);
                        goto error_ret;
                }

                ie = (struct indirect_entry *)ibp->b_un.b_addr;
                if (ud_verify_tag_and_desc(&ie->ie_tag,
                    UD_INDIRECT_ENT, ip->i_icb_block + 1,
                    1, udf_vfsp->udf_lbsize) == 0) {
                        struct long_ad *lad;

                        lad = &ie->ie_indirecticb;
                        ip->i_icb_prn = SWAP_16(lad->lad_ext_prn);
                        ip->i_icb_block = SWAP_32(lad->lad_ext_loc);
                        ip->i_icb_lbano = ud_xlate_to_daddr(udf_vfsp,
                            ip->i_icb_prn, ip->i_icb_block,
                            1, &dummy);
                        brelse(ibp);
                        brelse(bp);
                        goto read_de;
                }

                /*
                 * If this block is TE or unrecorded we
                 * are at the last entry
                 */
                if (ud_check_te_unrec(udf_vfsp, ibp->b_un.b_addr,
                    ip->i_icb_block + 1) != 0) {
                        /*
                         * This is not an unrecorded block
                         * Check if it a valid IE and
                         * get the address of DE that
                         * this IE points to
                         */
                        goto ie_error;
                }
                /*
                 * If ud_check_unrec returns "0"
                 * this is the last in the chain
                 * Latest file_entry
                 */
                brelse(ibp);
        }

end_4096:

        ip->i_uid = SWAP_32(fe->fe_uid);
        if (ip->i_uid == -1) {
                ip->i_uid = ud_default_uid;
        }
        ip->i_gid = SWAP_32(fe->fe_gid);
        if (ip->i_gid == -1) {
                ip->i_gid = ud_default_gid;
        }
        ip->i_perm = SWAP_32(fe->fe_perms) & 0xFFFF;
        if (fe->fe_icb_tag.itag_strategy == SWAP_16(STRAT_TYPE4096)) {
                ip->i_perm &= ~(IWRITE | (IWRITE >> 5) | (IWRITE >> 10));
        }

        ip->i_nlink = SWAP_16(fe->fe_lcount);
        ip->i_size = SWAP_64(fe->fe_info_len);
        ip->i_lbr = SWAP_64(fe->fe_lbr);

        ud_dtime2utime(&ip->i_atime, &fe->fe_acc_time);
        ud_dtime2utime(&ip->i_mtime, &fe->fe_mod_time);
        ud_dtime2utime(&ip->i_ctime, &fe->fe_attr_time);


        ip->i_uniqid = SWAP_64(fe->fe_uniq_id);
        icb_tag_flags = SWAP_16(fe->fe_icb_tag.itag_flags);

        if ((fe->fe_icb_tag.itag_ftype == FTYPE_CHAR_DEV) ||
            (fe->fe_icb_tag.itag_ftype == FTYPE_BLOCK_DEV)) {

                eah = (struct ext_attr_hdr *)fe->fe_spec;
                ea_off = GET_32(&eah->eah_ial);
                ea_len = GET_32(&fe->fe_len_ear);
                if (ea_len && (ud_verify_tag_and_desc(&eah->eah_tag,
                    UD_EXT_ATTR_HDR, ip->i_icb_block, 1,
                    sizeof (struct file_entry) -
                    offsetof(struct file_entry, fe_spec)) == 0)) {

                        while (ea_off < ea_len) {
                                /*
                                 * We now check the validity of ea_off.
                                 * (ea_len - ea_off) should be large enough to
                                 * hold the attribute header atleast.
                                 */
                                if ((ea_len - ea_off) <
                                    sizeof (struct attr_hdr)) {
                                        cmn_err(CE_NOTE,
                                            "ea_len(0x%x) - ea_off(0x%x) is "
                                            "too small to hold attr. info. "
                                            "blockno 0x%x\n",
                                            ea_len, ea_off, ip->i_icb_block);
                                        goto error_ret;
                                }
                                ah = (struct attr_hdr *)&fe->fe_spec[ea_off];

                                /*
                                 * Device Specification EA
                                 */
                                if ((GET_32(&ah->ahdr_atype) == 12) &&
                                        (ah->ahdr_astype == 1)) {
                                        struct dev_spec_ear *ds;

                                        if ((ea_len - ea_off) <
                                            sizeof (struct dev_spec_ear)) {
                                                cmn_err(CE_NOTE,
                                                    "ea_len(0x%x) - "
                                                    "ea_off(0x%x) is too small "
                                                    "to hold dev_spec_ear."
                                                    " blockno 0x%x\n",
                                                    ea_len, ea_off,
                                                    ip->i_icb_block);
                                                goto error_ret;
                                        }
                                        ds = (struct dev_spec_ear *)ah;
                                        ip->i_major = GET_32(&ds->ds_major_id);
                                        ip->i_minor = GET_32(&ds->ds_minor_id);
                                }

                                /*
                                 * Impl Use EA
                                 */
                                if ((GET_32(&ah->ahdr_atype) == 2048) &&
                                        (ah->ahdr_astype == 1)) {
                                        struct iu_ea *iuea;
                                        struct copy_mgt_info *cmi;

                                        if ((ea_len - ea_off) <
                                            sizeof (struct iu_ea)) {
                                                cmn_err(CE_NOTE,
"ea_len(0x%x) - ea_off(0x%x) is too small to hold iu_ea. blockno 0x%x\n",
                                                    ea_len, ea_off,
                                                    ip->i_icb_block);
                                                goto error_ret;
                                        }
                                        iuea = (struct iu_ea *)ah;
                                        if (strncmp(iuea->iuea_ii.reg_id,
                                            UDF_FREEEASPACE,
                                            sizeof (iuea->iuea_ii.reg_id))
                                            == 0) {
                                                /* skip it */
                                                iuea = iuea;
                                        } else if (strncmp(iuea->iuea_ii.reg_id,
                                            UDF_CGMS_INFO,
                                            sizeof (iuea->iuea_ii.reg_id))
                                            == 0) {
                                                cmi = (struct copy_mgt_info *)
                                                        iuea->iuea_iu;
                                                cmi = cmi;
                                        }
                                }
                                /* ??? PARANOIA */
                                if (GET_32(&ah->ahdr_length) == 0) {
                                        break;
                                }
                                ea_off += GET_32(&ah->ahdr_length);
                        }
                }
        }

        ip->i_nextr = 0;

        ip->i_maxent = SWAP_16(fe->fe_icb_tag.itag_max_ent);
        ip->i_astrat = SWAP_16(fe->fe_icb_tag.itag_strategy);

        ip->i_desc_type = icb_tag_flags & 0x7;

        /* Strictly Paranoia */
        ip->i_ext = NULL;
        ip->i_ext_count = ip->i_ext_used = 0;
        ip->i_con = 0;
        ip->i_con_count = ip->i_con_used = ip->i_con_read = 0;

        ip->i_data_off = 0xB0 + SWAP_32(fe->fe_len_ear);
        ip->i_max_emb =  udf_vfsp->udf_lbsize - ip->i_data_off;
        if (ip->i_desc_type == ICB_FLAG_SHORT_AD) {
                /* Short allocation desc */
                struct short_ad *sad;

                ip->i_ext_used = 0;
                ip->i_ext_count = ndesc =
                    SWAP_32(fe->fe_len_adesc) / sizeof (struct short_ad);
                ip->i_ext_count =
                    ((ip->i_ext_count / EXT_PER_MALLOC) + 1) * EXT_PER_MALLOC;
                ip->i_ext = (struct icb_ext  *)kmem_zalloc(ip->i_ext_count *
                    sizeof (struct icb_ext), KM_SLEEP);
                ip->i_cur_max_ext = ip->i_max_emb / sizeof (struct short_ad);
                ip->i_cur_max_ext --;

                if ((ip->i_astrat != STRAT_TYPE4) &&
                    (ip->i_astrat != STRAT_TYPE4096)) {
                        goto error_ret;
                }

                sad = (struct short_ad *)
                    (fe->fe_spec + SWAP_32(fe->fe_len_ear));
                iext = ip->i_ext;
                while (ndesc --) {
                        length = SWAP_32(sad->sad_ext_len);
                        if ((length & 0x3FFFFFFF) == 0) {
                                break;
                        }
                        if (((length >> 30) & IB_MASK) == IB_CON) {
                                if (ip->i_con == NULL) {
                                        ip->i_con_count = EXT_PER_MALLOC;
                                        ip->i_con_used = 0;
                                        ip->i_con_read = 0;
                                        ip->i_con = kmem_zalloc(
                                            ip->i_con_count *
                                            sizeof (struct icb_ext),
                                            KM_SLEEP);
                                }
                                con = &ip->i_con[ip->i_con_used];
                                con->ib_prn = 0;
                                con->ib_block = SWAP_32(sad->sad_ext_loc);
                                con->ib_count = length & 0x3FFFFFFF;
                                con->ib_flags = (length >> 30) & IB_MASK;
                                ip->i_con_used++;
                                sad ++;
                                break;
                        }
                        iext->ib_prn = 0;
                        iext->ib_block = SWAP_32(sad->sad_ext_loc);
                        length = SWAP_32(sad->sad_ext_len);
                        iext->ib_count = length & 0x3FFFFFFF;
                        iext->ib_offset = offset;
                        iext->ib_marker1 = (uint32_t)0xAAAAAAAA;
                        iext->ib_marker2 = (uint32_t)0xBBBBBBBB;
                        offset += (iext->ib_count + udf_vfsp->udf_lbmask) &
                            (~udf_vfsp->udf_lbmask);

                        iext->ib_flags = (length >> 30) & IB_MASK;

                        ip->i_ext_used++;
                        iext++;
                        sad ++;
                }
        } else if (ip->i_desc_type == ICB_FLAG_LONG_AD) {
                /* Long allocation desc */
                struct long_ad *lad;

                ip->i_ext_used = 0;
                ip->i_ext_count = ndesc =
                    SWAP_32(fe->fe_len_adesc) / sizeof (struct long_ad);
                ip->i_ext_count =
                    ((ip->i_ext_count / EXT_PER_MALLOC) + 1) * EXT_PER_MALLOC;
                ip->i_ext = (struct icb_ext  *)kmem_zalloc(ip->i_ext_count *
                    sizeof (struct icb_ext), KM_SLEEP);

                ip->i_cur_max_ext = ip->i_max_emb / sizeof (struct long_ad);
                ip->i_cur_max_ext --;

                if ((ip->i_astrat != STRAT_TYPE4) &&
                    (ip->i_astrat != STRAT_TYPE4096)) {
                        goto error_ret;
                }

                lad = (struct long_ad *)
                    (fe->fe_spec + SWAP_32(fe->fe_len_ear));
                iext = ip->i_ext;
                while (ndesc --) {
                        length = SWAP_32(lad->lad_ext_len);
                        if ((length & 0x3FFFFFFF) == 0) {
                                break;
                        }
                        if (((length >> 30) & IB_MASK) == IB_CON) {
                                if (ip->i_con == NULL) {
                                        ip->i_con_count = EXT_PER_MALLOC;
                                        ip->i_con_used = 0;
                                        ip->i_con_read = 0;
                                        ip->i_con = kmem_zalloc(
                                            ip->i_con_count *
                                            sizeof (struct icb_ext),
                                            KM_SLEEP);
                                }
                                con = &ip->i_con[ip->i_con_used];
                                con->ib_prn = SWAP_16(lad->lad_ext_prn);
                                con->ib_block = SWAP_32(lad->lad_ext_loc);
                                con->ib_count = length & 0x3FFFFFFF;
                                con->ib_flags = (length >> 30) & IB_MASK;
                                ip->i_con_used++;
                                lad ++;
                                break;
                        }
                        iext->ib_prn = SWAP_16(lad->lad_ext_prn);
                        iext->ib_block = SWAP_32(lad->lad_ext_loc);
                        iext->ib_count = length & 0x3FFFFFFF;
                        iext->ib_offset = offset;
                        iext->ib_marker1 = (uint32_t)0xAAAAAAAA;
                        iext->ib_marker2 = (uint32_t)0xBBBBBBBB;
                        offset += (iext->ib_count + udf_vfsp->udf_lbmask) &
                            (~udf_vfsp->udf_lbmask);

                        iext->ib_flags = (length >> 30) & IB_MASK;

                        ip->i_ext_used++;
                        iext++;
                        lad ++;
                }
        } else if (ip->i_desc_type == ICB_FLAG_ONE_AD) {
                ASSERT(SWAP_32(fe->fe_len_ear) < udf_vfsp->udf_lbsize);

                if (SWAP_32(fe->fe_len_ear) > udf_vfsp->udf_lbsize) {
                        goto error_ret;
                }
        } else {
                /* Not to be used in UDF 1.50 */
                cmn_err(CE_NOTE, "Invalid Allocation Descriptor type %x\n",
                    ip->i_desc_type);
                goto error_ret;
        }


        if (icb_tag_flags & ICB_FLAG_SETUID) {
                ip->i_char = ISUID;
        } else {
                ip->i_char = 0;
        }
        if (icb_tag_flags & ICB_FLAG_SETGID) {
                ip->i_char |= ISGID;
        }
        if (icb_tag_flags & ICB_FLAG_STICKY) {
                ip->i_char |= ISVTX;
        }
        switch (fe->fe_icb_tag.itag_ftype) {
                case FTYPE_DIRECTORY :
                        ip->i_type = VDIR;
                        break;
                case FTYPE_FILE :
                        ip->i_type = VREG;
                        break;
                case FTYPE_BLOCK_DEV :
                        ip->i_type = VBLK;
                        break;
                case FTYPE_CHAR_DEV :
                        ip->i_type = VCHR;
                        break;
                case FTYPE_FIFO :
                        ip->i_type = VFIFO;
                        break;
                case FTYPE_C_ISSOCK :
                        ip->i_type = VSOCK;
                        break;
                case FTYPE_SYMLINK :
                        ip->i_type = VLNK;
                        break;
                default :
                        ip->i_type = VNON;
                        break;
        }

        if (ip->i_type == VBLK || ip->i_type == VCHR) {
                ip->i_rdev = makedevice(ip->i_major, ip->i_minor);
        }

        /*
         * Fill in the rest.  Don't bother with the vnode lock because nobody
         * should be looking at this vnode.  We have already invalidated the
         * pages if it had any so pageout shouldn't be referencing this vnode
         * and we are holding the write contents lock so a look up can't use
         * the vnode.
         */
        vp->v_vfsp = vfsp;
        vp->v_type = ip->i_type;
        vp->v_rdev = ip->i_rdev;
        if (ip->i_udf->udf_root_blkno == loc) {
                vp->v_flag = VROOT;
        } else {
                vp->v_flag = 0;
        }

        brelse(bp);
        *ipp = ip;
        rw_exit(&ip->i_contents);
        vn_exists(vp);
        return (0);
}

void
ud_iinactive(struct ud_inode *ip, struct cred *cr)
{
        int32_t busy = 0;
        struct vnode *vp;
        vtype_t type;
        caddr_t addr, addr1;
        size_t size, size1;


        ud_printf("ud_iinactive\n");

        /*
         * Get exclusive access to inode data.
         */
        rw_enter(&ip->i_contents, RW_WRITER);

        /*
         * Make sure no one reclaimed the inode before we put
         * it on the freelist or destroy it. We keep our 'hold'
         * on the vnode from vn_rele until we are ready to
         * do something with the inode (freelist/destroy).
         *
         * Pageout may put a VN_HOLD/VN_RELE at anytime during this
         * operation via an async putpage, so we must make sure
         * we don't free/destroy the inode more than once. ud_iget
         * may also put a VN_HOLD on the inode before it grabs
         * the i_contents lock. This is done so we don't kmem_free
         * an inode that a thread is waiting on.
         */
        vp = ITOV(ip);

        mutex_enter(&vp->v_lock);
        if (vp->v_count < 1) {
                cmn_err(CE_WARN, "ud_iinactive: v_count < 1\n");
                return;
        }
        if ((vp->v_count > 1) || ((ip->i_flag & IREF) == 0)) {
                VN_RELE_LOCKED(vp);
                mutex_exit(&vp->v_lock);
                rw_exit(&ip->i_contents);
                return;
        }
        mutex_exit(&vp->v_lock);

        /*
         * For forced umount case: if i_udf is NULL, the contents of
         * the inode and all the pages have already been pushed back
         * to disk. It can be safely destroyed.
         */
        if (ip->i_udf == NULL) {
                addr = (caddr_t)ip->i_ext;
                size = sizeof (struct icb_ext) * ip->i_ext_count;
                ip->i_ext = 0;
                ip->i_ext_count = ip->i_ext_used = 0;
                addr1 = (caddr_t)ip->i_con;
                size1 = sizeof (struct icb_ext) * ip->i_con_count;
                ip->i_con = 0;
                ip->i_con_count = ip->i_con_used = ip->i_con_read = 0;
                rw_exit(&ip->i_contents);
                vn_invalid(vp);

                mutex_enter(&ud_nino_lock);
                ud_cur_inodes--;
                mutex_exit(&ud_nino_lock);

                cv_destroy(&ip->i_wrcv);  /* throttling */
                rw_destroy(&ip->i_rwlock);
                rw_exit(&ip->i_contents);
                rw_destroy(&ip->i_contents);
                kmem_free(addr, size);
                kmem_free(addr1, size1);
                vn_free(vp);
                kmem_free(ip, sizeof (struct ud_inode));
                return;
        }

        if ((ip->i_udf->udf_flags & UDF_FL_RDONLY) == 0) {
                if (ip->i_nlink <= 0) {
                        ip->i_marker3 = (uint32_t)0xDDDD0000;
                        ip->i_nlink = 1;        /* prevent free-ing twice */
                        (void) ud_itrunc(ip, 0, 0, cr);
                        type = ip->i_type;
                        ip->i_perm = 0;
                        ip->i_uid = 0;
                        ip->i_gid = 0;
                        ip->i_rdev = 0; /* Zero in core version of rdev */
                        mutex_enter(&ip->i_tlock);
                        ip->i_flag |= IUPD|ICHG;
                        mutex_exit(&ip->i_tlock);
                        ud_ifree(ip, type);
                        ip->i_icb_prn = 0xFFFF;
                } else if (!IS_SWAPVP(vp)) {
                        /*
                         * Write the inode out if dirty. Pages are
                         * written back and put on the freelist.
                         */
                        (void) ud_syncip(ip, B_FREE | B_ASYNC, 0);
                        /*
                         * Do nothing if inode is now busy -- inode may
                         * have gone busy because ud_syncip
                         * releases/reacquires the i_contents lock
                         */
                        mutex_enter(&vp->v_lock);
                        if (vp->v_count > 1) {
                                VN_RELE_LOCKED(vp);
                                mutex_exit(&vp->v_lock);
                                rw_exit(&ip->i_contents);
                                return;
                        }
                        mutex_exit(&vp->v_lock);
                } else {
                        ud_iupdat(ip, 0);
                }
        }


        /*
         * Put the inode on the end of the free list.
         * Possibly in some cases it would be better to
         * put the inode at the head of the free list,
         * (e.g.: where i_perm == 0 || i_number == 0)
         * but I will think about that later.
         * (i_number is rarely 0 - only after an i/o error in ud_iget,
         * where i_perm == 0, the inode will probably be wanted
         * again soon for an ialloc, so possibly we should keep it)
         */
        /*
         * If inode is invalid or there is no page associated with
         * this inode, put the inode in the front of the free list.
         * Since we have a VN_HOLD on the vnode, and checked that it
         * wasn't already on the freelist when we entered, we can safely
         * put it on the freelist even if another thread puts a VN_HOLD
         * on it (pageout/ud_iget).
         */
tryagain:
        mutex_enter(&ud_nino_lock);
        if (vn_has_cached_data(vp)) {
                mutex_exit(&ud_nino_lock);
                mutex_enter(&vp->v_lock);
                VN_RELE_LOCKED(vp);
                mutex_exit(&vp->v_lock);
                mutex_enter(&ip->i_tlock);
                mutex_enter(&udf_ifree_lock);
                ud_add_to_free_list(ip, UD_END);
                mutex_exit(&udf_ifree_lock);
                ip->i_flag &= IMODTIME;
                mutex_exit(&ip->i_tlock);
                rw_exit(&ip->i_contents);
        } else if (busy || ud_cur_inodes < ud_max_inodes) {
                mutex_exit(&ud_nino_lock);
                /*
                 * We're not over our high water mark, or it's
                 * not safe to kmem_free the inode, so put it
                 * on the freelist.
                 */
                mutex_enter(&vp->v_lock);
                if (vn_has_cached_data(vp)) {
                        cmn_err(CE_WARN, "ud_iinactive: v_pages not NULL\n");
                }
                VN_RELE_LOCKED(vp);
                mutex_exit(&vp->v_lock);

        mutex_enter(&ip->i_tlock);
                mutex_enter(&udf_ifree_lock);
                ud_add_to_free_list(ip, UD_BEGIN);
                mutex_exit(&udf_ifree_lock);
        ip->i_flag &= IMODTIME;
        mutex_exit(&ip->i_tlock);
                rw_exit(&ip->i_contents);
        } else {
                mutex_exit(&ud_nino_lock);
                if (vn_has_cached_data(vp)) {
                        cmn_err(CE_WARN, "ud_iinactive: v_pages not NULL\n");
                }
                /*
                 * Try to free the inode. We must make sure
                 * it's o.k. to destroy this inode. We can't destroy
                 * if a thread is waiting for this inode. If we can't get the
                 * cache now, put it back on the freelist.
                 */
                if (!mutex_tryenter(&ud_icache_lock)) {
                        busy = 1;
                        goto tryagain;
                }
                mutex_enter(&vp->v_lock);
                if (vp->v_count > 1) {
                        /* inode is wanted in ud_iget */
                        busy = 1;
                        mutex_exit(&vp->v_lock);
                        mutex_exit(&ud_icache_lock);
                        goto tryagain;
                }
                mutex_exit(&vp->v_lock);
                remque(ip);
                ip->i_forw = ip;
                ip->i_back = ip;
                mutex_enter(&ud_nino_lock);
                ud_cur_inodes--;
                mutex_exit(&ud_nino_lock);
                mutex_exit(&ud_icache_lock);
                if (ip->i_icb_prn != 0xFFFF) {
                        ud_iupdat(ip, 0);
                }
                addr = (caddr_t)ip->i_ext;
                size = sizeof (struct icb_ext) * ip->i_ext_count;
                ip->i_ext = 0;
                ip->i_ext_count = ip->i_ext_used = 0;
                addr1 = (caddr_t)ip->i_con;
                size1 = sizeof (struct icb_ext) * ip->i_con_count;
                ip->i_con = 0;
                ip->i_con_count = ip->i_con_used = ip->i_con_read = 0;
                cv_destroy(&ip->i_wrcv);  /* throttling */
                rw_destroy(&ip->i_rwlock);
                rw_exit(&ip->i_contents);
                rw_destroy(&ip->i_contents);
                kmem_free(addr, size);
                kmem_free(addr1, size1);
                ip->i_marker3 = (uint32_t)0xDDDDDDDD;
                vn_free(vp);
                kmem_free(ip, sizeof (struct ud_inode));
        }
}


void
ud_iupdat(struct ud_inode *ip, int32_t waitfor)
{
        uint16_t flag, tag_flags;
        int32_t error;
        struct buf *bp;
        struct udf_vfs *udf_vfsp;
        struct file_entry *fe;
        uint16_t crc_len = 0;

        ASSERT(RW_WRITE_HELD(&ip->i_contents));

        ud_printf("ud_iupdat\n");
        /*
         * Return if file system has been forcibly umounted.
         */
        if (ip->i_udf == NULL) {
                return;
        }

        udf_vfsp = ip->i_udf;
        flag = ip->i_flag;      /* Atomic read */
        if ((flag & (IUPD|IACC|ICHG|IMOD|IMODACC)) != 0) {
                if (udf_vfsp->udf_flags & UDF_FL_RDONLY) {
                        ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD|IMODACC|IATTCHG);
                        return;
                }

                bp = ud_bread(ip->i_dev,
                    ip->i_icb_lbano << udf_vfsp->udf_l2d_shift,
                    ip->i_udf->udf_lbsize);
                if (bp->b_flags & B_ERROR) {
                        brelse(bp);
                        return;
                }
                fe = (struct file_entry *)bp->b_un.b_addr;
                if (ud_verify_tag_and_desc(&fe->fe_tag, UD_FILE_ENTRY,
                    ip->i_icb_block,
                    1, ip->i_udf->udf_lbsize) != 0) {
                        brelse(bp);
                        return;
                }

                mutex_enter(&ip->i_tlock);
                if (ip->i_flag & (IUPD|IACC|ICHG)) {
                        IMARK(ip);
                }
                ip->i_flag &= ~(IUPD|IACC|ICHG|IMOD|IMODACC);
                mutex_exit(&ip->i_tlock);

                fe->fe_uid = SWAP_32(ip->i_uid);
                fe->fe_gid = SWAP_32(ip->i_gid);

                fe->fe_perms = SWAP_32(ip->i_perm);

                fe->fe_lcount = SWAP_16(ip->i_nlink);
                fe->fe_info_len = SWAP_64(ip->i_size);
                fe->fe_lbr = SWAP_64(ip->i_lbr);

                ud_utime2dtime(&ip->i_atime, &fe->fe_acc_time);
                ud_utime2dtime(&ip->i_mtime, &fe->fe_mod_time);
                ud_utime2dtime(&ip->i_ctime, &fe->fe_attr_time);

                if (ip->i_char & ISUID) {
                        tag_flags = ICB_FLAG_SETUID;
                } else {
                        tag_flags = 0;
                }
                if (ip->i_char & ISGID) {
                        tag_flags |= ICB_FLAG_SETGID;
                }
                if (ip->i_char & ISVTX) {
                        tag_flags |= ICB_FLAG_STICKY;
                }
                tag_flags |= ip->i_desc_type;

                /*
                 * Remove the following it is no longer contig
                 * if (ip->i_astrat  == STRAT_TYPE4) {
                 *      tag_flags |= ICB_FLAG_CONTIG;
                 * }
                 */

                fe->fe_icb_tag.itag_flags &= ~SWAP_16((uint16_t)0x3C3);
                fe->fe_icb_tag.itag_strategy = SWAP_16(ip->i_astrat);
                fe->fe_icb_tag.itag_flags |= SWAP_16(tag_flags);

                ud_update_regid(&fe->fe_impl_id);

                crc_len = offsetof(struct file_entry, fe_spec) +
                    SWAP_32(fe->fe_len_ear);
                if (ip->i_desc_type == ICB_FLAG_ONE_AD) {
                        crc_len += ip->i_size;
                        fe->fe_len_adesc = SWAP_32(((uint32_t)ip->i_size));
                } else if ((ip->i_size != 0) && (ip->i_ext != NULL) &&
                    (ip->i_ext_used != 0)) {

                        if ((error = ud_read_icb_till_off(ip,
                            ip->i_size)) == 0) {
                                if (ip->i_astrat == STRAT_TYPE4) {
                                        error = ud_updat_ext4(ip, fe);
                                } else if (ip->i_astrat == STRAT_TYPE4096) {
                                        error = ud_updat_ext4096(ip, fe);
                                }
                                if (error) {
                                        udf_vfsp->udf_mark_bad = 1;
                                }
                        }
                        crc_len += SWAP_32(fe->fe_len_adesc);
                } else {
                        fe->fe_len_adesc = 0;
                }

                /*
                 * Zero out the rest of the block
                 */
                bzero(bp->b_un.b_addr + crc_len,
                    ip->i_udf->udf_lbsize - crc_len);

                ud_make_tag(ip->i_udf, &fe->fe_tag,
                    UD_FILE_ENTRY, ip->i_icb_block, crc_len);


                if (waitfor) {
                        BWRITE(bp);

                        /*
                         * Synchronous write has guaranteed that inode
                         * has been written on disk so clear the flag
                         */
                        ip->i_flag &= ~(IBDWRITE);
                } else {
                        bdwrite(bp);

                        /*
                         * This write hasn't guaranteed that inode has been
                         * written on the disk.
                         * Since, all updat flags on indoe are cleared, we must
                         * remember the condition in case inode is to be updated
                         * synchronously later (e.g.- fsync()/fdatasync())
                         * and inode has not been modified yet.
                         */
                        ip->i_flag |= (IBDWRITE);
                }
        } else {
                /*
                 * In case previous inode update was done asynchronously
                 * (IBDWRITE) and this inode update request wants guaranteed
                 * (synchronous) disk update, flush the inode.
                 */
                if (waitfor && (flag & IBDWRITE)) {
                        blkflush(ip->i_dev,
                            (daddr_t)fsbtodb(udf_vfsp, ip->i_icb_lbano));
                        ip->i_flag &= ~(IBDWRITE);
                }
        }
}

int32_t
ud_updat_ext4(struct ud_inode *ip, struct file_entry *fe)
{
        uint32_t dummy;
        int32_t elen, ndent, index, count, con_index;
        daddr_t bno;
        struct buf *bp;
        struct short_ad *sad;
        struct long_ad *lad;
        struct icb_ext *iext, *icon;


        ASSERT(ip);
        ASSERT(fe);
        ASSERT((ip->i_desc_type == ICB_FLAG_SHORT_AD) ||
            (ip->i_desc_type == ICB_FLAG_LONG_AD));

        if (ip->i_desc_type == ICB_FLAG_SHORT_AD) {
                elen = sizeof (struct short_ad);
                sad = (struct short_ad *)
                    (fe->fe_spec + SWAP_32(fe->fe_len_ear));
        } else if (ip->i_desc_type == ICB_FLAG_LONG_AD) {
                elen = sizeof (struct long_ad);
                lad = (struct long_ad *)
                    (fe->fe_spec + SWAP_32(fe->fe_len_ear));
        } else {
                /* This cannot happen return */
                return (EINVAL);
        }

        ndent = ip->i_max_emb / elen;

        if (ip->i_ext_used < ndent) {

                if (ip->i_desc_type == ICB_FLAG_SHORT_AD) {
                        ud_make_sad(ip->i_ext, sad, ip->i_ext_used);
                } else {
                        ud_make_lad(ip->i_ext, lad, ip->i_ext_used);
                }
                fe->fe_len_adesc = SWAP_32(ip->i_ext_used * elen);
                con_index = 0;
        } else {

                con_index = index = 0;

                while (index < ip->i_ext_used) {
                        if (index == 0) {
                                /*
                                 * bp is already read
                                 * First few extents will go
                                 * into the file_entry
                                 */
                                count = ndent - 1;
                                fe->fe_len_adesc = SWAP_32(ndent * elen);
                                bp = NULL;

                                /*
                                 * Last entry to be cont ext
                                 */
                                icon = &ip->i_con[con_index];
                        } else {
                                /*
                                 * Read the buffer
                                 */
                                icon = &ip->i_con[con_index];

                                bno = ud_xlate_to_daddr(ip->i_udf,
                                    icon->ib_prn, icon->ib_block,
                                    icon->ib_count >> ip->i_udf->udf_l2d_shift,
                                    &dummy);
                                bp = ud_bread(ip->i_dev,
                                    bno << ip->i_udf->udf_l2d_shift,
                                    ip->i_udf->udf_lbsize);
                                if (bp->b_flags & B_ERROR) {
                                        brelse(bp);
                                        return (EIO);
                                }

                                /*
                                 * Figure out how many extents in
                                 * this time
                                 */
                                count = (bp->b_bcount -
                                    sizeof (struct alloc_ext_desc)) / elen;
                                if (count > (ip->i_ext_used - index)) {
                                        count = ip->i_ext_used - index;
                                } else {
                                        count --;
                                }
                                con_index++;
                                if (con_index >= ip->i_con_used) {
                                        icon = NULL;
                                } else {
                                        icon = &ip->i_con[con_index];
                                }
                        }



                        /*
                         * convert to on disk form and
                         * update
                         */
                        iext = &ip->i_ext[index];
                        if (ip->i_desc_type == ICB_FLAG_SHORT_AD) {
                                if (index != 0) {
                                        sad = (struct short_ad *)
                                            (bp->b_un.b_addr +
                                            sizeof (struct alloc_ext_desc));
                                }
                                ud_make_sad(iext, sad, count);
                                sad += count;
                                if (icon != NULL) {
                                        ud_make_sad(icon, sad, 1);
                                }
                        } else {
                                if (index != 0) {
                                        lad = (struct long_ad *)
                                            (bp->b_un.b_addr +
                                            sizeof (struct alloc_ext_desc));
                                }
                                ud_make_lad(iext, lad, count);
                                lad += count;
                                if (icon != NULL) {
                                        ud_make_lad(icon, lad, 1);
                                }
                        }

                        if (con_index != 0) {
                                struct alloc_ext_desc *aed;
                                int32_t sz;
                                struct icb_ext *oicon;

                                oicon = &ip->i_con[con_index - 1];
                                sz = count * elen;
                                if (icon != NULL) {
                                        sz += elen;
                                }
                                aed = (struct alloc_ext_desc *)bp->b_un.b_addr;
                                aed->aed_len_aed = SWAP_32(sz);
                                if (con_index == 1) {
                                        aed->aed_rev_ael =
                                            SWAP_32(ip->i_icb_block);
                                } else {
                                        aed->aed_rev_ael =
                                            SWAP_32(oicon->ib_block);
                                }
                                sz += sizeof (struct alloc_ext_desc);
                                ud_make_tag(ip->i_udf, &aed->aed_tag,
                                    UD_ALLOC_EXT_DESC, oicon->ib_block, sz);
                        }

                        /*
                         * Write back to disk
                         */
                        if (bp != NULL) {
                                BWRITE(bp);
                        }
                        index += count;
                }

        }

        if (con_index != ip->i_con_used) {
                int32_t lbmask, l2b, temp;

                temp = con_index;
                lbmask = ip->i_udf->udf_lbmask;
                l2b = ip->i_udf->udf_l2b_shift;
                /*
                 * Free unused continuation extents
                 */
                for (; con_index < ip->i_con_used; con_index++) {
                        icon = &ip->i_con[con_index];
                        count = (icon->ib_count + lbmask) >> l2b;
                        ud_free_space(ip->i_udf->udf_vfs, icon->ib_prn,
                            icon->ib_block, count);
                        count = (count << l2b) - sizeof (struct alloc_ext_desc);
                        ip->i_cur_max_ext -= (count / elen) - 1;
                }
                ip->i_con_used = temp;
        }
        return (0);
}

/* ARGSUSED */
int32_t
ud_updat_ext4096(struct ud_inode *ip, struct file_entry *fe)
{
        return (ENXIO);
}

void
ud_make_sad(struct icb_ext *iext, struct short_ad *sad, int32_t count)
{
        int32_t index = 0, scount;

        ASSERT(iext);
        ASSERT(sad);

        if (count != 0) {
                ASSERT(count > 0);
                while (index < count) {
                        scount = (iext->ib_count & 0x3FFFFFFF) |
                            (iext->ib_flags << 30);
                        sad->sad_ext_len = SWAP_32(scount);
                        sad->sad_ext_loc = SWAP_32(iext->ib_block);
                        sad++;
                        iext++;
                        index++;
                }
        }
}

void
ud_make_lad(struct icb_ext *iext, struct long_ad *lad, int32_t count)
{
        int32_t index = 0, scount;

        ASSERT(iext);
        ASSERT(lad);

        if (count != 0) {
                ASSERT(count > 0);

                while (index < count) {
                        lad->lad_ext_prn = SWAP_16(iext->ib_prn);
                        scount = (iext->ib_count & 0x3FFFFFFF) |
                            (iext->ib_flags << 30);
                        lad->lad_ext_len = SWAP_32(scount);
                        lad->lad_ext_loc = SWAP_32(iext->ib_block);
                        lad++;
                        iext++;
                        index++;
                }
        }
}

/*
 * Truncate the inode ip to at most length size.
 * Free affected disk blocks -- the blocks of the
 * file are removed in reverse order.
 */
/* ARGSUSED */
int
ud_itrunc(struct ud_inode *oip, u_offset_t length,
    int32_t flags, struct cred *cr)
{
        int32_t error, boff;
        off_t bsize;
        mode_t mode;
        struct udf_vfs *udf_vfsp;

        ud_printf("ud_itrunc\n");

        ASSERT(RW_WRITE_HELD(&oip->i_contents));
        udf_vfsp = oip->i_udf;
        bsize = udf_vfsp->udf_lbsize;

        /*
         * We only allow truncation of regular files and directories
         * to arbritary lengths here.  In addition, we allow symbolic
         * links to be truncated only to zero length.  Other inode
         * types cannot have their length set here.
         */
        mode = oip->i_type;
        if (mode == VFIFO) {
                return (0);
        }
        if ((mode != VREG) && (mode != VDIR) &&
            (!(mode == VLNK && length == 0))) {
                return (EINVAL);
        }
        if (length == oip->i_size) {
                /* update ctime and mtime to please POSIX tests */
                mutex_enter(&oip->i_tlock);
                oip->i_flag |= ICHG |IUPD;
                mutex_exit(&oip->i_tlock);
                return (0);
        }

        boff = blkoff(udf_vfsp, length);

        if (length > oip->i_size) {
                /*
                 * Trunc up case.ud_bmap_write will insure that the right blocks
                 * are allocated.  This includes doing any work needed for
                 * allocating the last block.
                 */
                if (boff == 0) {
                        error = ud_bmap_write(oip, length - 1,
                            (int)bsize, 0, cr);
                } else {
                        error = ud_bmap_write(oip, length - 1, boff, 0, cr);
                }
                if (error == 0) {
                        u_offset_t osize = oip->i_size;
                        oip->i_size  = length;

                        /*
                         * Make sure we zero out the remaining bytes of
                         * the page in case a mmap scribbled on it. We
                         * can't prevent a mmap from writing beyond EOF
                         * on the last page of a file.
                         */
                        if ((boff = blkoff(udf_vfsp, osize)) != 0) {
                                pvn_vpzero(ITOV(oip), osize,
                                    (uint32_t)(bsize - boff));
                        }
                        mutex_enter(&oip->i_tlock);
                        oip->i_flag |= ICHG;
                        ITIMES_NOLOCK(oip);
                        mutex_exit(&oip->i_tlock);
                }
                return (error);
        }

        /*
         * Update the pages of the file.  If the file is not being
         * truncated to a block boundary, the contents of the
         * pages following the end of the file must be zero'ed
         * in case it ever become accessable again because
         * of subsequent file growth.
         */
        if (boff == 0) {
                (void) pvn_vplist_dirty(ITOV(oip), length,
                    ud_putapage, B_INVAL | B_TRUNC, CRED());
        } else {
                /*
                 * Make sure that the last block is properly allocated.
                 * We only really have to do this if the last block is
                 * actually allocated.  Just to be sure, we do it now
                 * independent of current allocation.
                 */
                error = ud_bmap_write(oip, length - 1, boff, 0, cr);
                if (error) {
                        return (error);
                }

                pvn_vpzero(ITOV(oip), length, (uint32_t)(bsize - boff));

                (void) pvn_vplist_dirty(ITOV(oip), length,
                    ud_putapage, B_INVAL | B_TRUNC, CRED());
        }


        /* Free the blocks */
        if (oip->i_desc_type == ICB_FLAG_ONE_AD) {
                if (length > oip->i_max_emb) {
                        return (EFBIG);
                }
                oip->i_size = length;
                mutex_enter(&oip->i_tlock);
                oip->i_flag |= ICHG|IUPD;
                mutex_exit(&oip->i_tlock);
                ud_iupdat(oip, 1);
        } else {
                if ((error = ud_read_icb_till_off(oip, oip->i_size)) != 0) {
                        return (error);
                }

                if (oip->i_astrat == STRAT_TYPE4) {
                        ud_trunc_ext4(oip, length);
                } else if (oip->i_astrat == STRAT_TYPE4096) {
                        ud_trunc_ext4096(oip, length);
                }
        }

        return (0);
}

void
ud_trunc_ext4(struct ud_inode *ip, u_offset_t length)
{
        int32_t index, l2b, count, ecount;
        int32_t elen, ndent, nient;
        u_offset_t ext_beg, ext_end;
        struct icb_ext *iext, *icon;
        int32_t lbmask, ext_used;
        uint32_t loc;
        struct icb_ext text;
        uint32_t con_freed;

        ASSERT((ip->i_desc_type == ICB_FLAG_SHORT_AD) ||
            (ip->i_desc_type == ICB_FLAG_LONG_AD));

        if (ip->i_ext_used == 0) {
                return;
        }

        ext_used = ip->i_ext_used;

        lbmask = ip->i_udf->udf_lbmask;
        l2b = ip->i_udf->udf_l2b_shift;

        ASSERT(ip->i_ext);

        ip->i_lbr = 0;
        for (index = 0; index < ext_used; index++) {
                iext = &ip->i_ext[index];

                /*
                 * Find the begining and end
                 * of current extent
                 */
                ext_beg = iext->ib_offset;
                ext_end = iext->ib_offset +
                    ((iext->ib_count + lbmask) & ~lbmask);

                /*
                 * This is the extent that has offset "length"
                 * make a copy of this extent and
                 * remember the index. We can use
                 * it to free blocks
                 */
                if ((length <= ext_end) && (length >= ext_beg)) {
                        text = *iext;

                        iext->ib_count = length - ext_beg;
                        ip->i_ext_used = index + 1;
                        break;
                }
                if (iext->ib_flags != IB_UN_RE_AL) {
                        ip->i_lbr += iext->ib_count >> l2b;
                }
        }
        if (ip->i_ext_used != index) {
                if (iext->ib_flags != IB_UN_RE_AL) {
                        ip->i_lbr +=
                            ((iext->ib_count + lbmask) & ~lbmask) >> l2b;
                }
        }

        ip->i_size = length;
        mutex_enter(&ip->i_tlock);
        ip->i_flag |= ICHG|IUPD;
        mutex_exit(&ip->i_tlock);
        ud_iupdat(ip, 1);

        /*
         * Free the unused space
         */
        if (text.ib_flags != IB_UN_RE_AL) {
                count = (ext_end - length) >> l2b;
                if (count) {
                        loc = text.ib_block +
                            (((length - text.ib_offset) + lbmask) >> l2b);
                        ud_free_space(ip->i_udf->udf_vfs, text.ib_prn,
                            loc, count);
                }
        }
        for (index = ip->i_ext_used; index < ext_used; index++) {
                iext = &ip->i_ext[index];
                if (iext->ib_flags != IB_UN_RE_AL) {
                        count = (iext->ib_count + lbmask) >> l2b;
                        ud_free_space(ip->i_udf->udf_vfs, iext->ib_prn,
                            iext->ib_block, count);
                }
                bzero(iext, sizeof (struct icb_ext));
                continue;
        }

        /*
         * release any continuation blocks
         */
        if (ip->i_con) {

                ASSERT(ip->i_con_count >= ip->i_con_used);

                /*
                 * Find out how many indirect blocks
                 * are required and release the rest
                 */
                if (ip->i_desc_type == ICB_FLAG_SHORT_AD) {
                        elen = sizeof (struct short_ad);
                } else if (ip->i_desc_type == ICB_FLAG_LONG_AD) {
                        elen = sizeof (struct long_ad);
                }
                ndent = ip->i_max_emb / elen;
                if (ip->i_ext_used > ndent) {
                        ecount = ip->i_ext_used - ndent;
                } else {
                        ecount = 0;
                }
                con_freed = 0;
                for (index = 0; index < ip->i_con_used; index++) {
                        icon = &ip->i_con[index];
                        nient = icon->ib_count -
                            (sizeof (struct alloc_ext_desc) + elen);
                        /* Header + 1 indirect extent */
                        nient /= elen;
                        if (ecount) {
                                if (ecount > nient) {
                                        ecount -= nient;
                                } else {
                                        ecount = 0;
                                }
                        } else {
                                count = ((icon->ib_count + lbmask) &
                                    ~lbmask) >> l2b;
                                ud_free_space(ip->i_udf->udf_vfs,
                                    icon->ib_prn, icon->ib_block, count);
                                con_freed++;
                                ip->i_cur_max_ext -= nient;
                        }
                }
                /*
                 * set the continuation extents used(i_con_used)i to correct
                 * value. It is possible for i_con_used to be zero,
                 * if we free up all continuation extents. This happens
                 * when ecount is 0 before entering the for loop above.
                 */
                ip->i_con_used -= con_freed;
                if (ip->i_con_read > ip->i_con_used) {
                        ip->i_con_read = ip->i_con_used;
                }
        }
}

void
ud_trunc_ext4096(struct ud_inode *ip, u_offset_t length)
{
        /*
         * Truncate code is the same for
         * both file of type 4 and 4096
         */
        ud_trunc_ext4(ip, length);
}

/*
 * Remove any inodes in the inode cache belonging to dev
 *
 * There should not be any active ones, return error if any are found but
 * still invalidate others (N.B.: this is a user error, not a system error).
 *
 * Also, count the references to dev by block devices - this really
 * has nothing to do with the object of the procedure, but as we have
 * to scan the inode table here anyway, we might as well get the
 * extra benefit.
 */
int32_t
ud_iflush(struct vfs *vfsp)
{
        int32_t index, busy = 0;
        union ihead *ih;
        struct udf_vfs *udf_vfsp;
        dev_t dev;
        struct vnode *rvp, *vp;
        struct ud_inode *ip, *next;

        ud_printf("ud_iflush\n");
        udf_vfsp = (struct udf_vfs *)vfsp->vfs_data;
        rvp = udf_vfsp->udf_root;
        dev = vfsp->vfs_dev;

        mutex_enter(&ud_icache_lock);
        for (index = 0; index < UD_HASH_SZ; index++) {
                ih = &ud_ihead[index];

                next = ih->ih_chain[0];
                while (next != (struct ud_inode *)ih) {
                        ip = next;
                        next = ip->i_forw;
                        if (ip->i_dev != dev) {
                                continue;
                        }
                        vp = ITOV(ip);
                        /*
                         * root inode is processed by the caller
                         */
                        if (vp == rvp) {
                                if (vp->v_count > 1) {
                                        busy = -1;
                                }
                                continue;
                        }
                        if (ip->i_flag & IREF) {
                                /*
                                 * Set error indicator for return value,
                                 * but continue invalidating other
                                 * inodes.
                                 */
                                busy = -1;
                                continue;
                        }

                        rw_enter(&ip->i_contents, RW_WRITER);
                        remque(ip);
                        ip->i_forw = ip;
                        ip->i_back = ip;
                        /*
                         * Hold the vnode since its not done
                         * in VOP_PUTPAGE anymore.
                         */
                        VN_HOLD(vp);
                        /*
                         * XXX Synchronous write holding
                         * cache lock
                         */
                        (void) ud_syncip(ip, B_INVAL, I_SYNC);
                        rw_exit(&ip->i_contents);
                        VN_RELE(vp);
                }
        }
        mutex_exit(&ud_icache_lock);

        return (busy);
}


/*
 * Check mode permission on inode.  Mode is READ, WRITE or EXEC.
 * In the case of WRITE, the read-only status of the file system
 * is checked.  The applicable mode bits are compared with the
 * requested form of access.  If bits are missing, the secpolicy
 * function will check for privileges.
 */
int
ud_iaccess(struct ud_inode *ip, int32_t mode, struct cred *cr, int dolock)
{
        int shift = 0;
        int ret = 0;

        if (dolock)
                rw_enter(&ip->i_contents, RW_READER);
        ASSERT(RW_LOCK_HELD(&ip->i_contents));

        ud_printf("ud_iaccess\n");
        if (mode & IWRITE) {
                /*
                 * Disallow write attempts on read-only
                 * file systems, unless the file is a block
                 * or character device or a FIFO.
                 */
                if (ip->i_udf->udf_flags & UDF_FL_RDONLY) {
                        if ((ip->i_type != VCHR) &&
                            (ip->i_type != VBLK) &&
                            (ip->i_type != VFIFO)) {
                                ret = EROFS;
                                goto out;
                        }
                }
        }

        /*
         * Access check is based on only
         * one of owner, group, public.
         * If not owner, then check group.
         * If not a member of the group, then
         * check public access.
         */
        if (crgetuid(cr) != ip->i_uid) {
                shift += 5;
                if (!groupmember((uid_t)ip->i_gid, cr))
                        shift += 5;
        }

        ret = secpolicy_vnode_access2(cr, ITOV(ip), ip->i_uid,
            UD2VA_PERM(ip->i_perm << shift), UD2VA_PERM(mode));

out:
        if (dolock)
                rw_exit(&ip->i_contents);
        return (ret);
}

void
ud_imark(struct ud_inode *ip)
{
        timestruc_t     now;

        gethrestime(&now);
        ud_printf("ud_imark\n");
        if (ip->i_flag & IACC) {
                ip->i_atime.tv_sec = now.tv_sec;
                ip->i_atime.tv_nsec = now.tv_nsec;
        }
        if (ip->i_flag & IUPD) {
                ip->i_mtime.tv_sec = now.tv_sec;
                ip->i_mtime.tv_nsec = now.tv_nsec;
                ip->i_flag |= IMODTIME;
        }
        if (ip->i_flag & ICHG) {
                ip->i_diroff = 0;
                ip->i_ctime.tv_sec = now.tv_sec;
                ip->i_ctime.tv_nsec = now.tv_nsec;
        }
}


void
ud_itimes_nolock(struct ud_inode *ip)
{
        ud_printf("ud_itimes_nolock\n");

        if (ip->i_flag & (IUPD|IACC|ICHG)) {
                if (ip->i_flag & ICHG) {
                        ip->i_flag |= IMOD;
                } else {
                        ip->i_flag |= IMODACC;
                }
                ud_imark(ip);
                ip->i_flag &= ~(IACC|IUPD|ICHG);
        }
}

void
ud_delcache(struct ud_inode *ip)
{
        ud_printf("ud_delcache\n");

        mutex_enter(&ud_icache_lock);
        remque(ip);
        ip->i_forw = ip;
        ip->i_back = ip;
        mutex_exit(&ud_icache_lock);
}

void
ud_idrop(struct ud_inode *ip)
{
        struct vnode *vp = ITOV(ip);

        ASSERT(RW_WRITE_HELD(&ip->i_contents));

        ud_printf("ud_idrop\n");

        mutex_enter(&vp->v_lock);
        VN_RELE_LOCKED(vp);
        if (vp->v_count > 0) {
                mutex_exit(&vp->v_lock);
                return;
        }
        mutex_exit(&vp->v_lock);

        /*
         *  if inode is invalid or there is no page associated with
         *  this inode, put the inode in the front of the free list
         */
        mutex_enter(&ip->i_tlock);
        mutex_enter(&udf_ifree_lock);
        if (!vn_has_cached_data(vp) || ip->i_perm == 0) {
                ud_add_to_free_list(ip, UD_BEGIN);
        } else {
                /*
                 * Otherwise, put the inode back on the end of the free list.
                 */
                ud_add_to_free_list(ip, UD_END);
        }
        mutex_exit(&udf_ifree_lock);
        ip->i_flag &= IMODTIME;
        mutex_exit(&ip->i_tlock);
}

void
ud_add_to_free_list(struct ud_inode *ip, uint32_t at)
{
        ASSERT(ip);
        ASSERT(mutex_owned(&udf_ifree_lock));

#ifdef  DEBUG
        /* Search if the element is already in the list */
        if (udf_ifreeh != NULL) {
                struct ud_inode *iq;

                iq = udf_ifreeh;
                while (iq) {
                        if (iq == ip) {
                                cmn_err(CE_WARN, "Duplicate %p\n", (void *)ip);
                        }
                        iq = iq->i_freef;
                }
        }
#endif

        ip->i_freef = NULL;
        ip->i_freeb = NULL;
        if (udf_ifreeh == NULL) {
                /*
                 * Nothing on the list just add it
                 */
                udf_ifreeh = ip;
                udf_ifreet = ip;
        } else {
                if (at == UD_BEGIN) {
                        /*
                         * Add at the begining of the list
                         */
                        ip->i_freef = udf_ifreeh;
                        udf_ifreeh->i_freeb = ip;
                        udf_ifreeh = ip;
                } else {
                        /*
                         * Add at the end of the list
                         */
                        ip->i_freeb = udf_ifreet;
                        udf_ifreet->i_freef = ip;
                        udf_ifreet = ip;
                }
        }
}

void
ud_remove_from_free_list(struct ud_inode *ip, uint32_t at)
{
        ASSERT(ip);
        ASSERT(mutex_owned(&udf_ifree_lock));

#ifdef  DEBUG
        {
                struct ud_inode *iq;
                uint32_t found = 0;

                iq = udf_ifreeh;
                while (iq) {
                        if (iq == ip) {
                                found++;
                        }
                        iq = iq->i_freef;
                }
                if (found != 1) {
                        cmn_err(CE_WARN, "ip %p is found %x times\n",
                            (void *)ip,  found);
                }
        }
#endif

        if ((ip->i_freef == NULL) && (ip->i_freeb == NULL)) {
                if (ip != udf_ifreeh) {
                        return;
                }
        }

        if ((at == UD_BEGIN) || (ip == udf_ifreeh)) {
                udf_ifreeh = ip->i_freef;
                if (ip->i_freef == NULL) {
                        udf_ifreet = NULL;
                } else {
                        udf_ifreeh->i_freeb = NULL;
                }
        } else {
                ip->i_freeb->i_freef = ip->i_freef;
                if (ip->i_freef) {
                        ip->i_freef->i_freeb = ip->i_freeb;
                } else {
                        udf_ifreet = ip->i_freeb;
                }
        }
        ip->i_freef = NULL;
        ip->i_freeb = NULL;
}

void
ud_init_inodes(void)
{
        union ihead *ih = ud_ihead;
        int index;

#ifndef __lint
        _NOTE(NO_COMPETING_THREADS_NOW);
#endif
        for (index = 0; index < UD_HASH_SZ; index++, ih++) {
                ih->ih_head[0] = ih;
                ih->ih_head[1] = ih;
        }
        mutex_init(&ud_icache_lock, NULL, MUTEX_DEFAULT, NULL);
        mutex_init(&ud_nino_lock, NULL, MUTEX_DEFAULT, NULL);

        udf_ifreeh = NULL;
        udf_ifreet = NULL;
        mutex_init(&udf_ifree_lock, NULL, MUTEX_DEFAULT, NULL);

        mutex_init(&ud_sync_busy, NULL, MUTEX_DEFAULT, NULL);
        udf_vfs_instances = NULL;
        mutex_init(&udf_vfs_mutex, NULL, MUTEX_DEFAULT, NULL);

#ifndef __lint
        _NOTE(COMPETING_THREADS_NOW);
#endif
}