freebsd-dev/sys/fs/hpfs/hpfs_subr.c
Poul-Henning Kamp 55dca57ef2 Convert to nmount. Add backwards compat cmount method.
Same comment as msdosfs applies: It would be nice if we had generic option
names for charset conversions.

Use vfs_mountefrom().  Rely on vfs_mount.c calling VFS_STATFS().
2004-12-06 20:14:20 +00:00

870 lines
17 KiB
C

/*-
* 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/bio.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, 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 mount *mp,
struct hpfsmount *hpmp)
{
struct buf *bp;
int error, i;
lsn_t lsn;
int cpicnt;
struct cpisec * cpisp;
struct cpiblk * cpibp;
struct cpdblk * cpdbp;
dprintf(("hpfs_cpinit: \n"));
error = vfs_copyopt(mp->mnt_optnew, "d2u", hpmp->hpm_d2u,
sizeof hpmp->hpm_d2u);
if (error == ENOENT)
for (i=0x0; i<0x80;i++)
hpmp->hpm_d2u[i] = i + 0x80;
else if (error)
return (error);
error = vfs_copyopt(mp->mnt_optnew, "u2d", hpmp->hpm_u2d,
sizeof hpmp->hpm_u2d);
if (error == ENOENT)
for (i=0x0; i<0x80;i++)
hpmp->hpm_u2d[i] = i + 0x80;
else if (error)
return (error);
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,
LK_EXCLUSIVE, &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,
LK_EXCLUSIVE, &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, 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);
}