#include <u.h>#include <libc.h>#include <ctype.h>#include <fcall.h>#include <thread.h>#include <9p.h> #include "git.h" enum { Qroot, Qhead, Qbranch, Qcommit, Qmsg, Qparent, Qtree, Qhash, Qauthor, Qcommitter, Qobject, Qctl, Qmax, Internal=1<<7,}; typedef struct Gitaux Gitaux;typedef struct Crumb Crumb;typedef struct Cache Cache;typedef struct Uqid Uqid;struct Crumb { char *name; Object *obj; Qid qid; int mode; vlong mtime;}; struct Gitaux { int ncrumb; Crumb *crumb; char *refpath; int qdir; /* For listing object dir */ Objlist *ols; Object *olslast;}; struct Uqid { vlong uqid; vlong ppath; vlong oid; int t; int idx;}; struct Cache { Uqid *cache; int n; int max;}; char *qroot[] = { "HEAD", "branch", "object", "ctl",}; #define Eperm "permission denied"#define Eexist "file does not exist"#define Enotdir "is not a directory"#define E2long "path too long"#define Enodir "not a directory"#define Erepo "unable to read repo"#define Eimpl "not implemented"#define Egreg "wat"#define Ebadobj "invalid object"#define Ename "invalid path element" char gitdir[512];char *username;char *groupname;char *mntpt = ".git/fs";char **branches = nil;Cache uqidcache[512];vlong nextqid = Qmax; static Object* walklink(Gitaux *, char *, int, int, int*); vlongqpath(Crumb *p, int idx, vlong id, vlong t){ int h, i; vlong pp; Cache *c; Uqid *u; pp = p ? p->qid.path : 0; h = (pp*333 + id*7 + t) & (nelem(uqidcache) - 1); c = &uqidcache[h]; u = c->cache; for(i=0; i <c->n ; i++){ if(u->ppath == pp && u->oid == id && u->t == t && u->idx == idx) return (u->uqid << 8) | t; u++; } if(c->n == c->max){ c->max += c->max/2 + 1; c->cache = erealloc(c->cache, c->max*sizeof(Uqid)); } nextqid++; c->cache[c->n] = (Uqid){nextqid, pp, id, t, idx}; c->n++; return (nextqid << 8) | t;} static Crumb*crumb(Gitaux *aux, int n){ if(n < aux->ncrumb) return &aux->crumb[aux->ncrumb - n - 1]; return nil;} static voidpopcrumb(Gitaux *aux){ Crumb *c; if(aux->ncrumb > 1){ c = crumb(aux, 0); free(c->name); unref(c->obj); aux->ncrumb--; }} static vlongbranchid(Gitaux *aux, char *path){ int i; for(i = 0; branches[i]; i++) if(strcmp(path, branches[i]) == 0) goto found; branches = realloc(branches, sizeof(char *)*(i + 2)); branches[i] = estrdup(path); branches[i + 1] = nil; found: if(aux){ if(aux->refpath) free(aux->refpath); aux->refpath = estrdup(branches[i]); } return i;} static voidobj2dir(Dir *d, Crumb *c, Object *o, char *name){ d->qid = c->qid; d->atime = c->mtime; d->mtime = c->mtime; d->mode = c->mode; d->name = estrdup9p(name); d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); if(o->type == GBlob || o->type == GTag){ d->qid.type = 0; d->mode &= 0777; d->length = o->size; } } static introotgen(int i, Dir *d, void *p){ Crumb *c; c = crumb(p, 0); if (i >= nelem(qroot)) return -1; d->mode = 0555 | DMDIR; d->name = estrdup9p(qroot[i]); d->qid.vers = 0; d->qid.type = strcmp(qroot[i], "ctl") == 0 ? 0 : QTDIR; d->qid.path = qpath(nil, i, i, Qroot); d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mtime = c->mtime; return 0;} static intbranchgen(int i, Dir *d, void *p){ Gitaux *aux; Dir *refs; Crumb *c; int n; aux = p; c = crumb(aux, 0); refs = nil; d->qid.vers = 0; d->qid.type = QTDIR; d->qid.path = qpath(c, i, branchid(aux, aux->refpath), Qbranch | Internal); d->mode = 0555 | DMDIR; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mtime = c->mtime; d->atime = c->mtime; if((n = slurpdir(aux->refpath, &refs)) < 0) return -1; if(i < n){ d->name = estrdup9p(refs[i].name); free(refs); return 0; }else{ free(refs); return -1; }} static intgtreegen(int i, Dir *d, void *p){ Object *o, *l, *e; Gitaux *aux; Crumb *c; int m; aux = p; c = crumb(aux, 0); e = c->obj; if(i >= e->tree->nent) return -1; m = e->tree->ent[i].mode; if(e->tree->ent[i].ismod) o = emptydir(); else if((o = readobject(e->tree->ent[i].h)) == nil) sysfatal("could not read object %H: %r", e->tree->ent[i].h); if(e->tree->ent[i].islink) if((l = walklink(aux, o->data, o->size, 0, &m)) != nil) o = l; d->qid.vers = 0; d->qid.type = o->type == GTree ? QTDIR : 0; d->qid.path = qpath(c, i, o->id, aux->qdir); d->mode = m; d->atime = c->mtime; d->mtime = c->mtime; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->name = estrdup9p(e->tree->ent[i].name); d->length = o->size; return 0;} static intgcommitgen(int i, Dir *d, void *p){ Object *o; Crumb *c; c = crumb(p, 0); o = c->obj; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mode = 0444; d->atime = o->commit->ctime; d->mtime = o->commit->ctime; d->qid.type = 0; d->qid.vers = 0; switch(i){ case 0: d->mode = DMDIR | gitdirmode; d->name = estrdup9p("tree"); d->qid.type = QTDIR; d->qid.path = qpath(c, i, o->id, Qtree); break; case 1: d->name = estrdup9p("parent"); d->qid.path = qpath(c, i, o->id, Qparent); break; case 2: d->name = estrdup9p("msg"); d->qid.path = qpath(c, i, o->id, Qmsg); break; case 3: d->name = estrdup9p("hash"); d->qid.path = qpath(c, i, o->id, Qhash); break; case 4: d->name = estrdup9p("author"); d->qid.path = qpath(c, i, o->id, Qauthor); break; default: free(d->uid); free(d->gid); free(d->muid); return -1; } return 0;} static intobjgen(int i, Dir *d, void *p){ Gitaux *aux; Object *o; Crumb *c; char name[64]; Objlist *ols; Hash h; aux = p; c = crumb(aux, 0); if(!aux->ols) aux->ols = mkols(); ols = aux->ols; o = nil; /* We tried to sent it, but it didn't fit */ if(aux->olslast && ols->idx == i + 1){ snprint(name, sizeof(name), "%H", aux->olslast->hash); obj2dir(d, c, aux->olslast, name); return 0; } while(ols->idx <= i){ if(olsnext(ols, &h) == -1) return -1; if((o = readobject(h)) == nil){ fprint(2, "corrupt object %H\n", h); return -1; } } if(o != nil){ snprint(name, sizeof(name), "%H", o->hash); obj2dir(d, c, o, name); unref(aux->olslast); aux->olslast = ref(o); return 0; } return -1;} static voidobjread(Req *r, Gitaux *aux){ Object *o; o = crumb(aux, 0)->obj; switch(o->type){ case GBlob: readbuf(r, o->data, o->size); break; case GTag: readbuf(r, o->data, o->size); break; case GTree: dirread9p(r, gtreegen, aux); break; case GCommit: dirread9p(r, gcommitgen, aux); break; default: sysfatal("invalid object type %d", o->type); }} static voidreadcommitparent(Req *r, Object *o){ char *buf, *p, *e; int i, n; /* 40 bytes per hash, 1 per nl, 1 for terminator */ n = o->commit->nparent * (40 + 1) + 1; buf = emalloc(n); p = buf; e = buf + n; for (i = 0; i < o->commit->nparent; i++) p = seprint(p, e, "%H\n", o->commit->parent[i]); readbuf(r, buf, p - buf); free(buf);} static voidgitattach(Req *r){ Gitaux *aux; Dir *d; if((d = dirstat(".git")) == nil) sysfatal("git/fs: %r"); if(getwd(gitdir, sizeof(gitdir)) == nil) sysfatal("getwd: %r"); aux = emalloc(sizeof(Gitaux)); aux->crumb = emalloc(sizeof(Crumb)); aux->crumb[0].qid = (Qid){Qroot, 0, QTDIR}; aux->crumb[0].obj = nil; aux->crumb[0].mode = DMDIR | 0555; aux->crumb[0].mtime = d->mtime; aux->crumb[0].name = estrdup("/"); aux->ncrumb = 1; r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; r->fid->qid = r->ofcall.qid; r->fid->aux = aux; respond(r, nil);} static Object*walklink(Gitaux *aux, char *link, int nlink, int ndotdot, int *mode){ char *p, *e, *path; Object *o, *n; int i; path = emalloc(nlink + 1); memcpy(path, link, nlink); cleanname(path); o = crumb(aux, ndotdot)->obj; assert(o->type == GTree); for(p = path; *p; p = e){ n = nil; e = p + strcspn(p, "/"); if(*e == '/') *e++ = '\0'; /* * cleanname guarantees these show up at the start of the name, * which allows trimming them from the end of the trail of crumbs * instead of needing to keep track of full parentage. */ if(strcmp(p, "..") == 0) n = crumb(aux, ++ndotdot)->obj; else if(o->type == GTree) for(i = 0; i < o->tree->nent; i++) if(strcmp(o->tree->ent[i].name, p) == 0){ *mode = o->tree->ent[i].mode; n = readobject(o->tree->ent[i].h); break; } o = n; if(o == nil) break; } free(path); for(i = 0; o != nil && i < aux->ncrumb; i++) if(crumb(aux, i)->obj == o) return nil; return o;} static char *objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux *aux){ Object *w, *l; char *e; int i, m; w = nil; e = nil; if(!o) return Eexist; switch(o->type){ case GBlob: e = Enotdir; break; case GTree: q->type = 0; for(i = 0; i < o->tree->nent; i++){ if(strcmp(o->tree->ent[i].name, name) != 0) continue; m = o->tree->ent[i].mode; w = readobject(o->tree->ent[i].h); if(!w && o->tree->ent[i].ismod) w = emptydir(); if(w && o->tree->ent[i].islink) if((l = walklink(aux, w->data, w->size, 1, &m)) != nil) w = l; if(!w) return Ebadobj; q->type = (w->type == GTree) ? QTDIR : 0; q->path = qpath(p, i, w->id, qdir); c->mode = m; c->mode |= (w->type == GTree) ? (DMDIR|0755) : 0644; c->obj = w; break;Lines 1–500 of 928. Load more
#include <u.h> #include <libc.h> #include <ctype.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "git.h" enum { Qroot, Qhead, Qbranch, Qcommit, Qmsg, Qparent, Qtree, Qhash, Qauthor, Qcommitter, Qobject, Qctl, Qmax, Internal=1<<7, }; typedef struct Gitaux Gitaux; typedef struct Crumb Crumb; typedef struct Cache Cache; typedef struct Uqid Uqid; struct Crumb { char *name; Object *obj; Qid qid; int mode; vlong mtime; }; struct Gitaux { int ncrumb; Crumb *crumb; char *refpath; int qdir; /* For listing object dir */ Objlist *ols; Object *olslast; }; struct Uqid { vlong uqid; vlong ppath; vlong oid; int t; int idx; }; struct Cache { Uqid *cache; int n; int max; }; char *qroot[] = { "HEAD", "branch", "object", "ctl", }; #define Eperm "permission denied" #define Eexist "file does not exist" #define Enotdir "is not a directory" #define E2long "path too long" #define Enodir "not a directory" #define Erepo "unable to read repo" #define Eimpl "not implemented" #define Egreg "wat" #define Ebadobj "invalid object" #define Ename "invalid path element" char gitdir[512]; char *username; char *groupname; char *mntpt = ".git/fs"; char **branches = nil; Cache uqidcache[512]; vlong nextqid = Qmax; static Object* walklink(Gitaux *, char *, int, int, int*); vlong qpath(Crumb *p, int idx, vlong id, vlong t) { int h, i; vlong pp; Cache *c; Uqid *u; pp = p ? p->qid.path : 0; h = (pp*333 + id*7 + t) & (nelem(uqidcache) - 1); c = &uqidcache[h]; u = c->cache; for(i=0; i <c->n ; i++){ if(u->ppath == pp && u->oid == id && u->t == t && u->idx == idx) return (u->uqid << 8) | t; u++; } if(c->n == c->max){ c->max += c->max/2 + 1; c->cache = erealloc(c->cache, c->max*sizeof(Uqid)); } nextqid++; c->cache[c->n] = (Uqid){nextqid, pp, id, t, idx}; c->n++; return (nextqid << 8) | t; } static Crumb* crumb(Gitaux *aux, int n) { if(n < aux->ncrumb) return &aux->crumb[aux->ncrumb - n - 1]; return nil; } static void popcrumb(Gitaux *aux) { Crumb *c; if(aux->ncrumb > 1){ c = crumb(aux, 0); free(c->name); unref(c->obj); aux->ncrumb--; } } static vlong branchid(Gitaux *aux, char *path) { int i; for(i = 0; branches[i]; i++) if(strcmp(path, branches[i]) == 0) goto found; branches = realloc(branches, sizeof(char *)*(i + 2)); branches[i] = estrdup(path); branches[i + 1] = nil; found: if(aux){ if(aux->refpath) free(aux->refpath); aux->refpath = estrdup(branches[i]); } return i; } static void obj2dir(Dir *d, Crumb *c, Object *o, char *name) { d->qid = c->qid; d->atime = c->mtime; d->mtime = c->mtime; d->mode = c->mode; d->name = estrdup9p(name); d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); if(o->type == GBlob || o->type == GTag){ d->qid.type = 0; d->mode &= 0777; d->length = o->size; } } static int rootgen(int i, Dir *d, void *p) { Crumb *c; c = crumb(p, 0); if (i >= nelem(qroot)) return -1; d->mode = 0555 | DMDIR; d->name = estrdup9p(qroot[i]); d->qid.vers = 0; d->qid.type = strcmp(qroot[i], "ctl") == 0 ? 0 : QTDIR; d->qid.path = qpath(nil, i, i, Qroot); d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mtime = c->mtime; return 0; } static int branchgen(int i, Dir *d, void *p) { Gitaux *aux; Dir *refs; Crumb *c; int n; aux = p; c = crumb(aux, 0); refs = nil; d->qid.vers = 0; d->qid.type = QTDIR; d->qid.path = qpath(c, i, branchid(aux, aux->refpath), Qbranch | Internal); d->mode = 0555 | DMDIR; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mtime = c->mtime; d->atime = c->mtime; if((n = slurpdir(aux->refpath, &refs)) < 0) return -1; if(i < n){ d->name = estrdup9p(refs[i].name); free(refs); return 0; }else{ free(refs); return -1; } } static int gtreegen(int i, Dir *d, void *p) { Object *o, *l, *e; Gitaux *aux; Crumb *c; int m; aux = p; c = crumb(aux, 0); e = c->obj; if(i >= e->tree->nent) return -1; m = e->tree->ent[i].mode; if(e->tree->ent[i].ismod) o = emptydir(); else if((o = readobject(e->tree->ent[i].h)) == nil) sysfatal("could not read object %H: %r", e->tree->ent[i].h); if(e->tree->ent[i].islink) if((l = walklink(aux, o->data, o->size, 0, &m)) != nil) o = l; d->qid.vers = 0; d->qid.type = o->type == GTree ? QTDIR : 0; d->qid.path = qpath(c, i, o->id, aux->qdir); d->mode = m; d->atime = c->mtime; d->mtime = c->mtime; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->name = estrdup9p(e->tree->ent[i].name); d->length = o->size; return 0; } static int gcommitgen(int i, Dir *d, void *p) { Object *o; Crumb *c; c = crumb(p, 0); o = c->obj; d->uid = estrdup9p(username); d->gid = estrdup9p(groupname); d->muid = estrdup9p(username); d->mode = 0444; d->atime = o->commit->ctime; d->mtime = o->commit->ctime; d->qid.type = 0; d->qid.vers = 0; switch(i){ case 0: d->mode = DMDIR | gitdirmode; d->name = estrdup9p("tree"); d->qid.type = QTDIR; d->qid.path = qpath(c, i, o->id, Qtree); break; case 1: d->name = estrdup9p("parent"); d->qid.path = qpath(c, i, o->id, Qparent); break; case 2: d->name = estrdup9p("msg"); d->qid.path = qpath(c, i, o->id, Qmsg); break; case 3: d->name = estrdup9p("hash"); d->qid.path = qpath(c, i, o->id, Qhash); break; case 4: d->name = estrdup9p("author"); d->qid.path = qpath(c, i, o->id, Qauthor); break; default: free(d->uid); free(d->gid); free(d->muid); return -1; } return 0; } static int objgen(int i, Dir *d, void *p) { Gitaux *aux; Object *o; Crumb *c; char name[64]; Objlist *ols; Hash h; aux = p; c = crumb(aux, 0); if(!aux->ols) aux->ols = mkols(); ols = aux->ols; o = nil; /* We tried to sent it, but it didn't fit */ if(aux->olslast && ols->idx == i + 1){ snprint(name, sizeof(name), "%H", aux->olslast->hash); obj2dir(d, c, aux->olslast, name); return 0; } while(ols->idx <= i){ if(olsnext(ols, &h) == -1) return -1; if((o = readobject(h)) == nil){ fprint(2, "corrupt object %H\n", h); return -1; } } if(o != nil){ snprint(name, sizeof(name), "%H", o->hash); obj2dir(d, c, o, name); unref(aux->olslast); aux->olslast = ref(o); return 0; } return -1; } static void objread(Req *r, Gitaux *aux) { Object *o; o = crumb(aux, 0)->obj; switch(o->type){ case GBlob: readbuf(r, o->data, o->size); break; case GTag: readbuf(r, o->data, o->size); break; case GTree: dirread9p(r, gtreegen, aux); break; case GCommit: dirread9p(r, gcommitgen, aux); break; default: sysfatal("invalid object type %d", o->type); } } static void readcommitparent(Req *r, Object *o) { char *buf, *p, *e; int i, n; /* 40 bytes per hash, 1 per nl, 1 for terminator */ n = o->commit->nparent * (40 + 1) + 1; buf = emalloc(n); p = buf; e = buf + n; for (i = 0; i < o->commit->nparent; i++) p = seprint(p, e, "%H\n", o->commit->parent[i]); readbuf(r, buf, p - buf); free(buf); } static void gitattach(Req *r) { Gitaux *aux; Dir *d; if((d = dirstat(".git")) == nil) sysfatal("git/fs: %r"); if(getwd(gitdir, sizeof(gitdir)) == nil) sysfatal("getwd: %r"); aux = emalloc(sizeof(Gitaux)); aux->crumb = emalloc(sizeof(Crumb)); aux->crumb[0].qid = (Qid){Qroot, 0, QTDIR}; aux->crumb[0].obj = nil; aux->crumb[0].mode = DMDIR | 0555; aux->crumb[0].mtime = d->mtime; aux->crumb[0].name = estrdup("/"); aux->ncrumb = 1; r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; r->fid->qid = r->ofcall.qid; r->fid->aux = aux; respond(r, nil); } static Object* walklink(Gitaux *aux, char *link, int nlink, int ndotdot, int *mode) { char *p, *e, *path; Object *o, *n; int i; path = emalloc(nlink + 1); memcpy(path, link, nlink); cleanname(path); o = crumb(aux, ndotdot)->obj; assert(o->type == GTree); for(p = path; *p; p = e){ n = nil; e = p + strcspn(p, "/"); if(*e == '/') *e++ = '\0'; /* * cleanname guarantees these show up at the start of the name, * which allows trimming them from the end of the trail of crumbs * instead of needing to keep track of full parentage. */ if(strcmp(p, "..") == 0) n = crumb(aux, ++ndotdot)->obj; else if(o->type == GTree) for(i = 0; i < o->tree->nent; i++) if(strcmp(o->tree->ent[i].name, p) == 0){ *mode = o->tree->ent[i].mode; n = readobject(o->tree->ent[i].h); break; } o = n; if(o == nil) break; } free(path); for(i = 0; o != nil && i < aux->ncrumb; i++) if(crumb(aux, i)->obj == o) return nil; return o; } static char * objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux *aux) { Object *w, *l; char *e; int i, m; w = nil; e = nil; if(!o) return Eexist; switch(o->type){ case GBlob: e = Enotdir; break; case GTree: q->type = 0; for(i = 0; i < o->tree->nent; i++){ if(strcmp(o->tree->ent[i].name, name) != 0) continue; m = o->tree->ent[i].mode; w = readobject(o->tree->ent[i].h); if(!w && o->tree->ent[i].ismod) w = emptydir(); if(w && o->tree->ent[i].islink) if((l = walklink(aux, w->data, w->size, 1, &m)) != nil) w = l; if(!w) return Ebadobj; q->type = (w->type == GTree) ? QTDIR : 0; q->path = qpath(p, i, w->id, qdir); c->mode = m; c->mode |= (w->type == GTree) ? (DMDIR|0755) : 0644; c->obj = w; break;