shithub: git9

Download patch

ref: 22383ae54cfeb178b150dc9417b0d773cc72c434
parent: 2856dd8f8c8cf799212163fe79ba9752f1bcd797
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Feb 8 13:07:50 EST 2020

fix symlinks

--- a/fs.c
+++ b/fs.c
@@ -7,13 +7,6 @@
 
 #include "git.h"
 
-char *Eperm = "permission denied";
-char *Eexist = "does not exist";
-char *E2long = "path too long";
-char *Enodir = "not a directory";
-char *Erepo = "unable to read repo";
-char *Egreg = "wat";
-
 enum {
 	Qroot,
 	Qhead,
@@ -35,7 +28,6 @@
 typedef struct Crumb Crumb;
 typedef struct Cache Cache;
 typedef struct Uqid Uqid;
-
 struct Crumb {
 	char	*name;
 	Object	*obj;
@@ -77,6 +69,14 @@
 	"ctl",
 };
 
+#define Eperm	"permission denied";
+#define Eexist	"does not exist";
+#define E2long	"path too long";
+#define Enodir	"not a directory";
+#define Erepo	"unable to read repo";
+#define Egreg	"wat";
+#define Ebadobj	"invalid object";
+
 char	gitdir[512];
 char	*username;
 char	*mtpt = "/mnt/git";
@@ -84,7 +84,7 @@
 Cache	uqidcache[512];
 vlong	nextqid = Qmax;
 
-static char*	gitwalk1(Fid *fid, char *name, Qid *q);
+static Object*	walklink(Gitaux *, char *, int, int, int*);
 
 vlong
 qpath(Crumb *p, int idx, vlong id, vlong t)
@@ -228,7 +228,7 @@
 
 /* FIXME: walk to the appropriate submodule.. */
 static Object*
-modrefobj(Dirent *e)
+emptydir(Dirent *e)
 {
 	Object *m;
 
@@ -248,24 +248,28 @@
 static int
 gtreegen(int i, Dir *d, void *p)
 {
-	Object *o, *e;
+	Object *o, *l, *e;
 	Gitaux *aux;
 	Crumb *c;
+	int m;
 
 	aux = p;
 	c = crumb(aux, 0);
 	e = c->obj;
+	m = e->tree->ent[i].mode;
 	if(i >= e->tree->nent)
 		return -1;
-	if((o = readobject(e->tree->ent[i].h)) == nil)
-		if(e->tree->ent[i].modref)
-			o = modrefobj(&e->tree->ent[i]);
-		else
-			die("could not read object %H: %r", e->tree->ent[i].h, e->hash);
+	if(e->tree->ent[i].ismod)
+		o = emptydir(&e->tree->ent[i]);
+	else if((o = readobject(e->tree->ent[i].h)) == nil)
+		die("could not read object %H: %r", e->tree->ent[i].h, e->hash);
+	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 = e->tree->ent[i].mode;
+	d->mode = m;
 	d->mode |= (o->type == GTree) ? 0755 : 0644;
 	d->atime = c->mtime;
 	d->mtime = c->mtime;
@@ -426,33 +430,52 @@
 	respond(r, nil);
 }
 
-static char*
-walklink(Fid *fid, Qid *q, char *link, int nlink)
+static Object*
+walklink(Gitaux *aux, char *link, int nlink, int ndotdot, int *mode)
 {
-	char *p, *e, *err, *path;
+	char *p, *e, *path;
+	Object *o, *n;
+	int i;
 
-	err = nil;
 	path = emalloc(nlink + 1);
 	memcpy(path, link, nlink);
 	cleanname(path);
-	popcrumb(fid->aux);
+
+	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';
-		if((err = gitwalk1(fid, p, q)) != nil)
+		/*
+		 * 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);
-	return err;
+	return o;
 }
 
 static char *
-objwalk1(Fid *fid, Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir)
+objwalk1(Qid *q, Object *o, Crumb *p, Crumb *c, char *name, vlong qdir, Gitaux *aux)
 {
-	Object *w;
+	Object *w, *l;
 	char *e;
-	int i;
+	int i, m;
 
 	w = nil;
 	e = nil;
@@ -463,18 +486,21 @@
 		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].modref)
-				w = modrefobj(&o->tree->ent[i]);
+			if(!w && o->tree->ent[i].ismod)
+				w = emptydir(&o->tree->ent[i]);
+			if(w && o->tree->ent[i].islink)
+				if((l = walklink(aux, w->data, w->size, 1, &m)) != nil)
+					w = l;
 			if(!w)
-				die("could not read object for %s: %r", name);
-			if(o->tree->ent[i].mode == 0)
-				return walklink(fid, q, w->data, w->size);
+				return Ebadobj;
 			q->type = (w->type == GTree) ? QTDIR : 0;
 			q->path = qpath(c, i, w->id, qdir);
-			c->mode = o->tree->ent[i].mode;
-			c->mode |= (w->type == GTree) ? 0755 : 0644;
+			c->mode = m;
+			c->mode |= (w->type == GTree) ? DMDIR|0755 : 0644;
 			c->obj = w;
+			break;
 		}
 		if(!w)
 			e = Eexist;
@@ -608,7 +634,7 @@
 		break;
 	case Qobject:
 		if(c->obj){
-			e = objwalk1(fid, q, o->obj, o, c, name, Qobject);
+			e = objwalk1(q, o->obj, o, c, name, Qobject, aux);
 		}else{
 			if(hparse(&h, name) == -1)
 				return "invalid object name";
@@ -621,13 +647,13 @@
 		}
 		break;
 	case Qhead:
-		e = objwalk1(fid, q, o->obj, o, c, name, Qhead);
+		e = objwalk1(q, o->obj, o, c, name, Qhead, aux);
 		break;
 	case Qcommit:
-		e = objwalk1(fid, q, o->obj, o, c, name, Qcommit);
+		e = objwalk1(q, o->obj, o, c, name, Qcommit, aux);
 		break;
 	case Qcommittree:
-		e = objwalk1(fid, q, o->obj, o, c, name, Qcommittree);
+		e = objwalk1(q, o->obj, o, c, name, Qcommittree, aux);
 		break;
 	case Qcommitparent:
 	case Qcommitmsg:
@@ -640,12 +666,6 @@
 		return Egreg;
 	}
 
-	/* 
-	 * if we get called recursively from
-	 * objwalk1, we realloc the crumb array;
-	 * get a valid crumb again.
-	 */
-	c = crumb(aux, 0);
 	c->name = estrdup(name);
 	c->qid = *q;
 	fid->qid = *q;
--- a/git.h
+++ b/git.h
@@ -74,9 +74,10 @@
 
 struct Dirent {
 	char *name;
-	int modref;
 	int mode;
 	Hash h;
+	char ismod;
+	char islink;
 };
 
 struct Object {
--- a/pack.c
+++ b/pack.c
@@ -765,7 +765,10 @@
 		/* FIXME: symlinks and other BS */
 		if(m == 0160000){
 			t->mode |= DMDIR;
-			t->modref = 1;
+			t->ismod = 1;
+		}else if(m == 0120000){
+			t->mode = 0;
+			t->islink = 1;
 		}
 		t->mode = m & 0777;
 		if(m & 0040000)
--- a/query.c
+++ b/query.c
@@ -35,7 +35,7 @@
 	p = d->tree->ent;
 	e = p + d->tree->nent;
 	for(; p != e; p++){
-		if(p->modref)
+		if(p->ismod)
 			continue;
 		if(p->mode & DMDIR)
 			showdir(p->h, p->name, m);