code: mafs

ref: 5c21eca829380c28769f79beabb7cc51f1787f97
dir: /user.c/

View raw version
#include "all.h"

static User	*users;
static u16	nusers;
static RWLock userslock;

int
checkname9p2(char* name)
{
	char *p;

	/*
	 * Return length of string if valid, 0 if not.
	 */
	if(name == nil)
		return 0;

	for(p = name; *p != 0; p++){
		if((*p & 0xFF) <= 040)
			return 0;
	}

	return p-name;
}

int
baduname(char *name)
{
	int n;

	if((n = checkname9p2(name)) == 0 ||
		n+1 > Userlen ||
		strchr(name,'-') ||
		(strlen(name) == 1 && name[0] == '\0')){
		dprint("mafs: bad user name %s\n", name);
		return 1;
	}
	return 0;
}

/* t should be rlock'ed */
User *
lookupuser(char *name, int locked)
{
	User *u;

	if(locked == 0)
		rlock(&userslock);
	for(u = users; u != nil; u = u->next)
		if(strcmp(u->name, name) == 0)
			return u;
	if(locked == 0)
		chkrunlock(&userslock);
	return nil;
}

s16
lookupid(char *name, int locked)
{
	User *u;
	s16 id;

	if(locked == 0)
		rlock(&userslock);
	u = lookupuser(name, 1);
	if(u == nil)
		id = 0;
	else
		id = u->id;
	if(locked == 0)
		chkrunlock(&userslock);
	return id;
}

/*
	there is a chance that u->name could be free'ed.
	Hence, always call this with an rlock(&userslock)
 */
char *
username(s16 uid, int locked, char *username)
{
	User *u;
	char *ret;

	ret = nil;
	if(locked == 0)
		rlock(&userslock);
	for(u = users; u != nil; u = u->next){
		if(u->id == uid){
			if(username == nil)
				username = emalloc9p(Userlen); /* potential for a leak */
			snprint(username, Userlen, "%s", u->name);
			ret = username;
			break;
		}
	}
	if(locked == 0)
		chkrunlock(&userslock);
	return ret;
}

int
ingroup(s16 uid, s16 gid, int locked)
{
	User *u;
	s16 i, found;

	found = 0;
	if(locked == 0)
		rlock(&userslock);
	if(uid == gid){
		found = 1;
		goto Found;
	}
	for(u = users; u != nil; u = u->next){
		if(u->id == gid){
			for(i = 0; i < u->nmembers; i++){
				if(u->members[i] == uid){
					found = 1;
					break;
				}
			}
			break;
		}
	}
Found:
	if(locked == 0)
		chkrunlock(&userslock);
	return found;
}

/* used by wstat */
int
leadgroup(s16 uid, s16 gid)
{
	User *u;
	int ret;

	if(uid == gid)
		return 1;
	rlock(&userslock);
	for(u = users; u != nil; u = u->next)
		if(u->id == gid){
			if(u->lid == uid){
				ret = 1;
				goto Unlock;
			}
			if(u->lid == 0){
				ret = ingroup(uid, gid, 1);
				goto Unlock;
			}
			ret = 0;
			goto Unlock;
		}
	ret = 0;
Unlock:
	chkrunlock(&userslock);
	return ret;
}

/* should be called with a locked t */
void
syncusers(Tree *)
{
	/* TODO */
}

void
addmember(User *u, char *memname)
{
	s16 x;
	int i;

	x = lookupid(memname, 1);
	if(x > 0){
		/* check for duplicate members */
		for(i=0; i<u->nmembers; i++)
			if(x == u->members[i])
				return;
		if(i == u->nmembers){
			u->members[u->nmembers++] = x;
		}
	}
}

void
adduser(int id, char *name, char *leader, char *members)
{
	char *p, *ep;
	User *u;
	char memname[Userlen];

	if(name == nil)
		panic("invalid name");
	if(baduname(name))
		panic("invalid name");

	for(u = users; u != nil; u = u->next){
		if(u->id == id){
			dprint("mafs: duplicate user ID %d (name %q)\n", id, u->name);
			panic("mafs: duplicate user ID");
			return;
		}else if(strcmp(u->name, name) == 0){
			dprint("mafs: duplicate user name %q (id %d)\n", name, u->id);
			panic("mafs: duplicate user name");
			return;
		}
	}

	u = malloc(sizeof(User));
	u->id = id;
	u->nmembers = 0;
	strcpy(u->name, name);
	u->next = users;
	users = u;
	nusers++;

	if(leader == nil || leader[0] == '\0' || baduname(leader))
		u->lid = 0;
	else if(strcmp(name, leader) == 0)
		u->lid = id;
	else
		u->lid = lookupid(leader, 1);

	p = members;
	while((ep = strchr(p, ',')) != nil){
		if(ep-p >= Userlen)
			panic("invalid name");
		else if(ep-p > 0){
			*ep = '\0';
			strncpy(memname, p, ep-p);
			memname[ep-p] = '\0';
			if(memname != nil && baduname(memname) == 0)
				addmember(u, memname);
		}
		p = ep+1;
	}
	if(p != nil && p[0] != '\0' && baduname(p) == 0)
		addmember(u, p);
}

void
parseusers(char *raw)
{
	char *p, *ep;
	char *flds[5];
	User *u;
	s32 i;
	char name[Userlen];

	p = raw;
	/* (uid:name:leader:member1,member2,...\n)+ */
	while((ep = strchr(p, '\n')) != nil){
		if(ep-p > 502){
			p[100] = '\0';
			panic("name line %s too long\n", p);
		}else if(ep-p > 0){
			*ep = '\0';
			if(getfields(p, flds, 4, 0, ":") != 4){
				dprint("invalid user data");
				goto Next;
			}
			wlock(&userslock);
			adduser(atoi(flds[0]), flds[1], flds[2], flds[3]);
			chkwunlock(&userslock);
		}
Next:
		p = ep+1;
	}

	if(chatty9p > 2)
	for(u = users; u != nil; u = u->next){
		dprint("%d:%s:%d(%s):", u->id, u->name, u->lid, username(u->lid, 1, name));
		for(i = 0; i<u->nmembers; i++)
			dprint("	%d(%s)", u->members[i], username(u->members[i], 1, name));
		dprint("\n");
	}
}

/*
	TODO not bothering when the users file > 502 bytes.
	Can add that stuff later
 */
void
usersinit(void)
{
	Iobuf *ubuf, *ucbuf;
	char rem[Blocksize*2];
	Dentry *du;

	ubuf = getbufchk(Bdusers, 1, Breadonly, Tdentry, Qpusers);
	du = &ubuf->io->d;
	/* for(i = 0; i < Ndspanid; i++){ */
		if(du->dspans[0].blkno == 0){
			putbuf(ubuf);
			return;
		}
		ucbuf = getbufchk(du->dspans[0].blkno, 1, Breadonly, Tdata, Qpusers);
		/* fill(rem, ucbuf->iobuf); */
		strncpy(rem, (char*)ucbuf->io->buf, Blocksize);
		parseusers(rem);
		putbuf(ucbuf);
	/* } */
	putbuf(ubuf);
}