diff options
Diffstat (limited to 'sys/fs/hpfs/hpfs_subr.c')
-rw-r--r-- | sys/fs/hpfs/hpfs_subr.c | 861 |
1 files changed, 861 insertions, 0 deletions
diff --git a/sys/fs/hpfs/hpfs_subr.c b/sys/fs/hpfs/hpfs_subr.c new file mode 100644 index 0000000..4d60f92 --- /dev/null +++ b/sys/fs/hpfs/hpfs_subr.c @@ -0,0 +1,861 @@ +/*- + * Copyright (c) 1998, 1999 Semen Ustimenko (semenu@FreeBSD.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/namei.h> +#include <sys/malloc.h> +#include <sys/buf.h> + +#include <fs/hpfs/hpfs.h> +#include <fs/hpfs/hpfsmount.h> +#include <fs/hpfs/hpfs_subr.h> + +u_long +hpfs_checksum( + u_int8_t *object, + int size) +{ + register int i; + u_long csum=0L; + for (i=0; i < size; i++) { + csum += (u_long) *object++; + csum = (csum << 7) + (csum >> (25)); + } + return (csum); +} + +void +hpfs_bmdeinit( + struct hpfsmount *hpmp) +{ + struct buf *bp; + int i; + + dprintf(("hpmp_bmdeinit: ")); + + if (!(hpmp->hpm_mp->mnt_flag & MNT_RDONLY)) { + /* + * Write down BitMap. + */ + for (i=0; i<hpmp->hpm_dbnum; i++) { + dprintf(("[%d: 0x%x] ", i, hpmp->hpm_bmind[i])); + + bp = getblk(hpmp->hpm_devvp, hpmp->hpm_bmind[i], + BMSIZE, 0, 0); + clrbuf(bp); + + bcopy(hpmp->hpm_bitmap + BMSIZE * i, bp->b_data, + BMSIZE); + + bwrite(bp); + } + } + + FREE(hpmp->hpm_bitmap,M_HPFSMNT); + FREE(hpmp->hpm_bmind,M_HPFSMNT); + + dprintf(("\n")); +} + +/* + * Initialize BitMap management, includes calculation of + * available blocks number. + */ +int +hpfs_bminit( + struct hpfsmount *hpmp) +{ + struct buf *bp; + int error, i, k; + u_long dbavail; + + dprintf(("hpfs_bminit: ")); + + hpmp->hpm_dbnum = (hpmp->hpm_su.su_btotal + 0x3FFF) / 0x4000; + + dprintf(("0x%lx data bands, ", hpmp->hpm_dbnum)); + + MALLOC(hpmp->hpm_bmind, lsn_t *, hpmp->hpm_dbnum * sizeof(lsn_t), + M_HPFSMNT, M_WAITOK); + + MALLOC(hpmp->hpm_bitmap, u_int8_t *, hpmp->hpm_dbnum * BMSIZE, + M_HPFSMNT, M_WAITOK); + + error = bread(hpmp->hpm_devvp, hpmp->hpm_su.su_bitmap.lsn1, + ((hpmp->hpm_dbnum + 0x7F) & ~(0x7F)) << 2, NOCRED, &bp); + if (error) { + brelse(bp); + FREE(hpmp->hpm_bitmap, M_HPFSMNT); + FREE(hpmp->hpm_bmind, M_HPFSMNT); + dprintf((" error %d\n", error)); + return (error); + } + bcopy(bp->b_data, hpmp->hpm_bmind, hpmp->hpm_dbnum * sizeof(lsn_t)); + + brelse(bp); + + /* + * Read in all BitMap + */ + for (i=0; i<hpmp->hpm_dbnum; i++) { + dprintf(("[%d: 0x%x] ", i, hpmp->hpm_bmind[i])); + + error = bread(hpmp->hpm_devvp, hpmp->hpm_bmind[i], + BMSIZE, NOCRED, &bp); + if (error) { + brelse(bp); + FREE(hpmp->hpm_bitmap, M_HPFSMNT); + FREE(hpmp->hpm_bmind, M_HPFSMNT); + dprintf((" error %d\n", error)); + return (error); + } + bcopy(bp->b_data, hpmp->hpm_bitmap + BMSIZE * i, BMSIZE); + + brelse(bp); + } + + /* + * Look througth BitMap and count free bits + */ + dbavail = 0; + for (i=0; i < hpmp->hpm_su.su_btotal >> 5; i++) { + register u_int32_t mask; + for (k=0, mask=1; k < 32; k++, mask<<=1) + if(((u_int32_t *)hpmp->hpm_bitmap)[i] & mask) + dbavail ++; + + } + hpmp->hpm_bavail = dbavail; + + return (0); +} + +int +hpfs_cmpfname ( + struct hpfsmount *hpmp, + char * uname, + int ulen, + char * dname, + int dlen, + u_int16_t cp) +{ + register int i, res; + + for (i = 0; i < ulen && i < dlen; i++) { + res = hpfs_toupper(hpmp, hpfs_u2d(hpmp, uname[i]), cp) - + hpfs_toupper(hpmp, dname[i], cp); + if (res) + return res; + } + return (ulen - dlen); +} + +int +hpfs_cpstrnnicmp ( + struct hpfsmount *hpmp, + char * str1, + int str1len, + u_int16_t str1cp, + char * str2, + int str2len, + u_int16_t str2cp) +{ + int i, res; + + for (i = 0; i < str1len && i < str2len; i++) { + res = (int)hpfs_toupper(hpmp, ((u_char *)str1)[i], str1cp) - + (int)hpfs_toupper(hpmp, ((u_char *)str2)[i], str2cp); + if (res) + return res; + } + return (str1len - str2len); +} + + +int +hpfs_cpload ( + struct hpfsmount *hpmp, + struct cpiblk *cpibp, + struct cpdblk *cpdbp) +{ + struct buf *bp; + struct cpdsec * cpdsp; + int error, i; + + error = bread(hpmp->hpm_devvp, cpibp->b_cpdsec, DEV_BSIZE, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + + cpdsp = (struct cpdsec *)bp->b_data; + + for (i=cpdsp->d_cpfirst; i<cpdsp->d_cpcnt; i++) { + if (cpdsp->d_cpdblk[i].b_cpid == cpibp->b_cpid) { + bcopy(cpdsp->d_cpdblk + i, cpdbp, + sizeof(struct cpdblk)); + + brelse(bp); + + return (0); + } + } + + brelse(bp); + + return (ENOENT); +} + + +/* + * Initialize Code Page information management. + * Load all copdepages in memory. + */ +int +hpfs_cpinit ( + struct hpfsmount *hpmp, + struct hpfs_args *argsp) +{ + struct buf *bp; + int error, i; + lsn_t lsn; + int cpicnt; + struct cpisec * cpisp; + struct cpiblk * cpibp; + struct cpdblk * cpdbp; + + dprintf(("hpfs_cpinit: \n")); + + if (argsp->flags & HPFSMNT_TABLES) { + bcopy(argsp->d2u, hpmp->hpm_d2u, sizeof(u_char) * 0x80); + bcopy(argsp->u2d, hpmp->hpm_u2d, sizeof(u_char) * 0x80); + } else { + for (i=0x0; i<0x80;i++) { + hpmp->hpm_d2u[i] = i + 0x80; + hpmp->hpm_u2d[i] = i + 0x80; + } + } + + cpicnt = hpmp->hpm_sp.sp_cpinum; + + MALLOC(hpmp->hpm_cpdblk, struct cpdblk *, + cpicnt * sizeof(struct cpdblk), M_HPFSMNT, M_WAITOK); + + cpdbp = hpmp->hpm_cpdblk; + lsn = hpmp->hpm_sp.sp_cpi; + + while (cpicnt > 0) { + error = bread(hpmp->hpm_devvp, lsn, DEV_BSIZE, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + + cpisp = (struct cpisec *)bp->b_data; + + cpibp = cpisp->s_cpi; + for (i=0; i<cpisp->s_cpicnt; i++, cpicnt --, cpdbp++, cpibp++) { + dprintf(("hpfs_cpinit: Country: %d, CP: %d (%d)\n", + cpibp->b_country, cpibp->b_cpid, + cpibp->b_vcpid)); + + error = hpfs_cpload(hpmp, cpibp, cpdbp); + if (error) { + brelse(bp); + return (error); + } + } + lsn = cpisp->s_next; + brelse(bp); + } + + return (0); +} + +int +hpfs_cpdeinit ( + struct hpfsmount *hpmp) +{ + dprintf(("hpmp_cpdeinit: ")); + FREE(hpmp->hpm_cpdblk,M_HPFSMNT); + return (0); +} + +/* + * Lookup for a run of blocks. + */ +int +hpfs_bmlookup ( + struct hpfsmount *hpmp, + u_long flags, /* 1 means we want right len blocks in run, not less */ + lsn_t lsn, /* We want near this one */ + u_long len, /* We want such long */ + lsn_t *lsnp, /* We got here */ + u_long *lenp) /* We got this long */ +{ + u_int32_t * bitmap; + register u_int32_t mask; + int i,k; + int cband, vcband; + u_int bandsz; + int count; + + dprintf(("hpfs_bmlookup: lsn: 0x%x, len 0x%lx | Step1\n", lsn, len)); + + if (lsn > hpmp->hpm_su.su_btotal) { + printf("hpfs_bmlookup: OUT OF VOLUME\n"); + return ENOSPC; + } + if (len > hpmp->hpm_bavail) { + printf("hpfs_bmlookup: OUT OF SPACE\n"); + return ENOSPC; + } + i = lsn >> 5; + k = lsn & 0x1F; + mask = 1 << k; + bitmap = (u_int32_t *)hpmp->hpm_bitmap + i; + + if (*bitmap & mask) { + *lsnp = lsn; + *lenp = 0; + for (; k < 32; k++, mask<<=1) { + if (*bitmap & mask) + (*lenp) ++; + else { + if (flags & 1) + goto step2; + else + return (0); + } + + if (*lenp == len) + return (0); + } + + bitmap++; + i++; + for (; i < hpmp->hpm_su.su_btotal >> 5; i++, bitmap++) { + for (k=0, mask=1; k < 32; k++, mask<<=1) { + if (*bitmap & mask) + (*lenp) ++; + else { + if (flags & 1) + goto step2; + else + return (0); + } + + if (*lenp == len) + return (0); + } + } + return (0); + } + +step2: + /* + * Lookup all bands begining from cband, lookup for first block + */ + cband = (lsn >> 14); + dprintf(("hpfs_bmlookup: Step2: band 0x%x (0x%lx)\n", + cband, hpmp->hpm_dbnum)); + for (vcband = 0; vcband < hpmp->hpm_dbnum; vcband ++, cband++) { + cband = cband % hpmp->hpm_dbnum; + bandsz = min (hpmp->hpm_su.su_btotal - (cband << 14), 0x4000); + dprintf(("hpfs_bmlookup: band: %d, sz: 0x%x\n", cband, bandsz)); + + bitmap = (u_int32_t *)hpmp->hpm_bitmap + (cband << 9); + *lsnp = cband << 14; + *lenp = 0; + count = 0; + for (i=0; i < bandsz >> 5; i++, bitmap++) { + for (k=0, mask=1; k < 32; k++, mask<<=1) { + if (*bitmap & mask) { + if (count) { + (*lenp) ++; + } else { + count = 1; + *lsnp = (cband << 14) + (i << 5) + k; + *lenp = 1; + } + } else { + if ((*lenp) && !(flags & 1)) { + return (0); + } else { + count = 0; + } + } + + if (*lenp == len) + return (0); + } + } + if (cband == hpmp->hpm_dbnum - 1) { + if ((*lenp) && !(flags & 1)) { + return (0); + } else { + count = 0; + } + } + } + + return (ENOSPC); +} + +/* + * Lookup a single free block. XXX Need locking on BitMap operations + * VERY STUPID ROUTINE!!! + */ +int +hpfs_bmfblookup ( + struct hpfsmount *hpmp, + lsn_t *lp) +{ + u_int32_t * bitmap; + int i,k; + + dprintf(("hpfs_bmfblookup: ")); + + bitmap = (u_int32_t *)hpmp->hpm_bitmap; + for (i=0; i < hpmp->hpm_su.su_btotal >> 5; i++, bitmap++) { + k = ffs(*bitmap); + if (k) { + *lp = (i << 5) + k - 1; + dprintf((" found: 0x%x\n",*lp)); + return (0); + } + } + + return (ENOSPC); +} + +/* + * Mark contignous block of blocks. + */ +int +hpfs_bmmark ( + struct hpfsmount *hpmp, + lsn_t bn, + u_long bl, + int state) +{ + u_int32_t * bitmap; + int i, didprint = 0; + + dprintf(("hpfs_bmmark(0x%x, 0x%lx, %d): \n",bn,bl, state)); + + if ((bn > hpmp->hpm_su.su_btotal) || (bn+bl > hpmp->hpm_su.su_btotal)) { + printf("hpfs_bmmark: MARKING OUT OF VOLUME\n"); + return 0; + } + bitmap = (u_int32_t *)hpmp->hpm_bitmap; + bitmap += bn >> 5; + + while (bl > 0) { + for (i = bn & 0x1F; (i < 0x20) && (bl > 0) ; i++, bl--) { + if (state) { + if ( *bitmap & (1 << i)) { + if (!didprint) { + printf("hpfs_bmmark: ALREADY FREE\n"); + didprint = 1; + } + } else + hpmp->hpm_bavail++; + + *bitmap |= (1 << i); + } else { + if ((~(*bitmap)) & (1 << i)) { + if (!didprint) { + printf("hpfs_bmmark: ALREADY BUSY\n"); + didprint = 1; + } + } else + hpmp->hpm_bavail--; + + *bitmap &= ~(1 << i); + } + } + bn = 0; + bitmap++; + } + + return (0); +} + + +int +hpfs_validateparent ( + struct hpfsnode *hp) +{ + struct hpfsnode *dhp; + struct vnode *dvp; + struct hpfsmount *hpmp = hp->h_hpmp; + struct buf *bp; + struct dirblk *dp; + struct hpfsdirent *dep; + lsn_t lsn, olsn; + int level, error; + + dprintf(("hpfs_validatetimes(0x%x): [parent: 0x%x] ", + hp->h_no, hp->h_fn.fn_parent)); + + if (hp->h_no == hp->h_fn.fn_parent) { + dhp = hp; + } else { + error = VFS_VGET(hpmp->hpm_mp, hp->h_fn.fn_parent, &dvp); + if (error) + return (error); + dhp = VTOHP(dvp); + } + + lsn = ((alleaf_t *)dhp->h_fn.fn_abd)->al_lsn; + + olsn = 0; + level = 1; + bp = NULL; + +dive: + dprintf(("[dive 0x%x] ", lsn)); + if (bp != NULL) + brelse(bp); + error = bread(dhp->h_devvp, lsn, D_BSIZE, NOCRED, &bp); + if (error) + goto failed; + + dp = (struct dirblk *) bp->b_data; + if (dp->d_magic != D_MAGIC) { + printf("hpfs_validatetimes: magic doesn't match\n"); + error = EINVAL; + goto failed; + } + + dep = D_DIRENT(dp); + + if (olsn) { + dprintf(("[restore 0x%x] ", olsn)); + + while(!(dep->de_flag & DE_END) ) { + if((dep->de_flag & DE_DOWN) && + (olsn == DE_DOWNLSN(dep))) + break; + dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); + } + + if((dep->de_flag & DE_DOWN) && (olsn == DE_DOWNLSN(dep))) { + if (dep->de_flag & DE_END) + goto blockdone; + + if (hp->h_no == dep->de_fnode) { + dprintf(("[found] ")); + goto readdone; + } + + dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); + } else { + printf("hpfs_validatetimes: ERROR! oLSN not found\n"); + error = EINVAL; + goto failed; + } + } + + olsn = 0; + + while(!(dep->de_flag & DE_END)) { + if(dep->de_flag & DE_DOWN) { + lsn = DE_DOWNLSN(dep); + level++; + goto dive; + } + + if (hp->h_no == dep->de_fnode) { + dprintf(("[found] ")); + goto readdone; + } + + dep = (hpfsdirent_t *)((caddr_t)dep + dep->de_reclen); + } + + if(dep->de_flag & DE_DOWN) { + dprintf(("[enddive] ")); + lsn = DE_DOWNLSN(dep); + level++; + goto dive; + } + +blockdone: + dprintf(("[EOB] ")); + olsn = lsn; + lsn = dp->d_parent; + level--; + dprintf(("[level %d] ", level)); + if (level > 0) + goto dive; /* undive really */ + + goto failed; + +readdone: + bcopy(dep->de_name,hp->h_name,dep->de_namelen); + hp->h_name[dep->de_namelen] = '\0'; + hp->h_namelen = dep->de_namelen; + hp->h_ctime = dep->de_ctime; + hp->h_atime = dep->de_atime; + hp->h_mtime = dep->de_mtime; + hp->h_flag |= H_PARVALID; + + dprintf(("[readdone]")); + +failed: + dprintf(("\n")); + if (bp != NULL) + brelse(bp); + if (hp != dhp) + vput(dvp); + + return (error); +} + +struct timespec +hpfstimetounix ( + u_long hptime) +{ + struct timespec t; + + t.tv_nsec = 0; + t.tv_sec = hptime; + + return t; +} + +/* + * Write down changes done to parent dir, these are only times for now. + * hpfsnode have to be locked. + */ +int +hpfs_updateparent ( + struct hpfsnode *hp) +{ + struct hpfsnode *dhp; + struct vnode *dvp; + struct hpfsdirent *dep; + struct buf * bp; + int error; + + dprintf(("hpfs_updateparent(0x%x): \n", hp->h_no)); + + if (!(hp->h_flag & H_PARCHANGE)) + return (0); + + if (!(hp->h_flag & H_PARVALID)) { + error = hpfs_validateparent (hp); + if (error) + return (error); + } + + if (hp->h_no == hp->h_fn.fn_parent) { + dhp = hp; + } else { + error = VFS_VGET(hp->h_hpmp->hpm_mp, hp->h_fn.fn_parent, + &dvp); + if (error) + return (error); + dhp = VTOHP(dvp); + } + + error = hpfs_genlookupbyname (dhp, hp->h_name, hp->h_namelen, + &bp, &dep); + if (error) { + goto failed; + } + + dep->de_atime = hp->h_atime; + dep->de_mtime = hp->h_mtime; + dep->de_size = hp->h_fn.fn_size; + + bdwrite (bp); + + hp->h_flag &= ~H_PARCHANGE; + + error = 0; +failed: + if (hp != dhp) + vput(dvp); + + return (0); +} + +/* + * Write down on disk changes done to fnode. hpfsnode have to be locked. + */ +int +hpfs_update ( + struct hpfsnode *hp) +{ + struct buf * bp; + + dprintf(("hpfs_update(0x%x): \n", hp->h_no)); + + if (!(hp->h_flag & H_CHANGE)) + return (0); + + bp = getblk(hp->h_devvp, hp->h_no, FNODESIZE, 0, 0); + clrbuf(bp); + + bcopy (&hp->h_fn, bp->b_data, sizeof(struct fnode)); + bdwrite (bp); + + hp->h_flag &= ~H_CHANGE; + + if (hp->h_flag & H_PARCHANGE) + return (hpfs_updateparent(hp)); + + return (0); +} + +/* + * Truncate file to specifed size. hpfsnode have to be locked. + */ +int +hpfs_truncate ( + struct hpfsnode *hp, + u_long size) +{ + struct hpfsmount *hpmp = hp->h_hpmp; + lsn_t newblen, oldblen; + int error, pf; + + dprintf(("hpfs_truncate(0x%x, 0x%x -> 0x%lx): ", + hp->h_no, hp->h_fn.fn_size, size)); + + newblen = (size + DEV_BSIZE - 1) >> DEV_BSHIFT; + oldblen = (hp->h_fn.fn_size + DEV_BSIZE - 1) >> DEV_BSHIFT; + + dprintf(("blen: 0x%x -> 0x%x\n", oldblen, newblen)); + + error = hpfs_truncatealblk (hpmp, &hp->h_fn.fn_ab, newblen, &pf); + if (error) + return (error); + if (pf) { + hp->h_fn.fn_ab.ab_flag = 0; + hp->h_fn.fn_ab.ab_freecnt = 0x8; + hp->h_fn.fn_ab.ab_busycnt = 0x0; + hp->h_fn.fn_ab.ab_freeoff = sizeof(alblk_t); + } + + hp->h_fn.fn_size = size; + + hp->h_flag |= (H_CHANGE | H_PARCHANGE); + + dprintf(("hpfs_truncate: successful\n")); + + return (0); +} + +/* + * Enlarge file to specifed size. hpfsnode have to be locked. + */ +int +hpfs_extend ( + struct hpfsnode *hp, + u_long size) +{ + struct hpfsmount *hpmp = hp->h_hpmp; + lsn_t newblen, oldblen; + int error; + + dprintf(("hpfs_extend(0x%x, 0x%x -> 0x%lx): ", + hp->h_no, hp->h_fn.fn_size, size)); + + if (hpmp->hpm_bavail < 0x10) + return (ENOSPC); + + newblen = (size + DEV_BSIZE - 1) >> DEV_BSHIFT; + oldblen = (hp->h_fn.fn_size + DEV_BSIZE - 1) >> DEV_BSHIFT; + + dprintf(("blen: 0x%x -> 0x%x\n", oldblen, newblen)); + + error = hpfs_addextent(hpmp, hp, newblen - oldblen); + if (error) { + printf("hpfs_extend: FAILED TO ADD EXTENT %d\n", error); + return (error); + } + + hp->h_fn.fn_size = size; + + hp->h_flag |= (H_CHANGE | H_PARCHANGE); + + dprintf(("hpfs_extend: successful\n")); + + return (0); +} + +/* + * Read AlSec structure, and check if magic is valid. + * You don't need to brelse buf on error. + */ +int +hpfs_breadstruct ( + struct hpfsmount *hpmp, + lsn_t lsn, + u_int len, + u_int32_t magic, + struct buf **bpp) +{ + struct buf *bp; + u_int32_t *mp; + int error; + + dprintf(("hpfs_breadstruct: reading at 0x%x\n", lsn)); + + *bpp = NULL; + + error = bread(hpmp->hpm_devvp, lsn, len, NOCRED, &bp); + if (error) { + brelse(bp); + return (error); + } + mp = (u_int32_t *) bp->b_data; + if (*mp != magic) { + brelse(bp); + printf("hpfs_breadstruct: MAGIC DOESN'T MATCH (0x%08x != 0x%08x)\n", + *mp, magic); + return (EINVAL); + } + + *bpp = bp; + + return (0); +} + |