/* * 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. */ #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> int32_t ud_trace; /* * HASH chains and mutex */ extern union ihead ud_ihead[UD_HASH_SZ]; extern kmutex_t ud_icache_lock; extern kmutex_t ud_sync_busy; /* * udf_vfs list manipulation routines */ extern kmutex_t udf_vfs_mutex; extern struct udf_vfs *udf_vfs_instances; /* * Used to verify that a given entry on the udf_instances list (see below) * still refers to a mounted file system. * * XXX: This is a crock that substitutes for proper locking to coordinate * updates to and uses of the entries in udf_instances. */ struct check_node { struct vfs *vfsp; struct udf_vfs *udf_vfs; dev_t vfs_dev; }; vfs_t *ud_still_mounted(struct check_node *); void ud_checkclean(struct vfs *, struct udf_vfs *, dev_t, time_t); int32_t ud_icheck(struct udf_vfs *); void ud_flushi(int32_t); /* * Link udf_vfsp in at the head of the list of udf_vfs_instances. */ void ud_vfs_add(struct udf_vfs *udf_vfsp) { mutex_enter(&udf_vfs_mutex); udf_vfsp->udf_next = udf_vfs_instances; udf_vfs_instances = udf_vfsp; mutex_exit(&udf_vfs_mutex); } /* * Remove udf_vfsp from the list of udf_vfs_instances. * * Does no error checking; udf_vfsp is assumed to actually be on the list. */ void ud_vfs_remove(struct udf_vfs *udf_vfsp) { struct udf_vfs **delpt = &udf_vfs_instances; mutex_enter(&udf_vfs_mutex); for (; *delpt != NULL; delpt = &((*delpt)->udf_next)) { if (*delpt == udf_vfsp) { *delpt = udf_vfsp->udf_next; udf_vfsp->udf_next = NULL; break; } } mutex_exit(&udf_vfs_mutex); } /* * Search for the prn in the array * of partitions and translate * to the disk block number */ daddr_t ud_xlate_to_daddr(struct udf_vfs *udf_vfsp, uint16_t prn, uint32_t blkno, int32_t nblks, uint32_t *count) { int32_t i; struct ud_map *map; struct ud_part *ud_parts; uint32_t lblkno, retblkno = 0, *addr; uint32_t begin_req, end_req; uint32_t begin_bad, end_bad; ud_printf("ud_xlate_to_daddr\n"); /* Is prn valid */ if (prn < udf_vfsp->udf_nmaps) { map = &(udf_vfsp->udf_maps[prn]); if (map->udm_flags == UDM_MAP_VPM) { /* * Map is Virtual Parition Map * first check for the appropriate * table and then return the converted * block number */ for (i = 0; i < map->udm_nent; i++) { if (blkno < map->udm_count[i]) { addr = map->udm_addr[i]; lblkno = SWAP_32(addr[blkno]); *count = 1; break; } else { blkno -= map->udm_count[i]; } } } else if (map->udm_flags == UDM_MAP_SPM) { struct stbl *stbl; struct stbl_entry *te; int32_t entry_count; /* * Map type is Sparable Parition Map * if the block is in the map * return the translated block * other wise use the regular * partition stuff */ begin_req = blkno; end_req = begin_req + nblks; stbl = (struct stbl *)map->udm_spaddr[0]; te = (struct stbl_entry *)&stbl->stbl_entry; entry_count = SWAP_16(stbl->stbl_len); for (i = 0; i < entry_count; i++, te++) { begin_bad = SWAP_32(te->sent_ol); end_bad = begin_bad + map->udm_plen; /* * Either unmapped or reserved * or defective. need not consider */ if (begin_bad >= (uint32_t)0xFFFFFFF0) { continue; } if ((end_req < begin_bad) || (begin_req >= end_bad)) { continue; } if (begin_req < begin_bad) { ASSERT(end_req >= begin_bad); end_req = begin_bad; } else { retblkno = SWAP_32(te->sent_ml) + begin_req - begin_bad; if (end_req < end_bad) { *count = end_req - begin_req; } else { *count = end_bad - begin_req; } goto end; } } lblkno = blkno; *count = end_req - begin_req; } else { /* * regular partition */ lblkno = blkno; *count = nblks; } ud_parts = udf_vfsp->udf_parts; for (i = 0; i < udf_vfsp->udf_npart; i++) { if (map->udm_pn == ud_parts->udp_number) { /* * Check if the block is inside * the partition or not */ if (lblkno >= ud_parts->udp_length) { retblkno = 0; } else { retblkno = ud_parts->udp_start + lblkno; } goto end; } ud_parts ++; } } end: return (retblkno); } #ifdef UNDEF uint32_t ud_xlate_to_addr(struct udf_vfs *udf_vfsp, uint16_t prn, daddr_t blkno, int32_t lad) { int32_t i; struct ud_part *ud_parts; ud_printf("ud_xlate_to_addr\n"); if (lad == 0) { return (blkno); } ud_parts = udf_vfsp->udf_parts; for (i = 0; i < udf_vfsp->udf_npart; i++) { if (prn == ud_parts->udp_number) { return (blkno - ud_parts->udp_start); } } return (0); } #endif /* * Directories do not have holes */ int32_t ud_ip_off2bno(struct ud_inode *ip, uint32_t offset, uint32_t *bno) { int32_t i, error; struct icb_ext *iext; ASSERT(ip->i_type == VDIR); if (ip->i_desc_type == ICB_FLAG_ONE_AD) { *bno = ip->i_icb_block; return (0); } if ((error = ud_read_icb_till_off(ip, (u_offset_t)offset)) != 0) { return (error); } for (i = 0; i < ip->i_ext_used; i++) { iext = &ip->i_ext[i]; if ((iext->ib_offset <= offset) && (offset < (iext->ib_offset + iext->ib_count))) { *bno = iext->ib_block + ((offset - iext->ib_offset) >> ip->i_udf->udf_l2b_shift); break; } } return (0); } static uint32_t cum_sec[] = { 0x0, 0x28de80, 0x4dc880, 0x76a700, 0x9e3400, 0xc71280, 0xee9f80, 0x1177e00, 0x1405c80, 0x167e980, 0x190c800, 0x1b85500 }; static uint32_t cum_sec_leap[] = { 0x0, 0x28de80, 0x4f1a00, 0x77f880, 0x9f8580, 0xc86400, 0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680 }; #define DAYS_PER_YEAR 365 #define SEC_PER_DAY 0x15180 #define SEC_PER_YEAR 0x1e13380 /* This holds good till yr 2100 */ void ud_dtime2utime(struct timespec32 *utime, struct tstamp const *dtime) { int16_t year, tzone; int32_t sec; uint32_t *cp; ud_printf("ud_dtime2utime\n"); year = SWAP_16(dtime->ts_year); cp = (year % 4) ? cum_sec : cum_sec_leap; utime->tv_sec = cp[dtime->ts_month - 1]; utime->tv_sec += (dtime->ts_day - 1) * SEC_PER_DAY; utime->tv_sec += ((dtime->ts_hour * 60) + dtime->ts_min) * 60 + dtime->ts_sec; tzone = SWAP_16(dtime->ts_tzone); if ((tzone & TMODE) == 0x1000) { /* Local time */ if ((tzone & TINVALID) != TINVALID) { if (tzone & TSIGN) { /* * Sign extend the tzone */ sec = tzone | 0xFFFFF000; } else { sec = tzone & TOFFSET; } sec *= 60; utime->tv_sec -= sec; } } utime->tv_nsec = ((((dtime->ts_csec * 100) + dtime->ts_husec) * 100) + dtime->ts_usec) * 1000; if (year >= 1970) { utime->tv_sec += (year - 1970) * SEC_PER_YEAR; utime->tv_sec += ((year - 1969) / 4) * SEC_PER_DAY; } else { utime->tv_sec = ((1970 - year) * SEC_PER_YEAR + ((1972 - year) / 4) * SEC_PER_DAY - utime->tv_sec) * -1; if (utime->tv_nsec) { utime->tv_sec++; utime->tv_nsec = 1000 * 1000 * 1000 - utime->tv_nsec; } } } void ud_utime2dtime(struct timespec32 const *utime, struct tstamp *dtime) { time32_t sec = utime->tv_sec; int32_t usec = utime->tv_nsec / 1000; uint32_t lyrs, nyrs, dummy; uint32_t *cp; int32_t before = 0; ud_printf("ud_utime2dtime\n"); if (sec < 0) { before = 1; sec = sec * -1; if (usec) { sec = sec + 1; usec = 1000 * 1000 - usec; } } dtime->ts_csec = usec / 10000; usec %= 10000; dtime->ts_husec = usec / 100; dtime->ts_usec = usec % 100; nyrs = sec / SEC_PER_YEAR; if (before == 0) { lyrs = (nyrs + 1) / 4; } else { lyrs = (nyrs + 2) / 4; } if (nyrs != ((sec - (lyrs * SEC_PER_DAY)) / SEC_PER_YEAR)) { nyrs--; if (before == 0) { lyrs = (nyrs + 1) / 4; } else { lyrs = (nyrs + 2) / 4; } } sec -= nyrs * SEC_PER_YEAR + lyrs * SEC_PER_DAY; if (before == 1) { nyrs = 1970 - nyrs; if (sec != 0) { nyrs --; if ((nyrs % 4) == 0) { sec = SEC_PER_YEAR + SEC_PER_DAY - sec; } else { sec = SEC_PER_YEAR - sec; } } } else { nyrs += 1970; } cp = (nyrs % 4) ? cum_sec : cum_sec_leap; dummy = sec / (SEC_PER_DAY * 29); if (dummy > 11) { dummy = 11; } if (sec < cp[dummy]) { dummy--; } dtime->ts_year = SWAP_16(nyrs); dtime->ts_month = dummy; sec -= cp[dtime->ts_month]; dtime->ts_month++; dtime->ts_day = sec / SEC_PER_DAY; sec -= dtime->ts_day * SEC_PER_DAY; dtime->ts_day++; dtime->ts_hour = sec / SECS_PER_HOUR; sec -= dtime->ts_hour * SECS_PER_HOUR; dtime->ts_min = sec / SECS_PER_MIN; sec -= dtime->ts_min * SECS_PER_MIN; dtime->ts_sec = (uint8_t)sec; /* GMT offset is 0 */ dtime->ts_tzone = SWAP_16(0x1000); } int32_t ud_syncip(struct ud_inode *ip, int32_t flags, int32_t waitfor) { int32_t error; struct vnode *vp = ITOV(ip); ud_printf("ud_syncip\n"); if (ip->i_udf == NULL) { return (0); } if (!vn_has_cached_data(vp) || (vp->v_type == VCHR)) { error = 0; } else { rw_exit(&ip->i_contents); error = VOP_PUTPAGE(vp, (offset_t)0, (uint32_t)0, flags, CRED(), NULL); rw_enter(&ip->i_contents, RW_WRITER); } if (ip->i_flag & (IUPD |IACC | ICHG | IMOD)) { ud_iupdat(ip, waitfor); } return (error); } /* ARGSUSED */ int32_t ud_fbwrite(struct fbuf *fbp, struct ud_inode *ip) { ud_printf("ud_fbwrite\n"); ASSERT(fbp != NULL); return (fbwrite(fbp)); } void ud_sbwrite(struct udf_vfs *udf_vfsp) { struct log_vol_int_desc *lvid; struct ud_part *ud_part; struct lvid_iu *iu; uint32_t *temp; int32_t i, c; ud_printf("ud_sbwrite\n"); ASSERT(udf_vfsp); ASSERT(MUTEX_HELD(&udf_vfsp->udf_lock)); /* * updatable information in the superblock * integrity type, udf_maxuniq, udf_nfiles, udf_ndirs * udp_nfree in lvid */ lvid = (struct log_vol_int_desc *)udf_vfsp->udf_lvid; if (udf_vfsp->udf_clean == UDF_DIRTY) { lvid->lvid_int_type = SWAP_32(LOG_VOL_OPEN_INT); } else { lvid->lvid_int_type = SWAP_32(LOG_VOL_CLOSE_INT); } lvid->lvid_uniqid = SWAP_64(udf_vfsp->udf_maxuniq); temp = lvid->lvid_fst; c = SWAP_32(lvid->lvid_npart); ud_part = udf_vfsp->udf_parts; for (i = 0; i < c; i++) { temp[i] = SWAP_32(ud_part->udp_nfree); ud_part++; } iu = (struct lvid_iu *)(temp + c * 2); iu->lvidiu_nfiles = SWAP_32(udf_vfsp->udf_nfiles); iu->lvidiu_ndirs = SWAP_32(udf_vfsp->udf_ndirs); ud_update_regid(&iu->lvidiu_regid); ud_make_tag(udf_vfsp, &lvid->lvid_tag, UD_LOG_VOL_INT, udf_vfsp->udf_iseq_loc, sizeof (struct log_vol_int_desc) - 8 + 8 * udf_vfsp->udf_npart + SWAP_32(lvid->lvid_liu)); /* * Don't release the buffer after writing to the disk */ bwrite2(udf_vfsp->udf_iseq); } int32_t ud_sync_indir(struct ud_inode *ip) { int32_t elen; ud_printf("ud_sync_indir\n"); if (ip->i_desc_type == ICB_FLAG_ONE_AD) { return (0); } else 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); } else { return (EINVAL); } if (ip->i_astrat == STRAT_TYPE4) { int32_t ndentry; ndentry = ip->i_max_emb / elen; if (ip->i_ext_used < ndentry) { return (0); } ASSERT(ip->i_con); } else { cmn_err(CE_WARN, "unsupported strategy type\n"); return (EINVAL); } return (0); } void ud_update(int32_t flag) { struct vfs *vfsp; struct udf_vfs *udfsp, *udfsnext, *update_list = NULL; int32_t check_cnt = 0; size_t check_size; struct check_node *check_list, *ptr; time_t start_time; ud_printf("ud_update\n"); mutex_enter(&ud_sync_busy); /* * Examine all udf_vfs structures and add those that we can lock to the * update list. This is so that we don't hold the list lock for a * long time. If vfs_lock fails for a file system instance, then skip * it because somebody is doing a unmount on it. */ mutex_enter(&udf_vfs_mutex); for (udfsp = udf_vfs_instances; udfsp != NULL; udfsp = udfsp->udf_next) { vfsp = udfsp->udf_vfs; if (vfs_lock(vfsp) != 0) { continue; } udfsp->udf_wnext = update_list; update_list = udfsp; check_cnt++; } mutex_exit(&udf_vfs_mutex); if (update_list == NULL) { mutex_exit(&ud_sync_busy); return; } check_size = sizeof (struct check_node) * check_cnt; check_list = ptr = kmem_alloc(check_size, KM_NOSLEEP); /* * Write back modified superblocks. * Consistency check that the superblock of * each file system is still in the buffer cache. * * Note that the update_list traversal is done without the protection * of an overall list lock, so it's necessary to rely on the fact that * each entry of the list is vfs_locked when moving from one entry to * the next. This works because a concurrent attempt to add an entry * to another thread's update_list won't find it, since it'll already * be locked. */ check_cnt = 0; for (udfsp = update_list; udfsp != NULL; udfsp = udfsnext) { /* * Need to grab the next ptr before we unlock this one so * another thread doesn't grab it and change it before we move * on to the next vfs. (Once we unlock it, it's ok if another * thread finds it to add it to its own update_list; we don't * attempt to refer to it through our list any more.) */ udfsnext = udfsp->udf_wnext; vfsp = udfsp->udf_vfs; if (!vfsp->vfs_data) { vfs_unlock(vfsp); continue; } mutex_enter(&udfsp->udf_lock); /* * Build up the STABLE check list, so we can unlock the vfs * until we do the actual checking. */ if (check_list != NULL) { if ((udfsp->udf_flags & UDF_FL_RDONLY) == 0) { ptr->vfsp = vfsp; ptr->udf_vfs = udfsp; ptr->vfs_dev = vfsp->vfs_dev; ptr++; check_cnt++; } } /* * superblock is not modified */ if (udfsp->udf_mod == 0) { mutex_exit(&udfsp->udf_lock); vfs_unlock(vfsp); continue; } if ((udfsp->udf_flags & UDF_FL_RDONLY) == 0) { mutex_exit(&udfsp->udf_lock); mutex_exit(&ud_sync_busy); cmn_err(CE_WARN, "update ro udfs mod\n"); return; } udfsp->udf_mod = 0; mutex_exit(&udfsp->udf_lock); ud_update_superblock(vfsp); vfs_unlock(vfsp); } ud_flushi(flag); /* * Force stale buffer cache information to be flushed, * for all devices. This should cause any remaining control * information (e.g., inode info) to be flushed back. */ bflush((dev_t)NODEV); if (check_list == NULL) { mutex_exit(&ud_sync_busy); return; } /* * For each udf filesystem in the STABLE check_list, update * the clean flag if warranted. */ start_time = gethrestime_sec(); for (ptr = check_list; check_cnt > 0; check_cnt--, ptr++) { /* * ud_still_mounted() returns with vfsp and the vfs_reflock * held if ptr refers to a vfs that is still mounted. */ if ((vfsp = ud_still_mounted(ptr)) == NULL) { continue; } ud_checkclean(vfsp, ptr->udf_vfs, ptr->vfs_dev, start_time); vfs_unlock(vfsp); } mutex_exit(&ud_sync_busy); kmem_free(check_list, check_size); } /* * Returns vfsp and hold the lock if the vfs is still being mounted. * Otherwise, returns 0. * * For our purposes, "still mounted" means that the file system still appears * on the list of UFS file system instances. */ vfs_t * ud_still_mounted(struct check_node *checkp) { struct vfs *vfsp; struct udf_vfs *udf_vfsp; ud_printf("ud_still_mounted\n"); mutex_enter(&udf_vfs_mutex); for (udf_vfsp = udf_vfs_instances; udf_vfsp != NULL; udf_vfsp = udf_vfsp->udf_next) { if (udf_vfsp != checkp->udf_vfs) { continue; } /* * Tentative match: verify it and try to lock. (It's not at * all clear how the verification could fail, given that we've * gotten this far. We would have had to reallocate the * ufsvfs struct at hand for a new incarnation; is that really * possible in the interval from constructing the check_node * to here?) */ vfsp = udf_vfsp->udf_vfs; if (vfsp != checkp->vfsp) { continue; } if (vfsp->vfs_dev != checkp->vfs_dev) { continue; } if (vfs_lock(vfsp) != 0) { continue; } mutex_exit(&udf_vfs_mutex); return (vfsp); } mutex_exit(&udf_vfs_mutex); return (NULL); } /* ARGSUSED */ void ud_checkclean(struct vfs *vfsp, struct udf_vfs *udf_vfsp, dev_t dev, time_t timev) { ud_printf("ud_checkclean\n"); udf_vfsp = (struct udf_vfs *)vfsp->vfs_data; /* * ignore if buffers or inodes are busy */ if ((bcheck(dev, udf_vfsp->udf_iseq)) || (ud_icheck(udf_vfsp))) { return; } mutex_enter(&udf_vfsp->udf_lock); ud_sbwrite(udf_vfsp); mutex_exit(&udf_vfsp->udf_lock); } int32_t ud_icheck(struct udf_vfs *udf_vfsp) { int32_t index, error = 0; union ihead *ih; struct ud_inode *ip; mutex_enter(&ud_icache_lock); for (index = 0; index < UD_HASH_SZ; index++) { ih = &ud_ihead[index]; for (ip = ih->ih_chain[0]; ip != (struct ud_inode *)ih; ip = ip->i_forw) { if ((ip->i_udf == udf_vfsp) && ((ip->i_flag & (IMOD|IUPD|ICHG)) || (RW_ISWRITER(&ip->i_rwlock)) || ((ip->i_nlink <= 0) && (ip->i_flag & IREF)))) { error = 1; goto end; } } } end: mutex_exit(&ud_icache_lock); return (error); } void ud_flushi(int32_t flag) { struct ud_inode *ip, *lip; struct vnode *vp; int cheap = flag & SYNC_ATTR; int32_t index; union ihead *ih; /* * Write back each (modified) inode, * but don't sync back pages if vnode is * part of the virtual swap device. */ mutex_enter(&ud_icache_lock); for (index = 0; index < UD_HASH_SZ; index++) { ih = &ud_ihead[index]; lip = NULL; for (ip = ih->ih_chain[0], lip = NULL; ip && ip != (struct ud_inode *)ih; ip = ip->i_forw) { int flag = ip->i_flag; vp = ITOV(ip); /* * Skip locked & inactive inodes. * Skip vnodes w/ no cached data and no inode changes. * Skip read-only vnodes */ if ((flag & IREF) == 0 || (!vn_has_cached_data(vp) && ((flag & (IMOD|IACC|IUPD|ICHG)) == 0)) || (vp->v_vfsp == NULL) || vn_is_readonly(vp)) { continue; } if (!rw_tryenter(&ip->i_contents, RW_WRITER)) { continue; } VN_HOLD(vp); if (lip != NULL) { ITIMES(lip); VN_RELE(ITOV(lip)); } lip = ip; /* * If this is an inode sync for file system hardening * or this is a full sync but file is a swap file, * don't sync pages but make sure the inode is up * to date. In other cases, push everything out. */ if (cheap || IS_SWAPVP(vp)) { ud_iupdat(ip, 0); } else { (void) ud_syncip(ip, B_ASYNC, I_SYNC); } rw_exit(&ip->i_contents); } if (lip != NULL) { ITIMES(lip); VN_RELE(ITOV(lip)); } } mutex_exit(&ud_icache_lock); } void ud_update_regid(struct regid *reg) { ud_printf("ud_update_regid\n"); bzero(reg->reg_id, 23); (void) strncpy(reg->reg_id, SUN_IMPL_ID, SUN_IMPL_ID_LEN); reg->reg_ids[0] = SUN_OS_CLASS; reg->reg_ids[1] = SUN_OS_ID; } /* ARGSUSED4 */ void ud_make_tag(struct udf_vfs *udf_vfsp, 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; ud_printf("ud_make_tag\n"); ASSERT(crc_len > 0x10); addr = (uint8_t *)tag; crc_len -= sizeof (struct tag); crc = ud_crc(addr + 0x10, crc_len); tag->tag_id = SWAP_16(tag_id); tag->tag_desc_ver = SWAP_16(2); tag->tag_cksum = 0; tag->tag_res = 0; tag->tag_sno = SWAP_16(udf_vfsp->udf_tsno); tag->tag_crc = SWAP_16(crc); tag->tag_crc_len = SWAP_16(crc_len); tag->tag_loc = SWAP_32(blkno); addr = (uint8_t *)tag; for (i = 0; i <= 15; i++) { cksum += addr[i]; } tag->tag_cksum = cksum; } int32_t ud_make_dev_spec_ear(struct dev_spec_ear *ds, major_t major, minor_t minor) { int32_t attr_len; ud_printf("ud_make_dev_spec_ear\n"); bzero(ds, sizeof (struct dev_spec_ear)); attr_len = sizeof (struct dev_spec_ear); ds->ds_atype = SWAP_32(12); ds->ds_astype = 1; ds->ds_attr_len = SWAP_32(attr_len); ds->ds_iu_len = 0; ds->ds_major_id = SWAP_32(major); ds->ds_minor_id = SWAP_32(minor); return (attr_len); } int32_t ud_get_next_fid(struct ud_inode *ip, struct fbuf **fbp, uint32_t offset, struct file_id **fid, uint8_t **name, uint8_t *buf) { struct vnode *vp = ITOV(ip); caddr_t beg, end; int32_t error, lbsize, lbmask, sz, iulen, idlen, copied = 0; struct udf_vfs *udf_vfsp; uint8_t *obuf; int32_t count; uint32_t tbno; uint16_t crc_len; uint32_t len; ud_printf("ud_get_next_fid\n"); obuf = buf; udf_vfsp = ip->i_udf; lbsize = udf_vfsp->udf_lbsize; lbmask = udf_vfsp->udf_lbmask; if ((error = ud_ip_off2bno(ip, offset, &tbno)) != 0) { return (error); } /* First time read */ if (*fbp == NULL) { if ((error = fbread(vp, (offset_t)(offset & ~lbmask), lbsize, S_READ, fbp)) != 0) { return (error); } } end = (*fbp)->fb_addr + (*fbp)->fb_count; beg = (*fbp)->fb_addr + (offset & lbmask); if ((offset % lbsize) || (offset == 0)) { sz = end - beg; } else { sz = 0; } if (F_LEN <= sz) { *fid = (struct file_id *)beg; beg += F_LEN; } else { copied = 1; bcopy(beg, buf, sz); fbrelse(*fbp, S_OTHER); *fbp = NULL; /* Skip to next block */ if (offset & lbmask) { offset = (offset & ~lbmask) + lbsize; } if ((error = fbread(vp, (offset_t)offset, lbsize, S_READ, fbp)) != 0) { return (error); } end = (*fbp)->fb_addr + (*fbp)->fb_count; beg = (*fbp)->fb_addr; bcopy(beg, buf + sz, F_LEN - sz); beg = beg + F_LEN - sz; *fid = (struct file_id *)buf; buf += F_LEN; } /* * Check if this a valid file_identifier */ if (ud_verify_tag_and_desc(&(*fid)->fid_tag, UD_FILE_ID_DESC, tbno, 0, lbsize) != 0) { /* * Either end of directory or corrupted */ return (EINVAL); } crc_len = SWAP_16((*fid)->fid_tag.tag_crc_len); if (crc_len > udf_vfsp->udf_lbsize) { /* * Entries cannot be larger than * blocksize */ return (EINVAL); } if (crc_len < (F_LEN - sizeof (struct tag))) { iulen = SWAP_16((*fid)->fid_iulen); idlen = FID_LEN(*fid) - F_LEN; goto use_id_iu_len; } /* * By now beg points to the start fo the file name */ sz = end - beg; len = crc_len + sizeof (struct tag) - (F_LEN); if (len <= sz) { if (copied == 1) { bcopy(beg, buf, len); buf += len; } beg += len; } else { copied = 1; /* * We are releasing the * old buffer so copy fid to buf */ if (obuf == buf) { count = F_LEN + sz; bcopy(*fid, buf, count); *fid = (struct file_id *)buf; buf += count; } else { bcopy(beg, buf, sz); *fid = (struct file_id *)buf; buf += sz; } fbrelse(*fbp, S_OTHER); *fbp = NULL; /* Skip to next block */ if (offset & lbmask) { offset = (offset & ~lbmask) + lbsize; } if ((error = fbread(vp, (offset_t)offset, lbsize, S_READ, fbp)) != 0) { return (error); } end = (*fbp)->fb_addr + (*fbp)->fb_count; beg = (*fbp)->fb_addr; count = len - sz; bcopy(beg, buf, count); beg += count; } /* * First we verify that the tag id and the FID_LEN are valid. * Next we verify the crc of the descriptor. */ if (ud_verify_tag_and_desc(&(*fid)->fid_tag, UD_FILE_ID_DESC, tbno, 0, lbsize) != 0) { /* directory is corrupted */ return (EINVAL); } if (ud_verify_tag_and_desc(&(*fid)->fid_tag, UD_FILE_ID_DESC, tbno, 1, FID_LEN(*fid)) != 0) { /* directory is corrupted */ return (EINVAL); } idlen = FID_LEN(*fid); idlen -= F_LEN; iulen = SWAP_16((*fid)->fid_iulen); if (crc_len < (F_LEN - sizeof (struct tag) + idlen)) { use_id_iu_len: len = (F_LEN - sizeof (struct tag) + idlen) - crc_len; sz = end - beg; if (len <= sz) { if (copied == 1) { bcopy(beg, buf, len); } } else { if (obuf == buf) { count = crc_len + sizeof (struct tag); bcopy(*fid, buf, count); *fid = (struct file_id *)buf; buf += count; } else { bcopy(beg, buf, sz); *fid = (struct file_id *)buf; buf += sz; } fbrelse(*fbp, S_OTHER); *fbp = NULL; /* Skip to next block */ if (offset & lbmask) { offset = (offset & ~lbmask) + lbsize; } if ((error = fbread(vp, (offset_t)offset, lbsize, S_READ, fbp)) != 0) { return (error); } end = (*fbp)->fb_addr + (*fbp)->fb_count; beg = (*fbp)->fb_addr; count = len - sz; bcopy(beg, buf, count); beg += count; } } *name = ((uint8_t *)*fid) + F_LEN + iulen; return (0); } int32_t ud_verify_tag_and_desc(struct tag *tag, uint16_t id, uint32_t blockno, int32_t verify_desc, int32_t desc_len) { int32_t i; uint8_t *addr, cksum = 0; uint16_t crc; file_entry_t *fe; struct ext_attr_hdr *eah; struct file_id *fid; int32_t fidlen, ea_off; if (tag->tag_id != SWAP_16(id)) { return (1); } addr = (uint8_t *)tag; eah = (struct ext_attr_hdr *)tag; for (i = 0; i < 4; i++) { cksum += addr[i]; } for (i = 5; i <= 15; i++) { cksum += addr[i]; } if (cksum != tag->tag_cksum) { cmn_err(CE_NOTE, "Checksum Does not Verify TAG %x CALC %x blockno 0x%x\n", tag->tag_cksum, cksum, blockno); return (1); } /* * Validate the meta data for UD_FILE_ID_DESC. * The FID_LEN should not exceed the desc_len. * This validation is done before the entire descriptor is read. * A call to this routine is made initially with verify_desc set as 0 * but a non zero value in desc_len. */ if (id == UD_FILE_ID_DESC) { fid = (struct file_id *)tag; fidlen = FID_LEN(fid); if (fidlen > desc_len) { cmn_err(CE_NOTE, "Invalid FID_LEN(0x%x). Greater than expected(0x%x) blockno 0x%x\n", fidlen, desc_len, blockno); return (1); } } if (verify_desc == 0) return (0); /* * We are done verifying the tag. We proceed with verifying the * the descriptor. desc_len indicates the size of the structure * pointed to by argument tag. It includes the size of struct tag. * We first check the tag_crc_len since we use this to compute the * crc of the descriptor. * Verifying the crc is normally sufficient to ensure the integrity * of the meta data in the descriptor. However given the paranoia * about the panic caused by illegal meta data values we do an * additional check of the meta data for decriptor UD_FILE_ENTRY. * (The original panic was caused because this routine was not called * to verify the integrity of the tag and descriptor.) */ if (SWAP_16(tag->tag_crc_len) > (desc_len - sizeof (struct tag))) { cmn_err(CE_NOTE, "tag_crc_len(0x%x) is greater than expected len(0x%x) blockno 0x%x\n", SWAP_16(tag->tag_crc_len), desc_len, blockno); return (1); } if (tag->tag_crc_len) { crc = ud_crc(addr + 0x10, SWAP_16(tag->tag_crc_len)); if (crc != SWAP_16(tag->tag_crc)) { cmn_err(CE_NOTE, "CRC mismatch TAG_ID 0x%x TAG_CRC 0x%x" " Computed crc 0x%x tag_loc %x blockno 0x%x\n", id, SWAP_16(tag->tag_crc), crc, SWAP_32(tag->tag_loc), blockno); return (1); } } switch (id) { case UD_FILE_ENTRY: fe = (file_entry_t *)tag; if ((offsetof(struct file_entry, fe_spec) + SWAP_32(fe->fe_len_ear) + SWAP_32(fe->fe_len_adesc)) > desc_len) { cmn_err(CE_NOTE, "fe_len_ear(0x%x) fe_len_adesc(0x%x) fields are not OK. blockno 0x%x\n", SWAP_32(fe->fe_len_ear), SWAP_32(fe->fe_len_adesc), blockno); return (1); } break; case UD_EXT_ATTR_HDR: eah = (struct ext_attr_hdr *)tag; if (SWAP_32(eah->eah_aal) > desc_len) { cmn_err(CE_NOTE, "eah_all(0x%x) exceeds desc. len(0x%x) blockno 0x%x\n", SWAP_32(eah->eah_aal), desc_len, blockno); return (1); } ea_off = GET_32(&eah->eah_ial); if (ea_off >= desc_len) { cmn_err(CE_NOTE, "ea_off(0x%x) is not less than ea_len(0x%x) blockno 0x%x\n", ea_off, desc_len, blockno); return (1); } break; default: break; } if (SWAP_32(blockno) != tag->tag_loc) { cmn_err(CE_NOTE, "Tag Location mismatch blockno %x tag_blockno %x\n", blockno, SWAP_32(tag->tag_loc)); return (1); } return (0); } /* **************** udf specific subroutines *********************** */ 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 }; 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); } typedef unsigned short unicode_t; #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'}; /* * An unrecorded block will return all * 0's on a WORM media. to simulate * a unrecorded block on a rw media * we fill it with all zero's * return 0 : If unrecorded * return 1 : If recorded. */ uint32_t ud_check_te_unrec(struct udf_vfs *udf_vfsp, caddr_t addr, uint32_t blkno) { int32_t i, lbsize; struct term_entry *te; ASSERT(udf_vfsp); ASSERT(addr); te = (struct term_entry *)addr; if (ud_verify_tag_and_desc(&te->te_tag, UD_TERMINAL_ENT, blkno, 1, udf_vfsp->udf_lbsize) != 0) { lbsize = udf_vfsp->udf_lbsize; for (i = 0; i < lbsize; i++) { if (addr[i] != 0) { return (1); } } } return (0); } /* * The algorithms ud_utf82utf16 and ud_utf162utf8 * donot handle surrogates. This is unicode 1.1 as I * understand. When writing udf2.0 this code has * to be changed to process surrogates also * (Dont ask me what is a surrogate character) */ /* * This will take a utf8 string convert the first character * to utf16 and return the number of bytes consumed in this * process. A 0 will be returned if the character is invalid */ uint8_t bytes_from_utf8[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; int32_t ud_utf82utf16(uint8_t *s_8, uint16_t *c_16, int32_t count) { int32_t extra_bytes; uint32_t c_32; ASSERT(s_8); ASSERT(c_16); /* * First convert to a 32-bit * character */ c_32 = 0; extra_bytes = bytes_from_utf8[*s_8]; if (extra_bytes > count) { return (0); } /* * verify if the string is a valid * utf8 string */ if (extra_bytes == 0) { /* * Apply one byte rule */ if (*s_8 & 0x80) { return (0); } c_32 = *s_8 & 0x7F; } else if (extra_bytes == 1) { if (((*s_8 & 0xE0) != 0xC0) || ((*(s_8 + 1) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x1F; } else if (extra_bytes == 2) { if (((*s_8 & 0xF0) != 0xE0) || ((*(s_8 + 1) & 0xC0) != 0x80) || ((*(s_8 + 2) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x0F; } else if (extra_bytes == 3) { if (((*s_8 & 0xF8) != 0xF0) || ((*(s_8 + 1) & 0xC0) != 0x80) || ((*(s_8 + 2) & 0xC0) != 0x80) || ((*(s_8 + 3) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x07; } else if (extra_bytes == 4) { if (((*s_8 & 0xFC) != 0xF8) || ((*(s_8 + 1) & 0xC0) != 0x80) || ((*(s_8 + 2) & 0xC0) != 0x80) || ((*(s_8 + 3) & 0xC0) != 0x80) || ((*(s_8 + 4) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x03; } else if (extra_bytes == 5) { if (((*s_8 & 0xFE) != 0xFC) || ((*(s_8 + 1) & 0xC0) != 0x80) || ((*(s_8 + 2) & 0xC0) != 0x80) || ((*(s_8 + 3) & 0xC0) != 0x80) || ((*(s_8 + 4) & 0xC0) != 0x80) || ((*(s_8 + 5) & 0xC0) != 0x80)) { return (0); } c_32 = *s_8 & 0x01; } else { return (0); } s_8++; /* * Convert to 32-bit character */ switch (extra_bytes) { case 5 : c_32 <<= 6; c_32 += (*s_8++ & 0x3F); /* FALLTHROUGH */ case 4 : c_32 <<= 6; c_32 += (*s_8++ & 0x3F); /* FALLTHROUGH */ case 3 : c_32 <<= 6; c_32 += (*s_8++ & 0x3F); /* FALLTHROUGH */ case 2 : c_32 <<= 6; c_32 += (*s_8++ & 0x3F); /* FALLTHROUGH */ case 1 : c_32 <<= 6; c_32 += (*s_8++ & 0x3F); /* FALLTHROUGH */ case 0 : break; } /* * now convert the 32-bit * character into a 16-bit character */ *c_16 = c_32; return (extra_bytes + 1); } /* * Convert to a form that can be put on the media * out_len has the size of out_str when we are called. * This routine will set out_len to actual bytes written to out_str. * We make sure that we will not attempt to write beyond the out_str_len. */ int32_t ud_compress(int32_t in_len, int32_t *out_len, uint8_t *in_str, uint8_t *out_str) { int32_t error, in_index, out_index, index, c_tx_sz, out_str_len; uint16_t w2_char, *w2_str; uint8_t comp_id; out_str_len = *out_len; if (in_len > (out_str_len - 2)) { return (ENAMETOOLONG); } *out_len = 0; w2_str = (uint16_t *)kmem_zalloc(512, KM_SLEEP); error = in_index = out_index = c_tx_sz = 0; comp_id = 8; for (in_index = 0; in_index < in_len; in_index += c_tx_sz) { if ((c_tx_sz = ud_utf82utf16(&in_str[in_index], &w2_char, in_len - in_index)) == 0) { error = EINVAL; goto end; } /* * utf-8 characters can be * of 1 - 6 bytes in length */ ASSERT(c_tx_sz > 0); ASSERT(c_tx_sz < 7); if ((comp_id == 8) && (w2_char & 0xff00)) { comp_id = 0x10; } w2_str[out_index++] = w2_char; } if (((comp_id == 0x10) && (out_index > ((out_str_len - 2)/2))) || ((comp_id == 0x8) && (out_index > (out_str_len - 2)))) { error = ENAMETOOLONG; goto end; } in_index = out_index; out_index = 0; out_str[out_index++] = comp_id; for (index = 0; index < in_index; index++) { if (comp_id == 0x10) { out_str[out_index++] = (w2_str[index] & 0xFF00) >> 8; } out_str[out_index++] = w2_str[index] & 0xFF; } ASSERT(out_index <= (out_str_len - 1)); *out_len = out_index; end: if (w2_str != NULL) { kmem_free((caddr_t)w2_str, 512); } return (error); } /* * Take a utf16 character and convert * it into a utf8 character. * A 0 will be returned if the conversion fails */ uint8_t first_byte_mark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; int32_t ud_utf162utf8(uint16_t c_16, uint8_t *s_8) { int32_t nc; uint32_t c_32; uint32_t byte_mask = 0xBF; uint32_t byte_mark = 0x80; ASSERT(s_8); /* * Convert the 16-bit character to * a 32-bit character */ c_32 = c_16; /* * By here the 16-bit character is converted * to a 32-bit wide character */ if (c_32 < 0x80) { nc = 1; } else if (c_32 < 0x800) { nc = 2; } else if (c_32 < 0x10000) { nc = 3; } else if (c_32 < 0x200000) { nc = 4; } else if (c_32 < 0x4000000) { nc = 5; } else if (c_32 < (uint32_t)0x80000000) { nc = 6; } else { nc = 0; } s_8 += nc; switch (nc) { case 6 : *(--s_8) = (c_32 | byte_mark) & byte_mask; c_32 >>= 6; /* FALLTHROUGH */ case 5 : *(--s_8) = (c_32 | byte_mark) & byte_mask; c_32 >>= 6; /* FALLTHROUGH */ case 4 : *(--s_8) = (c_32 | byte_mark) & byte_mask; c_32 >>= 6; /* FALLTHROUGH */ case 3 : *(--s_8) = (c_32 | byte_mark) & byte_mask; c_32 >>= 6; /* FALLTHROUGH */ case 2 : *(--s_8) = (c_32 | byte_mark) & byte_mask; c_32 >>= 6; /* FALLTHROUGH */ case 1 : *(--s_8) = c_32 | first_byte_mark[nc]; } return (nc); } /* * Convert to a form that can be transferred to the user * Assumption's * in_length < 256, out_str is at least 255 bytes long * The converted byte stream length is returned in out_len */ #define MAX_ALLOWABLE_STRING 250 int32_t ud_uncompress(int32_t in_len, int32_t *out_len, uint8_t *in_str, uint8_t *out_str) { uint8_t comp_id, utf8[6]; uint16_t w2_char, crc; int32_t error, index, c_tx_sz, len_till_now; int32_t make_crc, lic, dot_loc, crc_start_loc = 0, k = 0; if (in_len == 0) { *out_len = 0; out_str[0] = '\0'; return (0); } error = len_till_now = make_crc = 0; dot_loc = lic = -2; *out_len = 0; crc = 0; comp_id = in_str[0]; /* * File names "." and ".." are invalid under unix. * Transform them into something */ if (comp_id == 8) { if ((in_str[1] == DOT) && ((in_len == 2) || ((in_len == 3) && (in_str[2] == DOT)))) { out_str[k++] = UNDERBAR; len_till_now = 1; goto make_append_crc; } } else if (comp_id == 0x10) { if (((in_str[1] << 8 | in_str[2]) == DOT) && ((in_len == 3) || ((in_len == 5) && ((in_str[3] << 8 | in_str[4]) == DOT)))) { out_str[k++] = UNDERBAR; len_till_now = 1; goto make_append_crc; } } else { *out_len = 0; return (EINVAL); } for (index = 1; index < in_len; ) { /* * Uncompress each character */ if (comp_id == 0x10) { w2_char = in_str[index++] << 8; w2_char |= in_str[index++]; } else { w2_char = in_str[index++]; } if (make_crc != 0) { crc += w2_char; } if (w2_char == DOT) { dot_loc = len_till_now; } /* * Get rid of invalid characters */ if ((w2_char == SLASH) || (w2_char == NULL)) { make_crc = 1; if (((comp_id == 8) && (lic != (index - 1))) || (comp_id == 0x10) && (lic != (index - 2))) { w2_char = UNDERBAR; lic = index; } else { lic = index; continue; } } /* * Conver a 16bit character to a * utf8 byte stream */ if ((c_tx_sz = ud_utf162utf8(w2_char, utf8)) == 0) { error = EINVAL; goto end; } ASSERT(c_tx_sz > 0); ASSERT(c_tx_sz < 7); /* * The output string is larger than * the maximum allowed string length */ if ((crc_start_loc == 0) && ((len_till_now + c_tx_sz) > MAX_ALLOWABLE_STRING)) { crc_start_loc = len_till_now; } if ((len_till_now + c_tx_sz) < MAXNAMELEN) { (void) strncpy((caddr_t)&out_str[len_till_now], (caddr_t)utf8, c_tx_sz); len_till_now += c_tx_sz; } else { break; } } /* * If we need to append CRC do it now */ if (make_crc) { if (len_till_now > MAX_ALLOWABLE_STRING) { len_till_now = crc_start_loc; } if (dot_loc > 0) { /* * Make space for crc before the DOT * move the rest of the file name to the end */ for (k = len_till_now - 1; k >= dot_loc; k--) { out_str[k + 5] = out_str[k]; } k = dot_loc; } else { k = len_till_now; } make_append_crc: crc = ud_crc(in_str, in_len); out_str[k++] = POUND; out_str[k++] = htoc[(uint16_t)(crc & 0xf000) >> 12]; out_str[k++] = htoc[(uint16_t)(crc & 0xf00) >> 8]; out_str[k++] = htoc[(uint16_t)(crc & 0xf0) >> 4]; out_str[k++] = htoc[crc & 0xf]; len_till_now += 5; } *out_len = len_till_now; end: return (error); } struct buf * ud_bread(dev_t dev, daddr_t blkno, long bsize) { struct buf *bp; begin: bp = bread(dev, blkno, bsize); if (((bp->b_flags & B_ERROR) == 0) && (bp->b_bcount != bsize)) { /* * Buffer cache returned a * wrong number of bytes * flush the old buffer and * reread it again */ if (bp->b_flags & B_DELWRI) { bwrite(bp); } else { bp->b_flags |= (B_AGE | B_STALE); brelse(bp); } goto begin; } return (bp); } /* * Decide whether it is okay to remove within a sticky directory. * Two conditions need to be met: write access to the directory * is needed. In sticky directories, write access is not sufficient; * you can remove entries from a directory only if you own the directory, * if you are privileged, if you own the entry or if they entry is * a plain file and you have write access to that file. * Function returns 0 if remove access is granted. */ int ud_sticky_remove_access(struct ud_inode *dir, struct ud_inode *entry, struct cred *cr) { uid_t uid; ASSERT(RW_LOCK_HELD(&entry->i_contents)); if ((dir->i_char & ISVTX) && (uid = crgetuid(cr)) != dir->i_uid && uid != entry->i_uid && (entry->i_type != VREG || ud_iaccess(entry, IWRITE, cr, 0) != 0)) return (secpolicy_vnode_remove(cr)); return (0); }