code: plan9front

Download patch

ref: b904edadd8869787b4419ce93c1c29b5beee3556
parent: 577033228209f28350dc3f75ef9d4ce88dfdf190
author: Ori Bernstein <ori@eigenstate.org>
date: Tue Jun 22 19:55:54 EDT 2021

git/fs: use a better heuristic for permissions.

Since we now store /dist/plan9front in git, the
initial assumption that the owner of the repo
is the person touching it is not always true.

This change gives us a better heuristic for the
file permissions we should have in the files we
copy around, basing it off of the permissions of
the .git directory.

--- a/sys/man/4/gitfs
+++ b/sys/man/4/gitfs
@@ -74,6 +74,15 @@
 .PP
 Trees are presented as directory listings, and blobs
 as files.
+The repository controls the user permissions.
+The group and world permissions are derived by masking
+the user permissions with the permissions of the
+.I .git
+directory.
+The user and group presented is the same as the user and
+group of the
+.I .git
+directory.
 
 .SH FILES
 .TP
@@ -100,11 +109,14 @@
 
 .SH BUGS
 Symlinks are only partially supported.
-Symlinks are treated as regular files when reading.
-Modifying symlinks is unsupported.
+They will be followed when reading, but a commit that
+modifies a symlink is an error.
 
 .PP
-There is no way to inspect the raw objects. This is
-a feature that would be useful for debugging.
+For efficiency, git/fs only loads repo permissions at startup
+
+.PP
+There is no way to inspect the raw objects.
+Inspecting raw objects would be useful for debugging.
 
 
--- a/sys/src/cmd/git/fs.c
+++ b/sys/src/cmd/git/fs.c
@@ -79,6 +79,7 @@
 
 char	gitdir[512];
 char	*username;
+char	*groupname;
 char	*mntpt = ".git/fs";
 char	**branches = nil;
 Cache	uqidcache[512];
@@ -164,7 +165,7 @@
 	d->mode = c->mode;
 	d->name = estrdup9p(name);
 	d->uid = estrdup9p(username);
-	d->gid = estrdup9p(username);
+	d->gid = estrdup9p(groupname);
 	d->muid = estrdup9p(username);
 	if(o->type == GBlob || o->type == GTag){
 		d->qid.type = 0;
@@ -188,7 +189,7 @@
 	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(username);
+	d->gid = estrdup9p(groupname);
 	d->muid = estrdup9p(username);
 	d->mtime = c->mtime;
 	return 0;
@@ -210,7 +211,7 @@
 	d->qid.path = qpath(c, i, branchid(aux, aux->refpath), Qbranch | Internal);
 	d->mode = 0555 | DMDIR;
 	d->uid = estrdup9p(username);
-	d->gid = estrdup9p(username);
+	d->gid = estrdup9p(groupname);
 	d->muid = estrdup9p(username);
 	d->mtime = c->mtime;
 	d->atime = c->mtime;
@@ -251,11 +252,10 @@
 	d->qid.type = o->type == GTree ? QTDIR : 0;
 	d->qid.path = qpath(c, i, o->id, aux->qdir);
 	d->mode = m;
-	d->mode |= (o->type == GTree) ? 0755 : 0644;
 	d->atime = c->mtime;
 	d->mtime = c->mtime;
 	d->uid = estrdup9p(username);
-	d->gid = estrdup9p(username);
+	d->gid = estrdup9p(groupname);
 	d->muid = estrdup9p(username);
 	d->name = estrdup9p(e->tree->ent[i].name);
 	d->length = o->size;
@@ -271,7 +271,7 @@
 	c = crumb(p, 0);
 	o = c->obj;
 	d->uid = estrdup9p(username);
-	d->gid = estrdup9p(username);
+	d->gid = estrdup9p(groupname);
 	d->muid = estrdup9p(username);
 	d->mode = 0444;
 	d->atime = o->commit->ctime;
@@ -490,7 +490,7 @@
 	}else if(o->type == GCommit){
 		q->type = 0;
 		c->mtime = o->commit->mtime;
-		c->mode = 0444;
+		c->mode = 0644;
 		assert(qdir == Qcommit || qdir == Qobject || qdir == Qcommittree || qdir == Qhead);
 		if(strcmp(name, "msg") == 0)
 			q->path = qpath(p, 0, o->id, Qcommitmsg);
@@ -804,7 +804,7 @@
 	aux = r->fid->aux;
 	c = crumb(aux, 0);
 	r->d.uid = estrdup9p(username);
-	r->d.gid = estrdup9p(username);
+	r->d.gid = estrdup9p(groupname);
 	r->d.muid = estrdup9p(username);
 	r->d.qid = r->fid->qid;
 	r->d.mtime = c->mtime;
@@ -837,6 +837,8 @@
 void
 main(int argc, char **argv)
 {
+	Dir *d;
+
 	gitinit();
 	ARGBEGIN{
 	case 'd':
@@ -852,7 +854,12 @@
 	if(argc != 0)
 		usage();
 
-	username = getuser();
+	if((d = dirstat(".git")) == nil)
+		sysfatal("dirstat .git: %r");
+	username = strdup(d->uid);
+	groupname = strdup(d->gid);
+	free(d);
+
 	branches = emalloc(sizeof(char*));
 	branches[0] = nil;
 	postmountsrv(&gitsrv, nil, mntpt, MCREATE);
--- a/sys/src/cmd/git/pack.c
+++ b/sys/src/cmd/git/pack.c
@@ -70,6 +70,7 @@
 Packf	*packf;
 int	npackf;
 int	openpacks;
+int	gitdirmode = -1;
 
 static void
 clear(Object *o)
@@ -887,7 +888,7 @@
 static void
 parsetree(Object *o)
 {
-	int m, entsz, nent;
+	int m, a, entsz, nent;
 	Dirent *t, *ent;
 	char *p, *ep;
 
@@ -908,7 +909,15 @@
 		if(*p != ' ')
 			sysfatal("malformed tree %H: *p=(%d) %c\n", o->hash, *p, *p);
 		p++;
-		t->mode = m & 0777;	
+		/*
+		 * only the stored permissions for the user
+		 * are relevant; git fills group and world
+		 * bits with whatever -- so to serve with
+		 * useful permissions, replicate the mode
+		 * of the git repo dir.
+		 */
+		a = (m & 0777)>>6;
+		t->mode = ((a<<6)|(a<<3)|a) & gitdirmode;
 		t->ismod = 0;
 		t->islink = 0;
 		if(m == 0160000){
@@ -1048,7 +1057,14 @@
 readobject(Hash h)
 {
 	Object *o;
+	Dir *d;
 
+	if(gitdirmode == -1){
+		if((d = dirstat(".git")) == nil)
+			sysfatal("stat .git: %r");
+		gitdirmode = d->mode & 0777;
+		free(d);
+	}
 	if((o = readidxobject(nil, h, 0)) == nil)
 		return nil;
 	parseobject(o);