freebsd-dev/sys/fs/hpfs/hpfs_subr.c
Kirk McKusick a0595d0249 Add a flags parameter to VFS_VGET to pass through the desired
locking flags when acquiring a vnode. The immediate purpose is
to allow polling lock requests (LK_NOWAIT) needed by soft updates
to avoid deadlock when enlisting other processes to help with
the background cleanup. For the future it will allow the use of
shared locks for read access to vnodes. This change touches a
lot of files as it affects most filesystems within the system.
It has been well tested on FFS, loopback, and CD-ROM filesystems.
only lightly on the others, so if you find a problem there, please
let me (mckusick@mckusick.com) know.
2002-03-17 01:25:47 +00:00

864 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);
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,
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);
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);
}