2104 lines · 38.8 KB · C
RawDownload
#include "stdinc.h"#include "vac.h"#include "dat.h"#include "fns.h"#include "error.h" #define debug 0 /* * Vac file system.  This is a simplified version of the same code in Fossil. *  * The locking order in the tree is upward: a thread can hold the lock * for a VacFile and then acquire the lock of f->up (the parent), * but not vice-versa. *  * A vac file is one or two venti files.  Plain data files are one venti file, * while directores are two: a venti data file containing traditional * directory entries, and a venti directory file containing venti  * directory entries.  The traditional directory entries in the data file * contain integers indexing into the venti directory entry file. * It's a little complicated, but it makes the data usable by standard * tools like venti/copy. * */ static int filemetaflush(VacFile*, char*); 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;		uvlong	qidoffset;	/* qid offset */}; 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 voidfilefree(VacFile *f){	vtfileclose(f->source);	vtfileclose(f->msource);	vdcleanup(&f->dir);	memset(f, ~0, sizeof *f);	/* paranoia */	vtfree(f);} static intchksource(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 intfilelock(VacFile *f){	wlock(&f->lk);	if(chksource(f) < 0){		wunlock(&f->lk);		return -1;	}	return 0;} static voidfileunlock(VacFile *f){	wunlock(&f->lk);} static intfilerlock(VacFile *f){	rlock(&f->lk);	if(chksource(f) < 0){		runlock(&f->lk);		return -1;	}	return 0;} static voidfilerunlock(VacFile *f){	runlock(&f->lk);} /* * The file metadata, like f->dir and f->ref, * are synchronized via the parent's lock. * This is why locking order goes up. */static voidfilemetalock(VacFile *f){	assert(f->up != nil);	wlock(&f->up->lk);} static voidfilemetaunlock(VacFile *f){	wunlock(&f->up->lk);} uvlongvacfilegetid(VacFile *f){	/* immutable */	return f->qidoffset + f->dir.qid;} uvlongvacfilegetqidoffset(VacFile *f){	return f->qidoffset;} ulongvacfilegetmcount(VacFile *f){	ulong mcount; 	filemetalock(f);	mcount = f->dir.mcount;	filemetaunlock(f);	return mcount;} ulongvacfilegetmode(VacFile *f){	ulong mode; 	filemetalock(f);	mode = f->dir.mode;	filemetaunlock(f);	return mode;} intvacfileisdir(VacFile *f){	/* immutable */	return (f->dir.mode & ModeDir) != 0;} intvacfileisroot(VacFile *f){	return f == f->fs->root;} /* * The files are reference counted, and while the reference * is bigger than zero, each file can be found in its parent's * f->down list (chains via f->next), so that multiple threads * end up sharing a VacFile* when referring to the same file. * * Each VacFile holds a reference to its parent. */VacFile*vacfileincref(VacFile *vf){	filemetalock(vf);	assert(vf->ref > 0);	vf->ref++;	filemetaunlock(vf);	return vf;} intvacfiledecref(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); 	if(f->source && vtfilelock(f->source, -1) >= 0){		vtfileflush(f->source);		vtfileunlock(f->source);	}	if(f->msource && vtfilelock(f->msource, -1) >= 0){		vtfileflush(f->msource);		vtfileunlock(f->msource);	} 	/*	 * Flush f's directory information to the cache.	 */	filemetaflush(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;}  /*  * Construct a vacfile for the root of a vac tree, given the  * venti file for the root information.  That venti file is a  * directory file containing VtEntries for three more venti files: * the two venti files making up the root directory, and a  * third venti file that would be the metadata half of the  * "root's parent". * * Fossil generates slightly different vac files, due to a now * impossible-to-change bug, which contain a VtEntry * for just one venti file, that itself contains the expected * three directory entries.  Sigh. */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;	vtfileunlock(r); 	if(vtfilelock(mr->msource, VtOREAD) < 0)		goto Err1;	b = vtfileblock(mr->msource, 0, VtOREAD);	vtfileunlock(mr->msource);	if(b == nil)		goto Err1; 	if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)		goto Err1; 	meunpack(&me, &mb, 0);	if(vdunpack(&root->dir, &me) < 0)		goto Err1;	vtblockput(b); 	return root;Err:	vtfileunlock(r);Err1:	vtblockput(b);	if(r0)		vtfileclose(r0);	if(r1)		vtfileclose(r1);	if(r2)		vtfileclose(r2);	if(mr)		filefree(mr);	if(root)		filefree(root); 	return nil;} /* * Vac directories are a sequence of metablocks, each of which * contains a bunch of metaentries sorted by file name. * The whole sequence isn't sorted, though, so you still have * to look at every block to find a given name. * Dirlookup looks in f for an element name elem. * It returns a new VacFile with the dir, boff, and mode * filled in, but the sources (venti files) are not, and f is  * not yet linked into the tree.  These details must be taken * care of by the caller. * * f must be locked, f->msource must not. */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;			}			ff->qidoffset = f->qidoffset + ff->dir.qidoffset;			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;} /* * Open the venti file at offset in the directory f->source. * f is locked. */static VtFile *fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode){	VtFile *r; 	if((r = vtfileopen(f->source, offset, mode)) == nil)		return nil;	if(r == nil)		return nil;	if(r->gen != gen){		werrstr(ERemoved);		vtfileclose(r);		return nil;	}	if(r->dir != dir && r->mode != -1){		werrstr(EBadMeta);		vtfileclose(r);		return nil;	}	return r;} VacFile*vacfilegetparent(VacFile *f){	if(vacfileisroot(f))		return vacfileincref(f);	return vacfileincref(f->up);} /* * Given an unlocked vacfile (directory) f, * return the vacfile named elem in f. * Interprets . and .. as a convenience to callers. */VacFile*vacfilewalk(VacFile *f, char *elem){	VacFile *ff; 	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)		return vacfilegetparent(f); 	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)

Lines 1–500 of 2104.