shithub: gefs

Download patch

ref: cc6e4b6231bb28fe268feea3bfb50f7e0ee59166
parent: 7e0d72a78a9c2a01b2ba83674478a56cffddd0f7
author: Ori Bernstein <[email protected]>
date: Fri Dec 31 11:46:42 EST 2021

users: implement loading /adm/users

--- a/cons.c
+++ b/cons.c
@@ -72,6 +72,53 @@
 }
 
 static void
+refreshusers(int fd, char **ap, int na)
+{
+	char *l, *e;
+	Tree *t;
+
+	l = (na == 0) ? "main" : ap[0];
+	if((t = openlabel(l)) == nil){
+		fprint(fd, "load users: no label %s", l);
+		return;
+	}
+	e = loadusers(fd, t);
+	if(e != nil)
+		fprint(fd, "load users: %s\n", e);
+	else
+		fprint(fd, "refreshed users\n");
+	closesnap(t);
+}
+
+static void
+showusers(int fd, char**, int)
+{
+	User *u, *v;
+	int i, j;
+	char *sep;
+
+	rlock(&fs->userlk);
+	for(i = 0; i < fs->nusers; i++){
+		u = &fs->users[i];
+		fprint(fd, "%d:%s:", u->id, u->name);
+		if((v = uid2user(fs->users, fs->nusers, u->lead)) == nil)
+			fprint(fd, "???:");
+		else
+			fprint(fd, "%s:", v->name);
+		sep = "";
+		for(j = 0; j < u->nmemb; j++){
+			if((v = uid2user(fs->users, fs->nusers, u->memb[j])) == nil)
+				fprint(fd, "%s???", sep);
+			else
+				fprint(fd, "%s%s", sep, v->name);
+			sep = ",";
+		}
+		fprint(fd, "\n");
+	}
+	runlock(&fs->userlk);
+}		
+
+static void
 help(int fd, char **ap, int na)
 {
 	Cmd *c;
@@ -92,12 +139,12 @@
 	}
 }
 
-
 Cmd cmdtab[] = {
 	{.name="sync",	.sub=nil,	.minarg=0, .maxarg=0, .fn=syncfs},
 	{.name="snap",	.sub=nil,	.minarg=1, .maxarg=2, .fn=snapfs},
 	{.name="check",	.sub=nil,	.minarg=1, .maxarg=1, .fn=fsckfs},
-	{.name="help",	.sub=nil,	.minarg=0, .maxarg=1, .fn=help},
+	{.name="help",	.sub=nil,	.minarg=0, .maxarg=0, .fn=help},
+	{.name="users",	.sub=nil,	.minarg=0, .maxarg=1, .fn=refreshusers},
 
 	/* debugging */
 	{.name="show",	.sub="cache",	.minarg=0, .maxarg=0, .fn=showcache},
@@ -105,6 +152,7 @@
 	{.name="show",	.sub="snap",	.minarg=0, .maxarg=1, .fn=showsnap},
 	{.name="show",	.sub="fid",	.minarg=0, .maxarg=0, .fn=showfid},
 	{.name="show",	.sub="free",	.minarg=0, .maxarg=0, .fn=showfree},
+	{.name="show",	.sub="users",	.minarg=0, .maxarg=0, .fn=showusers},
 	{.name="debug",	.sub=nil,	.minarg=1, .maxarg=1, .fn=setdbg},
 	{.name=nil, .sub=nil},
 };
--- a/dat.h
+++ b/dat.h
@@ -20,6 +20,7 @@
 typedef struct Tree	Tree;
 typedef struct Oplog	Oplog;
 typedef struct Mount	Mount;
+typedef struct User	User;
 
 enum {
 	KiB	= 1024ULL,
@@ -113,6 +114,9 @@
 #define Eattach	"attach required"
 #define Enosnap	"attach -- bad specifier"
 #define Edir	"invalid directory"
+#define Esyntax "syntax error"
+#define Enouser	"user does not exist"
+#define Efsize	"file too big"
 #define Ebadu	"attach -- unknown user or failed authentication"
 
 #define Ewstatb	"wstat -- unknown bits in qid.type/mode"
@@ -338,6 +342,14 @@
 	Bfree	*next;
 };
 
+struct User {
+	int	id;
+	int	lead;
+	int	*memb;
+	int	nmemb;
+	char	name[128];
+};
+
 /*
  * Overall state of the file sytem.
  * Shadows the superblock contents.
@@ -376,6 +388,10 @@
 	long	roundrobin;
 	vlong	arenasz;
 
+	RWLock	userlk;
+	User	*users;
+	int	nusers;
+
 	Lock	fidtablk;
 	Fid	*fidtab[Nfidtab];
 	Lock	dtablk;
@@ -446,6 +462,7 @@
 struct Mount {
 	long	ref;
 	vlong	gen;
+	char	*user;
 	char	*name;
 	Tree	*root;
 };
--- a/fns.h
+++ b/fns.h
@@ -51,6 +51,10 @@
 int	compresslog(Arena*);
 void	setval(Blk*, int, Kvp*);
 
+char*	loadusers(int, Tree*);
+User*	uid2user(User*, int, int);
+User*	name2user(User*, int, char*);
+
 char*	btupsert(Tree*, Msg*, int);
 char*	btlookup(Tree*, Key*, Kvp*, char*, int);
 char*	btscan(Tree*, Scan*, char*, int);
--- a/fs.c
+++ b/fs.c
@@ -513,7 +513,14 @@
 		rerror(m, Emem);
 		return;
 	}
-	mnt->name = strdup(m->aname);
+	if((mnt->name = strdup(m->aname)) == nil){
+		rerror(m, Emem);
+		return;
+	}
+	if((mnt->user = strdup("glenda")) == nil){
+		rerror(m, Emem);
+		return;
+	}
 	if((mnt->root = openlabel(m->aname)) == nil){
 		rerror(m, Enosnap);
 		return;
@@ -995,12 +1002,57 @@
 }
 
 int
-fsaccess(Dir*, int)
+ingroup(char *user, char *group)
 {
-	/* all is permitted */
-	return 0;
+	User *u, *g;
+	int i, in;
+
+	rlock(&fs->userlk);
+	in = 0;
+	u = name2user(fs->users, fs->nusers, user);
+	g = name2user(fs->users, fs->nusers, group);
+	if(u != nil && g != nil)
+		for(i = 0; i < g->nmemb; i++)
+			if(u->id == g->memb[i])
+				in = 1;
+	runlock(&fs->userlk);
+	return in;
 }
 
+int
+fsaccess(Mount *mnt, Dir *d, int req)
+{
+	int m;
+
+	m = 0;
+	switch(req&0xf){
+	case OREAD:	m = DMREAD;		break;
+	case OWRITE:	m = DMWRITE;		break;
+	case ORDWR:	m = DMREAD|DMWRITE;	break;
+	}
+	if(req&OEXEC)
+		m |= DMEXEC;
+	if(req&OTRUNC)
+		m |= DMWRITE;
+
+	/* uid none gets only other permissions */
+	if(strcmp(mnt->user, "none") != 0) {
+		if(strcmp(mnt->user, d->uid) == 0)
+			if((m<<6) & d->mode)
+				return 0;
+		if(ingroup(mnt->user, d->gid))
+			if((m<<3) & d->mode)
+				return 0;
+	}
+	if(m & d->mode) {
+		if((d->mode & DMDIR) && (m == DMEXEC))
+			return 0;
+		if(!ingroup(mnt->user, "noworld"))
+			return 0;
+	}
+	return -1;
+}
+
 void
 fsopen(Fmsg *m)
 {
@@ -1024,7 +1076,7 @@
 		putfid(f);
 		return;
 	}
-	if(fsaccess(&d, m->mode) == -1){
+	if(fsaccess(f->mnt, &d, m->mode) == -1){
 		rerror(m, Eperm);
 		putfid(f);
 		return;
--- a/load.c
+++ b/load.c
@@ -35,12 +35,13 @@
 void
 loadfs(char *dev)
 {
+	int blksz, bufspc, hdrsz;
+	int i, dirty;
 	vlong sb;
-	char *p;
+	char *p, *e;
+	Tree *t;
 	Blk *b;
 	Dir *d;
-	int i, dirty;
-	int blksz, bufspc, hdrsz;
 
 	fs->osnap = nil;
 	if((fs->fd = open(dev, ORDWR)) == -1)
@@ -71,7 +72,6 @@
 	fs->nextqid = GBIT64(p);	p += 8;
 	fs->super = b;
 	fs->nextgen = fs->snap.bp.gen + 1;
-
 	for(i = 0; i < Ndead; i++){
 		fs->snap.prev[i] = -1;
 		fs->snap.dead[i].head.addr = -1;
@@ -99,4 +99,8 @@
 		fprint(2, "file system was not unmounted cleanly");
 		/* TODO: start gc pass */
 	}
+	if((t = openlabel("main")) == nil)
+		sysfatal("load users: no main label");
+	if((e = loadusers(2, t)) != nil)
+		sysfatal("load users: %s\n", e);
 }
--- a/mkfile
+++ b/mkfile
@@ -15,6 +15,7 @@
 	ream.$O\
 	snap.$O\
 	tree.$O\
+	user.$O\
 
 HFILES=\
 	dat.h\
--- /dev/null
+++ b/user.c
@@ -1,0 +1,269 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <avl.h>
+#include <bio.h>
+
+#include "dat.h"
+#include "fns.h"
+
+typedef struct User	User;
+
+
+char *defaultusers =
+	"-1:adm::\n"
+	"0:none::\n"
+	"1:tor:tor:\n"
+	"1:glenda:glenda:\n"
+	"10000:sys::\n"
+	"10001:map:map:\n"
+	"10002:doc::\n"
+	"10003:upas:upas:\n"
+	"10004:font::\n"
+	"10005:bootes:bootes:\n";
+
+static int
+walk1(Tree *t, vlong up, char *name, Qid *qid, vlong *len)
+{
+	char *p, kbuf[Keymax], rbuf[Kvmax];
+	int err;
+	Kvp kv;
+	Key k;
+	Dir d;
+
+	err = 0;
+	if((p = packdkey(kbuf, sizeof(kbuf), up, name)) == nil)
+		return -1;
+	k.k = kbuf;
+	k.nk = p - kbuf;
+	if(err)
+		return -1;
+	if(btlookup(t, &k, &kv, rbuf, sizeof(rbuf)) != nil)
+		return -1;
+	if(kv2dir(&kv, &d) == -1)
+		return -1;
+	*qid = d.qid;
+	*len = d.length;
+	return 0;
+}
+
+static char*
+slurp(Tree *t, vlong path, vlong len)
+{
+	char *e, *ret, buf[Offksz], kvbuf[Offksz + Ptrsz];
+	vlong o;
+	Blk *b;
+	Bptr bp;
+	Key k;
+	Kvp kv;
+
+	if((ret = malloc(len + 1)) == nil)
+		return Emem;
+	k.k = buf;
+	k.nk = Offksz;
+	for(o = 0; o < len; o += Blksz){
+		k.k[0] = Kdat;
+		PBIT64(k.k+1, path);
+		PBIT64(k.k+9, o);
+		if((e = btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf))) != nil)
+			return e;
+		bp = unpackbp(kv.v, kv.nv);
+		if((b = getblk(bp, GBraw)) == nil)
+			return Eio;
+		if(len - o >= Blksz)
+			memcpy(ret + o, b->buf, Blksz);
+		else
+			memcpy(ret + o, b->buf, len - o);
+	}
+	ret[len] = 0;
+	return ret;
+}
+
+char*
+readline(char **p, char *buf, int nbuf)
+{
+	char *e;
+	int n;
+
+	if((e = strchr(*p, '\n')) == nil)
+		return nil;
+	n = (e - *p) + 1;
+	if(n >= nbuf)
+		n = nbuf - 1;
+	strecpy(buf, buf + n, *p);
+	*p = e+1;
+	return buf;
+}
+
+char*
+getfield(char **p, char delim)
+{
+	char *r;
+
+	if(*p == nil)
+		return nil;
+	r = *p;
+	*p = strchr(*p, delim);
+	if(*p != nil){
+		**p = '\0';
+		*p += 1;
+	}
+	return r;
+}
+
+User*
+name2user(User *users, int nusers, char *name)
+{
+	int i;
+
+	for(i = 0; i < nusers; i++)
+		if(strcmp(users[i].name, name) == 0)
+			return &users[i];
+	return nil;
+}
+
+User*
+uid2user(User *users, int nusers, int id)
+{
+	int i;
+
+	for(i = 0; i < nusers; i++)
+		if(users[i].id == id)
+			return &users[i];
+	return nil;
+}
+
+char*
+parseusers(int fd, char *udata)
+{
+	char *pu, *p, *f, *m, *err, buf[8192];
+	int i, lnum, ngrp, nusers, usersz;
+	User *u, *n, *users;
+	int *g, *grp;
+
+	i = 0;
+	err = nil;
+	nusers = 0;
+	usersz = 8;
+	if((users = calloc(usersz, sizeof(User))) == nil)
+		return Emem;
+	pu = udata;
+	lnum = 0;
+	while((p = readline(&pu, buf, sizeof(buf))) != nil){
+		lnum++;
+		if(p[0] == '#' || p[0] == 0)
+			continue;
+		if(i == usersz){
+			usersz *= 2;
+			n = realloc(users, usersz*sizeof(User));
+			if(n == nil){
+				free(users);
+				return Enomem;
+			}
+			users = n;
+		}
+		if((f = getfield(&p, ':')) == nil){
+			fprint(fd, "/adm/users:%d: missing ':' after id\n", lnum);
+			err = Esyntax;
+			goto Error;
+		}
+		users[i].id = atol(f);
+		if((f = getfield(&p, ':')) == nil){
+			fprint(fd, "/adm/users:%d: missing ':' after name\n", lnum);
+			err = Esyntax;
+			goto Error;
+		}
+		snprint(users[i].name, sizeof(users[i].name), "%s", f);
+		users[i].memb = nil;
+		users[i].nmemb = 0;
+		i++;
+	}
+	nusers = i;
+
+
+	i = 0;
+	pu = udata;
+	lnum = 0;
+	while((p = readline(&pu, buf, sizeof(buf))) != nil){
+		lnum++;
+		if(buf[0] == '#' || buf[0] == 0)
+			continue;
+		getfield(&p, ':');	/* skip id */
+		getfield(&p, ':');	/* skip name */
+		if((f = getfield(&p, ':')) == nil)
+			return Esyntax;
+		if(f[0] != '\0'){
+			if((u = name2user(users, nusers, f)) == nil){
+				fprint(fd, "/adm/users:%d: leader %s does not exist\n", lnum, f);
+				err = Enouser;
+				goto Error;
+			}
+			users[i].lead = u->id;
+		}
+		if((f = getfield(&p, ':')) == nil)
+			return Esyntax;
+		grp = nil;
+		ngrp = 0;
+		while((m = getfield(&f, ',')) != nil){
+			if(m[0] == '\0')
+				continue;
+			if((u = name2user(users, nusers, m)) == nil){
+				fprint(fd, "/adm/users:%d: user %s does not exist\n", lnum, m);
+				err = Enouser;
+				goto Error;
+			}
+			if((g = realloc(grp, (ngrp+1)*sizeof(int))) == nil){
+				err = Emem;
+				goto Error;
+			}
+			grp = g;
+			grp[ngrp++] = u->id;
+		}
+		users[i].memb = grp;
+		users[i].nmemb = ngrp;
+		i++;
+	}
+
+	wlock(&fs->userlk);
+	n = fs->users;
+	fs->users = users;
+	fs->nusers = nusers;
+	users = n;
+	wunlock(&fs->userlk);
+
+Error:
+	if(users != nil)
+		for(i = 0; i < nusers; i++)
+			free(users[i].memb);
+	free(users);
+		
+	return err;
+		
+}
+
+char*
+loadusers(int fd, Tree *t)
+{
+	char *s, *e;
+	vlong len;
+	Qid q;
+
+	s = defaultusers;
+	if(walk1(t, -1, "", &q, &len) == -1)
+		return Efs;
+	if(walk1(t, q.path, "adm", &q, &len) == -1)
+		goto Defaulted;
+	if(walk1(t, q.path, "users", &q, &len) == -1)
+		goto Defaulted;
+	if(q.type != QTFILE)
+		return Etype;
+	if(len >= 1*MiB)
+		return Efsize;
+	if((s = slurp(t, q.path, len)) == nil)
+		return Eio;
+Defaulted:
+	e = parseusers(fd, s);
+	if(s != defaultusers)
+		free(s);
+	return e;
+}