ref: bc1ff6985c03402497a052d2b4e2a299b3cdf218
parent: 8fa679d04171417d9420b91471cf45e148b4cc50
author: cinap_lenrek <cinap_lenrek@localhost>
date: Sun Jun 26 03:03:12 EDT 2011
add hgfs, a mercurial filesystem
--- /dev/null
+++ b/sys/src/cmd/hgfs/dat.h
@@ -1,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;
+};
--- /dev/null
+++ b/sys/src/cmd/hgfs/fns.h
@@ -1,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);
+
--- /dev/null
+++ b/sys/src/cmd/hgfs/fs.c
@@ -1,0 +1,587 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+#include <ctype.h>
+#include <flate.h>
+#include <auth.h>
+#include <fcall.h>
+#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; i<rl->nmap; 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);
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/hash.c
@@ -1,0 +1,69 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+#include <mp.h>
+#include <libsec.h>
+
+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;
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/info.c
@@ -1,0 +1,64 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/mkfile
@@ -1,0 +1,10 @@
+</$objtype/mkfile
+
+TARG=hgfs
+CFLAGS=-FTVw
+
+HFILES=dat.h fns.h
+
+OFILES=zip.$O patch.$O hash.$O revlog.$O tree.$O info.$O fs.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/hgfs/patch.c
@@ -1,0 +1,159 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/revlog.c
@@ -1,0 +1,272 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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; rev<r->nmap; 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;
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/tree.c
@@ -1,0 +1,267 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#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<nelem(frogs); i++)
+ if(strcmp(frogs[i], p) == 0)
+ return seprint(s, e, "%.2s~%.2x%s", p, p[2], p+3);
+
+ for(; s+4 < e && *p; p++){
+ if(*p == '_'){
+ *s++ = '_';
+ *s++ = '_';
+ } else if(*p >= '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);
+}
--- /dev/null
+++ b/sys/src/cmd/hgfs/zip.c
@@ -1,0 +1,97 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+#include <flate.h>
+
+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;
+}