709 lines · 12.0 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * I/O for a Wiki document set. *  * The files are kept in one flat directory. * There are three files for each document: *	nnn	- current version of the document *	nnn.hist	- history (all old versions) of the document *		append-only *	L.nnn	- write lock file for the document *  * At the moment, since we don't have read/write locks * in the file system, we use the L.nnn file as a read lock too. * It's a hack but there aren't supposed to be many readers * anyway. * * The nnn.hist file is in the format read by Brdwhist. * The nnn file is in that format too, but only contains the * last entry of the nnn.hist file. * * In addition to this set of files, there is an append-only * map file that provides a mapping between numbers and titles. * The map file is a sequence of lines of the form *	nnn Title Here * The lock file L.map must be held to add to the end, to * make sure that the numbers are allocated sequentially. *  * We assume that writes to the map file will fit in one message, * so that we don't have to read-lock the file. */ #include <u.h>#include <libc.h>#include <bio.h>#include <String.h>#include <thread.h>#include "wiki.h" enum {	Nhash = 64,	Mcache = 128,}; typedef struct Wcache Wcache;struct Wcache {	int n;	ulong use;	RWLock;	ulong tcurrent;	ulong thist;	Whist *hist;	Whist *current;	Qid qid;	Qid qidhist;	Wcache *hash;}; static RWLock cachelock;static Wcache *tab[Nhash];int ncache; voidclosewhist(Whist *wh){	int i; 	if(wh && decref(wh) == 0){		free(wh->title);		for(i=0; i<wh->ndoc; i++){			free(wh->doc[i].author);			free(wh->doc[i].comment);			freepage(wh->doc[i].wtxt);		}		free(wh->doc);		free(wh);	}} voidfreepage(Wpage *p){	Wpage *next; 	for(; p; p=next){		next = p->next;		free(p->text);		free(p->url);		free(p);	}} static Wcache*findcache(int n){	Wcache *w; 	for(w=tab[n%Nhash]; w; w=w->hash)		if(w->n == n){			w->use = time(0);			return w;		}	return nil;} static intgetlock(char *lock){	char buf[ERRMAX];	int i, fd;	enum { SECS = 200 }; 	for(i=0; i<SECS*10; i++){		fd = wcreate(lock, ORDWR, DMEXCL|0666);		if(fd >= 0)			return fd;		buf[0] = '\0';		rerrstr(buf, sizeof buf);		if(strstr(buf, "locked") == nil)			break;		sleep(1000/10);	}	werrstr("couldn't acquire lock %s: %r", lock);	return -1;} static Whist*readwhist(char *file, char *lock, Qid *qid){	int lfd;	Biobuf *b;	Dir *d;	Whist *wh; 	if((lfd=getlock(lock)) < 0)	// LOG?		return nil; 	if(qid){		if((d = wdirstat(file)) == nil){			close(lfd);			return nil;		}		*qid = d->qid;		free(d);	} 	if((b = wBopen(file, OREAD)) == nil){	//LOG?		close(lfd);		return nil;	} 	wh = Brdwhist(b); 	Bterm(b);	close(lfd);	return wh;} static voidgencurrent(Wcache *w, Qid *q, char *file, char *lock, ulong *t, Whist **wp, int n){	Dir *d;	Whist *wh; 	if(*wp && *t+Tcache >= time(0))		return; 	wlock(w);	if(*wp && *t+Tcache >= time(0)){		wunlock(w);		return;	} 	if(((d = wdirstat(file)) == nil) || (d->qid.path==q->path && d->qid.vers==q->vers)){		*t = time(0);		wunlock(w);		free(d);		return;	} 	free(d);	if(wh = readwhist(file, lock, q)){		wh->n = n;		*t = time(0);		closewhist(*wp);		*wp = wh;	}else fprint(2, "error file=%s lock=%s %r\n", file, lock);	wunlock(w);} static voidcurrent(Wcache *w){	char tmp[40];	char tmplock[40]; 	sprint(tmplock, "d/L.%d", w->n);	sprint(tmp, "d/%d", w->n);	gencurrent(w, &w->qid, tmp, tmplock, &w->tcurrent, &w->current, w->n);} static voidcurrenthist(Wcache *w){	char hist[40], lock[40]; 	sprint(hist, "d/%d.hist", w->n);	sprint(lock, "d/L.%d", w->n); 	gencurrent(w, &w->qidhist, hist, lock, &w->thist, &w->hist, w->n);} voidvoidcache(int n){	Wcache *c; 	rlock(&cachelock);	if(c = findcache(n)){		wlock(c);		c->tcurrent = 0;		c->thist = 0;		/* aggressively free memory */		closewhist(c->hist);		c->hist = nil;		closewhist(c->current);		c->current = nil;		wunlock(c);	}	runlock(&cachelock);} static Whist*getcache(int n, int hist){	int i, isw;	ulong t;	Wcache *c, **cp, **evict;	Whist *wh; 	isw = 0;	rlock(&cachelock);	if(c = findcache(n)){	Found:		current(c);		if(hist)			currenthist(c);		rlock(c);		if(hist)			wh = c->hist;		else			wh = c->current;		if(wh)			incref(wh);		runlock(c);		if(isw)			wunlock(&cachelock);		else			runlock(&cachelock);		return wh;	}	runlock(&cachelock); 	wlock(&cachelock);	if(c = findcache(n)){		isw = 1;	/* better to downgrade lock but can't */		goto Found;	} 	if(ncache < Mcache){	Alloc:		c = emalloc(sizeof *c);		ncache++;	}else{		/* find something to evict. */		t = ~0;		evict = nil;		for(i=0; i<Nhash; i++){			for(cp=&tab[i], c=*cp; c; cp=&c->hash, c=*cp){				if(c->use < t				&& (!c->hist || c->hist->ref==1)				&& (!c->current || c->current->ref==1)){					evict = cp;					t = c->use;				}			}		} 		if(evict == nil){			fprint(2, "wikifs: nothing to evict\n");			goto Alloc;		} 		c = *evict;		*evict = c->hash; 		closewhist(c->current);		closewhist(c->hist);		memset(c, 0, sizeof *c);	} 	c->n = n;	c->hash = tab[n%Nhash];	tab[n%Nhash] = c;	isw = 1;	goto Found;} Whist*getcurrent(int n){	return getcache(n, 0);} Whist*gethistory(int n){	return getcache(n, 1);} RWLock maplock;Map *map; static intmapcmp(const void *va, const void *vb){	Mapel *a, *b; 	a = (Mapel*)va;	b = (Mapel*)vb; 	return strcmp(a->s, b->s);} voidclosemap(Map *m){	if(decref(m)==0){		free(m->buf);		free(m->el);		free(m);	}} voidcurrentmap(int force){	char *p, *q, *r;	int lfd, fd, m, n;	Dir *d;	Map *nmap;	char *err = nil; 	lfd = -1;	fd = -1;	d = nil;	nmap = nil;	if(!force && map && map->t+Tcache >= time(0))		return; 	wlock(&maplock);	if(!force && map && map->t+Tcache >= time(0))		goto Return; 	if((lfd = getlock("d/L.map")) < 0){		err = "can't lock";		goto Return;	} 	if((d = wdirstat("d/map")) == nil)		goto Return; 	if(map && d->qid.path == map->qid.path && d->qid.vers == map->qid.vers){		map->t = time(0);		goto Return;	} 	if(d->length > Maxmap){		//LOG		err = "too long";		goto Return;	} 	if((fd = wopen("d/map", OREAD)) < 0)		goto Return; 	nmap = emalloc(sizeof *nmap);	nmap->buf = emalloc(d->length+1);	n = readn(fd, nmap->buf, d->length);	if(n != d->length){		err = "bad length";		goto Return;	}	nmap->buf[n] = '\0'; 	n = 0;	for(p=nmap->buf; p; p=strchr(p+1, '\n'))		n++;	nmap->el = emalloc(n*sizeof(nmap->el[0])); 	m = 0;	for(p=nmap->buf; p && *p && m < n; p=q){		if(q = strchr(p+1, '\n'))			*q++ = '\0';		nmap->el[m].n = strtol(p, &r, 10);		if(*r == ' ')			r++;		else			{}//LOG?		nmap->el[m].s = strcondense(r, 1);		m++;	}	//LOG if m != n 	nmap->qid = d->qid;	nmap->t = time(0);	nmap->nel = m;	qsort(nmap->el, nmap->nel, sizeof(nmap->el[0]), mapcmp);	if(map)		closemap(map);	map = nmap;	incref(map);	nmap = nil;	Return:	free(d);	if(nmap){		free(nmap->el);		free(nmap->buf);		free(nmap);	}	if(map == nil)		sysfatal("cannot get map: %s: %r", err); 	if(fd >= 0)		close(fd);	if(lfd >= 0)		close(lfd);	wunlock(&maplock);} intallocnum(char *title, int mustbenew){	char *p, *q;	int lfd, fd, n;	Biobuf b; 	if(strcmp(title, "map")==0 || strcmp(title, "new")==0){		werrstr("reserved title name");		return -1;	} 	if(title[0]=='\0' || strpbrk(title, "/<>:?")){		werrstr("invalid character in name");		return -1;	}	if((n = nametonum(title)) >= 0){		if(mustbenew){			werrstr("duplicate title");			return -1;		}		return n;	} 	title = estrdup(title);	strcondense(title, 1);	strlower(title);	if(strchr(title, '\n') || strlen(title) > 200){		werrstr("bad title");		free(title);		return -1;	} 	if((lfd = getlock("d/L.map")) < 0){		free(title);		return -1;	} 	if((fd = wopen("d/map", ORDWR)) < 0){	// LOG?		close(lfd);		free(title);		return -1;	} 	/*	 * What we really need to do here is make sure the 	 * map is up-to-date, then make sure the title isn't	 * taken, and then add it, all without dropping the locks.	 *	 * This turns out to be a mess when you start adding	 * all the necessary dolock flags, so instead we just

Lines 1–500 of 709.