![cinap_lenrek](/assets/img/avatar_default.png)
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.
1028 lines
18 KiB
C
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;
|
|
}
|