mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
1865 lines
31 KiB
C
1865 lines
31 KiB
C
#include "stdinc.h"
|
|
#include "vac.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "error.h"
|
|
|
|
#define debug 0
|
|
|
|
/*
|
|
* locking order is upwards. A thread can hold the lock for a VacFile
|
|
* and then acquire the lock of its parent
|
|
*/
|
|
struct VacFile
|
|
{
|
|
VacFs *fs; /* immutable */
|
|
|
|
/* meta data for file: protected by the lk in the parent */
|
|
int ref; /* holds this data structure up */
|
|
|
|
int partial; /* file was never really open */
|
|
int removed; /* file has been removed */
|
|
int dirty; /* dir is dirty with respect to meta data in block */
|
|
u32int boff; /* block offset within msource for this file's metadata */
|
|
VacDir dir; /* metadata for this file */
|
|
VacFile *up; /* parent file */
|
|
VacFile *next; /* sibling */
|
|
|
|
RWLock lk; /* lock for the following */
|
|
VtFile *source; /* actual data */
|
|
VtFile *msource; /* metadata for children in a directory */
|
|
VacFile *down; /* children */
|
|
int mode;
|
|
};
|
|
|
|
static int filelock(VacFile*);
|
|
static u32int filemetaalloc(VacFile*, VacDir*, u32int);
|
|
static int filemetaflush2(VacFile*, char*);
|
|
static void filemetalock(VacFile*);
|
|
static void filemetaunlock(VacFile*);
|
|
static void fileraccess(VacFile*);
|
|
static int filerlock(VacFile*);
|
|
static void filerunlock(VacFile*);
|
|
static void fileunlock(VacFile*);
|
|
static void filewaccess(VacFile*, char*);
|
|
|
|
void mbinit(MetaBlock*, u8int*, uint, uint);
|
|
int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
|
|
int mbresize(MetaBlock*, MetaEntry*, int);
|
|
VacFile *vdlookup(VacFile*, char*);
|
|
|
|
static VacFile*
|
|
filealloc(VacFs *fs)
|
|
{
|
|
VacFile *f;
|
|
|
|
f = vtmallocz(sizeof(VacFile));
|
|
f->ref = 1;
|
|
f->fs = fs;
|
|
f->boff = NilBlock;
|
|
f->mode = fs->mode;
|
|
return f;
|
|
}
|
|
|
|
static void
|
|
filefree(VacFile *f)
|
|
{
|
|
vtfileclose(f->source);
|
|
vtfileclose(f->msource);
|
|
vdcleanup(&f->dir);
|
|
memset(f, ~0, sizeof *f); /* paranoia */
|
|
vtfree(f);
|
|
}
|
|
|
|
/*
|
|
* the file is locked already
|
|
* f->msource is unlocked
|
|
*/
|
|
static VacFile*
|
|
dirlookup(VacFile *f, char *elem)
|
|
{
|
|
int i;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
VtBlock *b;
|
|
VtFile *meta;
|
|
VacFile *ff;
|
|
u32int bo, nb;
|
|
|
|
meta = f->msource;
|
|
b = nil;
|
|
if(vtfilelock(meta, -1) < 0)
|
|
return nil;
|
|
nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
|
|
for(bo=0; bo<nb; bo++){
|
|
b = vtfileblock(meta, bo, VtOREAD);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(mbunpack(&mb, b->data, meta->dsize) < 0)
|
|
goto Err;
|
|
if(mbsearch(&mb, elem, &i, &me) >= 0){
|
|
ff = filealloc(f->fs);
|
|
if(vdunpack(&ff->dir, &me) < 0){
|
|
filefree(ff);
|
|
goto Err;
|
|
}
|
|
vtfileunlock(meta);
|
|
vtblockput(b);
|
|
ff->boff = bo;
|
|
ff->mode = f->mode;
|
|
return ff;
|
|
}
|
|
|
|
vtblockput(b);
|
|
b = nil;
|
|
}
|
|
werrstr(ENoFile);
|
|
/* fall through */
|
|
Err:
|
|
vtfileunlock(meta);
|
|
vtblockput(b);
|
|
return nil;
|
|
}
|
|
|
|
VacFile*
|
|
_vacfileroot(VacFs *fs, VtFile *r)
|
|
{
|
|
int redirected;
|
|
char err[ERRMAX];
|
|
VtBlock *b;
|
|
VtFile *r0, *r1, *r2;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
VacFile *root, *mr;
|
|
|
|
redirected = 0;
|
|
Top:
|
|
b = nil;
|
|
root = nil;
|
|
mr = nil;
|
|
r1 = nil;
|
|
r2 = nil;
|
|
|
|
if(vtfilelock(r, -1) < 0)
|
|
return nil;
|
|
r0 = vtfileopen(r, 0, fs->mode);
|
|
if(debug)
|
|
fprint(2, "r0 %p\n", r0);
|
|
if(r0 == nil)
|
|
goto Err;
|
|
r2 = vtfileopen(r, 2, fs->mode);
|
|
if(debug)
|
|
fprint(2, "r2 %p\n", r2);
|
|
if(r2 == nil){
|
|
/*
|
|
* some vac files (e.g., from fossil)
|
|
* have an extra layer of indirection.
|
|
*/
|
|
rerrstr(err, sizeof err);
|
|
if(!redirected && strstr(err, "not active")){
|
|
redirected = 1;
|
|
vtfileunlock(r);
|
|
r = r0;
|
|
goto Top;
|
|
}
|
|
goto Err;
|
|
}
|
|
r1 = vtfileopen(r, 1, fs->mode);
|
|
if(debug)
|
|
fprint(2, "r1 %p\n", r1);
|
|
if(r1 == nil)
|
|
goto Err;
|
|
|
|
mr = filealloc(fs);
|
|
mr->msource = r2;
|
|
r2 = nil;
|
|
|
|
root = filealloc(fs);
|
|
root->boff = 0;
|
|
root->up = mr;
|
|
root->source = r0;
|
|
r0 = nil;
|
|
root->msource = r1;
|
|
r1 = nil;
|
|
|
|
mr->down = root;
|
|
|
|
if(vtfilelock(mr->msource, -1) < 0)
|
|
goto Err;
|
|
b = vtfileblock(mr->msource, 0, VtOREAD);
|
|
vtfileunlock(mr->msource);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
|
|
goto Err;
|
|
|
|
meunpack(&me, &mb, 0);
|
|
if(vdunpack(&root->dir, &me) < 0)
|
|
goto Err;
|
|
vtblockput(b);
|
|
vtfileunlock(r);
|
|
fileraccess(root);
|
|
|
|
return root;
|
|
Err:
|
|
vtblockput(b);
|
|
if(r0)
|
|
vtfileclose(r0);
|
|
if(r1)
|
|
vtfileclose(r1);
|
|
if(r2)
|
|
vtfileclose(r2);
|
|
if(mr)
|
|
filefree(mr);
|
|
if(root)
|
|
filefree(root);
|
|
vtfileunlock(r);
|
|
|
|
return nil;
|
|
}
|
|
|
|
static VtFile *
|
|
fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
|
|
{
|
|
VtFile *r;
|
|
|
|
if(vtfilelock(f->source, mode) < 0)
|
|
return nil;
|
|
r = vtfileopen(f->source, offset, mode);
|
|
vtfileunlock(f->source);
|
|
if(r == nil)
|
|
return nil;
|
|
if(r->gen != gen){
|
|
werrstr(ERemoved);
|
|
goto Err;
|
|
}
|
|
if(r->dir != dir && r->mode != -1){
|
|
fprint(2, "fileopensource: dir mismatch %d %d\n", r->dir, dir);
|
|
werrstr(EBadMeta);
|
|
goto Err;
|
|
}
|
|
return r;
|
|
Err:
|
|
vtfileclose(r);
|
|
return nil;
|
|
}
|
|
|
|
VacFile*
|
|
_filewalk(VacFile *f, char *elem, int partial)
|
|
{
|
|
VacFile *ff;
|
|
|
|
fileraccess(f);
|
|
|
|
if(elem[0] == 0){
|
|
werrstr(EBadPath);
|
|
return nil;
|
|
}
|
|
|
|
if(!vacfileisdir(f)){
|
|
werrstr(ENotDir);
|
|
return nil;
|
|
}
|
|
|
|
if(strcmp(elem, ".") == 0){
|
|
return vacfileincref(f);
|
|
}
|
|
|
|
if(strcmp(elem, "..") == 0){
|
|
if(vacfileisroot(f))
|
|
return vacfileincref(f);
|
|
return vacfileincref(f->up);
|
|
}
|
|
|
|
if(filelock(f) < 0)
|
|
return nil;
|
|
|
|
for(ff = f->down; ff; ff=ff->next){
|
|
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
ff->ref++;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
ff = dirlookup(f, elem);
|
|
if(ff == nil)
|
|
goto Err;
|
|
|
|
if(ff->dir.mode & ModeSnapshot)
|
|
ff->mode = VtOREAD;
|
|
|
|
if(partial){
|
|
/*
|
|
* Do nothing. We're opening this file only so we can clri it.
|
|
* Usually the sources can't be opened, hence we won't even bother.
|
|
* Be VERY careful with the returned file. If you hand it to a routine
|
|
* expecting ff->source and/or ff->msource to be non-nil, we're
|
|
* likely to dereference nil. FileClri should be the only routine
|
|
* setting partial.
|
|
*/
|
|
ff->partial = 1;
|
|
}else if(ff->dir.mode & ModeDir){
|
|
ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
|
|
ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
|
|
if(ff->source == nil || ff->msource == nil)
|
|
goto Err;
|
|
}else{
|
|
ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
|
|
if(ff->source == nil)
|
|
goto Err;
|
|
}
|
|
|
|
/* link in and up parent ref count */
|
|
ff->next = f->down;
|
|
f->down = ff;
|
|
ff->up = f;
|
|
vacfileincref(f);
|
|
Exit:
|
|
fileunlock(f);
|
|
return ff;
|
|
Err:
|
|
fileunlock(f);
|
|
if(ff != nil)
|
|
vacfiledecref(ff);
|
|
return nil;
|
|
}
|
|
|
|
VacFile*
|
|
vacfilewalk(VacFile *f, char *elem)
|
|
{
|
|
return _filewalk(f, elem, 0);
|
|
}
|
|
|
|
VacFile*
|
|
_fileopen(VacFs *fs, char *path, int partial)
|
|
{
|
|
VacFile *f, *ff;
|
|
char *p, elem[VtMaxStringSize], *opath;
|
|
int n;
|
|
|
|
f = fs->root;
|
|
vacfileincref(f);
|
|
opath = path;
|
|
while(*path != 0){
|
|
for(p = path; *p && *p != '/'; p++)
|
|
;
|
|
n = p - path;
|
|
if(n > 0){
|
|
if(n > VtMaxStringSize){
|
|
werrstr("%s: element too long", EBadPath);
|
|
goto Err;
|
|
}
|
|
memmove(elem, path, n);
|
|
elem[n] = 0;
|
|
ff = _filewalk(f, elem, partial && *p=='\0');
|
|
if(ff == nil){
|
|
werrstr("%.*s: %R", utfnlen(opath, p-opath), opath);
|
|
goto Err;
|
|
}
|
|
vacfiledecref(f);
|
|
f = ff;
|
|
}
|
|
if(*p == '/')
|
|
p++;
|
|
path = p;
|
|
}
|
|
return f;
|
|
Err:
|
|
vacfiledecref(f);
|
|
return nil;
|
|
}
|
|
|
|
VacFile*
|
|
vacfileopen(VacFs *fs, char *path)
|
|
{
|
|
return _fileopen(fs, path, 0);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
filesettmp(VacFile *f, int istmp)
|
|
{
|
|
int i;
|
|
VtEntry e;
|
|
VtFile *r;
|
|
|
|
for(i=0; i<2; i++){
|
|
if(i==0)
|
|
r = f->source;
|
|
else
|
|
r = f->msource;
|
|
if(r == nil)
|
|
continue;
|
|
if(vtfilegetentry(r, &e) < 0){
|
|
fprint(2, "vtfilegetentry failed (cannot happen): %r\n");
|
|
continue;
|
|
}
|
|
if(istmp)
|
|
e.flags |= VtEntryNoArchive;
|
|
else
|
|
e.flags &= ~VtEntryNoArchive;
|
|
if(vtfilesetentry(r, &e) < 0){
|
|
fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
VacFile*
|
|
vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
|
|
{
|
|
VacFile *ff;
|
|
VacDir *dir;
|
|
VtFile *pr, *r, *mr;
|
|
int isdir;
|
|
|
|
if(filelock(f) < 0)
|
|
return nil;
|
|
|
|
r = nil;
|
|
mr = nil;
|
|
for(ff = f->down; ff; ff=ff->next){
|
|
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
|
|
ff = nil;
|
|
werrstr(EExists);
|
|
goto Err1;
|
|
}
|
|
}
|
|
|
|
ff = dirlookup(f, elem);
|
|
if(ff != nil){
|
|
werrstr(EExists);
|
|
goto Err1;
|
|
}
|
|
|
|
pr = f->source;
|
|
if(pr->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
goto Err1;
|
|
}
|
|
|
|
if(vtfilelock2(f->source, f->msource, -1) < 0)
|
|
goto Err1;
|
|
|
|
ff = filealloc(f->fs);
|
|
isdir = mode & ModeDir;
|
|
|
|
r = vtfilecreate(pr, pr->psize, pr->dsize, isdir ? VtDirType : VtDataType);
|
|
if(r == nil)
|
|
goto Err;
|
|
if(isdir){
|
|
mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType);
|
|
if(mr == nil)
|
|
goto Err;
|
|
}
|
|
|
|
dir = &ff->dir;
|
|
dir->elem = vtstrdup(elem);
|
|
dir->entry = r->offset;
|
|
dir->gen = r->gen;
|
|
if(isdir){
|
|
dir->mentry = mr->offset;
|
|
dir->mgen = mr->gen;
|
|
}
|
|
dir->size = 0;
|
|
if(_vacfsnextqid(f->fs, &dir->qid) < 0)
|
|
goto Err;
|
|
dir->uid = vtstrdup(uid);
|
|
dir->gid = vtstrdup(f->dir.gid);
|
|
dir->mid = vtstrdup(uid);
|
|
dir->mtime = time(0L);
|
|
dir->mcount = 0;
|
|
dir->ctime = dir->mtime;
|
|
dir->atime = dir->mtime;
|
|
dir->mode = mode;
|
|
|
|
ff->boff = filemetaalloc(f, dir, 0);
|
|
if(ff->boff == NilBlock)
|
|
goto Err;
|
|
|
|
vtfileunlock(f->source);
|
|
vtfileunlock(f->msource);
|
|
|
|
ff->source = r;
|
|
ff->msource = mr;
|
|
|
|
#if 0
|
|
if(mode&ModeTemporary){
|
|
if(vtfilelock2(r, mr, -1) < 0)
|
|
goto Err1;
|
|
filesettmp(ff, 1);
|
|
vtfileunlock(r);
|
|
if(mr)
|
|
vtfileunlock(mr);
|
|
}
|
|
#endif
|
|
|
|
/* committed */
|
|
|
|
/* link in and up parent ref count */
|
|
ff->next = f->down;
|
|
f->down = ff;
|
|
ff->up = f;
|
|
vacfileincref(f);
|
|
|
|
filewaccess(f, uid);
|
|
|
|
fileunlock(f);
|
|
return ff;
|
|
|
|
Err:
|
|
vtfileunlock(f->source);
|
|
vtfileunlock(f->msource);
|
|
Err1:
|
|
if(r){
|
|
vtfilelock(r, -1);
|
|
vtfileremove(r);
|
|
}
|
|
if(mr){
|
|
vtfilelock(mr, -1);
|
|
vtfileremove(mr);
|
|
}
|
|
if(ff)
|
|
vacfiledecref(ff);
|
|
fileunlock(f);
|
|
return nil;
|
|
}
|
|
|
|
int
|
|
vacfileblockscore(VacFile *f, u32int bn, u8int *score)
|
|
{
|
|
VtFile *s;
|
|
uvlong size;
|
|
int dsize, ret;
|
|
|
|
ret = -1;
|
|
if(filerlock(f) < 0)
|
|
return -1;
|
|
fileraccess(f);
|
|
if(vtfilelock(f->source, VtOREAD) < 0)
|
|
goto out;
|
|
|
|
s = f->source;
|
|
dsize = s->dsize;
|
|
size = vtfilegetsize(s);
|
|
if((uvlong)bn*dsize >= size)
|
|
goto out;
|
|
ret = vtfileblockscore(f->source, bn, score);
|
|
|
|
out:
|
|
vtfileunlock(f->source);
|
|
filerunlock(f);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
|
|
{
|
|
VtFile *s;
|
|
uvlong size;
|
|
u32int bn;
|
|
int off, dsize, n, nn;
|
|
VtBlock *b;
|
|
uchar *p;
|
|
|
|
if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
|
|
|
|
if(filerlock(f) < 0)
|
|
return -1;
|
|
|
|
if(offset < 0){
|
|
werrstr(EBadOffset);
|
|
goto Err1;
|
|
}
|
|
|
|
fileraccess(f);
|
|
|
|
if(vtfilelock(f->source, VtOREAD) < 0)
|
|
goto Err1;
|
|
|
|
s = f->source;
|
|
dsize = s->dsize;
|
|
size = vtfilegetsize(s);
|
|
|
|
if(offset >= size)
|
|
offset = size;
|
|
|
|
if(cnt > size-offset)
|
|
cnt = size-offset;
|
|
bn = offset/dsize;
|
|
off = offset%dsize;
|
|
p = buf;
|
|
while(cnt > 0){
|
|
b = vtfileblock(s, bn, OREAD);
|
|
if(b == nil)
|
|
goto Err;
|
|
n = cnt;
|
|
if(n > dsize-off)
|
|
n = dsize-off;
|
|
nn = dsize-off;
|
|
if(nn > n)
|
|
nn = n;
|
|
memmove(p, b->data+off, nn);
|
|
memset(p+nn, 0, nn-n);
|
|
off = 0;
|
|
bn++;
|
|
cnt -= n;
|
|
p += n;
|
|
vtblockput(b);
|
|
}
|
|
vtfileunlock(s);
|
|
filerunlock(f);
|
|
return p-(uchar*)buf;
|
|
|
|
Err:
|
|
vtfileunlock(s);
|
|
Err1:
|
|
filerunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Changes the file block bn to be the given block score.
|
|
* Very sneaky. Only used by flfmt.
|
|
*/
|
|
int
|
|
filemapblock(VacFile *f, ulong bn, uchar score[VtScoreSize], ulong tag)
|
|
{
|
|
VtBlock *b;
|
|
VtEntry e;
|
|
VtFile *s;
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
|
|
s = nil;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
|
|
if(vtfilelock(f->source, -1) < 0)
|
|
goto Err;
|
|
|
|
s = f->source;
|
|
b = _vtfileblock(s, bn, VtORDWR, 1, tag);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(vtfilegetentry(s, &e) < 0)
|
|
goto Err;
|
|
if(b->l.type == BtDir){
|
|
memmove(e.score, score, VtScoreSize);
|
|
assert(e.tag == tag || e.tag == 0);
|
|
e.tag = tag;
|
|
e.flags |= VtEntryLocal;
|
|
vtentrypack(&e, b->data, f->source->offset % f->source->epb);
|
|
}else
|
|
memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
vtfileunlock(s);
|
|
fileunlock(f);
|
|
return 0;
|
|
|
|
Err:
|
|
if(s)
|
|
vtfileunlock(s);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
vacfilesetsize(VacFile *f, uvlong size)
|
|
{
|
|
int r;
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
r = 0;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
if(vtfilelock(f->source, -1) < 0)
|
|
goto Err;
|
|
r = vtfilesetsize(f->source, size);
|
|
vtfileunlock(f->source);
|
|
Err:
|
|
fileunlock(f);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
|
|
{
|
|
VtFile *s;
|
|
ulong bn;
|
|
int off, dsize, n;
|
|
VtBlock *b;
|
|
uchar *p;
|
|
vlong eof;
|
|
|
|
if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
|
|
s = nil;
|
|
if(f->dir.mode & ModeDir){
|
|
werrstr(ENotFile);
|
|
goto Err;
|
|
}
|
|
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
goto Err;
|
|
}
|
|
if(offset < 0){
|
|
werrstr(EBadOffset);
|
|
goto Err;
|
|
}
|
|
|
|
filewaccess(f, uid);
|
|
|
|
if(vtfilelock(f->source, -1) < 0)
|
|
goto Err;
|
|
s = f->source;
|
|
dsize = s->dsize;
|
|
|
|
eof = vtfilegetsize(s);
|
|
if(f->dir.mode & ModeAppend)
|
|
offset = eof;
|
|
bn = offset/dsize;
|
|
off = offset%dsize;
|
|
p = buf;
|
|
while(cnt > 0){
|
|
n = cnt;
|
|
if(n > dsize-off)
|
|
n = dsize-off;
|
|
b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
|
|
if(b == nil){
|
|
if(offset > eof)
|
|
vtfilesetsize(s, offset);
|
|
goto Err;
|
|
}
|
|
memmove(b->data+off, p, n);
|
|
off = 0;
|
|
cnt -= n;
|
|
p += n;
|
|
offset += n;
|
|
bn++;
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
}
|
|
if(offset > eof && vtfilesetsize(s, offset) < 0)
|
|
goto Err;
|
|
vtfileunlock(s);
|
|
fileunlock(f);
|
|
return p-(uchar*)buf;
|
|
Err:
|
|
if(s)
|
|
vtfileunlock(s);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
vacfilegetdir(VacFile *f, VacDir *dir)
|
|
{
|
|
if(filerlock(f) < 0)
|
|
return -1;
|
|
|
|
filemetalock(f);
|
|
vdcopy(dir, &f->dir);
|
|
filemetaunlock(f);
|
|
|
|
if(vacfileisdir(f) < 0){
|
|
if(vtfilelock(f->source, VtOREAD) < 0){
|
|
filerunlock(f);
|
|
return -1;
|
|
}
|
|
dir->size = vtfilegetsize(f->source);
|
|
vtfileunlock(f->source);
|
|
}
|
|
filerunlock(f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vacfiletruncate(VacFile *f, char *uid)
|
|
{
|
|
if(vacfileisdir(f)){
|
|
werrstr(ENotFile);
|
|
return -1;
|
|
}
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
if(vtfilelock(f->source, -1) < 0){
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
if(vtfiletruncate(f->source) < 0){
|
|
vtfileunlock(f->source);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
vtfileunlock(f->source);
|
|
fileunlock(f);
|
|
|
|
filewaccess(f, uid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
|
|
{
|
|
VacFile *ff;
|
|
char *oelem;
|
|
u32int mask;
|
|
u64int size;
|
|
|
|
/* can not set permissions for the root */
|
|
if(vacfileisroot(f)){
|
|
werrstr(ERoot);
|
|
return -1;
|
|
}
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
filemetalock(f);
|
|
|
|
/* check new name does not already exist */
|
|
if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
for(ff = f->up->down; ff; ff=ff->next){
|
|
if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
|
|
werrstr(EExists);
|
|
goto Err;
|
|
}
|
|
}
|
|
|
|
ff = dirlookup(f->up, dir->elem);
|
|
if(ff != nil){
|
|
vacfiledecref(ff);
|
|
werrstr(EExists);
|
|
goto Err;
|
|
}
|
|
}
|
|
|
|
if(vtfilelock2(f->source, f->msource, -1) < 0)
|
|
goto Err;
|
|
if(!vacfileisdir(f)){
|
|
size = vtfilegetsize(f->source);
|
|
if(size != dir->size){
|
|
if(vtfilesetsize(f->source, dir->size) < 0){
|
|
vtfileunlock(f->source);
|
|
if(f->msource)
|
|
vtfileunlock(f->msource);
|
|
goto Err;
|
|
}
|
|
/* commited to changing it now */
|
|
}
|
|
}
|
|
/* commited to changing it now */
|
|
#if 0
|
|
if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
|
|
filesettmp(f, dir->mode&ModeTemporary);
|
|
#endif
|
|
vtfileunlock(f->source);
|
|
if(f->msource)
|
|
vtfileunlock(f->msource);
|
|
|
|
oelem = nil;
|
|
if(strcmp(f->dir.elem, dir->elem) != 0){
|
|
oelem = f->dir.elem;
|
|
f->dir.elem = vtstrdup(dir->elem);
|
|
}
|
|
|
|
if(strcmp(f->dir.uid, dir->uid) != 0){
|
|
vtfree(f->dir.uid);
|
|
f->dir.uid = vtstrdup(dir->uid);
|
|
}
|
|
|
|
if(strcmp(f->dir.gid, dir->gid) != 0){
|
|
vtfree(f->dir.gid);
|
|
f->dir.gid = vtstrdup(dir->gid);
|
|
}
|
|
|
|
f->dir.mtime = dir->mtime;
|
|
f->dir.atime = dir->atime;
|
|
|
|
//fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
|
|
mask = ~(ModeDir|ModeSnapshot);
|
|
f->dir.mode &= ~mask;
|
|
f->dir.mode |= mask & dir->mode;
|
|
f->dirty = 1;
|
|
//fprint(2, "->%x\n", f->dir.mode);
|
|
|
|
filemetaflush2(f, oelem);
|
|
vtfree(oelem);
|
|
|
|
filemetaunlock(f);
|
|
fileunlock(f);
|
|
|
|
filewaccess(f->up, uid);
|
|
|
|
return 0;
|
|
Err:
|
|
filemetaunlock(f);
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
|
|
{
|
|
int ret;
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
filemetalock(f);
|
|
f->dir.qidspace = 1;
|
|
f->dir.qidoffset = offset;
|
|
f->dir.qidmax = max;
|
|
ret = filemetaflush2(f, nil);
|
|
filemetaunlock(f);
|
|
fileunlock(f);
|
|
return ret;
|
|
}
|
|
|
|
uvlong
|
|
vacfilegetid(VacFile *f)
|
|
{
|
|
/* immutable */
|
|
return f->dir.qid;
|
|
}
|
|
|
|
ulong
|
|
vacfilegetmcount(VacFile *f)
|
|
{
|
|
ulong mcount;
|
|
|
|
filemetalock(f);
|
|
mcount = f->dir.mcount;
|
|
filemetaunlock(f);
|
|
return mcount;
|
|
}
|
|
|
|
ulong
|
|
vacfilegetmode(VacFile *f)
|
|
{
|
|
ulong mode;
|
|
|
|
filemetalock(f);
|
|
mode = f->dir.mode;
|
|
filemetaunlock(f);
|
|
return mode;
|
|
}
|
|
|
|
int
|
|
vacfileisdir(VacFile *f)
|
|
{
|
|
/* immutable */
|
|
return (f->dir.mode & ModeDir) != 0;
|
|
}
|
|
|
|
int
|
|
vacfileisroot(VacFile *f)
|
|
{
|
|
return f == f->fs->root;
|
|
}
|
|
|
|
int
|
|
vacfilegetsize(VacFile *f, uvlong *size)
|
|
{
|
|
if(filerlock(f) < 0)
|
|
return 0;
|
|
if(vtfilelock(f->source, VtOREAD) < 0){
|
|
filerunlock(f);
|
|
return -1;
|
|
}
|
|
*size = vtfilegetsize(f->source);
|
|
vtfileunlock(f->source);
|
|
filerunlock(f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
vacfilegetvtentry(VacFile *f, VtEntry *e)
|
|
{
|
|
if(filerlock(f) < 0)
|
|
return 0;
|
|
if(vtfilelock(f->source, VtOREAD) < 0){
|
|
filerunlock(f);
|
|
return -1;
|
|
}
|
|
vtfilegetentry(f->source, e);
|
|
vtfileunlock(f->source);
|
|
filerunlock(f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
vacfilemetaflush(VacFile *f, int rec)
|
|
{
|
|
VacFile **kids, *p;
|
|
int nkids;
|
|
int i;
|
|
|
|
filemetalock(f);
|
|
filemetaflush2(f, nil);
|
|
filemetaunlock(f);
|
|
|
|
if(!rec || !vacfileisdir(f))
|
|
return;
|
|
|
|
if(filelock(f) < 0)
|
|
return;
|
|
nkids = 0;
|
|
for(p=f->down; p; p=p->next)
|
|
nkids++;
|
|
kids = vtmalloc(nkids*sizeof(VacFile*));
|
|
i = 0;
|
|
for(p=f->down; p; p=p->next){
|
|
kids[i++] = p;
|
|
p->ref++;
|
|
}
|
|
fileunlock(f);
|
|
|
|
for(i=0; i<nkids; i++){
|
|
vacfilemetaflush(kids[i], 1);
|
|
vacfiledecref(kids[i]);
|
|
}
|
|
vtfree(kids);
|
|
}
|
|
|
|
/* assumes metaLock is held */
|
|
static int
|
|
filemetaflush2(VacFile *f, char *oelem)
|
|
{
|
|
VacFile *fp;
|
|
VtBlock *b, *bb;
|
|
MetaBlock mb;
|
|
MetaEntry me, me2;
|
|
int i, n;
|
|
u32int boff;
|
|
|
|
if(!f->dirty)
|
|
return 0;
|
|
|
|
if(oelem == nil)
|
|
oelem = f->dir.elem;
|
|
|
|
fp = f->up;
|
|
|
|
if(vtfilelock(fp->msource, -1) < 0)
|
|
return 0;
|
|
/* can happen if source is clri'ed out from under us */
|
|
if(f->boff == NilBlock)
|
|
goto Err1;
|
|
b = vtfileblock(fp->msource, f->boff, VtORDWR);
|
|
if(b == nil)
|
|
goto Err1;
|
|
|
|
if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
|
|
goto Err;
|
|
if(mbsearch(&mb, oelem, &i, &me) < 0)
|
|
goto Err;
|
|
|
|
n = vdsize(&f->dir);
|
|
if(0)fprint(2, "old size %d new size %d\n", me.size, n);
|
|
|
|
if(mbresize(&mb, &me, n) >= 0){
|
|
/* fits in the block */
|
|
mbdelete(&mb, i, &me);
|
|
if(strcmp(f->dir.elem, oelem) != 0)
|
|
mbsearch(&mb, f->dir.elem, &i, &me2);
|
|
vdpack(&f->dir, &me);
|
|
mbinsert(&mb, i, &me);
|
|
mbpack(&mb);
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
vtfileunlock(fp->msource);
|
|
f->dirty = 0;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* moving entry to another block
|
|
* it is feasible for the fs to crash leaving two copies
|
|
* of the directory entry. This is just too much work to
|
|
* fix. Given that entries are only allocated in a block that
|
|
* is less than PercentageFull, most modifications of meta data
|
|
* will fit within the block. i.e. this code should almost
|
|
* never be executed.
|
|
*/
|
|
boff = filemetaalloc(fp, &f->dir, f->boff+1);
|
|
if(boff == NilBlock){
|
|
/* mbResize might have modified block */
|
|
mbpack(&mb);
|
|
/* vtblockdirty(b); */
|
|
goto Err;
|
|
}
|
|
fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
|
|
f->boff = boff;
|
|
|
|
/* make sure deletion goes to disk after new entry */
|
|
bb = vtfileblock(fp->msource, f->boff, VtORDWR);
|
|
mbdelete(&mb, i, &me);
|
|
mbpack(&mb);
|
|
// blockDependency(b, bb, -1, nil, nil);
|
|
vtblockput(bb);
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
vtfileunlock(fp->msource);
|
|
|
|
f->dirty = 0;
|
|
|
|
return 0;
|
|
|
|
Err:
|
|
vtblockput(b);
|
|
Err1:
|
|
vtfileunlock(fp->msource);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
filemetaremove(VacFile *f, char *uid)
|
|
{
|
|
VtBlock *b;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
int i;
|
|
VacFile *up;
|
|
|
|
b = nil;
|
|
up = f->up;
|
|
filewaccess(up, uid);
|
|
filemetalock(f);
|
|
|
|
if(vtfilelock(up->msource, VtORDWR) < 0)
|
|
goto Err;
|
|
b = vtfileblock(up->msource, f->boff, VtORDWR);
|
|
if(b == nil)
|
|
goto Err;
|
|
|
|
if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
|
|
goto Err;
|
|
if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
|
|
goto Err;
|
|
mbdelete(&mb, i, &me);
|
|
mbpack(&mb);
|
|
vtfileunlock(up->msource);
|
|
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
|
|
f->removed = 1;
|
|
f->boff = NilBlock;
|
|
f->dirty = 0;
|
|
|
|
filemetaunlock(f);
|
|
return 0;
|
|
|
|
Err:
|
|
vtfileunlock(up->msource);
|
|
vtblockput(b);
|
|
filemetaunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
/* assume file is locked, assume f->msource is locked */
|
|
static int
|
|
filecheckempty(VacFile *f)
|
|
{
|
|
u32int i, n;
|
|
VtBlock *b;
|
|
MetaBlock mb;
|
|
VtFile *r;
|
|
|
|
r = f->msource;
|
|
n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
|
|
for(i=0; i<n; i++){
|
|
b = vtfileblock(r, i, VtORDWR);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(mbunpack(&mb, b->data, r->dsize) < 0)
|
|
goto Err;
|
|
if(mb.nindex > 0){
|
|
werrstr(ENotEmpty);
|
|
goto Err;
|
|
}
|
|
vtblockput(b);
|
|
}
|
|
return 0;
|
|
Err:
|
|
vtblockput(b);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
vacfileremove(VacFile *f, char *muid)
|
|
{
|
|
VacFile *ff;
|
|
|
|
/* can not remove the root */
|
|
if(vacfileisroot(f)){
|
|
werrstr(ERoot);
|
|
return -1;
|
|
}
|
|
|
|
if(filelock(f) < 0)
|
|
return -1;
|
|
|
|
if(f->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
goto Err1;
|
|
}
|
|
if(vtfilelock2(f->source, f->msource, -1) < 0)
|
|
goto Err1;
|
|
if(vacfileisdir(f) && filecheckempty(f)<0)
|
|
goto Err;
|
|
|
|
for(ff=f->down; ff; ff=ff->next)
|
|
assert(ff->removed);
|
|
|
|
vtfileremove(f->source);
|
|
f->source = nil;
|
|
if(f->msource){
|
|
vtfileremove(f->msource);
|
|
f->msource = nil;
|
|
}
|
|
|
|
fileunlock(f);
|
|
|
|
if(filemetaremove(f, muid) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
Err:
|
|
vtfileunlock(f->source);
|
|
if(f->msource)
|
|
vtfileunlock(f->msource);
|
|
Err1:
|
|
fileunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
clri(VacFile *f, char *uid)
|
|
{
|
|
int r;
|
|
|
|
if(f == nil)
|
|
return -1;
|
|
if(f->up->source->mode != VtORDWR){
|
|
werrstr(EReadOnly);
|
|
vacfiledecref(f);
|
|
return -1;
|
|
}
|
|
r = filemetaremove(f, uid);
|
|
vacfiledecref(f);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
vacfileclripath(VacFs *fs, char *path, char *uid)
|
|
{
|
|
return clri(_fileopen(fs, path, 1), uid);
|
|
}
|
|
|
|
int
|
|
vacfileclri(VacFile *dir, char *elem, char *uid)
|
|
{
|
|
return clri(_filewalk(dir, elem, 1), uid);
|
|
}
|
|
|
|
VacFile*
|
|
vacfileincref(VacFile *vf)
|
|
{
|
|
filemetalock(vf);
|
|
assert(vf->ref > 0);
|
|
vf->ref++;
|
|
filemetaunlock(vf);
|
|
return vf;
|
|
}
|
|
|
|
int
|
|
vacfiledecref(VacFile *f)
|
|
{
|
|
VacFile *p, *q, **qq;
|
|
|
|
if(f->up == nil){
|
|
/* never linked in */
|
|
assert(f->ref == 1);
|
|
filefree(f);
|
|
return 0;
|
|
}
|
|
|
|
filemetalock(f);
|
|
f->ref--;
|
|
if(f->ref > 0){
|
|
filemetaunlock(f);
|
|
return -1;
|
|
}
|
|
assert(f->ref == 0);
|
|
assert(f->down == nil);
|
|
|
|
filemetaflush2(f, nil);
|
|
|
|
p = f->up;
|
|
qq = &p->down;
|
|
for(q = *qq; q; q = *qq){
|
|
if(q == f)
|
|
break;
|
|
qq = &q->next;
|
|
}
|
|
assert(q != nil);
|
|
*qq = f->next;
|
|
|
|
filemetaunlock(f);
|
|
filefree(f);
|
|
vacfiledecref(p);
|
|
return 0;
|
|
}
|
|
|
|
VacFile*
|
|
vacfilegetparent(VacFile *f)
|
|
{
|
|
if(vacfileisroot(f))
|
|
return vacfileincref(f);
|
|
return vacfileincref(f->up);
|
|
}
|
|
|
|
int
|
|
vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid)
|
|
{
|
|
werrstr("read only file system");
|
|
return -1;
|
|
}
|
|
|
|
VacDirEnum*
|
|
vdeopen(VacFile *f)
|
|
{
|
|
VacDirEnum *vde;
|
|
VacFile *p;
|
|
|
|
if(!vacfileisdir(f)){
|
|
werrstr(ENotDir);
|
|
return nil;
|
|
}
|
|
|
|
/* flush out meta data */
|
|
if(filelock(f) < 0)
|
|
return nil;
|
|
for(p=f->down; p; p=p->next)
|
|
filemetaflush2(p, nil);
|
|
fileunlock(f);
|
|
|
|
vde = vtmallocz(sizeof(VacDirEnum));
|
|
vde->file = vacfileincref(f);
|
|
|
|
return vde;
|
|
}
|
|
|
|
static int
|
|
direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
|
|
{
|
|
VtBlock *b;
|
|
ulong bn;
|
|
VtEntry e;
|
|
int epb;
|
|
|
|
epb = s->dsize/VtEntrySize;
|
|
bn = elem/epb;
|
|
elem -= bn*epb;
|
|
|
|
b = vtfileblock(s, bn, VtOREAD);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(vtentryunpack(&e, b->data, elem) < 0)
|
|
goto Err;
|
|
|
|
/* hanging entries are returned as zero size */
|
|
if(!(e.flags & VtEntryActive) || e.gen != gen)
|
|
*size = 0;
|
|
else
|
|
*size = e.size;
|
|
vtblockput(b);
|
|
return 0;
|
|
|
|
Err:
|
|
vtblockput(b);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
vdefill(VacDirEnum *vde)
|
|
{
|
|
int i, n;
|
|
VtFile *meta, *source;
|
|
MetaBlock mb;
|
|
MetaEntry me;
|
|
VacFile *f;
|
|
VtBlock *b;
|
|
VacDir *de;
|
|
|
|
/* clean up first */
|
|
for(i=vde->i; i<vde->n; i++)
|
|
vdcleanup(vde->buf+i);
|
|
vtfree(vde->buf);
|
|
vde->buf = nil;
|
|
vde->i = 0;
|
|
vde->n = 0;
|
|
|
|
f = vde->file;
|
|
|
|
source = f->source;
|
|
meta = f->msource;
|
|
|
|
b = vtfileblock(meta, vde->boff, VtOREAD);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(mbunpack(&mb, b->data, meta->dsize) < 0)
|
|
goto Err;
|
|
|
|
n = mb.nindex;
|
|
vde->buf = vtmalloc(n * sizeof(VacDir));
|
|
|
|
for(i=0; i<n; i++){
|
|
de = vde->buf + i;
|
|
meunpack(&me, &mb, i);
|
|
if(vdunpack(de, &me) < 0)
|
|
goto Err;
|
|
vde->n++;
|
|
if(!(de->mode & ModeDir))
|
|
if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
|
|
goto Err;
|
|
}
|
|
vde->boff++;
|
|
vtblockput(b);
|
|
return 0;
|
|
Err:
|
|
vtblockput(b);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
vderead(VacDirEnum *vde, VacDir *de)
|
|
{
|
|
int ret, didread;
|
|
VacFile *f;
|
|
u32int nb;
|
|
|
|
f = vde->file;
|
|
if(filerlock(f) < 0)
|
|
return -1;
|
|
|
|
if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
|
|
filerunlock(f);
|
|
return -1;
|
|
}
|
|
|
|
nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
|
|
|
|
didread = 0;
|
|
while(vde->i >= vde->n){
|
|
if(vde->boff >= nb){
|
|
ret = 0;
|
|
goto Return;
|
|
}
|
|
didread = 1;
|
|
if(vdefill(vde) < 0){
|
|
ret = -1;
|
|
goto Return;
|
|
}
|
|
}
|
|
|
|
memmove(de, vde->buf + vde->i, sizeof(VacDir));
|
|
vde->i++;
|
|
ret = 1;
|
|
|
|
Return:
|
|
vtfileunlock(f->source);
|
|
vtfileunlock(f->msource);
|
|
filerunlock(f);
|
|
|
|
if(didread)
|
|
fileraccess(f);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
vdeclose(VacDirEnum *vde)
|
|
{
|
|
int i;
|
|
if(vde == nil)
|
|
return;
|
|
for(i=vde->i; i<vde->n; i++)
|
|
vdcleanup(vde->buf+i);
|
|
vtfree(vde->buf);
|
|
vacfiledecref(vde->file);
|
|
vtfree(vde);
|
|
}
|
|
|
|
/*
|
|
* caller must lock f->source and f->msource
|
|
* caller must NOT lock the source and msource
|
|
* referenced by dir.
|
|
*/
|
|
static u32int
|
|
filemetaalloc(VacFile *f, VacDir *dir, u32int start)
|
|
{
|
|
u32int nb, bo;
|
|
VtBlock *b;
|
|
MetaBlock mb;
|
|
int nn;
|
|
uchar *p;
|
|
int i, n;
|
|
MetaEntry me;
|
|
VtFile *s, *ms;
|
|
|
|
s = f->source;
|
|
ms = f->msource;
|
|
|
|
n = vdsize(dir);
|
|
nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
|
|
b = nil;
|
|
if(start > nb)
|
|
start = nb;
|
|
for(bo=start; bo<nb; bo++){
|
|
b = vtfileblock(ms, bo, VtORDWR);
|
|
if(b == nil)
|
|
goto Err;
|
|
if(mbunpack(&mb, b->data, ms->dsize) < 0)
|
|
goto Err;
|
|
nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
|
|
if(n <= nn && mb.nindex < mb.maxindex)
|
|
break;
|
|
vtblockput(b);
|
|
b = nil;
|
|
}
|
|
|
|
/* add block to meta file */
|
|
if(b == nil){
|
|
b = vtfileblock(ms, bo, VtORDWR);
|
|
if(b == nil)
|
|
goto Err;
|
|
vtfilesetsize(ms, (nb+1)*ms->dsize);
|
|
mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
|
|
}
|
|
|
|
p = mballoc(&mb, n);
|
|
if(p == nil){
|
|
/* mbAlloc might have changed block */
|
|
mbpack(&mb);
|
|
/* vtblockdirty(b); */
|
|
werrstr(EBadMeta);
|
|
goto Err;
|
|
}
|
|
|
|
mbsearch(&mb, dir->elem, &i, &me);
|
|
assert(me.p == nil);
|
|
me.p = p;
|
|
me.size = n;
|
|
vdpack(dir, &me);
|
|
mbinsert(&mb, i, &me);
|
|
mbpack(&mb);
|
|
|
|
#ifdef notdef /* XXX */
|
|
/* meta block depends on super block for qid ... */
|
|
bb = cacheLocal(b->c, PartSuper, 0, VtOREAD);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
vtblockput(bb);
|
|
|
|
/* ... and one or two dir entries */
|
|
epb = s->dsize/VtEntrySize;
|
|
bb = vtfileblock(s, dir->entry/epb, VtOREAD);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
vtblockput(bb);
|
|
if(dir->mode & ModeDir){
|
|
bb = sourceBlock(s, dir->mentry/epb, VtOREAD);
|
|
blockDependency(b, bb, -1, nil, nil);
|
|
vtblockput(bb);
|
|
}
|
|
#endif
|
|
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
return bo;
|
|
Err:
|
|
vtblockput(b);
|
|
return NilBlock;
|
|
}
|
|
|
|
static int
|
|
chksource(VacFile *f)
|
|
{
|
|
if(f->partial)
|
|
return 0;
|
|
|
|
if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
|
|
werrstr(ERemoved);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
filerlock(VacFile *f)
|
|
{
|
|
// assert(!canwlock(&f->fs->elk));
|
|
rlock(&f->lk);
|
|
if(chksource(f) < 0){
|
|
runlock(&f->lk);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
filerunlock(VacFile *f)
|
|
{
|
|
runlock(&f->lk);
|
|
}
|
|
|
|
static int
|
|
filelock(VacFile *f)
|
|
{
|
|
// assert(!canwlock(&f->fs->elk));
|
|
wlock(&f->lk);
|
|
if(chksource(f) < 0){
|
|
wunlock(&f->lk);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
fileunlock(VacFile *f)
|
|
{
|
|
wunlock(&f->lk);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
|
|
* We have to respect that ordering.
|
|
*/
|
|
static void
|
|
filemetalock(VacFile *f)
|
|
{
|
|
assert(f->up != nil);
|
|
// assert(!canwlock(&f->fs->elk));
|
|
wlock(&f->up->lk);
|
|
}
|
|
|
|
static void
|
|
filemetaunlock(VacFile *f)
|
|
{
|
|
wunlock(&f->up->lk);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* see filemetalock.
|
|
*/
|
|
static void
|
|
fileraccess(VacFile* f)
|
|
{
|
|
if(f->mode == VtOREAD)
|
|
return;
|
|
|
|
filemetalock(f);
|
|
f->dir.atime = time(0L);
|
|
f->dirty = 1;
|
|
filemetaunlock(f);
|
|
}
|
|
|
|
/*
|
|
* f->source and f->msource must NOT be locked.
|
|
* see filemetalock.
|
|
*/
|
|
static void
|
|
filewaccess(VacFile* f, char *mid)
|
|
{
|
|
if(f->mode == VtOREAD)
|
|
return;
|
|
|
|
filemetalock(f);
|
|
f->dir.atime = f->dir.mtime = time(0L);
|
|
if(strcmp(f->dir.mid, mid) != 0){
|
|
vtfree(f->dir.mid);
|
|
f->dir.mid = vtstrdup(mid);
|
|
}
|
|
f->dir.mcount++;
|
|
f->dirty = 1;
|
|
filemetaunlock(f);
|
|
|
|
/*RSC: let's try this */
|
|
/*presotto - lets not
|
|
if(f->up)
|
|
filewaccess(f->up, mid);
|
|
*/
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
markCopied(Block *b)
|
|
{
|
|
VtBlock *lb;
|
|
Label l;
|
|
|
|
if(globalToLocal(b->score) == NilBlock)
|
|
return;
|
|
|
|
if(!(b->l.state & BsCopied)){
|
|
/*
|
|
* We need to record that there are now pointers in
|
|
* b that are not unique to b. We do this by marking
|
|
* b as copied. Since we don't return the label block,
|
|
* the caller can't get the dependencies right. So we have
|
|
* to flush the block ourselves. This is a rare occurrence.
|
|
*/
|
|
l = b->l;
|
|
l.state |= BsCopied;
|
|
lb = _blockSetLabel(b, &l);
|
|
WriteAgain:
|
|
while(!blockWrite(lb)){
|
|
fprint(2, "getEntry: could not write label block\n");
|
|
sleep(10*1000);
|
|
}
|
|
while(lb->iostate != BioClean && lb->iostate != BioDirty){
|
|
assert(lb->iostate == BioWriting);
|
|
vtSleep(lb->ioready);
|
|
}
|
|
if(lb->iostate == BioDirty)
|
|
goto WriteAgain;
|
|
vtblockput(lb);
|
|
}
|
|
}
|
|
|
|
static int
|
|
getEntry(VtFile *r, Entry *e, int mark)
|
|
{
|
|
Block *b;
|
|
|
|
if(r == nil){
|
|
memset(&e, 0, sizeof e);
|
|
return 1;
|
|
}
|
|
|
|
b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtOREAD);
|
|
if(b == nil)
|
|
return 0;
|
|
if(!entryUnpack(e, b->data, r->offset % r->epb)){
|
|
vtblockput(b);
|
|
return 0;
|
|
}
|
|
|
|
if(mark)
|
|
markCopied(b);
|
|
vtblockput(b);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
setEntry(Source *r, Entry *e)
|
|
{
|
|
Block *b;
|
|
Entry oe;
|
|
|
|
b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtORDWR);
|
|
if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
|
|
if(b == nil)
|
|
return 0;
|
|
if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
|
|
vtblockput(b);
|
|
return 0;
|
|
}
|
|
e->gen = oe.gen;
|
|
entryPack(e, b->data, r->offset % r->epb);
|
|
|
|
/* BUG b should depend on the entry pointer */
|
|
|
|
markCopied(b);
|
|
/* vtblockdirty(b); */
|
|
vtblockput(b);
|
|
return 1;
|
|
}
|
|
|
|
/* assumes hold elk */
|
|
int
|
|
fileSnapshot(VacFile *dst, VacFile *src, u32int epoch, int doarchive)
|
|
{
|
|
Entry e, ee;
|
|
|
|
/* add link to snapshot */
|
|
if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
|
|
return 0;
|
|
|
|
e.snap = epoch;
|
|
e.archive = doarchive;
|
|
ee.snap = epoch;
|
|
ee.archive = doarchive;
|
|
|
|
if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileGetSources(VacFile *f, Entry *e, Entry *ee, int mark)
|
|
{
|
|
if(!getEntry(f->source, e, mark)
|
|
|| !getEntry(f->msource, ee, mark))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fileWalkSources(VacFile *f)
|
|
{
|
|
if(f->mode == VtOREAD)
|
|
return 1;
|
|
if(!sourceLock2(f->source, f->msource, VtORDWR))
|
|
return 0;
|
|
vtfileunlock(f->source);
|
|
vtfileunlock(f->msource);
|
|
return 1;
|
|
}
|
|
|
|
#endif
|