plan9port/src/cmd/vac/file.c

1835 lines
31 KiB
C
Raw Normal View History

#include "stdinc.h"
#include "vac.h"
#include "dat.h"
#include "fns.h"
#include "error.h"
/*
* locking order is upwards. A thread can hold the lock for a VacFile
* and then acquire the lock of its parent
*/
2004-03-15 01:56:49 +00:00
struct VacFile
{
VacFs *fs; /* immutable */
/* meta data for file: protected by the lk in the parent */
2004-03-15 01:56:49 +00:00
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;
};
2004-03-15 01:56:49 +00:00
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)
{
2004-03-15 01:56:49 +00:00
VacFile *f;
f = vtmallocz(sizeof(VacFile));
f->ref = 1;
f->fs = fs;
f->boff = NilBlock;
f->mode = fs->mode;
return f;
}
static void
2004-03-15 01:56:49 +00:00
filefree(VacFile *f)
{
2004-03-15 01:56:49 +00:00
vtfileclose(f->source);
vtfileclose(f->msource);
vdcleanup(&f->dir);
memset(f, ~0, sizeof *f); /* paranoia */
vtfree(f);
}
2004-03-15 01:56:49 +00:00
/*
* the file is locked already
* f->msource is unlocked
*/
static VacFile*
dirlookup(VacFile *f, char *elem)
{
int i;
MetaBlock mb;
MetaEntry me;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
if(mbunpack(&mb, b->data, meta->dsize) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
if(mbsearch(&mb, elem, &i, &me) < 0){
ff = filealloc(f->fs);
if(vdunpack(&ff->dir, &me) < 0){
filefree(ff);
goto Err;
}
2004-03-15 01:56:49 +00:00
vtfileunlock(meta);
vtblockput(b);
ff->boff = bo;
ff->mode = f->mode;
return ff;
}
2004-03-15 01:56:49 +00:00
vtblockput(b);
b = nil;
}
2004-03-15 01:56:49 +00:00
werrstr(ENoFile);
/* fall through */
Err:
2004-03-15 01:56:49 +00:00
vtfileunlock(meta);
vtblockput(b);
return nil;
}
2004-03-15 01:56:49 +00:00
VacFile*
_vacfileroot(VacFs *fs, VtFile *r)
{
2004-03-15 01:56:49 +00:00
VtBlock *b;
VtFile *r0, *r1, *r2;
MetaBlock mb;
MetaEntry me;
VacFile *root, *mr;
2004-03-15 01:56:49 +00:00
b = nil;
root = nil;
mr = nil;
r1 = nil;
r2 = nil;
2004-03-15 01:56:49 +00:00
if(vtfilelock(r, -1) < 0)
return nil;
r0 = vtfileopen(r, 0, fs->mode);
if(r0 == nil)
goto Err;
2004-03-15 01:56:49 +00:00
r1 = vtfileopen(r, 1, fs->mode);
if(r1 == nil)
goto Err;
r2 = vtfileopen(r, 2, fs->mode);
if(r2 == nil)
goto Err;
2004-03-15 01:56:49 +00:00
mr = filealloc(fs);
mr->msource = r2;
r2 = nil;
2004-03-15 01:56:49 +00:00
root = filealloc(fs);
root->boff = 0;
root->up = mr;
root->source = r0;
r0 = nil;
root->msource = r1;
r1 = nil;
mr->down = root;
2004-03-15 01:56:49 +00:00
if(vtfilelock(mr->msource, -1) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
b = vtfileblock(mr->msource, 0, VtOREAD);
vtfileunlock(mr->msource);
if(b == nil)
goto Err;
2004-03-15 01:56:49 +00:00
if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
meunpack(&me, &mb, 0);
if(vdunpack(&root->dir, &me) < 0)
goto Err;
vtblockput(b);
vtfileunlock(r);
fileraccess(root);
return root;
Err:
2004-03-15 01:56:49 +00:00
vtblockput(b);
if(r0)
2004-03-15 01:56:49 +00:00
vtfileclose(r0);
if(r1)
2004-03-15 01:56:49 +00:00
vtfileclose(r1);
if(r2)
2004-03-15 01:56:49 +00:00
vtfileclose(r2);
if(mr)
2004-03-15 01:56:49 +00:00
filefree(mr);
if(root)
2004-03-15 01:56:49 +00:00
filefree(root);
vtfileunlock(r);
return nil;
}
static VtFile *
fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
{
VtFile *r;
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
VacFile*
_filewalk(VacFile *f, char *elem, int partial)
{
2004-03-15 01:56:49 +00:00
VacFile *ff;
2004-03-15 01:56:49 +00:00
fileraccess(f);
2004-03-15 01:56:49 +00:00
if(elem[0] == 0){
werrstr(EBadPath);
return nil;
}
2004-03-15 01:56:49 +00:00
if(!vacfileisdir(f)){
werrstr(ENotDir);
return nil;
}
2004-03-15 01:56:49 +00:00
if(strcmp(elem, ".") == 0){
return vacfileincref(f);
}
2004-03-15 01:56:49 +00:00
if(strcmp(elem, "..") == 0){
if(vacfileisroot(f))
return vacfileincref(f);
return vacfileincref(f->up);
}
2004-03-15 01:56:49 +00:00
if(filelock(f) < 0)
return nil;
2004-03-15 01:56:49 +00:00
for(ff = f->down; ff; ff=ff->next){
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
ff->ref++;
goto Exit;
}
}
2004-03-15 01:56:49 +00:00
ff = dirlookup(f, elem);
if(ff == nil)
goto Err;
2004-03-15 01:56:49 +00:00
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 */
2004-03-15 01:56:49 +00:00
ff->next = f->down;
f->down = ff;
ff->up = f;
vacfileincref(f);
Exit:
2004-03-15 01:56:49 +00:00
fileunlock(f);
return ff;
Err:
2004-03-15 01:56:49 +00:00
fileunlock(f);
if(ff != nil)
vacfiledecref(ff);
return nil;
}
2004-03-15 01:56:49 +00:00
VacFile*
vacfilewalk(VacFile *f, char *elem)
{
2004-03-15 01:56:49 +00:00
return _filewalk(f, elem, 0);
}
VacFile*
_fileopen(VacFs *fs, char *path, int partial)
{
VacFile *f, *ff;
char *p, elem[VtMaxStringSize], *opath;
int n;
2004-03-15 01:56:49 +00:00
f = fs->root;
vacfileincref(f);
opath = path;
while(*path != 0){
for(p = path; *p && *p != '/'; p++)
;
n = p - path;
2004-03-15 01:56:49 +00:00
if(n > 0){
if(n > VtMaxStringSize){
werrstr("%s: element too long", EBadPath);
goto Err;
}
memmove(elem, path, n);
elem[n] = 0;
2004-03-15 01:56:49 +00:00
ff = _filewalk(f, elem, partial && *p=='\0');
if(ff == nil){
werrstr("%.*s: %R", utfnlen(opath, p-opath), opath);
goto Err;
2004-03-15 01:56:49 +00:00
}
vacfiledecref(f);
f = ff;
}
if(*p == '/')
p++;
path = p;
}
2004-03-15 01:56:49 +00:00
return f;
Err:
2004-03-15 01:56:49 +00:00
vacfiledecref(f);
return nil;
}
2004-03-15 01:56:49 +00:00
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)
{
2004-03-15 01:56:49 +00:00
VacFile *ff;
VacDir *dir;
2004-03-15 01:56:49 +00:00
VtFile *pr, *r, *mr;
int isdir;
2004-03-15 01:56:49 +00:00
if(filelock(f) < 0)
return nil;
r = nil;
mr = nil;
2004-03-15 01:56:49 +00:00
for(ff = f->down; ff; ff=ff->next){
if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
ff = nil;
werrstr(EExists);
goto Err1;
}
}
2004-03-15 01:56:49 +00:00
ff = dirlookup(f, elem);
if(ff != nil){
werrstr(EExists);
goto Err1;
}
pr = f->source;
if(pr->mode != VtORDWR){
werrstr(EReadOnly);
goto Err1;
}
2004-03-15 01:56:49 +00:00
if(vtfilelock2(f->source, f->msource, -1) < 0)
goto Err1;
ff = filealloc(f->fs);
isdir = mode & ModeDir;
2004-03-15 01:56:49 +00:00
r = vtfilecreate(pr, pr->dsize, isdir, 0);
if(r == nil)
goto Err;
2004-03-15 01:56:49 +00:00
if(isdir){
mr = vtfilecreate(pr, pr->dsize, 0, r->offset);
if(mr == nil)
goto Err;
}
2004-03-15 01:56:49 +00:00
dir = &ff->dir;
dir->elem = vtstrdup(elem);
dir->entry = r->offset;
dir->gen = r->gen;
2004-03-15 01:56:49 +00:00
if(isdir){
dir->mentry = mr->offset;
dir->mgen = mr->gen;
}
dir->size = 0;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
ff->boff = filemetaalloc(f, dir, 0);
if(ff->boff == NilBlock)
goto Err;
2004-03-15 01:56:49 +00:00
vtfileunlock(f->source);
vtfileunlock(f->msource);
ff->source = r;
ff->msource = mr;
2004-03-15 01:56:49 +00:00
#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 */
2004-03-15 01:56:49 +00:00
ff->next = f->down;
f->down = ff;
ff->up = f;
vacfileincref(f);
2004-03-15 01:56:49 +00:00
filewaccess(f, uid);
2004-03-15 01:56:49 +00:00
fileunlock(f);
return ff;
Err:
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
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
2004-03-15 01:56:49 +00:00
vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
{
2004-03-15 01:56:49 +00:00
VtFile *s;
uvlong size;
2004-03-15 01:56:49 +00:00
u32int bn;
int off, dsize, n, nn;
2004-03-15 01:56:49 +00:00
VtBlock *b;
uchar *p;
2004-03-15 01:56:49 +00:00
if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
2004-03-15 01:56:49 +00:00
if(filerlock(f) < 0)
return -1;
2004-03-15 01:56:49 +00:00
if(offset < 0){
werrstr(EBadOffset);
goto Err1;
}
2004-03-15 01:56:49 +00:00
fileraccess(f);
2004-03-15 01:56:49 +00:00
if(vtfilelock(f->source, VtOREAD) < 0)
goto Err1;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
p = buf;
while(cnt > 0){
b = vtfileblock(s, bn, OREAD);
if(b == nil)
goto Err;
n = cnt;
if(n > dsize-off)
n = dsize-off;
2004-03-15 01:56:49 +00:00
nn = dsize-off;
if(nn > n)
nn = n;
2004-03-15 01:56:49 +00:00
memmove(p, b->data+off, nn);
memset(p+nn, 0, nn-n);
off = 0;
bn++;
cnt -= n;
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
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:
2004-03-15 01:56:49 +00:00
if(s)
vtfileunlock(s);
fileunlock(f);
return -1;
}
2004-03-15 01:56:49 +00:00
#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
2004-03-15 01:56:49 +00:00
filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
{
2004-03-15 01:56:49 +00:00
VtFile *s;
ulong bn;
int off, dsize, n;
2004-03-15 01:56:49 +00:00
VtBlock *b;
uchar *p;
vlong eof;
2004-03-15 01:56:49 +00:00
if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
2004-03-15 01:56:49 +00:00
if(filelock(f) < 0)
return -1;
2004-03-15 01:56:49 +00:00
s = nil;
if(f->dir.mode & ModeDir){
werrstr(ENotFile);
goto Err;
}
2004-03-15 01:56:49 +00:00
if(f->source->mode != VtORDWR){
werrstr(EReadOnly);
goto Err;
}
2004-03-15 01:56:49 +00:00
if(offset < 0){
werrstr(EBadOffset);
goto Err;
}
2004-03-15 01:56:49 +00:00
filewaccess(f, uid);
if(vtfilelock(f->source, -1) < 0)
goto Err;
s = f->source;
dsize = s->dsize;
2004-03-15 01:56:49 +00:00
eof = vtfilegetsize(s);
if(f->dir.mode & ModeAppend)
offset = eof;
bn = offset/dsize;
off = offset%dsize;
2004-03-15 01:56:49 +00:00
p = buf;
while(cnt > 0){
n = cnt;
if(n > dsize-off)
n = dsize-off;
2004-03-15 01:56:49 +00:00
b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
if(b == nil){
if(offset > eof)
vtfilesetsize(s, offset);
goto Err;
}
2004-03-15 01:56:49 +00:00
memmove(b->data+off, p, n);
off = 0;
cnt -= n;
2004-03-15 01:56:49 +00:00
p += n;
offset += n;
bn++;
2004-03-15 01:56:49 +00:00
vtblockdirty(b);
vtblockput(b);
}
2004-03-15 01:56:49 +00:00
if(offset > eof && vtfilesetsize(s, offset) < 0)
goto Err;
vtfileunlock(s);
fileunlock(f);
return p-(uchar*)buf;
Err:
2004-03-15 01:56:49 +00:00
if(s)
vtfileunlock(s);
fileunlock(f);
return -1;
}
int
2004-03-15 01:56:49 +00:00
vacfilegetdir(VacFile *f, VacDir *dir)
{
2004-03-15 01:56:49 +00:00
if(filerlock(f) < 0)
return -1;
2004-03-15 01:56:49 +00:00
filemetalock(f);
vdcopy(dir, &f->dir);
filemetaunlock(f);
2004-03-15 01:56:49 +00:00
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
2004-03-15 01:56:49 +00:00
vacfilegetid(VacFile *f)
{
/* immutable */
2004-03-15 01:56:49 +00:00
return f->dir.qid;
}
ulong
2004-03-15 01:56:49 +00:00
vacfilegetmcount(VacFile *f)
{
ulong mcount;
2004-03-15 01:56:49 +00:00
filemetalock(f);
mcount = f->dir.mcount;
filemetaunlock(f);
return mcount;
}
2004-03-15 01:56:49 +00:00
ulong
vacfilegetmode(VacFile *f)
{
ulong mode;
filemetalock(f);
mode = f->dir.mode;
filemetaunlock(f);
return mode;
}
int
2004-03-15 01:56:49 +00:00
vacfileisdir(VacFile *f)
{
/* immutable */
2004-03-15 01:56:49 +00:00
return (f->dir.mode & ModeDir) != 0;
}
int
2004-03-15 01:56:49 +00:00
vacfileisroot(VacFile *f)
{
2004-03-15 01:56:49 +00:00
return f == f->fs->root;
}
int
2004-03-15 01:56:49 +00:00
vacfilegetsize(VacFile *f, uvlong *size)
{
2004-03-15 01:56:49 +00:00
if(filerlock(f) < 0)
return 0;
2004-03-15 01:56:49 +00:00
if(vtfilelock(f->source, VtOREAD) < 0){
filerunlock(f);
return -1;
}
*size = vtfilegetsize(f->source);
vtfileunlock(f->source);
filerunlock(f);
2004-03-15 01:56:49 +00:00
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);
}
2004-03-15 01:56:49 +00:00
/* assumes metaLock is held */
static int
2004-03-15 01:56:49 +00:00
filemetaflush2(VacFile *f, char *oelem)
{
2004-03-15 01:56:49 +00:00
VacFile *fp;
VtBlock *b, *bb;
MetaBlock mb;
2004-03-15 01:56:49 +00:00
MetaEntry me, me2;
int i, n;
u32int boff;
2004-03-15 01:56:49 +00:00
if(!f->dirty)
return 0;
2004-03-15 01:56:49 +00:00
if(oelem == nil)
oelem = f->dir.elem;
2004-03-15 01:56:49 +00:00
fp = f->up;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
if(mbsearch(&mb, oelem, &i, &me) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
}
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;
2004-03-15 01:56:49 +00:00
Err:
vtblockput(b);
Err1:
vtfileunlock(fp->msource);
return -1;
}
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
/* assume file is locked, assume f->msource is locked */
static int
2004-03-15 01:56:49 +00:00
filecheckempty(VacFile *f)
{
2004-03-15 01:56:49 +00:00
u32int i, n;
VtBlock *b;
MetaBlock mb;
2004-03-15 01:56:49 +00:00
VtFile *r;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
if(mbunpack(&mb, b->data, r->dsize) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
if(mb.nindex > 0){
werrstr(ENotEmpty);
goto Err;
}
2004-03-15 01:56:49 +00:00
vtblockput(b);
}
return 0;
2004-03-15 01:56:49 +00:00
Err:
vtblockput(b);
return -1;
}
int
2004-03-15 01:56:49 +00:00
vacfileremove(VacFile *f, char *muid)
{
VacFile *ff;
/* can not remove the root */
2004-03-15 01:56:49 +00:00
if(vacfileisroot(f)){
werrstr(ERoot);
return -1;
}
2004-03-15 01:56:49 +00:00
if(filelock(f) < 0)
return -1;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
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;
2004-03-15 01:56:49 +00:00
Err:
vtfileunlock(f->source);
if(f->msource)
vtfileunlock(f->msource);
Err1:
fileunlock(f);
return -1;
}
2004-03-15 01:56:49 +00:00
static int
clri(VacFile *f, char *uid)
{
2004-03-15 01:56:49 +00:00
int r;
2004-03-15 01:56:49 +00:00
if(f == nil)
return -1;
if(f->up->source->mode != VtORDWR){
werrstr(EReadOnly);
vacfiledecref(f);
return -1;
}
2004-03-15 01:56:49 +00:00
r = filemetaremove(f, uid);
vacfiledecref(f);
return r;
}
2004-03-15 01:56:49 +00:00
int
vacfileclripath(VacFs *fs, char *path, char *uid)
{
return clri(_fileopen(fs, path, 1), uid);
}
int
2004-03-15 01:56:49 +00:00
vacfileclri(VacFile *dir, char *elem, char *uid)
{
2004-03-15 01:56:49 +00:00
return clri(_filewalk(dir, elem, 1), uid);
}
2004-03-15 01:56:49 +00:00
VacFile*
vacfileincref(VacFile *vf)
{
filemetalock(vf);
assert(vf->ref > 0);
vf->ref++;
filemetaunlock(vf);
return vf;
}
int
2004-03-15 01:56:49 +00:00
vacfiledecref(VacFile *f)
{
2004-03-15 01:56:49 +00:00
VacFile *p, *q, **qq;
2004-03-15 01:56:49 +00:00
if(f->up == nil){
/* never linked in */
assert(f->ref == 1);
filefree(f);
return 0;
2004-03-15 01:56:49 +00:00
}
2004-03-15 01:56:49 +00:00
filemetalock(f);
f->ref--;
if(f->ref > 0){
filemetaunlock(f);
return -1;
}
2004-03-15 01:56:49 +00:00
assert(f->ref == 0);
assert(f->down == nil);
2004-03-15 01:56:49 +00:00
filemetaflush2(f, nil);
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
VacFile*
filegetparent(VacFile *f)
{
2004-03-15 01:56:49 +00:00
if(vacfileisroot(f))
return vacfileincref(f);
return vacfileincref(f->up);
}
2004-03-15 01:56:49 +00:00
VacDirEnum*
vdeopen(VacFile *f)
{
2004-03-15 01:56:49 +00:00
VacDirEnum *vde;
VacFile *p;
2004-03-15 01:56:49 +00:00
if(!vacfileisdir(f)){
werrstr(ENotDir);
return nil;
}
2004-03-15 01:56:49 +00:00
/* flush out meta data */
if(filelock(f) < 0)
return nil;
2004-03-15 01:56:49 +00:00
for(p=f->down; p; p=p->next)
filemetaflush2(p, nil);
fileunlock(f);
2004-03-15 01:56:49 +00:00
vde = vtmallocz(sizeof(VacDirEnum));
vde->file = vacfileincref(f);
2004-03-15 01:56:49 +00:00
return vde;
}
static int
2004-03-15 01:56:49 +00:00
direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
{
2004-03-15 01:56:49 +00:00
VtBlock *b;
ulong bn;
VtEntry e;
2004-03-15 01:56:49 +00:00
int epb;
2004-03-15 01:56:49 +00:00
epb = s->dsize/VtEntrySize;
bn = elem/epb;
elem -= bn*epb;
2004-03-15 01:56:49 +00:00
b = vtfileblock(s, bn, VtOREAD);
if(b == nil)
goto Err;
2004-03-15 01:56:49 +00:00
if(vtentryunpack(&e, b->data, elem) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
/* 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:
2004-03-15 01:56:49 +00:00
vtblockput(b);
return -1;
}
2004-03-15 01:56:49 +00:00
static int
vdefill(VacDirEnum *vde)
{
2004-03-15 01:56:49 +00:00
int i, n;
VtFile *meta, *source;
MetaBlock mb;
MetaEntry me;
2004-03-15 01:56:49 +00:00
VacFile *f;
VtBlock *b;
VacDir *de;
2004-03-15 01:56:49 +00:00
/* 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;
2004-03-15 01:56:49 +00:00
f = vde->file;
2004-03-15 01:56:49 +00:00
source = f->source;
meta = f->msource;
b = vtfileblock(meta, vde->boff, VtOREAD);
if(b == nil)
goto Err;
2004-03-15 01:56:49 +00:00
if(mbunpack(&mb, b->data, meta->dsize) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
vde->boff++;
vtblockput(b);
return 0;
Err:
2004-03-15 01:56:49 +00:00
vtblockput(b);
return -1;
}
2004-03-15 01:56:49 +00:00
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
2004-03-15 01:56:49 +00:00
vdeclose(VacDirEnum *vde)
{
2004-03-15 01:56:49 +00:00
int i;
if(vde == nil)
return;
2004-03-15 01:56:49 +00:00
for(i=vde->i; i<vde->n; i++)
vdcleanup(vde->buf+i);
vtfree(vde->buf);
vacfiledecref(vde->file);
vtfree(vde);
}
2004-03-15 01:56:49 +00:00
/*
* 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)
{
2004-03-15 01:56:49 +00:00
u32int nb, bo;
2005-01-04 22:17:58 +00:00
VtBlock *b;
MetaBlock mb;
2004-03-15 01:56:49 +00:00
int nn;
uchar *p;
2005-01-04 22:17:58 +00:00
int i, n;
2004-03-15 01:56:49 +00:00
MetaEntry me;
VtFile *s, *ms;
2004-03-15 01:56:49 +00:00
s = f->source;
ms = f->msource;
n = vdsize(dir);
nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
b = nil;
if(start > nb)
start = nb;
2004-03-15 01:56:49 +00:00
for(bo=start; bo<nb; bo++){
b = vtfileblock(ms, bo, VtORDWR);
if(b == nil)
goto Err;
2004-03-15 01:56:49 +00:00
if(mbunpack(&mb, b->data, ms->dsize) < 0)
goto Err;
2004-03-15 01:56:49 +00:00
nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
if(n <= nn && mb.nindex < mb.maxindex)
break;
2004-03-15 01:56:49 +00:00
vtblockput(b);
b = nil;
}
2004-03-15 01:56:49 +00:00
/* add block to meta file */
2004-03-15 01:56:49 +00:00
if(b == nil){
b = vtfileblock(ms, bo, VtORDWR);
if(b == nil)
goto Err;
2004-03-15 01:56:49 +00:00
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;
}
2004-03-15 01:56:49 +00:00
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:
2004-03-15 01:56:49 +00:00
vtblockput(b);
return NilBlock;
}
2004-03-15 01:56:49 +00:00
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