#include "all.h"
Dentry*
getdir(Iobuf *p, int slot)
{
Dentry *d;
if(!p)
return 0;
d = (Dentry*)p->iobuf + slot%DIRPERBUF;
return d;
}
void
accessdir(Iobuf *p, Dentry *d, int f, int uid)
{
Timet t;
if(p && p->dev->type != Devro) {
p->flags |= Bmod;
t = time();
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;
send(raheadq, rb);
cons.brahead.count++;
}
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, Bread);
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, Bread);
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, Bread);
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;
}
/* 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);
}
|