jehanne/sys/src/cmd/hjfs/fs1.c

1050 lines
19 KiB
C

/* Copyright (c) 20XX 9front
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <u.h>
#include <lib9.h>
#include <thread.h>
#include <9P2000.h>
#include "dat.h"
#include "fns.h"
int
chref(Fs *fs, uint64_t r, int stat)
{
uint64_t i;
int j;
uint32_t 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("getdent: file gone, d=%llux, l=%llud/%d %llux, callerpc %#p\n",
d->path, l->blk, l->deind, l->path, getcallerpc());
werrstr("phase error -- directory entry for nonexistent file");
return nil;
}
if(qidcmp(d, l) != 0){
dprint("getdent: wrong qid d=%llux != l=%llud/%d %llux, callerpc %#p\n",
d->path, l->blk, l->deind, l->path, getcallerpc());
werrstr("phase error -- qid mismatch");
return nil;
}
return d;
}
int
getfree(Fs *fs, uint64_t *r)
{
Dev *d;
Buf *b;
uint64_t 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) {
werrstr("could not find superblock");
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, uint64_t 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)
{
uint64_t 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, 0);
if(c == nil)
goto error;
fs->root = r;
c->sb.root = r;
c->op |= BDELWRI;
putbuf(c);
}
void
writeusers(Fs *fs)
{
Chan *ch;
ch = chanattach(fs, 0);
if(ch == nil)
goto error;
ch->uid = -1;
chancreat(ch, "cfg", DMDIR | 0775, NP_OREAD);
chanclunk(ch);
ch = chanattach(fs, 0);
if(ch == nil)
goto error;
ch->uid = -1;
if(chanwalk(ch, "cfg") <= 0)
goto error;
if(chanwalk(ch, "users") > 0){
if(chanopen(ch, NP_OWRITE|NP_OTRUNC) <= 0)
goto error;
}else if(chancreat(ch, "users", 0664, NP_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");
}
void
readusers(Fs *fs)
{
Chan *ch;
ch = chanattach(fs, 0);
if(ch == nil)
goto err;
ch->uid = -1;
if(chanwalk(ch, "cfg") <= 0)
goto err;
if(chanwalk(ch, "users") <= 0)
goto err;
if(chanopen(ch, NP_OREAD) < 0)
goto err;
if(usersload(fs, ch) < 0)
goto err;
chanclunk(ch);
return;
err:
if(ch != nil)
chanclunk(ch);
dprint("readusers: %r\nhjfs: using default user db\n");
}
void
ream(Fs *fs)
{
Dev *d;
Buf *b, *c;
uint64_t i, firsti, lasti;
int j, je;
d = fs->d;
dprint("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 * REFSIZ);
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(c->refs));
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("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(uint64_t), 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("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, uint64_t *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, uint64_t 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're 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 * _, uint64_t *l)
{
uint64_t 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, uint64_t blk, uint64_t *r, int mode)
{
uint64_t k, l;
uint64_t *loc;
int i, j, rc, prc;
Buf *b;
Dentry *d;
b = bd;
d = getdent(L, b);
if(d == nil){
dprint("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("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, uint64_t *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, uint64_t *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, uint64_t size)
{
uint64_t blk;
Dentry *d;
int a[NINDIRECT];
uint64_t l;
int i, j;
d = getdent(ll, bd);
if(d == nil)
return -1;
if(size >= d->size)
goto done;
blk = HOWMANY(size);
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)
{
uint64_t i;
int j;
Dentry *d;
uint64_t 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;
uint64_t 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;
uint64_t i, si, r;
int j, sj;
Buf *c;
d = getdent(l, b);
if(d == nil)
return -1;
si = sj = -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++){
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;
}