Add support for OFS, International FileSystems, and blocksizes > 512.

Corrrect floppy rootblock calculation.  Validate filesystem type. (Closes
PR 2232)
Filename hashing requires unsigned characters.  (Closes PR 1026)
Soft link path is null-terminated string, not BSTR.
Use actual file header block info for hard links.
Set nlink to 2 for hard linked files in adosfs_getattr().
Load allocation bitmap and set correct free space.
This commit is contained in:
mhitch 1996-04-05 05:06:07 +00:00
parent b35575d176
commit a0e658617d
5 changed files with 266 additions and 83 deletions

View File

@ -1,7 +1,8 @@
/* $NetBSD: adlookup.c,v 1.12 1996/02/13 17:05:47 christos Exp $ */
/* $NetBSD: adlookup.c,v 1.13 1996/04/05 05:06:07 mhitch Exp $ */
/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -74,7 +75,7 @@ adosfs_lookup(v)
struct anode *adp; /* anode of search dir */
struct ucred *ucp; /* lookup credentials */
u_long bn, plen, hval;
char *pelt;
u_char *pelt;
#ifdef ADOSFS_DIAGNOSTIC
advopprint(sp);
@ -90,7 +91,7 @@ adosfs_lookup(v)
last = flags & ISLASTCN;
lockp = flags & LOCKPARENT;
wantp = flags & (LOCKPARENT | WANTPARENT);
pelt = cnp->cn_nameptr;
pelt = (u_char *)cnp->cn_nameptr;
plen = cnp->cn_namelen;
nocache = 0;
@ -185,7 +186,7 @@ adosfs_lookup(v)
* then walk the chain. if chain has not been fully
* walked before, track the count in `tabi'
*/
hval = adoshash(pelt, plen, adp->ntabent);
hval = adoshash(pelt, plen, adp->ntabent, IS_INTER(adp->amp));
bn = adp->tab[hval];
i = min(adp->tabi[hval], 0);
while (bn != 0) {

View File

@ -1,7 +1,8 @@
/* $NetBSD: adosfs.h,v 1.9 1996/02/09 19:06:39 christos Exp $ */
/* $NetBSD: adosfs.h,v 1.10 1996/04/05 05:06:08 mhitch Exp $ */
/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -77,10 +78,10 @@ struct anode {
int flags; /* misc flags */
char *slinkto; /* name of file or dir */
};
#define VTOA(vp) ((struct anode *)(vp)->v_data)
#define ATOV(ap) ((ap)->vp)
#define ANODETABSZ(ap) (((ap)->nwords - 56) * sizeof(long))
#define ANODETABENT(ap) ((ap)->nwords - 56)
#define VTOA(vp) ((struct anode *)(vp)->v_data)
#define ATOV(ap) ((ap)->vp)
#define ANODETABSZ(ap) (((ap)->nwords - 56) * sizeof(long))
#define ANODETABENT(ap) ((ap)->nwords - 56)
#define ANODENDATBLKENT(ap) ((ap)->nwords - 56)
/*
@ -91,24 +92,34 @@ struct anode {
struct adosfsmount {
LIST_HEAD(anodechain, anode) anodetab[ANODEHASHSZ];
struct mount *mp; /* owner mount */
u_long dostype; /* type of volume */
u_long rootb; /* root block number */
u_long startb; /* start block */
u_long endb; /* one block past last */
u_long secsperblk; /* sectors per block */
u_long bsize; /* size of blocks */
u_long nwords; /* size of blocks in long words */
u_long dbsize; /* data bytes per block */
uid_t uid; /* uid of mounting user */
gid_t gid; /* gid of mounting user */
u_long mask; /* mode mask */
struct vnode *devvp; /* blk device mounted on */
struct vnode *rootvp; /* out root vnode */
struct netexport export;
u_long *bitmap; /* allocation bitmap */
u_long numblks; /* number of usable blocks */
u_long freeblks; /* number of free blocks */
};
#define VFSTOADOSFS(mp) ((struct adosfsmount *)(mp)->mnt_data)
#define IS_FFS(amp) ((amp)->dostype & 1)
#define IS_INTER(amp) (((amp)->dostype & 7) > 1)
/*
* AmigaDOS block stuff.
*/
#define BBOFF (0)
#define BPT_SHORT (2)
#define BPT_DATA (8)
#define BPT_LIST (16)
#define BST_RDIR (1)
@ -118,12 +129,19 @@ struct adosfsmount {
#define BST_FILE (-3L)
#define BST_LFILE (-4L)
#define OFS_DATA_OFFSET (24)
/*
* utility protos
*/
#ifndef m68k
long adoswordn __P((struct buf *, int));
#else
#define adoswordn(bp,wn) (*((long *)(bp)->b_data + (wn)))
#endif
long adoscksum __P((struct buf *, long));
int adoshash __P((const char *, int, int));
int adoshash __P((const u_char *, int, int, int));
int adunixprot __P((int));
int adosfs_getblktype __P((struct adosfsmount *, struct buf *));

View File

@ -1,7 +1,8 @@
/* $NetBSD: adutil.c,v 1.8 1996/02/09 19:06:41 christos Exp $ */
/* $NetBSD: adutil.c,v 1.9 1996/04/05 05:06:10 mhitch Exp $ */
/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -44,7 +45,7 @@
* look for anode in the mount's hash table, return locked.
*/
#define AHASH(an) ((an) & (ANODEHASHSZ - 1))
static int toupper __P((int));
static int CapitalChar __P((int, int));
struct vnode *
adosfs_ahashget(mp, an)
@ -98,7 +99,8 @@ adosfs_getblktype(amp, bp)
{
if (adoscksum(bp, amp->nwords)) {
#ifdef DIAGNOSTIC
printf("adosfs: aget: cksum of blk %d failed\n", bp->b_blkno);
printf("adosfs: aget: cksum of blk %d failed\n",
bp->b_blkno / amp->secsperblk);
#endif
return (-1);
}
@ -108,7 +110,8 @@ adosfs_getblktype(amp, bp)
*/
if (adoswordn(bp, 0) != BPT_SHORT) {
#ifdef DIAGNOSTIC
printf("adosfs: aget: bad primary type blk %d\n", bp->b_blkno);
printf("adosfs: aget: bad primary type blk %d\n",
bp->b_blkno / amp->secsperblk);
#endif
return (-1);
}
@ -135,22 +138,24 @@ adunixprot(adprot)
int adprot;
{
if (adprot & 0xc000ee00) {
adprot = ((adprot & 0xee00) | (~adprot & 0x000e)) >> 1;
return (((adprot & 0x7) << 6) | ((adprot & 0x700) >> 5) |
(adprot >> 12));
adprot = (adprot & 0xee0e) >> 1;
return (((adprot & 0x7) << 6) |
((adprot & 0x700) >> 5) |
((adprot & 0x7000) >> 12));
}
else {
adprot = (~adprot >> 1) & 0x7;
adprot = (adprot >> 1) & 0x7;
return((adprot << 6) | (adprot << 3) | adprot);
}
}
static int
toupper(ch)
int ch;
CapitalChar(ch, inter)
int ch, inter;
{
if (ch >= 'a' && ch <= 'z')
return(ch & ~(0x20));
if ((ch >= 'a' && ch <= 'z') ||
(inter && ch >= 0xe0 && ch <= 0xfe && ch != 0xf7))
return(ch - ('a' - 'A'));
return(ch);
}
@ -170,15 +175,15 @@ adoscksum(bp, n)
}
int
adoshash(nam, namlen, nelt)
const char *nam;
int namlen, nelt;
adoshash(nam, namlen, nelt, inter)
const u_char *nam;
int namlen, nelt, inter;
{
int val;
val = namlen;
while (namlen--)
val = ((val * 13) + toupper(*nam++)) & 0x7ff;
val = ((val * 13) + CapitalChar(*nam++, inter)) & 0x7ff;
return(val % nelt);
}
@ -204,6 +209,7 @@ tvtods(tvp, dsp)
}
#endif
#ifndef m68k
long
adoswordn(bp, wn)
struct buf *bp;
@ -214,3 +220,4 @@ adoswordn(bp, wn)
*/
return(ntohl(*((long *)bp->b_data + wn)));
}
#endif

View File

@ -1,7 +1,8 @@
/* $NetBSD: advfsops.c,v 1.16 1996/02/09 19:06:42 christos Exp $ */
/* $NetBSD: advfsops.c,v 1.17 1996/04/05 05:06:12 mhitch Exp $ */
/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -60,6 +61,7 @@ int adosfs_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
int adosfs_vptofh __P((struct vnode *, struct fid *));
int adosfs_mountfs __P((struct vnode *, struct mount *, struct proc *));
int adosfs_loadbitmap __P((struct adosfsmount *));
int
adosfs_mount(mp, path, data, ndp, p)
@ -154,6 +156,7 @@ adosfs_mountfs(devvp, mp, p)
struct disklabel dl;
struct partition *parp;
struct adosfsmount *amp;
struct buf *bp;
struct vnode *rvp;
int error, part, i;
@ -186,13 +189,33 @@ adosfs_mountfs(devvp, mp, p)
amp = malloc(sizeof(struct adosfsmount), M_ADOSFSMNT, M_WAITOK);
bzero((char *)amp, (u_long)sizeof(struct adosfsmount));
amp->mp = mp;
amp->startb = parp->p_offset;
amp->endb = parp->p_offset + parp->p_size;
amp->bsize = dl.d_secsize;
if (dl.d_type == DTYPE_FLOPPY) {
amp->bsize = dl.d_secsize;
amp->secsperblk = 1;
}
else {
amp->bsize = parp->p_fsize * parp->p_frag;
amp->secsperblk = parp->p_frag;
}
amp->rootb = (parp->p_size / amp->secsperblk - 1 + parp->p_cpg) >> 1;
amp->numblks = parp->p_size / amp->secsperblk - parp->p_cpg;
bp = NULL;
if ((error = bread(devvp, (daddr_t)BBOFF,
amp->bsize, NOCRED, &bp)) != 0)
goto fail;
amp->dostype = adoswordn(bp, 0);
brelse(bp);
if (amp->dostype < 0x444f5300 || amp->dostype > 0x444f5305) {
error = EINVAL;
goto fail;
}
amp->nwords = amp->bsize >> 2;
amp->dbsize = amp->bsize - (IS_FFS(amp) ? 0 : OFS_DATA_OFFSET);
amp->devvp = devvp;
/* amp->rootb = (parp->p_size - 1 + 2) >> 1;*/
amp->rootb = (parp->p_size - 1 + parp->p_cpg) >> 1;
mp->mnt_data = (qaddr_t)amp;
mp->mnt_stat.f_fsid.val[0] = (long)devvp->v_rdev;
@ -211,12 +234,26 @@ adosfs_mountfs(devvp, mp, p)
*/
if ((error = VFS_ROOT(mp, &rvp)) != 0)
goto fail;
/* allocate and load bitmap, set free space */
amp->bitmap = malloc(((amp->numblks + 31) / 32) * sizeof(*amp->bitmap),
M_ADOSFSBITMAP, M_WAITOK);
if (amp->bitmap)
adosfs_loadbitmap(amp);
if (mp->mnt_flag & MNT_RDONLY && amp->bitmap) {
/*
* Don't need the bitmap any more if it's read-only.
*/
free(amp->bitmap, M_ADOSFSBITMAP);
amp->bitmap = NULL;
}
vput(rvp);
return(0);
fail:
(void) VOP_CLOSE(devvp, FREAD, NOCRED, p);
if (amp && amp->bitmap)
free(amp->bitmap, M_ADOSFSBITMAP);
if (amp)
free(amp, M_ADOSFSMNT);
return (error);
@ -250,6 +287,8 @@ adosfs_unmount(mp, mntflags, p)
amp->devvp->v_specflags &= ~SI_MOUNTEDON;
error = VOP_CLOSE(amp->devvp, FREAD, NOCRED, p);
vrele(amp->devvp);
if (amp->bitmap)
free(amp->bitmap, M_ADOSFSBITMAP);
free(amp, M_ADOSFSMNT);
mp->mnt_data = (qaddr_t)0;
mp->mnt_flag &= ~MNT_LOCAL;
@ -266,6 +305,7 @@ adosfs_root(mp, vpp)
if ((error = VFS_VGET(mp, (ino_t)VFSTOADOSFS(mp)->rootb, &nvp)) != 0)
return (error);
/* XXX verify it's a root block? */
*vpp = nvp;
return (0);
}
@ -281,10 +321,10 @@ adosfs_statfs(mp, sbp, p)
amp = VFSTOADOSFS(mp);
sbp->f_type = 0;
sbp->f_bsize = amp->bsize;
sbp->f_iosize = amp->bsize;
sbp->f_blocks = 2; /* XXX */
sbp->f_bfree = 0; /* none */
sbp->f_bavail = 0; /* none */
sbp->f_iosize = amp->dbsize;
sbp->f_blocks = amp->numblks;
sbp->f_bfree = amp->freeblks;
sbp->f_bavail = amp->freeblks;
sbp->f_files = 0; /* who knows */
sbp->f_ffree = 0; /* " " */
if (sbp != &mp->mnt_stat) {
@ -310,7 +350,7 @@ adosfs_vget(mp, an, vpp)
struct anode *ap;
struct buf *bp;
char *nam, *tmp;
int namlen, error, tmplen;
int namlen, error;
error = 0;
amp = VFSTOADOSFS(mp);
@ -337,7 +377,8 @@ adosfs_vget(mp, an, vpp)
ap->nwords = amp->nwords;
adosfs_ainshash(amp, ap);
if ((error = bread(amp->devvp, an, amp->bsize, NOCRED, &bp)) != 0) {
if ((error = bread(amp->devvp, an * amp->secsperblk,
amp->bsize, NOCRED, &bp)) != 0) {
vput(vp);
return (error);
}
@ -357,15 +398,10 @@ adosfs_vget(mp, an, vpp)
ap->created.ticks = adoswordn(bp, ap->nwords - 5);
break;
case ALDIR:
vp->v_type = VDIR;
break;
case ADIR:
vp->v_type = VDIR;
break;
case ALFILE:
vp->v_type = VREG;
ap->fsize = adoswordn(bp, ap->nwords - 47);
break;
case AFILE:
vp->v_type = VREG;
ap->fsize = adoswordn(bp, ap->nwords - 47);
@ -377,9 +413,9 @@ adosfs_vget(mp, an, vpp)
* from: "part:dir/file" to: "/part/dir/file"
*/
nam = bp->b_data + (6 * sizeof(long));
tmplen = namlen = *(u_char *)nam++;
namlen = strlen(nam);
tmp = nam;
while (tmplen-- && *tmp != ':')
while (*tmp && *tmp != ':')
tmp++;
if (*tmp == 0) {
ap->slinkto = malloc(namlen + 1, M_ANODE, M_WAITOK);
@ -402,6 +438,28 @@ adosfs_vget(mp, an, vpp)
vput(vp);
return (EINVAL);
}
/*
* Get appropriate data from this block; hard link needs
* to get other data from the "real" block.
*/
/*
* copy in name (from original block)
*/
nam = bp->b_data + (ap->nwords - 20) * sizeof(long);
namlen = *(u_char *)nam++;
if (namlen > 30) {
#ifdef DIAGNOSTIC
printf("adosfs: aget: name length too long blk %d\n", an);
#endif
brelse(bp);
vput(vp);
return (EINVAL);
}
bcopy(nam, ap->name, namlen);
ap->name[namlen] = 0;
/*
* if dir alloc hash table and copy it in
*/
@ -428,17 +486,29 @@ adosfs_vget(mp, an, vpp)
* setup last indirect block cache.
*/
ap->lastlindblk = 0;
if (ap->type == AFILE)
if (ap->type == AFILE) {
ap->lastindblk = ap->block;
else if (ap->type == ALFILE)
if (adoswordn(bp, ap->nwords - 10))
ap->linkto = ap->block;
} else if (ap->type == ALFILE) {
ap->lastindblk = ap->linkto;
brelse(bp);
bp = NULL;
error = bread(amp->devvp, ap->linkto * amp->secsperblk,
amp->bsize, NOCRED, &bp);
ap->fsize = adoswordn(bp, ap->nwords - 47);
/*
* Should ap->block be set to the real file header block?
*/
ap->block = ap->linkto;
}
if (ap->type == AROOT) {
ap->adprot = 0;
ap->adprot = 15;
ap->uid = amp->uid;
ap->gid = amp->gid;
} else {
ap->adprot = adoswordn(bp, ap->nwords - 48);
ap->adprot = adoswordn(bp, ap->nwords - 48) ^ 15;
/*
* Get uid/gid from extensions in file header
* (really need to know if this is a muFS partition)
@ -465,27 +535,89 @@ adosfs_vget(mp, an, vpp)
ap->mtime.mins = adoswordn(bp, ap->nwords - 22);
ap->mtime.ticks = adoswordn(bp, ap->nwords - 21);
/*
* copy in name
*/
nam = bp->b_data + (ap->nwords - 20) * sizeof(long);
namlen = *(u_char *)nam++;
if (namlen > 30) {
#ifdef DIAGNOSTIC
printf("adosfs: aget: name length too long blk %d\n", an);
#endif
brelse(bp);
vput(vp);
return (EINVAL);
}
bcopy(nam, ap->name, namlen);
ap->name[namlen] = 0;
*vpp = vp; /* return vp */
brelse(bp); /* release buffer */
return (0);
}
/*
* Load the bitmap into memory, and count the number of available
* blocks.
* The bitmap will be released if the filesystem is read-only; it's
* only needed to find the free space.
*/
int
adosfs_loadbitmap(amp)
struct adosfsmount *amp;
{
struct buf *bp, *mapbp;
u_long bn;
int blkix, endix, mapix;
int bmsize;
int error;
bp = mapbp = NULL;
bn = amp->rootb;
if ((error = bread(amp->devvp, bn * amp->secsperblk, amp->bsize,
NOCRED, &bp)) != 0)
return (error);
blkix = amp->nwords - 49;
endix = amp->nwords - 24;
mapix = 0;
bmsize = (amp->numblks + 31) / 32;
while (mapix < bmsize) {
int n;
u_long bits;
if (adoswordn(bp, blkix) == 0)
break;
if (mapbp != NULL)
brelse(mapbp);
if ((error = bread(amp->devvp,
adoswordn(bp, blkix) * amp->secsperblk, amp->bsize,
NOCRED, &mapbp)) != 0)
break;
if (adoscksum(mapbp, amp->nwords)) {
#ifdef DIAGNOSTIC
printf("adosfs: loadbitmap - cksum of blk %d failed\n",
adoswordn(bp, blkix));
#endif
/* XXX Force read-only? Set free space 0? */
break;
}
n = 1;
while (n < amp->nwords && mapix < bmsize) {
amp->bitmap[mapix++] = bits = adoswordn(mapbp, n);
++n;
if (mapix == bmsize && amp->numblks & 31)
bits &= ~(0xffffffff << (amp->numblks & 31));
while (bits) {
if (bits & 1)
++amp->freeblks;
bits >>= 1;
}
}
++blkix;
if (mapix < bmsize && blkix == endix) {
bn = adoswordn(bp, blkix);
brelse(bp);
if ((error = bread(amp->devvp, bn * amp->secsperblk,
amp->bsize, NOCRED, &bp)) != 0)
break;
/*
* Why is there no checksum on these blocks?
*/
blkix = 0;
endix = amp->nwords - 1;
}
}
if (bp)
brelse(bp);
if (mapbp)
brelse(mapbp);
return (error);
}
/*
* File handle to vnode

View File

@ -1,7 +1,8 @@
/* $NetBSD: advnops.c,v 1.26 1996/02/10 00:44:18 christos Exp $ */
/* $NetBSD: advnops.c,v 1.27 1996/04/05 05:06:13 mhitch Exp $ */
/*
* Copyright (c) 1994 Christian E. Hopps
* Copyright (c) 1996 Matthias Scheler
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -206,18 +207,20 @@ adosfs_getattr(v)
/*
* XXX actually we can track this if we were to walk the list
* of links if it exists.
* XXX for now, just set nlink to 2 if this is a hard link
* to a file, or a file with a hard link.
*/
vap->va_nlink = 1;
vap->va_nlink = 1 + (ap->linkto != 0);
/*
* round up to nearest blocks add number of file list
* blocks needed and mutiply by number of bytes per block.
*/
fblks = howmany(ap->fsize, amp->bsize);
fblks = howmany(ap->fsize, amp->dbsize);
fblks += howmany(fblks, ANODENDATBLKENT(ap));
vap->va_bytes = fblks * amp->bsize;
vap->va_bytes = fblks * amp->dbsize;
vap->va_size = ap->fsize;
vap->va_blocksize = amp->bsize;
vap->va_blocksize = amp->dbsize;
}
#ifdef ADOSFS_DIAGNOSTIC
printf(" 0)");
@ -280,9 +283,9 @@ adosfs_read(v)
do {
/*
* we are only supporting ADosFFS currently
* (which have data blocks of 512 bytes)
* (which have data blocks without headers)
*/
size = amp->bsize;
size = amp->dbsize;
lbn = uio->uio_offset / size;
on = uio->uio_offset % size;
n = min((u_int)(size - on), uio->uio_resid);
@ -299,9 +302,29 @@ adosfs_read(v)
* but not much as ados makes little attempt to
* make things contigous
*/
error = bread(sp->a_vp, lbn, size, NOCRED, &bp);
error = bread(sp->a_vp, lbn * amp->secsperblk,
amp->bsize, NOCRED, &bp);
sp->a_vp->v_lastr = lbn;
n = min(n, (u_int)size - bp->b_resid);
if (!IS_FFS(amp)) {
if (bp->b_resid > 0)
error = EIO; /* OFS needs the complete block */
else if (adoswordn(bp, 0) != BPT_DATA) {
#ifdef DIAGNOSTIC
printf("adosfs: bad primary type blk %d\n",
bp->b_blkno / amp->secsperblk);
#endif
error=EINVAL;
}
else if ( adoscksum(bp, ap->nwords)) {
#ifdef DIAGNOSTIC
printf("adosfs: blk %d failed cksum.\n",
bp->b_blkno / amp->secsperblk);
#endif
error=EINVAL;
}
}
if (error) {
brelse(bp);
goto reterr;
@ -309,7 +332,9 @@ adosfs_read(v)
#ifdef ADOSFS_DIAGNOSTIC
printf(" %d+%d-%d+%d", lbn, on, lbn, n);
#endif
error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
n = min(n, (u_int)size - bp->b_resid);
error = uiomove(bp->b_un.b_addr + on +
amp->bsize - amp->dbsize, (int)n, uio);
brelse(bp);
} while (error == 0 && uio->uio_resid > 0 && n != 0);
reterr:
@ -568,10 +593,10 @@ adosfs_bmap(v)
#ifdef ADOSFS_DIAGNOSTIC
advopprint(sp);
#endif
bn = sp->a_bn;
ap = VTOA(sp->a_vp);
bn = sp->a_bn / ap->amp->secsperblk;
bnp = sp->a_bnp;
error = 0;
ap = VTOA(sp->a_vp);
if (sp->a_vpp != NULL)
*sp->a_vpp = ap->amp->devvp;
@ -622,8 +647,8 @@ adosfs_bmap(v)
error = EINVAL;
goto reterr;
}
error = bread(ap->amp->devvp, nb, ap->amp->bsize,
NOCRED, &flbp);
error = bread(ap->amp->devvp, nb * ap->amp->secsperblk,
ap->amp->bsize, NOCRED, &flbp);
if (error)
goto reterr;
if (adoscksum(flbp, ap->nwords)) {
@ -651,11 +676,11 @@ adosfs_bmap(v)
flblkoff = bn % ANODENDATBLKENT(ap);
if (flblkoff < adoswordn(flbp, 2 /* ADBI_NBLKTABENT */)) {
flblkoff = (ap->nwords - 51) - flblkoff;
*bnp = adoswordn(flbp, flblkoff);
*bnp = adoswordn(flbp, flblkoff) * ap->amp->secsperblk;
} else {
#ifdef DIAGNOSTIC
printf("flblk offset %d too large in lblk %d blk %d\n",
flblkoff, bn, flbp->b_blkno);
flblkoff, bn / ap->amp->secsperblk , flbp->b_blkno);
#endif
error = EINVAL;
}