From bc1ff6985c03402497a052d2b4e2a299b3cdf218 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sun, 26 Jun 2011 07:03:12 +0200 Subject: [PATCH] add hgfs, a mercurial filesystem --- sys/src/cmd/hgfs/dat.h | 84 ++++++ sys/src/cmd/hgfs/fns.h | 30 ++ sys/src/cmd/hgfs/fs.c | 587 ++++++++++++++++++++++++++++++++++++++ sys/src/cmd/hgfs/hash.c | 69 +++++ sys/src/cmd/hgfs/info.c | 64 +++++ sys/src/cmd/hgfs/mkfile | 10 + sys/src/cmd/hgfs/patch.c | 159 +++++++++++ sys/src/cmd/hgfs/revlog.c | 272 ++++++++++++++++++ sys/src/cmd/hgfs/tree.c | 267 +++++++++++++++++ sys/src/cmd/hgfs/zip.c | 97 +++++++ 10 files changed, 1639 insertions(+) create mode 100644 sys/src/cmd/hgfs/dat.h create mode 100644 sys/src/cmd/hgfs/fns.h create mode 100644 sys/src/cmd/hgfs/fs.c create mode 100644 sys/src/cmd/hgfs/hash.c create mode 100644 sys/src/cmd/hgfs/info.c create mode 100644 sys/src/cmd/hgfs/mkfile create mode 100644 sys/src/cmd/hgfs/patch.c create mode 100644 sys/src/cmd/hgfs/revlog.c create mode 100644 sys/src/cmd/hgfs/tree.c create mode 100644 sys/src/cmd/hgfs/zip.c diff --git a/sys/src/cmd/hgfs/dat.h b/sys/src/cmd/hgfs/dat.h new file mode 100644 index 000000000..7f411b35b --- /dev/null +++ b/sys/src/cmd/hgfs/dat.h @@ -0,0 +1,84 @@ +enum { + MAXPATH = 1024, + BUFSZ = 1024, + HASHSZ = 20, +}; + +typedef struct Revlog Revlog; +typedef struct Revmap Revmap; +typedef struct Revinfo Revinfo; +typedef struct Revtree Revtree; +typedef struct Revnode Revnode; +typedef struct Revfile Revfile; + +struct Revmap +{ + int rev; + int p1rev; + int p2rev; + int baserev; + int linkrev; + + uchar hash[HASHSZ]; + + int flags; + + vlong hoff; + vlong hlen; + + vlong flen; + + void *aux; +}; + +struct Revlog +{ + int ifd; + int dfd; + + int nmap; + Revmap *map; +}; + +struct Revnode +{ + char *name; + uchar *hash; + uvlong path; + + Revnode *up; + Revnode *next; + Revnode *down; +}; + +struct Revinfo +{ + uchar chash[HASHSZ]; + uchar mhash[HASHSZ]; + + char *who; + char *why; + long when; +}; + +struct Revtree +{ + Ref; + + int level; + + Revinfo *info; + Revnode *root; +}; + +struct Revfile +{ + int level; + + Revinfo *info; + Revtree *tree; + Revnode *node; + + char *buf; + int fd; +}; diff --git a/sys/src/cmd/hgfs/fns.h b/sys/src/cmd/hgfs/fns.h new file mode 100644 index 000000000..e676ae605 --- /dev/null +++ b/sys/src/cmd/hgfs/fns.h @@ -0,0 +1,30 @@ +/* hash */ +int Hfmt(Fmt *f); +int strhash(char *s, uchar *h); +int fhash(int fd, uchar p1[], uchar p2[], uchar h[]); + +/* patch */ +int fpatchmark(int pfd, char *mark); +int fpatch(int ofd, int bfd, int pfd); + +/* zip */ +int funzip(int ofd, int zfd, int len); + +/* revlog */ +int fmktemp(void); +int revlogopen(Revlog *r, char *path, int mode); +void revlogclose(Revlog *r); +int revlogextract(Revlog *r, int rev, int ofd); +uchar *revhash(Revlog *r, int rev); +int hashrev(Revlog *r, uchar hash[]); +int revlogopentemp(Revlog *r, int rev); + +/* info */ +Revinfo *loadrevinfo(Revlog *changelog, int rev); + +/* tree */ +char *nodepath(char *s, char *e, Revnode *nd); +Revtree *loadfilestree(Revlog *changelog, Revlog *manifest, Revinfo *ri); +Revtree *loadchangestree(Revlog *changelog, Revlog *manifest, Revinfo *ri); +void closerevtree(Revtree *t); + diff --git a/sys/src/cmd/hgfs/fs.c b/sys/src/cmd/hgfs/fs.c new file mode 100644 index 000000000..77e9177e2 --- /dev/null +++ b/sys/src/cmd/hgfs/fs.c @@ -0,0 +1,587 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#include +#include +#include +#include +#include <9p.h> + +enum { + Qroot, + Qrev, + Qrev1, + Qrev2, + Qlog, + Qwho, + Qwhy, + Qfiles, + Qchanges, + Qtree, +}; + +static char *nametab[Qtree+1] = { + "/", + nil, + "rev1", + "rev2", + "log", + "who", + "why", + "files", + "changes", + nil, +}; + +static Revlog changelog; +static Revlog manifest; + +static Revinfo* +getrevinfo(int rev) +{ + Revinfo *ri; + + if(rev < 0 || rev >= changelog.nmap) + return nil; + if(ri = changelog.map[rev].aux) + return ri; + if(ri = loadrevinfo(&changelog, rev)) + changelog.map[rev].aux = ri; + return ri; +} + +static char* +fsmkuid(char *s) +{ + if(s){ + char *x; + + while(x = strchr(s, '<')) + s = x+1; + s = estrdup9p(s); + if(x = strchr(s, '>')) + *x = 0; + if(x = strchr(s, '@')) + *x = 0; + if(x = strchr(s, '\n')) + *x = 0; + } + if(s == nil || *s == 0){ + free(s); + s = estrdup9p("hgfs"); + } + return s; +} + +static void +fsmkqid(Qid *q, int level, void *aux) +{ + Revnode *nd; + Revinfo *ri; + + switch(level){ + case Qroot: + q->type = QTDIR; + q->path = Qroot; + q->vers = 0; + break; + case Qrev: + case Qfiles: + case Qchanges: + q->type = QTDIR; + if(0){ + case Qrev1: + case Qrev2: + case Qlog: + case Qwho: + case Qwhy: + q->type = 0; + } + ri = aux; + q->path = *((uvlong*)ri->chash) + (level - Qrev); + q->vers = 0; + break; + case Qtree: + nd = aux; + if(nd->hash){ + q->type = 0; + } else { + q->type = QTDIR; + } + q->path = nd->path; + q->vers = 0; + break; + } +} + +static void +fsmkdir(Dir *d, int level, void *aux) +{ + char buf[64], *s; + Revnode *nd; + Revinfo *ri; + int rev; + + memset(d, 0, sizeof(*d)); + + fsmkqid(&d->qid, level, aux); + + d->mode = 0444; + if(d->qid.type == QTDIR) + d->mode |= DMDIR | 0111; + + s = nil; + ri = nil; + switch(level){ + case Qroot: + goto Namegen; + case Qrev: + case Qrev1: + case Qrev2: + ri = aux; + rev = hashrev(&changelog, ri->chash); + if(level == Qrev1) + rev = changelog.map[rev].p1rev; + else if(level == Qrev2) + rev = changelog.map[rev].p2rev; + if(rev >= 0) + snprint(s = buf, sizeof(buf), "%d.%H", rev, changelog.map[rev].hash); + if(level == Qrev){ + d->name = estrdup9p(buf); + break; + } + goto Strgen; + case Qlog: + ri = aux; + if((rev = hashrev(&changelog, ri->chash)) >= 0) + d->length = changelog.map[rev].flen; + goto Namegen; + case Qwho: + ri = aux; + s = ri->who; + goto Strgen; + case Qwhy: + ri = aux; + s = ri->why; + Strgen: + d->length = s ? strlen(s)+1 : 0; + case Qfiles: + case Qchanges: + ri = aux; + /* no break */ + Namegen: + d->name = estrdup9p(nametab[level]); + break; + case Qtree: + nd = aux; + d->name = estrdup9p(nd->name); + if(nd->hash){ + char path[MAXPATH]; + Revlog rl; + + nodepath(seprint(path, path+MAXPATH, ".hg/store/data"), path+MAXPATH, nd); + if(revlogopen(&rl, path, OREAD) < 0) + break; + if((rev = hashrev(&rl, nd->hash)) >= 0){ + d->length = rl.map[rev].flen; + ri = getrevinfo(rl.map[rev].linkrev); + } + revlogclose(&rl); + } + break; + } + + if(ri){ + d->atime = d->mtime = ri->when; + d->muid = fsmkuid(ri->who); + d->uid = fsmkuid(ri->who); + } else + d->atime = d->mtime = time(0); + + if(d->uid == nil) + d->uid = fsmkuid(nil); + if(d->gid == nil) + d->gid = fsmkuid(nil); + if(d->muid == nil) + d->muid = fsmkuid(nil); +} + +static void +fsattach(Req *r) +{ + Revfile *rf; + + if(r->ifcall.aname && r->ifcall.aname[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->fid->qid.path = Qroot; + r->fid->qid.type = QTDIR; + r->fid->qid.vers = 0; + r->ofcall.qid = r->fid->qid; + + rf = emalloc9p(sizeof(*rf)); + rf->level = Qroot; + rf->info = nil; + rf->tree = nil; + rf->node = nil; + + rf->fd = -1; + rf->buf = nil; + + r->fid->aux = rf; + + respond(r, nil); +} + +static void +fsstat(Req *r) +{ + Revfile *rf; + + rf = r->fid->aux; + if(rf->level < Qtree) + fsmkdir(&r->d, rf->level, rf->info); + else + fsmkdir(&r->d, rf->level, rf->node); + respond(r, nil); +} + +static int +findrev(Revlog *rl, char *name) +{ + uchar hash[HASHSZ]; + int n, i, rev; + char *s; + + rev = strtol(name, &s, 10); + if(s > name && (*s == 0 || ispunct(*s))) + return rev; + rev = -1; + if(s = strchr(name, '.')) + name = s+1; + if((n = strhash(name, hash)) > 0){ + for(i=0; inmap; i++){ + if(memcmp(rl->map[i].hash, hash, n) == 0){ + if(rev < 0) + rev = i; + else { + rev = -1; + break; + } + } + } + } + return rev; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + Revfile *rf; + Revnode *nd; + int i; + + if(!(fid->qid.type&QTDIR)) + return "walk in non-directory"; + + rf = fid->aux; + if(strcmp(name, "..") == 0){ + switch(rf->level){ + case Qroot: + break; + case Qrev: + rf->info = nil; + rf->level = Qroot; + break; + case Qfiles: + case Qchanges: + closerevtree(rf->tree); + rf->tree = nil; + rf->level = Qrev; + break; + case Qtree: + if((rf->node = rf->node->up) == rf->tree->root) + rf->level = rf->tree->level; + break; + } + } else { + switch(rf->level){ + case Qroot: + i = findrev(&changelog, name); + if(rf->info = getrevinfo(i)){ + rf->level = Qrev; + break; + } + Notfound: + return "directory entry not found"; + break; + case Qrev: + for(i = Qrev+1; i < Qtree; i++){ + if(nametab[i] == nil) + continue; + if(strcmp(name, nametab[i]) == 0) + break; + } + switch(i){ + case Qtree: + goto Notfound; + case Qfiles: + if((rf->tree = loadfilestree(&changelog, &manifest, rf->info)) == nil) + goto Notfound; + break; + case Qchanges: + if((rf->tree = loadchangestree(&changelog, &manifest, rf->info)) == nil) + goto Notfound; + break; + } + if(rf->tree){ + rf->node = rf->tree->root; + rf->tree->level = i; + } + rf->level = i; + break; + case Qtree: + case Qfiles: + case Qchanges: + for(nd = rf->node->down; nd; nd = nd->next) + if(strcmp(nd->name, name) == 0) + break; + if(nd == nil) + goto Notfound; + rf->node = nd; + rf->level = Qtree; + break; + } + } + + if(rf->level < Qtree) + fsmkqid(qid, rf->level, rf->info); + else + fsmkqid(qid, rf->level, rf->node); + fid->qid = *qid; + + return nil; +} + +static char* +fsclone(Fid *oldfid, Fid *newfid) +{ + Revfile *orf, *rf; + + rf = nil; + if(orf = oldfid->aux){ + rf = emalloc9p(sizeof(*rf)); + *rf = *orf; + if(rf->tree) + incref(rf->tree); + if(rf->fd >= 0) + rf->fd = dup(rf->fd, -1); + if(rf->buf) + rf->buf = estrdup9p(rf->buf); + } + newfid->aux = rf; + return nil; +} + +static void +fsdestroyfid(Fid *fid) +{ + Revfile *rf; + + if(rf = fid->aux){ + closerevtree(rf->tree); + if(rf->fd >= 0) + close(rf->fd); + free(rf->buf); + free(rf); + } +} + +static void +fsopen(Req *r) +{ + respond(r, nil); +} + +static int +rootgen(int i, Dir *d, void *) +{ + Revinfo *ri; + + if((ri = getrevinfo(i)) == nil) + return -1; + fsmkdir(d, Qrev, ri); + return 0; +} + +static int +revgen(int i, Dir *d, void *aux) +{ + i += Qrev+1; + if(i >= Qtree) + return -1; + fsmkdir(d, i, aux); + return 0; +} + +static int +treegen(int i, Dir *d, void *aux) +{ + Revnode *nd = aux; + + while(i > 0 && nd){ + nd = nd->next; + i--; + } + if(i || nd == nil) + return -1; + fsmkdir(d, Qtree, nd); + return 0; +} + +static void +fsread(Req *r) +{ + Revfile *rf; + + rf = r->fid->aux; + if(r->fid->qid.type == QTDIR){ + switch(rf->level){ + default: + respond(r, "bug in fsread"); + return; + case Qroot: + dirread9p(r, rootgen, nil); + respond(r, nil); + return; + case Qrev: + dirread9p(r, revgen, rf->info); + respond(r, nil); + return; + case Qtree: + case Qfiles: + case Qchanges: + dirread9p(r, treegen, rf->node->down); + respond(r, nil); + return; + } + } else { + char buf[MAXPATH]; + Revlog rl; + char *s; + int i, n; + + switch(rf->level){ + default: + respond(r, "bug in fsread"); + return; + case Qlog: + if(rf->fd >= 0) + break; + if((rf->fd = revlogopentemp(&changelog, hashrev(&changelog, rf->info->chash))) < 0){ + responderror(r); + return; + } + break; + case Qrev1: + case Qrev2: + s = nil; + if((i = hashrev(&changelog, rf->info->chash)) >= 0){ + if(rf->level == Qrev1) + i = changelog.map[i].p1rev; + else + i = changelog.map[i].p2rev; + if(i >= 0) + snprint(s = buf, sizeof(buf), "%d.%H", i, changelog.map[i].hash); + } + goto Strgen; + case Qwho: + s = rf->info->who; + goto Strgen; + case Qwhy: + s = rf->info->why; + Strgen: + if(rf->buf == nil) + rf->buf = s ? smprint("%s\n", s) : estrdup9p(""); + readstr(r, rf->buf); + respond(r, nil); + return; + case Qtree: + if(rf->fd >= 0) + break; + nodepath(seprint(buf, buf+sizeof(buf), ".hg/store/data"), buf+sizeof(buf), rf->node); + if(revlogopen(&rl, buf, OREAD) < 0){ + responderror(r); + return; + } + if((rf->fd = revlogopentemp(&rl, hashrev(&rl, rf->node->hash))) < 0){ + responderror(r); + revlogclose(&rl); + return; + } + revlogclose(&rl); + break; + } + if((n = pread(rf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset)) < 0){ + responderror(r); + return; + } + r->ofcall.count = n; + respond(r, nil); + } +} + +Srv fs = +{ + .attach=fsattach, + .stat=fsstat, + .walk1=fswalk1, + .clone=fsclone, + .destroyfid=fsdestroyfid, + .open=fsopen, + .read=fsread, +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-D] [-m mtpt] [-s srv]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + char *srv, *mtpt; + + inflateinit(); + fmtinstall('H', Hfmt); + + srv = nil; + mtpt = "/n/hg"; + + ARGBEGIN { + case 'D': + chatty9p++; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + srv = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + if(revlogopen(&changelog, ".hg/store/00changelog", OREAD) < 0) + sysfatal("can't open changelog: %r\n"); + if(revlogopen(&manifest, ".hg/store/00manifest", OREAD) < 0) + sysfatal("can't open menifest: %r\n"); + + postmountsrv(&fs, srv, mtpt, MREPL); +} diff --git a/sys/src/cmd/hgfs/hash.c b/sys/src/cmd/hgfs/hash.c new file mode 100644 index 000000000..768e35759 --- /dev/null +++ b/sys/src/cmd/hgfs/hash.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#include +#include + +int +Hfmt(Fmt *f) +{ + uchar *p; + + p = va_arg(f->args, uchar*); + return fmtprint(f, + "%.2x%.2x%.2x%.2x%.2x%.2x", + p[0], p[1], p[2], p[3], p[4], p[5]); +} + +int +fhash(int fd, uchar p1[], uchar p2[], uchar h[]) +{ + DigestState *ds; + uchar buf[BUFSZ]; + int n; + + ds = nil; + memset(h, 0, HASHSZ); + if(memcmp(p1, p2, HASHSZ) > 0){ + ds = sha1(p2, HASHSZ, nil, ds); + sha1(p1, HASHSZ, nil, ds); + } else { + ds = sha1(p1, HASHSZ, nil, ds); + sha1(p2, HASHSZ, nil, ds); + } + while((n = read(fd, buf, BUFSZ)) > 0) + sha1(buf, n, nil, ds); + sha1(buf, 0, h, ds); + + return 0; +} + +int +strhash(char *s, uchar *h) +{ + uchar *b; + int n; + + b = h; + memset(h, 0, HASHSZ); + n = HASHSZ*2; + while(*s && n > 0){ + if(*s >= '0' && *s <= '9') + *h |= *s - '0'; + else if(*s >= 'a' && *s <= 'f') + *h |= 10 + *s - 'a'; + else if(*s >= 'A' && *s <= 'F') + *h |= 10 + *s - 'A'; + else + break; + if(n-- & 1) + h++; + else + *h <<= 4; + s++; + } + return h - b; +} diff --git a/sys/src/cmd/hgfs/info.c b/sys/src/cmd/hgfs/info.c new file mode 100644 index 000000000..66246a9e2 --- /dev/null +++ b/sys/src/cmd/hgfs/info.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +Revinfo* +loadrevinfo(Revlog *changelog, int rev) +{ + char buf[BUFSZ], *p, *e; + int fd, line, inmsg, n; + Revinfo *ri; + + if((fd = revlogopentemp(changelog, rev)) < 0) + return nil; + + seek(fd, 0, 2); + write(fd, "\n", 1); + seek(fd, 0, 0); + + ri = malloc(sizeof(*ri)); + memset(ri, 0, sizeof(*ri)); + + memmove(ri->chash, changelog->map[rev].hash, HASHSZ); + + line = 0; + inmsg = 0; + p = buf; + e = buf + BUFSZ; + while((n = read(fd, p, e - p)) > 0){ + p += n; + while((p > buf) && (e = memchr(buf, '\n', p - buf))){ + *e++ = 0; + + switch(line++){ + case 0: + strhash(buf, ri->mhash); + break; + case 1: + ri->who = strdup(buf); + break; + case 2: + ri->when = strtol(buf, nil, 10); + break; + default: + if(!inmsg){ + if(*buf == 0) + inmsg = 1; + } else { + n = ri->why ? strlen(ri->why) : 0; + ri->why = realloc(ri->why, n + strlen(buf)+1); + strcpy(ri->why + n, buf); + } + } + p -= e - buf; + if(p > buf) + memmove(buf, e, p - buf); + } + e = buf + BUFSZ; + } + close(fd); + + return ri; +} diff --git a/sys/src/cmd/hgfs/mkfile b/sys/src/cmd/hgfs/mkfile new file mode 100644 index 000000000..6d08daba2 --- /dev/null +++ b/sys/src/cmd/hgfs/mkfile @@ -0,0 +1,10 @@ + +#include +#include +#include "dat.h" +#include "fns.h" + +int +fcopy(int dfd, int sfd, vlong len) +{ + uchar buf[BUFSZ]; + int n; + + while(len > 0){ + if((n = BUFSZ) > len) + n = len; + if((n = read(sfd, buf, n)) < 0) + return -1; + if(write(dfd, buf, n) != n) + return -1; + len -= n; + } + return 0; +} + +static uchar patchmark[12] = { + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, +}; + +int +fpatchmark(int pfd, char *) +{ + if(write(pfd, patchmark, 12) != 12) + return -1; + return 0; +} + +typedef struct Frag Frag; +struct Frag +{ + Frag *next; + int fd; + vlong len; + vlong off; +}; + +int +fpatch(int ofd, int bfd, int pfd) +{ + vlong off, fstart, fend, start, end, len; + int err, front, back; + Frag *h, *f, *p; + uchar buf[12]; + + h = nil; + err = -1; + + if(bfd >= 0){ + h = malloc(sizeof(Frag)); + h->next = nil; + h->off = 0; + h->fd = bfd; + h->len = seek(h->fd, 0, 2); + if(h->len < 0) + goto errout; + } + + off = 0; + while(pfd >= 0){ + if(readn(pfd, buf, 12) != 12) + break; + + if(memcmp(buf, patchmark, 12) == 0){ + off = 0; + continue; + } + + start = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3]; + end = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; + len = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11]; + + if(start > end){ + werrstr("bad patch: start > end"); + goto errout; + } + + start += off; + end += off; + off += start + len - end; + + fstart = 0; + for(f = h; f; f = f->next, fstart = fend){ + fend = fstart + f->len; + if(fend <= start) + continue; + if(fstart >= end) + break; + + front = start > fstart; + back = end < fend; + if(front && back){ + p = malloc(sizeof(Frag)); + *p = *f; + f->next = p; + f->len = start - fstart; + p->off += end - fstart; + p->len -= end - fstart; + break; + } else if(back){ + f->off += end - fstart; + f->len -= end - fstart; + break; + } else if(front){ + f->len = start - fstart; + } else { + f->len = 0; + } + } + + fstart = 0; + for(p = nil, f = h; f && fstart < start; p = f, f = f->next) + fstart += f->len; + + f = malloc(sizeof(Frag)); + f->fd = pfd; + f->len = len; + f->off = seek(f->fd, 0, 1); + + if(p){ + f->next = p->next; + p->next = f; + } else { + f->next = h; + h = f; + } + + if(f->off < 0) + goto errout; + if(seek(pfd, f->len, 1) < 0) + goto errout; + } + + for(f = h; f; f = f->next){ + if(seek(f->fd, f->off, 0) < 0) + goto errout; + if(fcopy(ofd, f->fd, f->len) < 0) + goto errout; + } + err = 0; + +errout: + while(f = h){ + h = f->next; + free(f); + } + + return err; +} diff --git a/sys/src/cmd/hgfs/revlog.c b/sys/src/cmd/hgfs/revlog.c new file mode 100644 index 000000000..e4d9fcfa1 --- /dev/null +++ b/sys/src/cmd/hgfs/revlog.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +int +fmktemp(void) +{ + char temp[MAXPATH]; + + snprint(temp, sizeof(temp), "/tmp/hgXXXXXXXXXXX"); + return create(mktemp(temp), OTRUNC|ORCLOSE|ORDWR, 0666); +} + +static void +readindex(Revlog *r) +{ + uchar buf[64]; + Revmap *m; + int rev; + + seek(r->ifd, 0, 0); + for(rev=0;;rev++){ + if(readn(r->ifd, buf, sizeof(buf)) != sizeof(buf)) + break; + if((rev % 16) == 0) + r->map = realloc(r->map, sizeof(r->map[0])*(rev+16)); + m = &r->map[rev]; + memset(m, 0, sizeof(*m)); + m->hoff = (vlong)buf[0]<<40 | + (vlong)buf[1]<<32 | + (vlong)buf[2]<<24 | + (vlong)buf[3]<<16 | + (vlong)buf[4]<<8 | + (vlong)buf[5]; + if(rev == 0) + m->hoff &= 0xFFFF; + m->rev = rev; + m->flags = buf[6]<<8 | buf[7]; + m->hlen = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11]; + m->flen = buf[12]<<24 | buf[13]<<16 | buf[14]<<8 | buf[15]; + m->baserev = buf[16]<<24 | buf[17]<<16 | buf[18]<<8 | buf[19]; + m->linkrev = buf[20]<<24 | buf[21]<<16 | buf[22]<<8 | buf[23]; + m->p1rev = buf[24]<<24 | buf[25]<<16 | buf[26]<<8 | buf[27]; + m->p2rev = buf[28]<<24 | buf[29]<<16 | buf[30]<<8 | buf[31]; + memmove(m->hash, buf+32, HASHSZ); + + if(r->dfd < 0){ + m->hoff = seek(r->ifd, 0, 1); + seek(r->ifd, m->hlen, 1); + } + } + r->nmap = rev; +} + +int +revlogopen(Revlog *r, char *path, int mode) +{ + r->ifd = -1; + r->dfd = -1; + path = smprint("%s.i", path); + if((r->ifd = open(path, mode)) < 0){ + free(path); + return -1; + } + path[strlen(path)-1] = 'd'; + r->dfd = open(path, mode); + free(path); + r->nmap = 0; + r->map = nil; + readindex(r); + return 0; +} + +void +revlogclose(Revlog *r) +{ + if(r->ifd >= 0){ + close(r->ifd); + r->ifd = -1; + } + if(r->dfd >= 0){ + close(r->dfd); + r->dfd = -1; + } + free(r->map); + r->map = nil; + r->nmap = 0; +} + +uchar* +revhash(Revlog *r, int rev) +{ + static uchar nullid[HASHSZ]; + if(rev < 0 || rev >= r->nmap) + return nullid; + return r->map[rev].hash; +} + +int +hashrev(Revlog *r, uchar hash[]) +{ + int rev; + + for(rev=0; revnmap; rev++) + if(memcmp(r->map[rev].hash, hash, HASHSZ) == 0) + return rev; + return -1; +} + +static int +prevlogrev(Revlog *r, int rev) +{ + if(r->map[rev].baserev == rev) + return -1; + return rev-1; +} + +static int +prevbundlerev(Revlog *r, int rev) +{ + if(r->map[rev].baserev == rev) + return -1; + return r->map[rev].p1rev; +} + +static Revmap** +getchain1(Revlog *r, int rev, int *count, int (*next)(Revlog *, int)) +{ + Revmap **chain; + + if(rev < 0 || rev >= r->nmap){ + if(*count <= 0) + return nil; + chain = malloc(sizeof(chain[0]) * ((*count)+1)); + chain[*count] = nil; + *count = 0; + }else{ + (*count)++; + if(chain = getchain1(r, (*next)(r, rev), count, next)) + chain[(*count)++] = &r->map[rev]; + } + return chain; +} + +static Revmap** +getchain(Revlog *r, int rev, int (*next)(Revlog *, int)) +{ + int count = 0; + return getchain1(r, rev, &count, next); +} + +int +revlogextract(Revlog *r, int rev, int ofd) +{ + int err, hfd, pfd, bfd; + uchar hash[HASHSZ]; + Revmap **chain, *m; + char buf[32]; + vlong off; + int i; + + err = -1; + bfd = -1; + pfd = -1; + + if((chain = getchain(r, rev, prevlogrev)) == nil){ + werrstr("bad patch chain"); + goto errout; + } + + off = seek(ofd, 0, 1); + if(off < 0){ + werrstr("seek outfile: %r"); + goto errout; + } + hfd = r->dfd < 0 ? r->ifd : r->dfd; + for(i=0; m = chain[i]; i++){ + if(seek(hfd, m->hoff, 0) < 0){ + werrstr("seek index: %r"); + goto errout; + } + if(m == chain[0] && m->baserev == m->rev){ + if(chain[1]){ + if(bfd >= 0 && bfd != ofd) + close(bfd); + if((bfd = fmktemp()) < 0){ + werrstr("create basefile: %r"); + goto errout; + } + } else + bfd = ofd; + if(funzip(bfd, hfd, m->hlen) < 0){ + werrstr("unzip basefile: %r"); + goto errout; + } + } else { + if(pfd < 0){ + if((pfd = fmktemp()) < 0){ + werrstr("create patchfile: %r"); + goto errout; + } + } + + /* put a mark before the patch data */ + snprint(buf, sizeof(buf), "%H", m->hash); + if(fpatchmark(pfd, buf) < 0){ + werrstr("patchmark: %r"); + goto errout; + } + + if(funzip(pfd, hfd, m->hlen) < 0){ + werrstr("unzip patchfile: %r"); + goto errout; + } + } + } + m = chain[i-1]; + + if(pfd >= 0 && bfd >= 0 && bfd != ofd){ + if(seek(pfd, 0, 0) < 0){ + werrstr("seek patchfile: %r"); + goto errout; + } + if(fpatch(ofd, bfd, pfd) < 0){ + werrstr("patch: %r"); + goto errout; + } + } + + if(seek(ofd, off, 0) < 0){ + werrstr("seek outfile: %r"); + goto errout; + } + if(fhash(ofd, revhash(r, m->p1rev), revhash(r, m->p2rev), hash) < 0){ + werrstr("hash outfile: %r"); + goto errout; + } + if(memcmp(m->hash, hash, HASHSZ)){ + werrstr("got bad hash"); + goto errout; + } + err = 0; + +errout: + if(pfd >= 0) + close(pfd); + if(bfd >= 0 && bfd != ofd) + close(bfd); + free(chain); + + return err; +} + +int +revlogopentemp(Revlog *r, int rev) +{ + int fd; + + if((fd = fmktemp()) < 0) + return -1; + if(revlogextract(r, rev, fd) < 0){ + close(fd); + return -1; + } + if(seek(fd, 0, 0) < 0){ + close(fd); + return -1; + } + return fd; +} diff --git a/sys/src/cmd/hgfs/tree.c b/sys/src/cmd/hgfs/tree.c new file mode 100644 index 000000000..118c9ac5e --- /dev/null +++ b/sys/src/cmd/hgfs/tree.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +char* +nodepath(char *s, char *e, Revnode *nd) +{ + static char *frogs[] = { + "con", "prn", "aux", "nul", + "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", + "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", + }; + char *p; + int i; + + if(nd == nil || nd->name == nil) + return s; + + s = seprint(nodepath(s, e, nd->up), e, "/"); + + p = nd->name; + for(i=0; i= 'A' && *p <= 'Z'){ + *s++ = '_'; + *s++ = 'a' + (*p - 'A'); + } else if(*p >= 126 || strchr("\\:*?\"<>|", *p)){ + *s++ = '~'; + s = seprint(s, e, "%.2x", *p); + } else + *s++ = *p; + } + *s = 0; + + return s; +} + +static void +addnode(Revnode *d, char *path, uchar *hash) +{ + char *slash, *x; + Revnode *c, *p; + + while(path && *path){ + if(slash = strchr(path, '/')) + *slash++ = 0; + p = nil; + for(c = d->down; c; p = c, c = c->next) + if(strcmp(c->name, path) == 0) + break; + if(c == nil){ + c = malloc(sizeof(*c) + (!slash ? HASHSZ : 0) + strlen(path)+1); + c->path = 1; + x = (char*)&c[1]; + if(!slash){ + memmove(c->hash = (uchar*)x, hash, HASHSZ); + x += HASHSZ; + } else + c->hash = nil; + strcpy(c->name = x, path); + c->up = d; + c->down = nil; + if(p){ + c->next = p->next; + p->next = c; + } else { + c->next = d->down; + d->down = c; + } + + if(c->hash){ + p = c; + p->path = *((uvlong*)c->hash); + while(d->up){ + d->path += p->path; + p = d; + d = d->up; + } + } + } + d = c; + path = slash; + } +} + +typedef struct Hashstr Hashstr; +struct Hashstr +{ + Hashstr *next; + char str[]; +}; + +static ulong +hashstr(char *s) +{ + ulong h, t; + char c; + + h = 0; + while(c = *s++){ + t = h & 0xf8000000; + h <<= 5; + h ^= t>>27; + h ^= (ulong)c; + } + return h; +} + +static int +loadmanifest(Revnode *root, int fd, Hashstr **ht, int nh) +{ + char buf[BUFSZ], *p, *e; + uchar hash[HASHSZ]; + int n; + + p = buf; + e = buf + BUFSZ; + while((n = read(fd, p, e - p)) > 0){ + p += n; + while((p > buf) && (e = memchr(buf, '\n', p - buf))){ + *e++ = 0; + + strhash(buf + strlen(buf) + 1, hash); + if(ht == nil) + addnode(root, buf, hash); + else { + Hashstr *he; + + for(he = ht[hashstr(buf) % nh]; he; he = he->next){ + if(strcmp(he->str, buf) == 0){ + addnode(root, buf, hash); + break; + } + } + } + + p -= e - buf; + if(p > buf) + memmove(buf, e, p - buf); + } + e = buf + BUFSZ; + } + return 0; +} + +static Revtree* +loadtree(Revlog *manifest, Revinfo *ri, Hashstr **ht, int nh) +{ + Revtree *t; + int fd; + + if((fd = revlogopentemp(manifest, hashrev(manifest, ri->mhash))) < 0) + return nil; + + t = malloc(sizeof(*t)); + memset(t, 0, sizeof(*t)); + incref(t); + + t->info = ri; + + t->root = malloc(sizeof(Revnode)); + t->root->path = 0; + t->root->name = 0; + t->root->up = nil; + t->root->down = nil; + t->root->next = nil; + t->root->hash = nil; + + if(loadmanifest(t->root, fd, ht, nh) < 0){ + close(fd); + closerevtree(t); + return nil; + } + + close(fd); + + return t; +} + +Revtree* +loadfilestree(Revlog *, Revlog *manifest, Revinfo *ri) +{ + return loadtree(manifest, ri, nil, 0); +} + +Revtree* +loadchangestree(Revlog *changelog, Revlog *manifest, Revinfo *ri) +{ + char buf[BUFSZ], *p, *e; + Hashstr *ht[256], *he, **hp; + int fd, done, line, n; + Revtree *t; + + if((fd = revlogopentemp(changelog, hashrev(changelog, ri->chash))) < 0) + return nil; + + done = 0; + line = 0; + memset(ht, 0, sizeof(ht)); + + p = buf; + e = buf + BUFSZ; + while((n = read(fd, p, e - p)) > 0){ + p += n; + while((p > buf) && (e = memchr(buf, '\n', p - buf))){ + *e++ = 0; + + if(++line >= 4){ + if(*buf == 0){ + done = 1; + break; + } + + he = malloc(sizeof(*he) + strlen(buf)+1); + hp = &ht[hashstr(strcpy(he->str, buf)) % nelem(ht)]; + he->next = *hp; + *hp = he; + } + + p -= e - buf; + if(p > buf) + memmove(buf, e, p - buf); + } + if(done) + break; + e = buf + BUFSZ; + } + close(fd); + + t = loadtree(manifest, ri, ht, nelem(ht)); + + for(hp = ht; hp != &ht[nelem(ht)]; hp++){ + while(he = *hp){ + *hp = he->next; + free(he); + } + } + + return t; +} + +static void +freenode(Revnode *nd) +{ + if(nd == nil) + return; + freenode(nd->down); + freenode(nd->next); + free(nd); +} + +void +closerevtree(Revtree *t) +{ + if(t == nil || decref(t)) + return; + freenode(t->root); + free(t); +} diff --git a/sys/src/cmd/hgfs/zip.c b/sys/src/cmd/hgfs/zip.c new file mode 100644 index 000000000..31ab65f64 --- /dev/null +++ b/sys/src/cmd/hgfs/zip.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#include + +struct zbuf { + int fd; + int len; + + uchar *b, *p, *e; +}; + +static int +zwrite(void *a, void *p, int n) +{ + int *ofd = a; + + if(write(*ofd, p, n) != n) + return -1; + return n; +} + +static int +zgetc(void *a) +{ + struct zbuf *z = a; + int n; + + for(;;){ + if(z->p < z->e) + return *z->p++; + if(z->len <= 0) + return -1; + if((n = BUFSZ) > z->len) + n = z->len; + if((n = read(z->fd, z->p = z->b, n)) <= 0) + return -1; + z->len -= n; + z->e = z->p + n; + } +} + +int +funzip(int ofd, int zfd, int len) +{ + uchar buf[BUFSZ]; + struct zbuf z; + int wlen, n; + uchar *p; + + if(len == 0) + return 0; + + wlen = 0; + if((n = BUFSZ) > len) + n = len; + if((n = read(zfd, p = buf, n)) <= 0) + return -1; + len -= n; + switch(*p){ + default: + return -1; + case 'u': + p++; + n--; + /* no break */ + case '\0': + while((n > 0) && (write(ofd, p, n) == n)){ + wlen += n; + if(len <= 0) + break; + if((n = BUFSZ) > len) + n = len; + if((n = read(zfd, p = buf, n)) <= 0) + break; + len -= n; + } + break; + case 'x': + if(n < 2) + return -1; + z.fd = zfd; + z.len = len; + z.b = buf; + z.p = p + 2; + z.e = p + n; + if((wlen = inflate(&ofd, zwrite, &z, zgetc)) < 0){ + werrstr("%s", flateerr(wlen)); + return -1; + } + break; + } + return wlen; +}