plan9fox/sys/src/cmd/hjfs/fs1.c
cinap_lenrek 9a39427183 hjfs: fix dump / create
getdent() introduced a bug as the qid check fails in
the case when we walk to DUMPROOTQID, but get ROOTQID
in the directory instead.

newentry() getblk(..., GBCREATE) caused the whole
directory to get dumped until it hit a free slot.
we cannot do this because this changes the addresses
of Dentries of files but doesnt update the loctree.

this caused the bogus walks when we hit a different
directory than we expected.

what we do now is the following: newentry() was changed
to never dump anything. it will just read the directory
and return a the address of a free slot or create a
new block having space for one. chancreat() then makes
a loc in the loctree for the free slot, drops the dirent
buffer and calls willmodify() on the new slot. this will
dump the block containing the slot (and possible other
slots) and updates the loctree to the new version.

after that, chancreate() modifies the slot filling the
Dentry. there should be no race, as newentry() skips
slots that have a loc in the loctree.

theres one case where newentry() can dump the block
immidiately when it is called from dump. added new
parameter for that and documented in the comment.

createuserdir() was simplified by just calling chancreat().
to get arround the permission check, a new per channel
flag CHFNOPERM was added.
2012-11-18 00:06:54 +01:00

1028 lines
18 KiB
C

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
int
chref(Fs *fs, uvlong r, int stat)
{
uvlong i;
int j;
ulong rc;
Buf *c;
i = fs->fstart + r / REFPERBLK;
j = r % REFPERBLK;
c = getbuf(fs->d, i, TREF, 0);
if(c == nil)
return -1;
if(stat < 0 && c->refs[j] < -stat)
c->refs[j] = 0;
else
c->refs[j] += stat;
rc = c->refs[j];
if(stat != 0)
c->op |= BDELWRI;
putbuf(c);
return rc;
}
static int
qidcmp(Qid *a, Qid *b)
{
if(a->type != b->type)
return 1;
if(a->path != b->path){
/* special case for dump, this is ok */
if(a->path==ROOTQID && b->path==DUMPROOTQID)
return 0;
return 1;
}
return 0;
}
Dentry*
getdent(FLoc *l, Buf *b)
{
Dentry *d;
d = &b->de[l->deind];
if((d->mode & (DGONE | DALLOC)) == 0){
dprint("hjfs: getdent: file gone, d=%llux, l=%llud/%d %llux, callerpc %#p\n",
d->path, l->blk, l->deind, l->path, getcallerpc(&l));
werrstr("phase error -- getdent");
return nil;
}
if(qidcmp(d, l) != 0){
dprint("hjfs: getdent: wrong qid d=%llux != l=%llud/%d %llux, callerpc %#p\n",
d->path, l->blk, l->deind, l->path, getcallerpc(&l));
werrstr("phase error -- getdent");
return nil;
}
return d;
}
int
getfree(Fs *fs, uvlong *r)
{
Dev *d;
Buf *b;
uvlong i, l, e;
int j, have;
d = fs->d;
while(nbrecv(fs->freelist, &l) > 0){
i = fs->fstart + l / REFPERBLK;
j = l % REFPERBLK;
b = getbuf(d, i, TREF, 0);
if(b != nil){
if(b->refs[j] == 0){
b->refs[j] = 1;
*r = l;
goto found;
}
putbuf(b);
}
}
b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0);
if(b == nil)
return -1;
e = b->sb.fend;
putbuf(b);
have = 0;
for(l = 0, i = fs->fstart; i < e; i++){
b = getbuf(d, i, TREF, 0);
if(b == nil){
l += REFPERBLK;
continue;
}
for(j = 0; j < REFPERBLK; j++, l++)
if(b->refs[j] == 0){
if(!have){
b->refs[j] = 1;
*r = l;
have = 1;
}
else if(nbsend(fs->freelist, &l) <= 0)
goto found;
}
if(have)
goto found;
putbuf(b);
}
werrstr("disk full");
return -1;
found:
b->op |= BDELWRI;
putbuf(b);
return 1;
}
int
putfree(Fs *fs, uvlong r)
{
int rc;
rc = chref(fs, r, -1);
if(rc < 0)
return -1;
if(rc == 0)
nbsend(fs->freelist, &r);
return 1;
}
static void
createroot(Fs *fs)
{
uvlong r;
Buf *c;
Dentry *d;
if(getfree(fs, &r) < 0)
sysfatal("ream: getfree: %r");
c = getbuf(fs->d, r, TDENTRY, 1);
if(c == nil)
error:
sysfatal("ream: getbuf: %r");
memset(c->de, 0, sizeof(c->de));
d = &c->de[0];
strcpy(d->name, "/");
d->mode = DALLOC | 0775;
d->path = ROOTQID;
d->type = QTDIR;
d->uid = -1;
d->muid = -1;
d->gid = -1;
d->mtime = time(0);
d->atime = d->mtime;
d++;
strcpy(d->name, "/");
d->mode = DALLOC | 0775;
d->path = ROOTQID;
d->type = QTDIR;
d->uid = -1;
d->muid = -1;
d->gid = -1;
d->mtime = time(0);
d->atime = d->mtime;
c->op |= BWRIM;
putbuf(c);
c = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 1);
if(c == nil)
goto error;
fs->root = r;
c->sb.root = r;
putbuf(c);
}
void
writeusers(Fs *fs)
{
Chan *ch;
ch = chanattach(fs, 0);
if(ch == nil)
goto error;
ch->uid = -1;
chancreat(ch, "adm", DMDIR | 0775, OREAD);
chanclunk(ch);
ch = chanattach(fs, 0);
if(ch == nil)
goto error;
ch->uid = -1;
if(chanwalk(ch, "adm") <= 0)
goto error;
if(chanwalk(ch, "users") > 0){
if(chanopen(ch, OWRITE|OTRUNC) <= 0)
goto error;
}else if(chancreat(ch, "users", 0664, OWRITE) <= 0)
goto error;
if(userssave(fs, ch) < 0){
chanremove(ch);
ch = nil;
goto error;
}
chanclunk(ch);
return;
error:
if(ch != nil)
chanclunk(ch);
dprint("writeusers: %r\n");
}
static void
readusers(Fs *fs)
{
Chan *ch;
ch = chanattach(fs, 0);
if(ch == nil)
goto err;
ch->uid = -1;
if(chanwalk(ch, "adm") <= 0)
goto err;
if(chanwalk(ch, "users") <= 0)
goto err;
if(chanopen(ch, OREAD) < 0)
goto err;
if(usersload(fs, ch) < 0)
goto err;
chanclunk(ch);
return;
err:
if(ch != nil)
chanclunk(ch);
dprint("hjfs: readusers: %r\nhjfs: using default user db\n");
}
void
ream(Fs *fs)
{
Dev *d;
Buf *b, *c;
uvlong i, firsti, lasti;
int j, je;
d = fs->d;
dprint("hjfs: reaming %s\n", d->name);
b = getbuf(d, SUPERBLK, TSUPERBLOCK, 1);
if(b == nil)
err:
sysfatal("ream: getbuf: %r");
memset(&b->sb, 0, sizeof(b->sb));
b->sb.magic = SUPERMAGIC;
b->sb.size = d->size;
b->sb.fstart = SUPERBLK + 1;
fs->fstart = b->sb.fstart;
b->sb.fend = b->sb.fstart + HOWMANY(b->sb.size * 3, RBLOCK);
b->sb.qidpath = DUMPROOTQID + 1;
firsti = b->sb.fstart + SUPERBLK / REFPERBLK;
lasti = b->sb.fstart + b->sb.fend / REFPERBLK;
for(i = b->sb.fstart; i < b->sb.fend; i++){
c = getbuf(d, i, TREF, 1);
if(c == nil)
goto err;
memset(c->refs, 0, sizeof(b->data));
if(i >= firsti && i <= lasti){
j = 0;
je = REFPERBLK;
if(i == firsti)
j = SUPERBLK % REFPERBLK;
if(i == lasti)
je = b->sb.fend % REFPERBLK;
for(; j < je; j++)
c->refs[j] = 1;
}
if(i == b->sb.fend - 1){
j = b->sb.size % REFPERBLK;
if(j != 0)
for(; j < REFPERBLK; j++)
c->refs[j] = -1;
}
c->op |= BWRIM;
putbuf(c);
}
b->op |= BDELWRI;
putbuf(b);
createroot(fs);
sync(1);
dprint("hjfs: ream successful\n");
}
Fs *
initfs(Dev *d, int doream, int flags)
{
Fs *fs;
Buf *b;
FLoc f;
fs = emalloc(sizeof(*fs));
fs->d = d;
if(doream)
ream(fs);
b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0);
if(b == nil || b->sb.magic != SUPERMAGIC)
goto error;
fs->root = b->sb.root;
fs->fstart = b->sb.fstart;
fs->freelist = chancreate(sizeof(uvlong), FREELISTLEN);
f.blk = fs->root;
f.deind = 0;
f.path = ROOTQID;
f.vers = 0;
f.type = QTDIR;
fs->rootloc = getloc(fs, f, nil);
f.deind++;
f.path = DUMPROOTQID;
fs->dumprootloc = getloc(fs, f, nil);
putbuf(b);
fs->flags = flags;
if(doream)
writeusers(fs);
readusers(fs);
dprint("hjfs: fs is %s\n", d->name);
return fs;
error:
if(b != nil)
putbuf(b);
free(fs);
return nil;
}
void
chbegin(Chan *ch)
{
if((ch->flags & CHFNOLOCK) == 0)
rlock(ch->fs);
}
void
chend(Chan *ch)
{
if((ch->flags & CHFNOLOCK) == 0)
runlock(ch->fs);
}
int
newqid(Fs *fs, uvlong *q)
{
Buf *b;
b = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 0);
if(b == nil)
return -1;
*q = b->sb.qidpath++;
b->op |= BDELWRI;
putbuf(b);
return 1;
}
Loc *
getloc(Fs *fs, FLoc f, Loc *next)
{
Loc *l;
qlock(&fs->loctree);
if(next != nil && next->child != nil){
l = next->child;
do{
if(l->blk == f.blk && l->deind == f.deind){
l->ref++;
qunlock(&fs->loctree);
return l;
}
l = l->cnext;
}while(l != next->child);
}
l = emalloc(sizeof(*l));
l->ref = 1;
l->FLoc = f;
l->next = next;
if(fs->rootloc != nil){
l->gnext = fs->rootloc;
l->gprev = l->gnext->gprev;
l->gnext->gprev = l;
l->gprev->gnext = l;
}else
l->gnext = l->gprev = l;
l->cprev = l->cnext = l;
if(next != nil){
if(next->child == nil)
next->child = l;
else{
l->cnext = next->child;
l->cprev = next->child->cprev;
l->cnext->cprev = l;
l->cprev->cnext = l;
}
}
qunlock(&fs->loctree);
return l;
}
int
haveloc(Fs *fs, uvlong blk, int deind, Loc *next)
{
Loc *l;
qlock(&fs->loctree);
l = next->child;
if(l != nil) do{
if(l->blk == blk && l->deind == deind){
qunlock(&fs->loctree);
return 1;
}
l = l->cnext;
} while(l != next->child);
qunlock(&fs->loctree);
return 0;
}
Loc *
cloneloc(Fs *fs, Loc *l)
{
Loc *m;
qlock(&fs->loctree);
for(m = l; m != nil; m = m->next)
m->ref++;
qunlock(&fs->loctree);
return l;
}
void
putloc(Fs *fs, Loc *l, int loop)
{
Loc *m;
Buf *b;
qlock(&fs->loctree);
if(!loop && --l->ref <= 0)
goto freeit;
while(loop && l != nil && l->ref <= 1){
freeit:
if((l->flags & LGONE) != 0){
/*
* safe to unlock here, the file is gone and
* we'r the last reference.
*/
qunlock(&fs->loctree);
b = getbuf(fs->d, l->blk, TDENTRY, 0);
if(b != nil){
delete(fs, l, b);
putbuf(b);
}
qlock(&fs->loctree);
}
l->cnext->cprev = l->cprev;
l->cprev->cnext = l->cnext;
l->gnext->gprev = l->gprev;
l->gprev->gnext = l->gnext;
if(l->next != nil && l->next->child == l){
if(l->cnext == l)
l->next->child = nil;
else
l->next->child = l->cnext;
}
m = l->next;
free(l);
l = m;
}
for(; loop && l != nil; l = l->next)
--l->ref;
qunlock(&fs->loctree);
}
static int
dumpblk(Fs *fs, FLoc *, uvlong *l)
{
uvlong n;
int i;
Buf *b, *c;
Dentry *d;
b = getbuf(fs->d, *l, TDONTCARE, 0);
if(b == nil)
return -1;
if(getfree(fs, &n) < 0){
berr:
putbuf(b);
return -1;
}
c = getbuf(fs->d, n, b->type, 1);
if(c == nil){
putfree(fs, n);
goto berr;
}
switch(b->type){
case TINDIR:
memcpy(c->offs, b->offs, sizeof(b->offs));
for(i = 0; i < OFFPERBLK; i++)
if(b->offs[i] != 0)
chref(fs, b->offs[i], 1);
break;
case TRAW:
memcpy(c->data, b->data, sizeof(b->data));
break;
case TDENTRY:
memcpy(c->de, b->de, sizeof(b->de));
for(d = b->de; d < &b->de[DEPERBLK]; d++){
if((d->mode & DALLOC) == 0)
continue;
if((d->type & QTTMP) != 0)
continue;
for(i = 0; i < NDIRECT; i++)
if(d->db[i] != 0)
chref(fs, d->db[i], 1);
for(i = 0; i < NINDIRECT; i++)
if(d->ib[i] != 0)
chref(fs, d->ib[i], 1);
}
break;
default:
werrstr("dumpblk -- unknown type %d", b->type);
putfree(fs, n);
putbuf(c);
putbuf(b);
return -1;
}
c->op |= BDELWRI;
putbuf(c);
putbuf(b);
putfree(fs, *l);
*l = n;
return 0;
}
/*
* getblk returns the address of a block in a file
* given its relative address blk
* the address are returned in *r
* mode has to be one of:
* - GBREAD: this block will only be read
* - GBWRITE: this block will be written, but don't create it
* if it doesn't exist
* - GBCREATE: this block will be written, create it if necessary
* - GBOVERWR: like GBCREATE, but return an empty block if a dump
* would be necessary
* return value is 1 if the block existed, -1 on error
* a return value of 0 means the block did not exist
* this is only an error in case of GBREAD and GBWRITE
*/
int
getblk(Fs *fs, FLoc *L, Buf *bd, uvlong blk, uvlong *r, int mode)
{
uvlong k, l;
uvlong *loc;
int i, j, rc, prc;
Buf *b;
Dentry *d;
b = bd;
d = getdent(L, b);
if(d == nil){
dprint("hjfs: getblk: dirent gone\n");
return -1;
}
if(blk < NDIRECT){
loc = &d->db[blk];
goto found;
}
blk -= NDIRECT;
l = 1;
for(i = 0; i < NINDIRECT; i++){
l *= OFFPERBLK;
if(blk < l)
break;
blk -= l;
}
if(i == NINDIRECT){
werrstr("getblk: block offset too large");
return -1;
}
loc = &d->ib[i];
for(j = 0; j <= i; j++){
if(*loc == 0){
if(mode == GBREAD || mode == GBWRITE){
if(b != bd)
putbuf(b);
return 0;
}
if(getfree(fs, loc) < 0){
if(b != bd)
putbuf(b);
return -1;
}
b->op |= BDELWRI;
k = *loc;
if(b != bd)
putbuf(b);
b = getbuf(fs->d, k, TINDIR, 1);
if(b == nil)
return -1;
memset(b->offs, 0, sizeof(b->offs));
b->op |= BDELWRI;
}else{
if(mode != GBREAD && chref(fs, *loc, 0) > 1){
if(dumpblk(fs, L, loc) < 0){
if(b != bd)
putbuf(b);
return -1;
}
b->op |= BDELWRI;
}
k = *loc;
if(b != bd)
putbuf(b);
b = getbuf(fs->d, k, TINDIR, 0);
if(b == nil)
return -1;
}
l /= OFFPERBLK;
loc = &b->offs[blk / l];
blk %= l;
}
found:
rc = 0;
prc = 0;
if(*loc != 0){
if(mode == GBREAD)
goto okay;
if((rc = chref(fs, *loc, 0)) > 1){
if(mode == GBOVERWR){
putfree(fs, *loc);
*loc = 0;
b->op |= BDELWRI;
prc = 1;
goto new;
}
if(dumpblk(fs, L, loc) < 0){
rc = -1;
goto end;
}
b->op |= BDELWRI;
}
if(rc < 0)
goto end;
if(rc == 0){
dprint("hjfs: getblk: block %lld has refcount 0\n");
werrstr("phase error -- getblk");
rc = -1;
goto end;
}
okay:
*r = *loc;
rc = 1;
}else if(mode == GBCREATE || mode == GBOVERWR){
new:
rc = getfree(fs, r);
if(rc > 0){
*loc = *r;
b->op |= BDELWRI;
rc = prc;
}
}
end:
if(b != bd)
putbuf(b);
return rc;
}
static void
delindir(Fs *fs, uvlong *l, int n)
{
Buf *b;
int k;
if(*l == 0)
return;
if(chref(fs, *l, 0) > 1){
*l = 0;
return;
}
if(n > 0){
b = getbuf(fs->d, *l, TINDIR, 0);
if(b != nil){
for(k = 0; k < OFFPERBLK; k++)
if(b->offs[k] != 0){
delindir(fs, &b->offs[k], n-1);
b->op |= BDELWRI;
}
putbuf(b);
}
}
putfree(fs, *l);
*l = 0;
}
static int
zeroes(int *a, int i)
{
while(i--)
if(*a++ != 0)
return 0;
return 1;
}
static void
delindirpart(Fs *fs, FLoc *p, uvlong *l, int *a, int n)
{
Buf *b;
int k;
if(*l == 0)
return;
if(n == 0){
putfree(fs, *l);
*l = 0;
return;
}
if(zeroes(a, n)){
delindir(fs, l, n);
return;
}
if(chref(fs, *l, 0) > 1)
dumpblk(fs, p, l);
b = getbuf(fs->d, *l, TINDIR, 0);
if(b == nil)
return;
delindirpart(fs, p, &b->offs[*a], a + 1, n - 1);
for(k = a[0] + 1; k < OFFPERBLK; k++)
if(b->offs[k] != 0)
delindir(fs, &b->offs[k], n - 1);
b->op |= BDELWRI;
putbuf(b);
}
/*
* call willmodify() before and modified()
* after calling this function
*/
int
trunc(Fs *fs, FLoc *ll, Buf *bd, uvlong size)
{
uvlong blk;
Dentry *d;
int a[NINDIRECT];
uvlong l;
int i, j;
d = getdent(ll, bd);
if(d == nil)
return -1;
if(size >= d->size)
goto done;
blk = HOWMANY(size, RBLOCK);
while(blk < NDIRECT){
if(d->db[blk] != 0){
putfree(fs, d->db[blk]);
d->db[blk] = 0;
bd->op |= BDELWRI;
}
blk++;
}
blk -= NDIRECT;
l = 1;
for(i = 0; i < NINDIRECT; i++){
l *= OFFPERBLK;
if(blk < l)
break;
blk -= l;
}
if(blk >= l){
werrstr("phase error -- truncate");
return -1;
}
if(blk == 0)
goto rest;
if(d->ib[i] == 0){
i++;
goto rest;
}
for(j = 0; j <= i; j++){
l /= OFFPERBLK;
a[j] = blk / l;
blk %= l;
}
delindirpart(fs, ll, &d->ib[i], a, i + 1);
i++;
rest:
for(; i < NINDIRECT; i++)
delindir(fs, &d->ib[i], i + 1);
done:
d->size = size;
bd->op |= BDELWRI;
return 1;
}
/*
* find a direntry
* name == nil allows any entry to match
* rl == nil allowed
* return value 1 on success, 0 on Enoent,
* -1 on other errors
*/
int
findentry(Fs *fs, FLoc *l, Buf *b, char *name, FLoc *rl, int dump)
{
uvlong i;
int j;
Dentry *d;
uvlong r;
Buf *c;
d = getdent(l, b);
if(d == nil)
return -1;
for(i = 0; i < d->size; i++){
if(getblk(fs, l, b, i, &r, GBREAD) <= 0)
continue;
c = getbuf(fs->d, r, TDENTRY, 0);
if(c == nil)
continue;
for(j = 0; j < DEPERBLK; j++)
if((c->de[j].mode & DALLOC) != 0 &&
(name == nil || strcmp(c->de[j].name, name) == 0)){
if(dump && (c->de[j].type & QTTMP) != 0)
continue;
if(rl != nil){
rl->Qid = c->de[j].Qid;
rl->blk = r;
rl->deind = j;
}
putbuf(c);
return 1;
}
putbuf(c);
}
werrstr(Enoent);
return 0;
}
void
modified(Chan *ch, Dentry *d)
{
d->mtime = time(0);
d->atime = d->mtime;
d->muid = ch->uid;
ch->loc->vers = ++d->vers;
}
typedef struct Del Del;
struct Del {
FLoc;
Del *next, *prev;
};
static int
deltraverse(Fs *fs, Del *p, Buf *b, Del **last)
{
Buf *c;
int frb;
Dentry *d;
uvlong i, s, r;
int j, rc;
Del *dd;
frb = b == nil;
if(frb){
b = getbuf(fs->d, p->blk, TDENTRY, 0);
if(b == nil)
return -1;
}
d = getdent(p, b);
if(d == nil){
if(frb)
putbuf(b);
return -1;
}
s = d->size;
for(i = 0; i < s; i++){
rc = getblk(fs, p, b, i, &r, GBREAD);
if(rc <= 0)
continue;
c = getbuf(fs->d, r, TDENTRY, 0);
if(c == nil)
continue;
for(j = 0; j < DEPERBLK; j++){
d = &c->de[j];
if((d->mode & (DALLOC|DGONE)) != 0){
if((d->type & QTDIR) == 0){
trunc(fs, p, b, 0);
memset(d, 0, sizeof(*d));
c->op |= BDELWRI;
}else{
dd = emalloc(sizeof(Del));
dd->blk = i;
dd->deind = j;
dd->Qid = d->Qid;
dd->prev = *last;
(*last)->next = dd;
*last = dd;
}
}
}
putbuf(c);
}
if(frb)
putbuf(b);
return 0;
}
int
delete(Fs *fs, FLoc *l, Buf *b)
{
Dentry *d;
Buf *c;
Del *first, *last, *p, *q;
d = getdent(l, b);
if(d == nil)
return -1;
if((d->type & QTDIR) == 0){
trunc(fs, l, b, 0);
memset(d, 0, sizeof(*d));
b->op |= BDELWRI;
return 0;
}
first = last = emalloc(sizeof(Del));
first->FLoc = *l;
for(p = first; p != nil; p = p->next)
deltraverse(fs, p, p == first ? b : nil, &last);
for(p = last; p != nil; q = p->prev, free(p), p = q){
if(p == first)
c = b;
else
c = getbuf(fs->d, p->blk, TDENTRY, 0);
if(c == nil)
continue;
d = getdent(p, c);
if(d != nil){
trunc(fs, p, c, 0);
memset(d, 0, sizeof(*d));
c->op |= BDELWRI;
}
if(p != first)
putbuf(c);
}
return 0;
}
/*
* newentry() looks for a free slot in the directory
* and returns FLoc pointing to the slot. if no free
* slot is available a new block is allocated. if
* dump == 0, then the resulting blk from the FLoc
* *is not* dumped, so to finally allocate the Dentry,
* one has to call willmodify() on res before modyfing it.
*/
int
newentry(Fs *fs, Loc *l, Buf *b, char *name, FLoc *res, int dump)
{
Dentry *d, *dd;
uvlong i, si, r;
int j, sj;
Buf *c;
si = sj = -1;
d = getdent(l, b);
if(d != nil){
for(i = 0; i < d->size; i++){
if(getblk(fs, l, b, i, &r, GBREAD) <= 0)
continue;
c = getbuf(fs->d, r, TDENTRY, 0);
if(c == nil)
continue;
for(j = 0; j < DEPERBLK; j++){
dd = &c->de[j];
if((dd->mode & DGONE) != 0)
continue;
if((dd->mode & DALLOC) != 0){
if(strcmp(dd->name, name) == 0){
werrstr(Eexists);
putbuf(c);
return 0;
}
continue;
}
if(si != -1 || haveloc(fs, r, j, l))
continue;
si = i;
sj = j;
}
putbuf(c);
}
if(si == -1 && i == d->size){
if(getblk(fs, l, b, i, &r, GBCREATE) >= 0){
c = getbuf(fs->d, r, TDENTRY, 1);
if(c != nil){
si = i;
sj = 0;
d->size = i+1;
b->op |= BDELWRI;
memset(c->de, 0, sizeof(c->de));
c->op |= BDELWRI;
putbuf(c);
}
}
}
}
if(si == -1 || sj == -1){
werrstr("phase error -- create");
return -1;
}
if(getblk(fs, l, b, si, &res->blk, dump != 0 ? GBWRITE : GBREAD) <= 0)
return -1;
res->deind = sj;
res->Qid = (Qid){0, 0, 0};
return 1;
}