Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "all.h"

Dentry*
getdir(Iobuf *p, int slot)
{
        if(!p)
                return 0;
        return (Dentry*)p->iobuf + slot%DIRPERBUF;
}

void
accessdir(Iobuf *p, Dentry *d, int f, int uid)
{
        Timet t;

        if(p && p->dev->type != Devro) {
                p->flags |= Bmod;
                t = time(nil);
                if(f & (FREAD|FWRITE))
                        d->atime = t;
                if(f & FWRITE) {
                        d->mtime = t;
                        d->muid = uid;
                        d->qid.version++;
                }
        }
}

void
preread(Device *d, Off addr)
{
        Rabuf *rb;

        if(addr == 0)
                return;
        if(raheadq->count+10 >= raheadq->size)  /* ugly knowing layout */
                return;
        lock(&rabuflock);
        rb = rabuffree;
        if(rb == 0) {
                unlock(&rabuflock);
                return;
        }
        rabuffree = rb->link;
        unlock(&rabuflock);
        rb->dev = d;
        rb->addr = addr;
        fs_send(raheadq, rb);
}

Off
rel2abs(Iobuf *p, Dentry *d, Off a, int tag, int putb, int uid)
{
        int i;
        Off addr, qpath, indaddrs = 1, div;
        Device *dev;

        if(a < 0) {
                print("rel2abs: neg offset\n");
                if(putb)
                        putbuf(p);
                return 0;
        }
        dev = p->dev;
        qpath = d->qid.path;

        /* is `a' a direct block? */
        if(a < NDBLOCK) {
                addr = d->dblock[a];
                if(!addr && tag) {
                        addr = bufalloc(dev, tag, qpath, uid);
                        d->dblock[a] = addr;
                        p->flags |= Bmod|Bimm;
                }
                if(putb)
                        putbuf(p);
                return addr;
        }
        a -= NDBLOCK;

        /*
         * loop through indirect block depths.
         */
        for (i = 0; i < NIBLOCK; i++) {
                indaddrs *= INDPERBUF;
                /* is a's disk addr in this indir block or one of its kids? */
                if (a < indaddrs) {
                        addr = d->iblocks[i];
                        if(!addr && tag) {
                                addr = bufalloc(dev, Tind1+i, qpath, uid);
                                d->iblocks[i] = addr;
                                p->flags |= Bmod|Bimm;
                        }
                        if(putb)
                                putbuf(p);

                        div = indaddrs;
                        for (; i >= 0; i--) {
                                div /= INDPERBUF;
                                if (div <= 0)
                                        panic("rel2abs: non-positive divisor");
                                addr = indfetch(dev, qpath, addr,
                                        (a/div)%INDPERBUF, Tind1+i,
                                        (i == 0? tag: Tind1+i-1), uid);
                        }
                        return addr;
                }
                a -= indaddrs;
        }
        if(putb)
                putbuf(p);

        /* quintuple-indirect blocks not implemented. */
        print("rel2abs: no %d-deep indirect\n", NIBLOCK+1);
        return 0;
}

/*
 * read-ahead strategy
 * on second block, read RAGAP blocks,
 * thereafter, read RAGAP ahead of current pos
 */
Off
dbufread(Iobuf *p, Dentry *d, Off a, Off ra, int uid)
{
        Off addr;

        if(a == 0)
                return 1;
        if(a == 1 && ra == 1) {
                while(ra < a+RAGAP) {
                        ra++;
                        addr = rel2abs(p, d, ra, 0, 0, uid);
                        if(!addr)
                                return 0;
                        preread(p->dev, addr);
                }
                return ra+1;
        }
        if(ra == a+RAGAP) {
                addr = rel2abs(p, d, ra, 0, 0, uid);
                if(!addr)
                        return 0;
                preread(p->dev, addr);
                return ra+1;
        }
        return ra;
}

Iobuf*
dnodebuf(Iobuf *p, Dentry *d, Off a, int tag, int uid)
{
        Off addr;

        addr = rel2abs(p, d, a, tag, 0, uid);
        if(addr)
                return getbuf(p->dev, addr, Brd);
        return 0;
}

/*
 * same as dnodebuf but it calls putbuf(p)
 * to reduce interference.
 */
Iobuf*
dnodebuf1(Iobuf *p, Dentry *d, Off a, int tag, int uid)
{
        Off addr;
        Device *dev;

        dev = p->dev;
        addr = rel2abs(p, d, a, tag, 1, uid);
        if(addr)
                return getbuf(dev, addr, Brd);
        return 0;

}

Off
indfetch(Device* d, Off qpath, Off addr, Off a, int itag, int tag, int uid)
{
        Iobuf *bp;

        if(!addr)
                return 0;
        bp = getbuf(d, addr, Brd);
        if(!bp || checktag(bp, itag, qpath)) {
                if(!bp) {
                        print("ind fetch bp = 0\n");
                        return 0;
                }
                print("ind fetch tag\n");
                putbuf(bp);
                return 0;
        }
        addr = ((Off *)bp->iobuf)[a];
        if(!addr && tag) {
                addr = bufalloc(d, tag, qpath, uid);
                if(addr) {
                        ((Off *)bp->iobuf)[a] = addr;
                        bp->flags |= Bmod;
                        if(tag == Tdir)
                                bp->flags |= Bimm;
                        settag(bp, itag, qpath);
                }
        }
        putbuf(bp);
        return addr;
}

/* return INDPERBUF^exp */
Off
ibbpow(int exp)
{
        static Off pows[] = {
                1,
                INDPERBUF,
                (Off)INDPERBUF*INDPERBUF,
                (Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF,
                (Off)INDPERBUF*(Off)INDPERBUF*(Off)INDPERBUF*INDPERBUF,
        };

        if (exp < 0)
                return 0;
        else if (exp >= nelem(pows)) {  /* not in table? do it long-hand */
                Off indpow = 1;

                while (exp-- > 0 && indpow > 0)
                        indpow *= INDPERBUF;
                return indpow;
        } else
                return pows[exp];
}

/* return sum of INDPERBUF^n for 1 ≤ n ≤ exp */
Off
ibbpowsum(int exp)
{
        Off indsum = 0;

        for (; exp > 0; exp--)
                indsum += ibbpow(exp);
        return indsum;
}

/* zero bytes past new file length; return an error code */
int
trunczero(Truncstate *ts)
{
        int blkoff = ts->newsize % BUFSIZE;
        Iobuf *pd;

        pd = dnodebuf(ts->p, ts->d, ts->lastblk, Tfile, ts->uid);
        if (pd == nil || checktag(pd, Tfile, QPNONE)) {
                if (pd != nil)
                        putbuf(pd);
                ts->err = Ephase;
                return Ephase;
        }
        memset(pd->iobuf+blkoff, 0, BUFSIZE - blkoff);
        putbuf(pd);
        return 0;
}

/*
 * truncate d (in p) to length `newsize'.
 * if larger, just increase size.
 * if smaller, deallocate blocks after last one
 * still in file at new size.  last byte to keep
 * is newsize-1, due to zero origin.
 * we free in forward order because it's simpler to get right.
 * if the final block at the new size is partially-filled,
 * zero the remainder.
 */
int
dtrunclen(Iobuf *p, Dentry *d, Off newsize, int uid)
{
        int i, pastlast;
        Truncstate trunc;

        if (newsize <= 0) {
                dtrunc(p, d, uid);
                return 0;
        }
        memset(&trunc, 0, sizeof trunc);
        trunc.d = d;
        trunc.p = p;
        trunc.uid = uid;
        trunc.newsize = newsize;
        trunc.lastblk = newsize/BUFSIZE;
        if (newsize % BUFSIZE == 0)
                trunc.lastblk--;
        else
                trunczero(&trunc);
        for (i = 0; i < NDBLOCK; i++)
                if (trunc.pastlast) {
                        trunc.relblk = i;
                        buffree(p->dev, d->dblock[i], 0, &trunc);
                        d->dblock[i] = 0;
                } else if (i == trunc.lastblk)
                        trunc.pastlast = 1;
        trunc.relblk = NDBLOCK;
        for (i = 0; i < NIBLOCK; i++) {
                pastlast = trunc.pastlast;
                buffree(p->dev, d->iblocks[i], i+1, &trunc);
                if (pastlast)
                        d->iblocks[i] = 0;
        }

        d->size = newsize;
        p->flags |= Bmod|Bimm;
        accessdir(p, d, FWRITE, uid);
        return trunc.err;
}

/*
 * truncate d (in p) to zero length.
 * freeing blocks in reverse order is traditional, from Unix,
 * in an attempt to keep the free list contiguous.
 */
void
dtrunc(Iobuf *p, Dentry *d, int uid)
{
        int i;

        for (i = NIBLOCK-1; i >= 0; i--) {
                buffree(p->dev, d->iblocks[i], i+1, nil);
                d->iblocks[i] = 0;
        }
        for (i = NDBLOCK-1; i >= 0; i--) {
                buffree(p->dev, d->dblock[i], 0, nil);
                d->dblock[i] = 0;
        }
        d->size = 0;
        p->flags |= Bmod|Bimm;
        accessdir(p, d, FWRITE, uid);
}