/* * Copyright (c) 2013, Coraid, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Coraid nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include <9p.h> #include "dat.h" static uvlong np2q, nq2m; static int maxp2q, maxq2m; static int p2qcoll, q2mcoll; /* FNV hash */ static ulong pathhash(char *path) { uchar *p; ulong h; h = 2166136261UL; for(p = (uchar *)path; *p; ++p) h = (h ^ *p) * 16777619; return h % super.nht; } static ulong qidhash(uvlong qpath) { return qpath % super.nht; } static uvlong qoffset(ulong bucket) { return BlkSize * (super.nhashblk + 1) + bucket * sizeof(uvlong); } static PQMap * nextpq(PQMap *pq) { uchar *p; p = (uchar *)pq; p += pq->plen + offsetof(PQMap, pname[0]); return (PQMap *)p; } uvlong p2q(int fd, char *path, int create) { PQMap *pq, *pend; uchar *p; uvlong *uvp; uvlong hlist, next, qpath; ulong bucket; int plen, nsearch, n; ++np2q; plen = strlen(path); bucket = pathhash(path); if(fd == -1) n = cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)); else n = spread(fd, &hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)); if(n < 0) sysfatal("cread failure: %r"); if(hlist == 0) { if(create) { hlist = allocblock(); if(hlist == 0) return 0; p = cbclean(hlist); pq = (PQMap *)p; qpath = super.qgen++ | ((uvlong)TFile << 60); pq->qpath = qpath; pq->plen = plen; memmove(pq->pname, path, plen); cbwrite(hlist); brelease(hlist); uvp = cbread(bucket / NPerBlk + 1); uvp[bucket % NPerBlk] = hlist; cbwrite(bucket / NPerBlk + 1); brelease(bucket / NPerBlk + 1); return qpath; } return 0; } nsearch = 1; p = nil; /* make the compiler happy */ if(fd != -1) p = θmalloc(BlkSize); while(hlist) { if(fd == -1) { p = cbread(hlist); if(p == nil) { fprint(2, "cbread failed on block %ulld\n", hlist); return 0; } } else spread(fd, p, BlkSize, hlist * BlkSize); pend = (PQMap *)(p + BlkSize); --pend; for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) { if(plen == pq->plen && memcmp(path, pq->pname, plen) == 0) goto found; ++nsearch; } next = *((uvlong *)(p + BlkSize - sizeof(uvlong))); if(next == 0 && create) goto addone; if(fd == -1) brelease(hlist); hlist = next; } if(fd != -1) free(p); return 0; found: if(nsearch > maxp2q) maxp2q = nsearch; next = pq->qpath; if(fd == -1) brelease(hlist); else free(p); if(create) return 0; return next; addone: for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) ; if(pq != (PQMap *)p) ++p2qcoll; if(pq >= pend) { fprint(2, "HUH?"); next = allocblock(); if(next == 0) return 0; *((uvlong *)(p + BlkSize - sizeof(uvlong))) = next; cbwrite(hlist); brelease(hlist); hlist = next; p = cbclean(hlist); pq = (PQMap *)p; } qpath = super.qgen++ | ((uvlong)TFile << 60); pq->qpath = qpath; pq->plen = plen; memmove(pq->pname, path, plen); if(hlist != 0) { /* shouldn't be possible, but just to be safe */ cbwrite(hlist); brelease(hlist); } return qpath; } void setqhash(uvlong qpath, uvlong midx) { ulong bucket; bucket = qidhash(qpath); cwrite(&midx, sizeof(uvlong), qoffset(bucket)); } uvlong q2m(int fd, uvlong qpath, int create) { uvlong val; uvlong first, meta; ulong bucket; int nsearch, n; if(qpath == 0) return 0; ++nq2m; bucket = qidhash(qpath); if(fd == -1) n = cread(&first, sizeof(uvlong), qoffset(bucket)); else n= spread(fd, &first, sizeof(uvlong), qoffset(bucket)); if(n < 0) sysfatal("cread failure: %r"); if(first == 0) { if(create) { meta = setmetaint(0, "qhnext", nil, 0); //setqhash(qpath, meta); return meta; } return 0; } nsearch = 1; for(meta = first; meta; ) { if(getmetaint(fd, meta, "qpath", &val) != MTnone && val == qpath) break; if(getmetaint(fd, meta, "qhnext", &meta) == MTnone) meta = 0; ++nsearch; } if(meta == 0) { if(create) { meta = setmetaint(0, "qhnext", nil, first); //setqhash(qpath, meta); } } else if(nsearch > maxq2m) maxq2m = nsearch; return meta; } void rehashone(uvlong qpath, char *from, char *to) { PQMap *pq, *rend; uchar *p; uvlong *uvp; uvlong hlist, next; ulong bucket; int plen; rmp(from); plen = strlen(to); bucket = pathhash(to); if(cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)) < 0) sysfatal("cread failure: %r"); if(hlist == 0) { hlist = allocblock(); if(hlist == 0) return; p = cbclean(hlist); pq = (PQMap *)p; pq->qpath = qpath; pq->plen = plen; memmove(pq->pname, to, plen); cbwrite(hlist); brelease(hlist); uvp = cbread(bucket / NPerBlk + 1); uvp[bucket % NPerBlk] = hlist; cbwrite(bucket / NPerBlk + 1); brelease(bucket / NPerBlk + 1); return; } while(hlist) { p = cbread(hlist); rend = (PQMap *)(p + BlkSize); --rend; for(pq = (PQMap *)p; pq < rend; pq = nextpq(pq)) if(plen == pq->plen && memcmp(to, pq->pname, plen) == 0) goto found; next = *((uvlong *)(p + BlkSize - sizeof(uvlong))); if(next == 0) goto addone; brelease(hlist); hlist = next; } return; found: fprint(2, "Impossible! Repath destination exists\n"); brelease(hlist); return; addone: for(pq = (PQMap *)p; pq < rend && pq->qpath != 0; pq = nextpq(pq)) ; if(pq >= rend) { next = allocblock(); *((uvlong *)(p + BlkSize - sizeof(uvlong))) = next; cbwrite(hlist); brelease(hlist); if(next == 0) return; hlist = next; p = cbclean(hlist); pq = (PQMap *)p; } pq->qpath = qpath; pq->plen = plen; memmove(pq->pname, to, plen); cbwrite(hlist); brelease(hlist); } void rehashpath(uvlong qpath, char *from, char *to) { char *f, *t, *name; uvlong cqid, meta; meta = q2m(-1, qpath, 0); if(meta != 0 && getmetaint(-1, meta, "child", &cqid) != MTnone) { while(cqid != 0) { meta = q2m(-1, cqid, 0); if(meta == 0) break; name = getmetastr(-1, meta, "name"); f = smprint("%s/%s", from, name); t = smprint("%s/%s", to, name); free(name); rehashpath(cqid, f, t); free(f); free(t); if(getmetaint(-1, meta, "sib", &cqid) == MTnone) break; } } rehashone(qpath, from, to); } static PQMap * rmpath(PQMap *full, PQMap *victim) { PQMap *next, *last; PQMap *rend; int plen; rend = (PQMap *)((char *)full + BlkSize - sizeof(uvlong)); for(last = victim; last < rend - 1 && last->plen > 0; last = nextpq(last)) ; /* * last now points to the start of the first empty path/qid map slot */ plen = victim->plen + offsetof(PQMap, pname[0]); next = nextpq(victim); memmove(victim, next, (char *)rend - (char *)next); /* * now last has moved up by plen bytes */ last = (PQMap *)((char *)last - plen); memset(last, 0, (char *)rend - (char *)last); return last; } void rmp(char *path) { PQMap *pq, *rend, *last; uchar *p; uvlong hlist, next; ulong bucket; int plen; plen = strlen(path); if(plen == 0) return; bucket = pathhash(path); if(cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)) < 0) sysfatal("cread failure: %r"); while(hlist) { p = cbread(hlist); rend = (PQMap *)(p + BlkSize); --rend; for(pq = (PQMap *)p; pq < rend; pq = nextpq(pq)) if(plen == pq->plen && memcmp(path, pq->pname, plen) == 0) goto found; next = *((uvlong *)(p + BlkSize - sizeof(uvlong))); brelease(hlist); hlist = next; } return; found: while(hlist) { last = rmpath((PQMap *)p, pq); next = *((uvlong *)(p + BlkSize - sizeof(uvlong))); if(next != 0) { p = cbread(next); pq = (PQMap *)p; if(pq->plen == 0 || pq->plen > (char *)rend - (char *)last) { brelease(next); next = 0; } else memmove(last, pq, pq->plen + offsetof(PQMap, pname[0])); } cbwrite(hlist); brelease(hlist); hlist = next; } } void rmq(uvlong qpath, uvlong victim) { uvlong prev, meta, next; ulong bucket; if(qpath == 0) return; bucket = qidhash(qpath); if(cread(&meta, sizeof(uvlong), qoffset(bucket)) < 0) sysfatal("cread failure: %r"); if(meta == victim) { if(getmetaint(-1, meta, "qhnext", &next) == MTnone) next = 0; if(cwrite(&next, sizeof(uvlong), qoffset(bucket)) < 0) sysfatal("cwrite failure: %r"); return; } for(prev = meta; prev; ) { if(getmetaint(-1, prev, "qhnext", &meta) == MTnone) meta = 0; if(meta == victim) { if(getmetaint(-1, victim, "qhnext", &next) == MTnone) next = 0; setmetaint(prev, "qhnext", nil, next); return; } prev = meta; } } static char hstatbuf[1024]; char * prhstat(void) { char *p, *e; p = hstatbuf; e = p + nelem(hstatbuf); p = seprint(p, e, "Hash stats:\n"); p = seprint(p, e, "np2q: %ulld\n", np2q); p = seprint(p, e, "p2qcoll: %ud\n", p2qcoll); p = seprint(p, e, "maxp2q: %ud\n", maxp2q); p = seprint(p, e, "nq2m: %ulld\n", nq2m); p = seprint(p, e, "q2mcoll: %ud\n", q2mcoll); seprint(p, e, "maxq2m: %ud\n", maxq2m); return hstatbuf; } void showphash(int fd, char *path) { uvlong hlist; ulong bucket; bucket = pathhash(path); cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)); fprint(fd, "%s: bucket:%uld hlist:%ulld\n", path, bucket, hlist); } void fixpaths(int fd) { PQMap *pq, *pend; uvlong *hb; uchar *p; char *path; uvlong hlist, next; ulong bucket; int i, j; fprint(fd, "Checking for dangling path names\n"); for(bucket = 0, i = 0; i < super.nhashblk; ++i) { hb = cbread(i + 1); for(j = 0; j < BlkSize / sizeof(uvlong) && bucket < super.nht; ++j, ++bucket) { if(bucket % 100000 == 0) fprint(fd, "."); restart: hlist = hb[j]; while(hlist) { p = cbread(hlist); if(p == nil) { fprint(fd, "hlist block read failure in fixpaths\n"); return; } pend = (PQMap *)(p + BlkSize); --pend; for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) { if(q2m(-1, pq->qpath, 0) == 0) { path = θmalloc(pq->plen + 1); memmove(path, pq->pname, pq->plen); fprint(fd, "removing dangling path %s\n", path); rmp(path); free(path); brelease(hlist); goto restart; } } next = *((uvlong *)(p + BlkSize - sizeof(uvlong))); brelease(hlist); hlist = next; } } brelease(i + 1); } }