#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/malloc.h>
#include <sys/buf.h>
#include <sys/buf2.h>
#include "hpfs.h"
#include "hpfs_subr.h"
#define AE_DONE 0
#define AE_SPLIT 2
int hpfs_addextentr (struct hpfsmount *, lsn_t, alleaf_t *,
alnode_t *, u_long *);
int hpfs_allocalsec (struct hpfsmount *, lsn_t, struct buf **);
int hpfs_alblk2alsec (struct hpfsmount *, alblk_t *, alsec_t **,
struct buf **);
int hpfs_splitalsec (struct hpfsmount *, alsec_t *, alsec_t **,
struct buf **);
int hpfs_concatalsec (struct hpfsmount *, alsec_t *, alsec_t *,
alnode_t *);
int
hpfs_hpbmap(struct hpfsnode *hp, daddr_t bn, daddr_t *bnp, int *runp)
{
struct buf *bp;
alblk_t * abp;
alleaf_t *alp;
alnode_t *anp;
int error, i;
dprintf(("hpfs_hpbmap(0x%x, 0x%x): ",hp->h_no, bn));
bp = NULL;
abp = &hp->h_fn.fn_ab;
alp = (alleaf_t *)&hp->h_fn.fn_abd;
anp = (alnode_t *)&hp->h_fn.fn_abd;
dive:
if (abp->ab_flag & AB_NODES) {
for (i=0; i<abp->ab_busycnt; i++, anp++) {
dprintf(("[0x%x,0x%x] ",anp->an_nextoff,anp->an_lsn));
if (bn < anp->an_nextoff) {
alsec_t *asp;
dprintf(("< found | "));
if (bp)
brelse(bp);
error = bread(hp->h_devvp,
dbtodoff(anp->an_lsn),
DEV_BSIZE, &bp);
if (error) {
kprintf("hpfs_hpbmap: bread error\n");
brelse(bp);
return (error);
}
asp = (alsec_t *) bp->b_data;
if (asp->as_magic != AS_MAGIC) {
brelse(bp);
kprintf("hpfs_hpbmap: "
"MAGIC DOESN'T MATCH");
return (EINVAL);
}
abp = &asp->as_ab;
alp = (alleaf_t *)&asp->as_abd;
anp = (alnode_t *)&asp->as_abd;
goto dive;
}
}
} else {
for (i=0; i<abp->ab_busycnt; i++, alp++) {
dprintf(("[0x%x,0x%x,0x%x] ",
alp->al_off,alp->al_len,alp->al_lsn));
if ((bn >= alp->al_off) &&
(!alp->al_len || (bn < alp->al_off + alp->al_len))) {
dprintf(("found, "));
*bnp = bn - alp->al_off + alp->al_lsn;
dprintf((" 0x%x ", *bnp));
if (runp != NULL) {
if (alp->al_len)
*runp = alp->al_off - 1 +
alp->al_len - bn;
else
*runp = 3;
dprintf((" 0x%x cont", *runp));
}
if (bp)
brelse(bp);
dprintf(("\n"));
return (0);
}
}
}
dprintf(("END, notfound\n"));
if (bp)
brelse(bp);
dprintf(("hpfs_hpbmap: offset too big\n"));
return (EFBIG);
}
int
hpfs_allocalsec(struct hpfsmount *hpmp, lsn_t parlsn, struct buf **bpp)
{
alsec_t * asp;
struct buf * bp;
lsn_t lsn;
int error;
*bpp = NULL;
error = hpfs_bmfblookup(hpmp, &lsn);
if (error) {
kprintf("hpfs_allocalsec: CAN'T ALLOC SPACE FOR AlSec\n");
return (error);
}
error = hpfs_bmmarkbusy(hpmp, lsn, 1);
if (error)
return (error);
bp = getblk(hpmp->hpm_devvp, dbtodoff(lsn), DEV_BSIZE, 0, 0);
clrbuf(bp);
asp = (alsec_t *) bp->b_data;
asp->as_magic = AS_MAGIC;
asp->as_self = lsn;
asp->as_parent = parlsn;
asp->as_ab.ab_flag = 0;
asp->as_ab.ab_busycnt = 0;
asp->as_ab.ab_freecnt = 0x28;
asp->as_ab.ab_freeoff = sizeof(alblk_t);
*bpp = bp;
return (0);
}
int
hpfs_splitalsec(struct hpfsmount *hpmp, alsec_t *asp, alsec_t **naspp,
struct buf **nbpp)
{
alsec_t *nasp;
struct buf *nbp;
alblk_t *abp;
alblk_t *nabp;
int error, n1, n2, sz;
error = hpfs_allocalsec(hpmp, asp->as_parent, &nbp);
if (error)
return (error);
nasp = (alsec_t *)nbp->b_data;
nabp = &nasp->as_ab;
abp = &asp->as_ab;
n1 = (abp->ab_busycnt + 1) / 2;
n2 = (abp->ab_busycnt - n1);
sz = (abp->ab_flag & AB_NODES) ? sizeof(alnode_t) : sizeof(alleaf_t);
bcopy((caddr_t)abp + sizeof(alblk_t) + n1 * sz,
(caddr_t)nabp + sizeof(alblk_t), n2 * sz);
nabp->ab_flag = abp->ab_flag;
nabp->ab_busycnt = n2;
nabp->ab_freecnt = (0x1e0 / sz - n2);
nabp->ab_freeoff += n2 * sz;
abp->ab_busycnt -= n1;
abp->ab_freecnt += n1;
abp->ab_freeoff -= n1 * sz;
*naspp = nasp;
*nbpp = nbp;
return (0);
}
int
hpfs_concatalsec(struct hpfsmount *hpmp, alsec_t *as0p, alsec_t *as1p,
alnode_t *aanp)
{
alblk_t *ab0p;
alblk_t *ab1p;
int sz;
dprintf(("hpfs_concatalsec: AlSecs at 0x%x and 0x%x \n",
as0p->as_self,as1p->as_self));
ab0p = &as0p->as_ab;
ab1p = &as1p->as_ab;
sz = (ab0p->ab_flag & AB_NODES) ? sizeof(alnode_t) : sizeof(alleaf_t);
if (ab0p->ab_freecnt > ab1p->ab_busycnt) {
if (ab0p->ab_flag & AB_NODES)
AB_LASTANP(ab0p)->an_nextoff = aanp[0].an_nextoff;
bcopy (AB_ALNODE(ab1p), AB_FREEANP(ab0p),
ab1p->ab_busycnt * sz);
AB_ADDNREC(ab0p, sz, ab1p->ab_busycnt);
return (0);
} else {
return (ENOSPC);
}
}
int
hpfs_alblk2alsec(struct hpfsmount *hpmp, alblk_t *abp, alsec_t **naspp,
struct buf **nbpp)
{
alsec_t *nasp;
alblk_t *nabp;
struct buf *nbp;
int error, sz;
error = hpfs_allocalsec(hpmp, 0, &nbp);
if (error)
return (error);
nasp = (alsec_t *)nbp->b_data;
nabp = &nasp->as_ab;
sz = (abp->ab_flag & AB_NODES) ? sizeof(alnode_t) : sizeof(alleaf_t);
bcopy (abp, nabp, sizeof(alblk_t) + sz * abp->ab_busycnt);
nabp->ab_freecnt = 0x1e0 / sz - nabp->ab_busycnt;
*naspp = nasp;
*nbpp = nbp;
return (0);
}
int
hpfs_addextent(struct hpfsmount *hpmp, struct hpfsnode *hp, u_long len)
{
alblk_t *rabp;
alnode_t ranp[2];
alleaf_t al;
int error;
u_long pf;
al.al_lsn = ~0;
al.al_len = len;
al.al_off = (hp->h_fn.fn_size + DEV_BSIZE - 1) >> DEV_BSHIFT;
rabp = &hp->h_fn.fn_ab;
if (al.al_off == 0) {
lsn_t nlsn;
u_long nlen;
dprintf(("hpfs_addextent: init AlBlk in root\n"));
rabp->ab_busycnt = 0;
rabp->ab_freecnt = 0x8;
rabp->ab_freeoff = sizeof(alblk_t);
rabp->ab_flag = 0;
error = hpfs_bmlookup (hpmp, 0, hp->h_no + 1, al.al_len, &nlsn, &nlen);
if (error)
return (error);
error = hpfs_bmmarkbusy(hpmp, nlsn, nlen);
if (error)
return (error);
dprintf(("hpfs_addextent: new: 0x%x 0x%lx, ", nlsn, nlen));
AL_SET(AB_FREEALP(rabp), al.al_off, nlen, nlsn);
AB_ADDAL(rabp);
al.al_off += nlen;
al.al_len -= nlen;
}
retry:
dprintf(("hpfs_addextent: AlBlk: [0x%x, 0x%x, 0x%x] need: 0x%x\n",
rabp->ab_freecnt, rabp->ab_busycnt, rabp->ab_flag, al.al_len));
while ((al.al_len) && (rabp->ab_freecnt > 0)) {
if (rabp->ab_flag & AB_NODES) {
alnode_t *anp;
anp = AB_LASTANP(rabp);
dprintf(("hpfs_addextent: AlNode: [0x%x,0x%x] \n",
anp->an_nextoff,anp->an_lsn));
error = hpfs_addextentr (hpmp, anp->an_lsn, &al, ranp, &pf);
if (error) {
kprintf("hpfs_addextent: FAILED %d\n",error);
return (error);
}
switch (pf) {
case AE_SPLIT:
dprintf(("hpfs_addextent: successful (split)\n"));
bcopy(ranp, AB_LASTANP(rabp), sizeof(alnode_t) * 2);
AB_ADDAN(rabp);
break;
default:
case AE_DONE:
dprintf(("hpfs_addextent: successful\n"));
break;
}
} else {
alleaf_t *alp;
alp = AB_LASTALP(rabp);
dprintf(("hpfs_addextent: AlLeaf: [0x%x,0x%x,0x%x] \n",
alp->al_off,alp->al_len,alp->al_lsn));
if (alp->al_off + alp->al_len == al.al_off) {
lsn_t nlsn;
u_long nlen;
error = hpfs_bmlookup (hpmp, 0,
alp->al_lsn + alp->al_len, al.al_len, &nlsn, &nlen);
if (error)
return (error);
error = hpfs_bmmarkbusy(hpmp, nlsn, nlen);
if (error)
return (error);
dprintf(("hpfs_addextent: new: 0x%x 0x%lx, ", nlsn, nlen));
if (alp->al_lsn + alp->al_len == nlsn) {
dprintf(("extended existed leaf\n"));
alp->al_len += nlen;
} else {
dprintf(("created new leaf\n"));
AL_SET(AB_FREEALP(rabp), al.al_off, nlen, nlsn);
AB_ADDAL(rabp);
}
al.al_off += nlen;
al.al_len -= nlen;
} else {
kprintf("hpfs_addextent: INTERNAL INCONSISTENCE\n");
return (EINVAL);
}
}
}
if (rabp->ab_freecnt <= 0) {
struct buf *nbp;
alsec_t * nrasp;
dprintf(("hpfs_addextent: overflow, convt\n"));
rabp->ab_flag |= AB_FNPARENT;
error = hpfs_alblk2alsec (hpmp, rabp, &nrasp, &nbp);
if (error) {
kprintf("hpfs_addextent: CAN'T CONVT\n");
return (error);
}
nrasp->as_parent = hp->h_no;
if (rabp->ab_flag & AB_NODES) {
int i;
alsec_t * asp;
alnode_t * anp;
struct buf * bp;
anp = AB_ALNODE(rabp);
for (i=0; i<rabp->ab_busycnt; i++) {
error = hpfs_breadalsec(hpmp, anp->an_lsn, &bp);
if (error)
return (error);
asp = (alsec_t *)bp->b_data;
asp->as_ab.ab_flag &= ~AB_FNPARENT;
asp->as_parent = nrasp->as_self;
bdwrite(bp);
anp ++;
}
}
rabp->ab_flag = AB_NODES;
rabp->ab_busycnt = 0;
rabp->ab_freecnt = 0xC;
rabp->ab_freeoff = sizeof(alblk_t);
AN_SET(AB_FREEANP(rabp), ~0, nrasp->as_self);
AB_ADDAN(rabp);
bdwrite(nbp);
}
if (al.al_len) {
dprintf(("hpfs_addextent: root retry\n"));
goto retry;
}
return (0);
}
int
hpfs_addextentr(struct hpfsmount *hpmp, lsn_t rlsn, alleaf_t *ralp,
alnode_t *ranp, u_long *resp)
{
struct buf *rbp;
alsec_t *rasp;
alblk_t *rabp;
alleaf_t *alp;
alnode_t *anp;
int error;
u_long pf;
u_long wb;
*resp = 0;
dprintf(("hpfs_addextentr: AlSec at 0x%x\n", rlsn));
error = hpfs_breadalsec(hpmp, rlsn, &rbp);
if (error)
return (error);
rasp = (alsec_t *)rbp->b_data;
rabp = &rasp->as_ab;
wb = 0;
dprintf(("hpfs_addextentr: AlBlk: [0x%x, 0x%x, 0x%x]\n",
rabp->ab_freecnt, rabp->ab_busycnt, rabp->ab_flag));
while ((ralp->al_len) && (rabp->ab_freecnt > 0)) {
if (rabp->ab_flag & AB_NODES) {
anp = AB_LASTANP(rabp);
dprintf(("hpfs_addextentr: AlNode: [0x%x,0x%x] \n",
anp->an_nextoff,anp->an_lsn));
error = hpfs_addextentr (hpmp, anp->an_lsn, ralp, ranp, &pf);
if (error) {
kprintf("hpfs_addextentr: FAILED %d\n",error);
goto fail;
}
switch (pf) {
case AE_SPLIT:
dprintf(("hpfs_addextentr: successful (split)\n"));
bcopy(ranp, AB_LASTANP(rabp), sizeof(alnode_t) * 2);
AB_ADDAN(rabp);
wb = 1;
break;
default:
case AE_DONE:
dprintf(("hpfs_addextentr: successful\n"));
break;
}
} else {
alp = AB_LASTALP(rabp);
dprintf(("hpfs_addextentr: AlLeaf: [0x%x,0x%x,0x%x] \n",
alp->al_off,alp->al_len,alp->al_lsn));
if (alp->al_off + alp->al_len == ralp->al_off) {
lsn_t nlsn;
u_long nlen;
error = hpfs_bmlookup (hpmp, 0,
alp->al_lsn + alp->al_len, ralp->al_len, &nlsn, &nlen);
if (error)
goto fail;
error = hpfs_bmmarkbusy(hpmp, nlsn, nlen);
if (error)
goto fail;
dprintf(("hpfs_addextentr: new: 0x%x 0x%lx, ", nlsn, nlen));
if (alp->al_lsn + alp->al_len == nlsn) {
dprintf(("concat\n"));
alp->al_len += nlen;
} else {
dprintf(("created new leaf\n"));
AL_SET(AB_FREEALP(rabp), ralp->al_off, nlen, nlsn);
AB_ADDAL(rabp);
}
ralp->al_len -= nlen;
ralp->al_off += nlen;
} else {
kprintf("hpfs_addextentr: INTERNAL INCONSISTENCE\n");
error = (EINVAL);
goto fail;
}
}
}
if (rabp->ab_freecnt <= 0) {
struct buf *nbp;
alsec_t * nrasp;
dprintf(("hpfs_addextentr: overflow, split\n"));
error = hpfs_splitalsec (hpmp, rasp, &nrasp, &nbp);
if (error) {
kprintf("hpfs_addextent: CAN'T SPLIT\n");
goto fail;
}
if (rabp->ab_flag & AB_NODES) {
int i;
alsec_t * asp;
alnode_t * anp;
struct buf * bp;
ranp[0].an_nextoff =
AB_LASTANP(&rasp->as_ab)->an_nextoff;
AB_LASTANP(&rasp->as_ab)->an_nextoff = ~0;
anp = AB_ALNODE(&nrasp->as_ab);
for (i=0; i<nrasp->as_ab.ab_busycnt; i++) {
error = hpfs_breadalsec(hpmp, anp->an_lsn, &bp);
if (error) {
brelse(nbp);
goto fail;
}
asp = (alsec_t *)bp->b_data;
asp->as_parent = nrasp->as_self;
bdwrite(bp);
anp ++;
}
} else {
ranp[0].an_nextoff =
AB_ALLEAF(&nrasp->as_ab)->al_off;
}
ranp[0].an_lsn = rasp->as_self;
ranp[1].an_nextoff = ~0;
ranp[1].an_lsn = nrasp->as_self;
bdwrite(nbp);
*resp = AE_SPLIT;
wb = 1;
}
if (wb)
bdwrite (rbp);
else
brelse(rbp);
return (0);
fail:
brelse(rbp);
return (error);
}
int
hpfs_truncatealblk(struct hpfsmount *hpmp, alblk_t *abp, lsn_t bn, int *resp)
{
int error;
alleaf_t *alp;
alnode_t *anp;
alsec_t *asp;
struct buf *bp;
dprintf(("hpfs_truncatealblk: AlBlk: [0x%x,0x%x, 0x%x]\n",
abp->ab_freecnt, abp->ab_busycnt, abp->ab_flag));
if (abp->ab_flag & AB_NODES) {
anp = AB_LASTANP(abp);
while (abp->ab_busycnt && (bn <= anp->an_nextoff)) {
dprintf(("hpfs_truncatealblk: AlNode: [0x%x,0x%x] \n",
anp->an_nextoff,anp->an_lsn));
error = hpfs_breadalsec(hpmp, anp->an_lsn, &bp);
if (error)
return (error);
asp = (alsec_t *)bp->b_data;
error = hpfs_truncatealblk (hpmp,
&asp->as_ab, bn, resp);
if (error) {
brelse(bp);
return (error);
}
if (*resp) {
brelse (bp);
error = hpfs_bmmarkfree(hpmp,
anp->an_lsn, 1);
if (error)
return (error);
AB_RMAN(abp);
anp --;
} else {
anp->an_nextoff = ~0;
if (abp->ab_busycnt >= 2) {
alsec_t *as0p;
struct buf *b0p;
error = hpfs_breadalsec(hpmp,
(anp-1)->an_lsn, &b0p);
if (error)
return (error);
as0p = (alsec_t *)b0p->b_data;
error = hpfs_concatalsec(hpmp,
as0p, asp, anp - 1);
if (error == ENOSPC) {
brelse (b0p);
bdwrite (bp);
} else if (error == 0) {
(anp-1)->an_nextoff = anp->an_nextoff;
bdwrite (b0p);
brelse (bp);
error = hpfs_bmmarkfree(hpmp,
anp->an_lsn, 1);
if (error)
return (error);
AB_RMAN(abp);
} else {
brelse (b0p);
brelse (bp);
return (error);
}
} else {
bdwrite (bp);
}
break;
}
}
if (abp->ab_busycnt == 0)
*resp = 1;
else
*resp = 0;
} else {
alp = AB_LASTALP(abp);
while (abp->ab_busycnt && (bn < alp->al_off + alp->al_len)){
dprintf(("hpfs_truncatealblk: AlLeaf: [0x%x,0x%x,0x%x] \n",
alp->al_off,alp->al_len,alp->al_lsn));
if (bn <= alp->al_off) {
error = hpfs_bmmarkfree(hpmp, alp->al_lsn,
alp->al_len);
if (error)
return (error);
AB_RMAL(abp);
alp --;
} else if ((bn > alp->al_off) &&
(bn < alp->al_off + alp->al_len)){
error = hpfs_bmmarkfree(hpmp,
alp->al_lsn + bn - alp->al_off,
alp->al_len - bn + alp->al_off);
if (error)
return (error);
alp->al_len = bn - alp->al_off;
break;
} else
break;
}
}
if (abp->ab_busycnt == 0)
*resp = 1;
else
*resp = 0;
return (0);
}