928 lines · 17.1 KB · C
RawDownload
#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.