19a747e4fSDavid du Colombier /* 29a747e4fSDavid du Colombier * I/O for a Wiki document set. 39a747e4fSDavid du Colombier * 49a747e4fSDavid du Colombier * The files are kept in one flat directory. 59a747e4fSDavid du Colombier * There are three files for each document: 69a747e4fSDavid du Colombier * nnn - current version of the document 79a747e4fSDavid du Colombier * nnn.hist - history (all old versions) of the document 89a747e4fSDavid du Colombier * append-only 99a747e4fSDavid du Colombier * L.nnn - write lock file for the document 109a747e4fSDavid du Colombier * 119a747e4fSDavid du Colombier * At the moment, since we don't have read/write locks 129a747e4fSDavid du Colombier * in the file system, we use the L.nnn file as a read lock too. 139a747e4fSDavid du Colombier * It's a hack but there aren't supposed to be many readers 149a747e4fSDavid du Colombier * anyway. 159a747e4fSDavid du Colombier * 169a747e4fSDavid du Colombier * The nnn.hist file is in the format read by Brdwhist. 179a747e4fSDavid du Colombier * The nnn file is in that format too, but only contains the 189a747e4fSDavid du Colombier * last entry of the nnn.hist file. 199a747e4fSDavid du Colombier * 209a747e4fSDavid du Colombier * In addition to this set of files, there is an append-only 219a747e4fSDavid du Colombier * map file that provides a mapping between numbers and titles. 229a747e4fSDavid du Colombier * The map file is a sequence of lines of the form 239a747e4fSDavid du Colombier * nnn Title Here 249a747e4fSDavid du Colombier * The lock file L.map must be held to add to the end, to 259a747e4fSDavid du Colombier * make sure that the numbers are allocated sequentially. 269a747e4fSDavid du Colombier * 279a747e4fSDavid du Colombier * We assume that writes to the map file will fit in one message, 289a747e4fSDavid du Colombier * so that we don't have to read-lock the file. 299a747e4fSDavid du Colombier */ 309a747e4fSDavid du Colombier 319a747e4fSDavid du Colombier #include <u.h> 329a747e4fSDavid du Colombier #include <libc.h> 339a747e4fSDavid du Colombier #include <bio.h> 349a747e4fSDavid du Colombier #include <String.h> 359a747e4fSDavid du Colombier #include <thread.h> 369a747e4fSDavid du Colombier #include "wiki.h" 379a747e4fSDavid du Colombier 389a747e4fSDavid du Colombier enum { 399a747e4fSDavid du Colombier Nhash = 64, 409a747e4fSDavid du Colombier Mcache = 128, 419a747e4fSDavid du Colombier }; 429a747e4fSDavid du Colombier 439a747e4fSDavid du Colombier typedef struct Wcache Wcache; 449a747e4fSDavid du Colombier struct Wcache { 459a747e4fSDavid du Colombier int n; 469a747e4fSDavid du Colombier ulong use; 479a747e4fSDavid du Colombier RWLock; 489a747e4fSDavid du Colombier ulong tcurrent; 499a747e4fSDavid du Colombier ulong thist; 509a747e4fSDavid du Colombier Whist *hist; 519a747e4fSDavid du Colombier Whist *current; 529a747e4fSDavid du Colombier Qid qid; 539a747e4fSDavid du Colombier Qid qidhist; 549a747e4fSDavid du Colombier Wcache *hash; 559a747e4fSDavid du Colombier }; 569a747e4fSDavid du Colombier 579a747e4fSDavid du Colombier static RWLock cachelock; 589a747e4fSDavid du Colombier static Wcache *tab[Nhash]; 599a747e4fSDavid du Colombier int ncache; 609a747e4fSDavid du Colombier 619a747e4fSDavid du Colombier void 629a747e4fSDavid du Colombier closewhist(Whist *wh) 639a747e4fSDavid du Colombier { 649a747e4fSDavid du Colombier int i; 659a747e4fSDavid du Colombier 669a747e4fSDavid du Colombier if(wh && decref(wh) == 0){ 679a747e4fSDavid du Colombier free(wh->title); 689a747e4fSDavid du Colombier for(i=0; i<wh->ndoc; i++){ 699a747e4fSDavid du Colombier free(wh->doc[i].author); 709a747e4fSDavid du Colombier free(wh->doc[i].comment); 719a747e4fSDavid du Colombier freepage(wh->doc[i].wtxt); 729a747e4fSDavid du Colombier } 739a747e4fSDavid du Colombier free(wh->doc); 749a747e4fSDavid du Colombier free(wh); 759a747e4fSDavid du Colombier } 769a747e4fSDavid du Colombier } 779a747e4fSDavid du Colombier 789a747e4fSDavid du Colombier void 799a747e4fSDavid du Colombier freepage(Wpage *p) 809a747e4fSDavid du Colombier { 819a747e4fSDavid du Colombier Wpage *next; 829a747e4fSDavid du Colombier 839a747e4fSDavid du Colombier for(; p; p=next){ 849a747e4fSDavid du Colombier next = p->next; 859a747e4fSDavid du Colombier free(p->text); 869a747e4fSDavid du Colombier free(p->url); 879a747e4fSDavid du Colombier free(p); 889a747e4fSDavid du Colombier } 899a747e4fSDavid du Colombier } 909a747e4fSDavid du Colombier 919a747e4fSDavid du Colombier static Wcache* 929a747e4fSDavid du Colombier findcache(int n) 939a747e4fSDavid du Colombier { 949a747e4fSDavid du Colombier Wcache *w; 959a747e4fSDavid du Colombier 969a747e4fSDavid du Colombier for(w=tab[n%Nhash]; w; w=w->hash) 979a747e4fSDavid du Colombier if(w->n == n){ 989a747e4fSDavid du Colombier w->use = time(0); 999a747e4fSDavid du Colombier return w; 1009a747e4fSDavid du Colombier } 1019a747e4fSDavid du Colombier return nil; 1029a747e4fSDavid du Colombier } 1039a747e4fSDavid du Colombier 1049a747e4fSDavid du Colombier static int 1059a747e4fSDavid du Colombier getlock(char *lock) 1069a747e4fSDavid du Colombier { 1079a747e4fSDavid du Colombier char buf[ERRMAX]; 1089a747e4fSDavid du Colombier int i, fd; 1099a747e4fSDavid du Colombier enum { SECS = 200 }; 1109a747e4fSDavid du Colombier 1119a747e4fSDavid du Colombier for(i=0; i<SECS*10; i++){ 1129a747e4fSDavid du Colombier fd = wcreate(lock, ORDWR, DMEXCL|0666); 1139a747e4fSDavid du Colombier if(fd >= 0) 1149a747e4fSDavid du Colombier return fd; 1159a747e4fSDavid du Colombier buf[0] = '\0'; 1169a747e4fSDavid du Colombier rerrstr(buf, sizeof buf); 1179a747e4fSDavid du Colombier if(strstr(buf, "locked") == nil) 1189a747e4fSDavid du Colombier break; 1199a747e4fSDavid du Colombier sleep(1000/10); 1209a747e4fSDavid du Colombier } 1219a747e4fSDavid du Colombier werrstr("couldn't acquire lock"); 1229a747e4fSDavid du Colombier return -1; 1239a747e4fSDavid du Colombier } 1249a747e4fSDavid du Colombier 1259a747e4fSDavid du Colombier static Whist* 1269a747e4fSDavid du Colombier readwhist(char *file, char *lock, Qid *qid) 1279a747e4fSDavid du Colombier { 1289a747e4fSDavid du Colombier int lfd; 1299a747e4fSDavid du Colombier Biobuf *b; 1309a747e4fSDavid du Colombier Dir *d; 1319a747e4fSDavid du Colombier Whist *wh; 1329a747e4fSDavid du Colombier 1339a747e4fSDavid du Colombier if((lfd=getlock(lock)) < 0) // LOG? 1349a747e4fSDavid du Colombier return nil; 1359a747e4fSDavid du Colombier 1369a747e4fSDavid du Colombier if(qid){ 1379a747e4fSDavid du Colombier if((d = wdirstat(file)) == nil){ 1389a747e4fSDavid du Colombier close(lfd); 1399a747e4fSDavid du Colombier return nil; 1409a747e4fSDavid du Colombier } 1419a747e4fSDavid du Colombier *qid = d->qid; 1429a747e4fSDavid du Colombier free(d); 1439a747e4fSDavid du Colombier } 1449a747e4fSDavid du Colombier 1459a747e4fSDavid du Colombier if((b = wBopen(file, OREAD)) == nil){ //LOG? 1469a747e4fSDavid du Colombier close(lfd); 1479a747e4fSDavid du Colombier return nil; 1489a747e4fSDavid du Colombier } 1499a747e4fSDavid du Colombier 1509a747e4fSDavid du Colombier wh = Brdwhist(b); 1519a747e4fSDavid du Colombier 1529a747e4fSDavid du Colombier Bterm(b); 1539a747e4fSDavid du Colombier close(lfd); 1549a747e4fSDavid du Colombier return wh; 1559a747e4fSDavid du Colombier } 1569a747e4fSDavid du Colombier 1579a747e4fSDavid du Colombier static void 1589a747e4fSDavid du Colombier gencurrent(Wcache *w, Qid *q, char *file, char *lock, ulong *t, Whist **wp, int n) 1599a747e4fSDavid du Colombier { 1609a747e4fSDavid du Colombier Dir *d; 1619a747e4fSDavid du Colombier Whist *wh; 1629a747e4fSDavid du Colombier 1639a747e4fSDavid du Colombier if(*wp && *t+Tcache >= time(0)) 1649a747e4fSDavid du Colombier return; 1659a747e4fSDavid du Colombier 1669a747e4fSDavid du Colombier wlock(w); 1679a747e4fSDavid du Colombier if(*wp && *t+Tcache >= time(0)){ 1689a747e4fSDavid du Colombier wunlock(w); 1699a747e4fSDavid du Colombier return; 1709a747e4fSDavid du Colombier } 1719a747e4fSDavid du Colombier 1729a747e4fSDavid du Colombier if(((d = wdirstat(file)) == nil) || (d->qid.path==q->path && d->qid.vers==q->vers)){ 1739a747e4fSDavid du Colombier *t = time(0); 1749a747e4fSDavid du Colombier wunlock(w); 1759a747e4fSDavid du Colombier free(d); 1769a747e4fSDavid du Colombier return; 1779a747e4fSDavid du Colombier } 1789a747e4fSDavid du Colombier 1799a747e4fSDavid du Colombier free(d); 1809a747e4fSDavid du Colombier if(wh = readwhist(file, lock, q)){ 1819a747e4fSDavid du Colombier wh->n = n; 1829a747e4fSDavid du Colombier *t = time(0); 1839a747e4fSDavid du Colombier closewhist(*wp); 1849a747e4fSDavid du Colombier *wp = wh; 1859a747e4fSDavid du Colombier } 1869a747e4fSDavid du Colombier else fprint(2, "error file=%s lock=%s %r\n", file, lock); 1879a747e4fSDavid du Colombier wunlock(w); 1889a747e4fSDavid du Colombier } 1899a747e4fSDavid du Colombier 1909a747e4fSDavid du Colombier static void 1919a747e4fSDavid du Colombier current(Wcache *w) 1929a747e4fSDavid du Colombier { 1939a747e4fSDavid du Colombier char tmp[40]; 1949a747e4fSDavid du Colombier char tmplock[40]; 1959a747e4fSDavid du Colombier 1969a747e4fSDavid du Colombier sprint(tmplock, "d/L.%d", w->n); 1979a747e4fSDavid du Colombier sprint(tmp, "d/%d", w->n); 1989a747e4fSDavid du Colombier gencurrent(w, &w->qid, tmp, tmplock, &w->tcurrent, &w->current, w->n); 1999a747e4fSDavid du Colombier } 2009a747e4fSDavid du Colombier 2019a747e4fSDavid du Colombier static void 2029a747e4fSDavid du Colombier currenthist(Wcache *w) 2039a747e4fSDavid du Colombier { 2049a747e4fSDavid du Colombier char hist[40], lock[40]; 2059a747e4fSDavid du Colombier 2069a747e4fSDavid du Colombier sprint(hist, "d/%d.hist", w->n); 2079a747e4fSDavid du Colombier sprint(lock, "d/L.%d", w->n); 2089a747e4fSDavid du Colombier 2099a747e4fSDavid du Colombier gencurrent(w, &w->qidhist, hist, lock, &w->thist, &w->hist, w->n); 2109a747e4fSDavid du Colombier } 2119a747e4fSDavid du Colombier 2129a747e4fSDavid du Colombier void 2139a747e4fSDavid du Colombier voidcache(int n) 2149a747e4fSDavid du Colombier { 2159a747e4fSDavid du Colombier Wcache *c; 2169a747e4fSDavid du Colombier 2179a747e4fSDavid du Colombier rlock(&cachelock); 2189a747e4fSDavid du Colombier if(c = findcache(n)){ 2199a747e4fSDavid du Colombier wlock(c); 2209a747e4fSDavid du Colombier c->tcurrent = 0; 2219a747e4fSDavid du Colombier c->thist = 0; 2229a747e4fSDavid du Colombier wunlock(c); 2239a747e4fSDavid du Colombier } 2249a747e4fSDavid du Colombier runlock(&cachelock); 2259a747e4fSDavid du Colombier } 2269a747e4fSDavid du Colombier 2279a747e4fSDavid du Colombier static Whist* 2289a747e4fSDavid du Colombier getcache(int n, int hist) 2299a747e4fSDavid du Colombier { 2309a747e4fSDavid du Colombier int i, isw; 2319a747e4fSDavid du Colombier ulong t; 2329a747e4fSDavid du Colombier Wcache *c, **cp, **evict; 2339a747e4fSDavid du Colombier Whist *wh; 2349a747e4fSDavid du Colombier 2359a747e4fSDavid du Colombier isw = 0; 2369a747e4fSDavid du Colombier rlock(&cachelock); 2379a747e4fSDavid du Colombier if(c = findcache(n)){ 2389a747e4fSDavid du Colombier Found: 2399a747e4fSDavid du Colombier current(c); 2409a747e4fSDavid du Colombier if(hist) 2419a747e4fSDavid du Colombier currenthist(c); 2429a747e4fSDavid du Colombier rlock(c); 2439a747e4fSDavid du Colombier if(hist) 2449a747e4fSDavid du Colombier wh = c->hist; 2459a747e4fSDavid du Colombier else 2469a747e4fSDavid du Colombier wh = c->current; 2479a747e4fSDavid du Colombier if(wh) 2489a747e4fSDavid du Colombier incref(wh); 2499a747e4fSDavid du Colombier runlock(c); 2509a747e4fSDavid du Colombier if(isw) 2519a747e4fSDavid du Colombier wunlock(&cachelock); 2529a747e4fSDavid du Colombier else 2539a747e4fSDavid du Colombier runlock(&cachelock); 2549a747e4fSDavid du Colombier return wh; 2559a747e4fSDavid du Colombier } 2569a747e4fSDavid du Colombier runlock(&cachelock); 2579a747e4fSDavid du Colombier 2589a747e4fSDavid du Colombier wlock(&cachelock); 2599a747e4fSDavid du Colombier if(c = findcache(n)){ 2609a747e4fSDavid du Colombier isw = 1; /* better to downgrade lock but can't */ 2619a747e4fSDavid du Colombier goto Found; 2629a747e4fSDavid du Colombier } 2639a747e4fSDavid du Colombier 2649a747e4fSDavid du Colombier if(ncache < Mcache){ 2659a747e4fSDavid du Colombier Alloc: 2669a747e4fSDavid du Colombier c = emalloc(sizeof *c); 2679a747e4fSDavid du Colombier ncache++; 2689a747e4fSDavid du Colombier }else{ 2699a747e4fSDavid du Colombier /* find something to evict. */ 2709a747e4fSDavid du Colombier t = ~0; 2719a747e4fSDavid du Colombier evict = nil; 2729a747e4fSDavid du Colombier for(i=0; i<Nhash; i++){ 2739a747e4fSDavid du Colombier for(cp=&tab[i], c=*cp; c; cp=&c->hash, c=*cp){ 2749a747e4fSDavid du Colombier if(c->use < t 2759a747e4fSDavid du Colombier && (!c->hist || c->hist->ref==1) 2769a747e4fSDavid du Colombier && (!c->current || c->current->ref==1)){ 2779a747e4fSDavid du Colombier evict = cp; 2789a747e4fSDavid du Colombier t = c->use; 2799a747e4fSDavid du Colombier } 2809a747e4fSDavid du Colombier } 2819a747e4fSDavid du Colombier } 2829a747e4fSDavid du Colombier 2839a747e4fSDavid du Colombier if(evict == nil){ 2849a747e4fSDavid du Colombier fprint(2, "wikifs: nothing to evict\n"); 2859a747e4fSDavid du Colombier goto Alloc; 2869a747e4fSDavid du Colombier } 2879a747e4fSDavid du Colombier 2889a747e4fSDavid du Colombier c = *evict; 2899a747e4fSDavid du Colombier *evict = c->hash; 2909a747e4fSDavid du Colombier 2919a747e4fSDavid du Colombier closewhist(c->current); 2929a747e4fSDavid du Colombier closewhist(c->hist); 2939a747e4fSDavid du Colombier memset(c, 0, sizeof *c); 2949a747e4fSDavid du Colombier } 2959a747e4fSDavid du Colombier 2969a747e4fSDavid du Colombier c->n = n; 2979a747e4fSDavid du Colombier c->hash = tab[n%Nhash]; 2989a747e4fSDavid du Colombier tab[n%Nhash] = c; 2999a747e4fSDavid du Colombier isw = 1; 3009a747e4fSDavid du Colombier goto Found; 3019a747e4fSDavid du Colombier } 3029a747e4fSDavid du Colombier 3039a747e4fSDavid du Colombier Whist* 3049a747e4fSDavid du Colombier getcurrent(int n) 3059a747e4fSDavid du Colombier { 3069a747e4fSDavid du Colombier return getcache(n, 0); 3079a747e4fSDavid du Colombier } 3089a747e4fSDavid du Colombier 3099a747e4fSDavid du Colombier Whist* 3109a747e4fSDavid du Colombier gethistory(int n) 3119a747e4fSDavid du Colombier { 3129a747e4fSDavid du Colombier return getcache(n, 1); 3139a747e4fSDavid du Colombier } 3149a747e4fSDavid du Colombier 3159a747e4fSDavid du Colombier RWLock maplock; 3169a747e4fSDavid du Colombier Map *map; 3179a747e4fSDavid du Colombier 3189a747e4fSDavid du Colombier static int 3199a747e4fSDavid du Colombier mapcmp(const void *va, const void *vb) 3209a747e4fSDavid du Colombier { 3219a747e4fSDavid du Colombier Mapel *a, *b; 3229a747e4fSDavid du Colombier 3239a747e4fSDavid du Colombier a = (Mapel*)va; 3249a747e4fSDavid du Colombier b = (Mapel*)vb; 3259a747e4fSDavid du Colombier 3269a747e4fSDavid du Colombier return strcmp(a->s, b->s); 3279a747e4fSDavid du Colombier } 3289a747e4fSDavid du Colombier 3299a747e4fSDavid du Colombier void 3309a747e4fSDavid du Colombier closemap(Map *m) 3319a747e4fSDavid du Colombier { 3329a747e4fSDavid du Colombier if(decref(m)==0){ 3339a747e4fSDavid du Colombier free(m->buf); 3349a747e4fSDavid du Colombier free(m->el); 3359a747e4fSDavid du Colombier free(m); 3369a747e4fSDavid du Colombier } 3379a747e4fSDavid du Colombier } 3389a747e4fSDavid du Colombier 3399a747e4fSDavid du Colombier void 3409a747e4fSDavid du Colombier currentmap(int force) 3419a747e4fSDavid du Colombier { 3429a747e4fSDavid du Colombier char *p, *q, *r; 3439a747e4fSDavid du Colombier int lfd, fd, m, n; 3449a747e4fSDavid du Colombier Dir *d; 3459a747e4fSDavid du Colombier Map *nmap; 346da51d93aSDavid du Colombier char *err = nil; 3479a747e4fSDavid du Colombier 3489a747e4fSDavid du Colombier lfd = -1; 3499a747e4fSDavid du Colombier fd = -1; 3509a747e4fSDavid du Colombier d = nil; 3519a747e4fSDavid du Colombier nmap = nil; 3529a747e4fSDavid du Colombier if(!force && map && map->t+Tcache >= time(0)) 3539a747e4fSDavid du Colombier return; 3549a747e4fSDavid du Colombier 3559a747e4fSDavid du Colombier wlock(&maplock); 3569a747e4fSDavid du Colombier if(!force && map && map->t+Tcache >= time(0)) 3579a747e4fSDavid du Colombier goto Return; 3589a747e4fSDavid du Colombier 359da51d93aSDavid du Colombier if((lfd = getlock("d/L.map")) < 0){ 360da51d93aSDavid du Colombier err = "can't lock"; 3619a747e4fSDavid du Colombier goto Return; 362da51d93aSDavid du Colombier } 3639a747e4fSDavid du Colombier 3649a747e4fSDavid du Colombier if((d = wdirstat("d/map")) == nil) 3659a747e4fSDavid du Colombier goto Return; 3669a747e4fSDavid du Colombier 3679a747e4fSDavid du Colombier if(map && d->qid.path == map->qid.path && d->qid.vers == map->qid.vers){ 3689a747e4fSDavid du Colombier map->t = time(0); 3699a747e4fSDavid du Colombier goto Return; 3709a747e4fSDavid du Colombier } 3719a747e4fSDavid du Colombier 3729a747e4fSDavid du Colombier if(d->length > Maxmap){ 3739a747e4fSDavid du Colombier //LOG 374da51d93aSDavid du Colombier err = "too long"; 3759a747e4fSDavid du Colombier goto Return; 3769a747e4fSDavid du Colombier } 3779a747e4fSDavid du Colombier 3789a747e4fSDavid du Colombier if((fd = wopen("d/map", OREAD)) < 0) 3799a747e4fSDavid du Colombier goto Return; 3809a747e4fSDavid du Colombier 3819a747e4fSDavid du Colombier nmap = emalloc(sizeof *nmap); 3829a747e4fSDavid du Colombier nmap->buf = emalloc(d->length+1); 3839a747e4fSDavid du Colombier n = readn(fd, nmap->buf, d->length); 384da51d93aSDavid du Colombier if(n != d->length){ 385da51d93aSDavid du Colombier err = "bad length"; 3869a747e4fSDavid du Colombier goto Return; 387da51d93aSDavid du Colombier } 3889a747e4fSDavid du Colombier nmap->buf[n] = '\0'; 3899a747e4fSDavid du Colombier 3909a747e4fSDavid du Colombier n = 0; 3919a747e4fSDavid du Colombier for(p=nmap->buf; p; p=strchr(p+1, '\n')) 3929a747e4fSDavid du Colombier n++; 3939a747e4fSDavid du Colombier nmap->el = emalloc(n*sizeof(nmap->el[0])); 3949a747e4fSDavid du Colombier 3959a747e4fSDavid du Colombier m = 0; 3969a747e4fSDavid du Colombier for(p=nmap->buf; p && *p && m < n; p=q){ 3979a747e4fSDavid du Colombier if(q = strchr(p+1, '\n')) 3989a747e4fSDavid du Colombier *q++ = '\0'; 3999a747e4fSDavid du Colombier nmap->el[m].n = strtol(p, &r, 10); 4009a747e4fSDavid du Colombier if(*r == ' ') 4019a747e4fSDavid du Colombier r++; 4029a747e4fSDavid du Colombier else 4039a747e4fSDavid du Colombier {}//LOG? 4049a747e4fSDavid du Colombier nmap->el[m].s = strcondense(r, 1); 4059a747e4fSDavid du Colombier m++; 4069a747e4fSDavid du Colombier } 4079a747e4fSDavid du Colombier //LOG if m != n 4089a747e4fSDavid du Colombier 4099a747e4fSDavid du Colombier nmap->qid = d->qid; 4109a747e4fSDavid du Colombier nmap->t = time(0); 4119a747e4fSDavid du Colombier nmap->nel = m; 4129a747e4fSDavid du Colombier qsort(nmap->el, nmap->nel, sizeof(nmap->el[0]), mapcmp); 4139a747e4fSDavid du Colombier if(map) 4149a747e4fSDavid du Colombier closemap(map); 4159a747e4fSDavid du Colombier map = nmap; 4169a747e4fSDavid du Colombier incref(map); 4179a747e4fSDavid du Colombier nmap = nil; 4189a747e4fSDavid du Colombier 4199a747e4fSDavid du Colombier Return: 4209a747e4fSDavid du Colombier free(d); 4219a747e4fSDavid du Colombier if(nmap){ 4229a747e4fSDavid du Colombier free(nmap->el); 4239a747e4fSDavid du Colombier free(nmap->buf); 4249a747e4fSDavid du Colombier free(nmap); 4259a747e4fSDavid du Colombier } 4269a747e4fSDavid du Colombier if(map == nil) 427da51d93aSDavid du Colombier sysfatal("cannot get map: %s: %r", err); 4289a747e4fSDavid du Colombier 4299a747e4fSDavid du Colombier if(fd >= 0) 4309a747e4fSDavid du Colombier close(fd); 4319a747e4fSDavid du Colombier if(lfd >= 0) 4329a747e4fSDavid du Colombier close(lfd); 4339a747e4fSDavid du Colombier wunlock(&maplock); 4349a747e4fSDavid du Colombier } 4359a747e4fSDavid du Colombier 4369a747e4fSDavid du Colombier int 4379a747e4fSDavid du Colombier allocnum(char *title, int mustbenew) 4389a747e4fSDavid du Colombier { 4399a747e4fSDavid du Colombier char *p, *q; 4409a747e4fSDavid du Colombier int lfd, fd, n; 4419a747e4fSDavid du Colombier Biobuf b; 4429a747e4fSDavid du Colombier 4439a747e4fSDavid du Colombier if(strcmp(title, "map")==0 || strcmp(title, "new")==0){ 4449a747e4fSDavid du Colombier werrstr("reserved title name"); 4459a747e4fSDavid du Colombier return -1; 4469a747e4fSDavid du Colombier } 4479a747e4fSDavid du Colombier 448*f0ed0fb6SDavid du Colombier if(title[0]=='\0' || strpbrk(title, "/<>:?")){ 4499a747e4fSDavid du Colombier werrstr("invalid character in name"); 4509a747e4fSDavid du Colombier return -1; 4519a747e4fSDavid du Colombier } 4529a747e4fSDavid du Colombier if((n = nametonum(title)) >= 0){ 4539a747e4fSDavid du Colombier if(mustbenew){ 4549a747e4fSDavid du Colombier werrstr("duplicate title"); 4559a747e4fSDavid du Colombier return -1; 4569a747e4fSDavid du Colombier } 4579a747e4fSDavid du Colombier return n; 4589a747e4fSDavid du Colombier } 4599a747e4fSDavid du Colombier 4609a747e4fSDavid du Colombier title = estrdup(title); 4619a747e4fSDavid du Colombier strcondense(title, 1); 4629a747e4fSDavid du Colombier strlower(title); 4639a747e4fSDavid du Colombier if(strchr(title, '\n') || strlen(title) > 200){ 4649a747e4fSDavid du Colombier werrstr("bad title"); 4659a747e4fSDavid du Colombier free(title); 4669a747e4fSDavid du Colombier return -1; 4679a747e4fSDavid du Colombier } 4689a747e4fSDavid du Colombier 4699a747e4fSDavid du Colombier if((lfd = getlock("d/L.map")) < 0){ 4709a747e4fSDavid du Colombier free(title); 4719a747e4fSDavid du Colombier return -1; 4729a747e4fSDavid du Colombier } 4739a747e4fSDavid du Colombier 4749a747e4fSDavid du Colombier if((fd = wopen("d/map", ORDWR)) < 0){ // LOG? 4759a747e4fSDavid du Colombier close(lfd); 4769a747e4fSDavid du Colombier free(title); 4779a747e4fSDavid du Colombier return -1; 4789a747e4fSDavid du Colombier } 4799a747e4fSDavid du Colombier 4809a747e4fSDavid du Colombier /* 4819a747e4fSDavid du Colombier * What we really need to do here is make sure the 4829a747e4fSDavid du Colombier * map is up-to-date, then make sure the title isn't 4839a747e4fSDavid du Colombier * taken, and then add it, all without dropping the locks. 4849a747e4fSDavid du Colombier * 4859a747e4fSDavid du Colombier * This turns out to be a mess when you start adding 4869a747e4fSDavid du Colombier * all the necessary dolock flags, so instead we just 4879a747e4fSDavid du Colombier * read through the file ourselves, and let our 4889a747e4fSDavid du Colombier * map catch up on its own. 4899a747e4fSDavid du Colombier */ 4909a747e4fSDavid du Colombier Binit(&b, fd, OREAD); 4919a747e4fSDavid du Colombier n = 0; 4929a747e4fSDavid du Colombier while(p = Brdline(&b, '\n')){ 4939a747e4fSDavid du Colombier p[Blinelen(&b)-1] = '\0'; 4949a747e4fSDavid du Colombier n = atoi(p)+1; 4959a747e4fSDavid du Colombier q = strchr(p, ' '); 4969a747e4fSDavid du Colombier if(q == nil) 4979a747e4fSDavid du Colombier continue; 4989a747e4fSDavid du Colombier if(strcmp(q+1, title) == 0){ 4999a747e4fSDavid du Colombier free(title); 5009a747e4fSDavid du Colombier close(fd); 5019a747e4fSDavid du Colombier close(lfd); 5029a747e4fSDavid du Colombier if(mustbenew){ 5039a747e4fSDavid du Colombier werrstr("duplicate title"); 5049a747e4fSDavid du Colombier return -1; 5059a747e4fSDavid du Colombier }else 5069a747e4fSDavid du Colombier return n; 5079a747e4fSDavid du Colombier } 5089a747e4fSDavid du Colombier } 5099a747e4fSDavid du Colombier 5109a747e4fSDavid du Colombier seek(fd, 0, 2); /* just in case it's not append only */ 5119a747e4fSDavid du Colombier fprint(fd, "%d %s\n", n, title); 5129a747e4fSDavid du Colombier close(fd); 5139a747e4fSDavid du Colombier close(lfd); 5149a747e4fSDavid du Colombier free(title); 5159a747e4fSDavid du Colombier /* kick the map */ 5169a747e4fSDavid du Colombier currentmap(1); 5179a747e4fSDavid du Colombier return n; 5189a747e4fSDavid du Colombier } 5199a747e4fSDavid du Colombier 5209a747e4fSDavid du Colombier int 5219a747e4fSDavid du Colombier nametonum(char *s) 5229a747e4fSDavid du Colombier { 5239a747e4fSDavid du Colombier char *p; 5249a747e4fSDavid du Colombier int i, lo, hi, m, rv; 5259a747e4fSDavid du Colombier 5269a747e4fSDavid du Colombier s = estrdup(s); 5279a747e4fSDavid du Colombier strlower(s); 5289a747e4fSDavid du Colombier for(p=s; *p; p++) 5299a747e4fSDavid du Colombier if(*p=='_') 5309a747e4fSDavid du Colombier *p = ' '; 5319a747e4fSDavid du Colombier 5329a747e4fSDavid du Colombier currentmap(0); 5339a747e4fSDavid du Colombier rlock(&maplock); 5349a747e4fSDavid du Colombier lo = 0; 5359a747e4fSDavid du Colombier hi = map->nel; 5369a747e4fSDavid du Colombier while(hi-lo > 1){ 5379a747e4fSDavid du Colombier m = (lo+hi)/2; 5389a747e4fSDavid du Colombier i = strcmp(s, map->el[m].s); 5399a747e4fSDavid du Colombier if(i < 0) 5409a747e4fSDavid du Colombier hi = m; 5419a747e4fSDavid du Colombier else 5429a747e4fSDavid du Colombier lo = m; 5439a747e4fSDavid du Colombier } 5449a747e4fSDavid du Colombier if(hi-lo == 1 && strcmp(s, map->el[lo].s)==0) 5459a747e4fSDavid du Colombier rv = map->el[lo].n; 5469a747e4fSDavid du Colombier else 5479a747e4fSDavid du Colombier rv = -1; 5489a747e4fSDavid du Colombier runlock(&maplock); 5499a747e4fSDavid du Colombier free(s); 5509a747e4fSDavid du Colombier return rv; 5519a747e4fSDavid du Colombier } 5529a747e4fSDavid du Colombier 5539a747e4fSDavid du Colombier char* 5549a747e4fSDavid du Colombier numtoname(int n) 5559a747e4fSDavid du Colombier { 5569a747e4fSDavid du Colombier int i; 5579a747e4fSDavid du Colombier char *s; 5589a747e4fSDavid du Colombier 5599a747e4fSDavid du Colombier currentmap(0); 5609a747e4fSDavid du Colombier rlock(&maplock); 5619a747e4fSDavid du Colombier for(i=0; i<map->nel; i++){ 5629a747e4fSDavid du Colombier if(map->el[i].n==n) 5639a747e4fSDavid du Colombier break; 5649a747e4fSDavid du Colombier } 5659a747e4fSDavid du Colombier if(i==map->nel){ 5669a747e4fSDavid du Colombier runlock(&maplock); 5679a747e4fSDavid du Colombier return nil; 5689a747e4fSDavid du Colombier } 5699a747e4fSDavid du Colombier s = estrdup(map->el[i].s); 5709a747e4fSDavid du Colombier runlock(&maplock); 5719a747e4fSDavid du Colombier return s; 5729a747e4fSDavid du Colombier } 5739a747e4fSDavid du Colombier 5749a747e4fSDavid du Colombier Whist* 5759a747e4fSDavid du Colombier getcurrentbyname(char *s) 5769a747e4fSDavid du Colombier { 5779a747e4fSDavid du Colombier int n; 5789a747e4fSDavid du Colombier 5799a747e4fSDavid du Colombier if((n = nametonum(s)) < 0) 5809a747e4fSDavid du Colombier return nil; 5819a747e4fSDavid du Colombier return getcache(n, 0); 5829a747e4fSDavid du Colombier } 5839a747e4fSDavid du Colombier 5849a747e4fSDavid du Colombier static String* 5859a747e4fSDavid du Colombier Brdstring(Biobuf *b) 5869a747e4fSDavid du Colombier { 5879a747e4fSDavid du Colombier String *s; 5889a747e4fSDavid du Colombier 5899a747e4fSDavid du Colombier s = s_new(); 5909a747e4fSDavid du Colombier while(s_read(b, s, 8192) > 0) 5919a747e4fSDavid du Colombier ; 5929a747e4fSDavid du Colombier return s; 5939a747e4fSDavid du Colombier } 5949a747e4fSDavid du Colombier 5959a747e4fSDavid du Colombier /* 5969a747e4fSDavid du Colombier * Attempt to install a new page. If t==0 we are creating. 5979a747e4fSDavid du Colombier * Otherwise, we are editing and t must be set to the current 5989a747e4fSDavid du Colombier * version (t is the version we started with) to avoid conflicting 5999a747e4fSDavid du Colombier * writes. 6009a747e4fSDavid du Colombier * 6019a747e4fSDavid du Colombier * If there is a conflicting write, we still write the page to 6029a747e4fSDavid du Colombier * the history file, but mark it as a failed write. 6039a747e4fSDavid du Colombier */ 6049a747e4fSDavid du Colombier int 6059a747e4fSDavid du Colombier writepage(int num, ulong t, String *s, char *title) 6069a747e4fSDavid du Colombier { 6079a747e4fSDavid du Colombier char tmp[40], tmplock[40], err[ERRMAX], hist[40], *p; 6089a747e4fSDavid du Colombier int conflict, lfd, fd; 6099a747e4fSDavid du Colombier Biobuf *b; 6109a747e4fSDavid du Colombier String *os; 6119a747e4fSDavid du Colombier 6129a747e4fSDavid du Colombier sprint(tmp, "d/%d", num); 6139a747e4fSDavid du Colombier sprint(tmplock, "d/L.%d", num); 6149a747e4fSDavid du Colombier sprint(hist, "d/%d.hist", num); 6159a747e4fSDavid du Colombier if((lfd = getlock(tmplock)) < 0) 6169a747e4fSDavid du Colombier return -1; 6179a747e4fSDavid du Colombier 6189a747e4fSDavid du Colombier conflict = 0; 6199a747e4fSDavid du Colombier if(b = wBopen(tmp, OREAD)){ 6209a747e4fSDavid du Colombier Brdline(b, '\n'); /* title */ 6219a747e4fSDavid du Colombier if(p = Brdline(b, '\n')) /* version */ 6229a747e4fSDavid du Colombier p[Blinelen(b)-1] = '\0'; 6239a747e4fSDavid du Colombier if(p==nil || p[0] != 'D'){ 6249a747e4fSDavid du Colombier snprint(err, sizeof err, "bad format in extant file"); 6259a747e4fSDavid du Colombier conflict = 1; 6269a747e4fSDavid du Colombier }else if(strtoul(p+1, 0, 0) != t){ 6279a747e4fSDavid du Colombier os = Brdstring(b); 6289a747e4fSDavid du Colombier p = strchr(s_to_c(s), '\n'); 6299a747e4fSDavid du Colombier if(p!=nil && strcmp(p+1, s_to_c(os))==0){ /* ignore dup write */ 6309a747e4fSDavid du Colombier close(lfd); 6319a747e4fSDavid du Colombier return 0; 6329a747e4fSDavid du Colombier } 6339a747e4fSDavid du Colombier s_free(os); 6349a747e4fSDavid du Colombier snprint(err, sizeof err, "update conflict %lud != %s", t, p+1); 6359a747e4fSDavid du Colombier conflict = 1; 6369a747e4fSDavid du Colombier } 6379a747e4fSDavid du Colombier Bterm(b); 6389a747e4fSDavid du Colombier }else{ 6399a747e4fSDavid du Colombier if(t != 0){ 6409a747e4fSDavid du Colombier close(lfd); 6419a747e4fSDavid du Colombier werrstr("did not expect to create"); 6429a747e4fSDavid du Colombier return -1; 6439a747e4fSDavid du Colombier } 6449a747e4fSDavid du Colombier } 6459a747e4fSDavid du Colombier 6469a747e4fSDavid du Colombier if((fd = wopen(hist, OWRITE)) < 0){ 6479a747e4fSDavid du Colombier if((fd = wcreate(hist, OWRITE, 0666)) < 0){ 6489a747e4fSDavid du Colombier close(lfd); 6499a747e4fSDavid du Colombier return -1; 6509a747e4fSDavid du Colombier }else 6519a747e4fSDavid du Colombier fprint(fd, "%s\n", title); 6529a747e4fSDavid du Colombier } 6539a747e4fSDavid du Colombier if(seek(fd, 0, 2) < 0 6549a747e4fSDavid du Colombier || (conflict && write(fd, "X\n", 2) != 2) 6559a747e4fSDavid du Colombier || write(fd, s_to_c(s), s_len(s)) != s_len(s)){ 6569a747e4fSDavid du Colombier close(fd); 6579a747e4fSDavid du Colombier close(lfd); 6589a747e4fSDavid du Colombier return -1; 6599a747e4fSDavid du Colombier } 6609a747e4fSDavid du Colombier close(fd); 6619a747e4fSDavid du Colombier 6629a747e4fSDavid du Colombier if(conflict){ 6639a747e4fSDavid du Colombier close(lfd); 6649a747e4fSDavid du Colombier voidcache(num); 6659a747e4fSDavid du Colombier werrstr(err); 6669a747e4fSDavid du Colombier return -1; 6679a747e4fSDavid du Colombier } 6689a747e4fSDavid du Colombier 6699a747e4fSDavid du Colombier if((fd = wcreate(tmp, OWRITE, 0666)) < 0){ 6709a747e4fSDavid du Colombier close(lfd); 6719a747e4fSDavid du Colombier voidcache(num); 6729a747e4fSDavid du Colombier return -1; 6739a747e4fSDavid du Colombier } 6749a747e4fSDavid du Colombier if(write(fd, title, strlen(title)) != strlen(title) 6759a747e4fSDavid du Colombier || write(fd, "\n", 1) != 1 6769a747e4fSDavid du Colombier || write(fd, s_to_c(s), s_len(s)) != s_len(s)){ 6779a747e4fSDavid du Colombier close(fd); 6789a747e4fSDavid du Colombier close(lfd); 6799a747e4fSDavid du Colombier voidcache(num); 6809a747e4fSDavid du Colombier return -1; 6819a747e4fSDavid du Colombier } 6829a747e4fSDavid du Colombier close(fd); 6839a747e4fSDavid du Colombier close(lfd); 6849a747e4fSDavid du Colombier voidcache(num); 6859a747e4fSDavid du Colombier return 0; 6869a747e4fSDavid du Colombier } 687