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;
+}