code: mafs

ref: b3e875d7db45fca0cde9f4efa12872f61afc54d7
dir: /user.c/

View raw version
#include "all.h"

static User	*lruuser;	/* least recently used user */
static RWLock userslock;
char userserrmsg[Nuserserrmsg];

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

User *
leastid(User *us)
{
	User *u;

	if(us == nil)
		return nil;
	for(u = us; u->prev != nil; u = u->prev)
		;
	return u;
}

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

	rlock(&userslock);
	for(u = leastid(lruuser); u != nil; u = u->next)
		if(strcmp(u->name, name) == 0)
			break;
	if(u == nil)
		id = 0;
	else
		id = u->id;
	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, char *username)
{
	User *u;
	char *ret;

	ret = nil;
	rlock(&userslock);
	for(u = leastid(lruuser); 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;
		}
	}
	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 = leastid(lruuser); 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 = leastid(lruuser); 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;
}

void
freeusers(User *us)
{
	User *u, *tmp;

	if(us == nil)
		return;
	u = leastid(us);
	while(u != nil){
		if(u->members != nil)
			free(u->members);
		tmp = u;
		u = u->next;
		free(tmp);
	}
}

/* used by the parseusers(). No locking needed. */
s16
lookforid(User *us, char *name)
{
	User *u;
	s16 id;

	for(u = leastid(us); u != nil; u = u->next)
		if(strcmp(u->name, name) == 0)
			break;
	if(u == nil)
		id = 0;
	else
		id = u->id;
	return id;
}

User *
lookforuser(User *us, s16 uid)
{
	User *u;

	for(u = leastid(us); u != nil; u = u->next)
		if(u->id == uid)
			return u;
	return nil;
}

char *
lookforname(User *us, s16 uid)
{
	User *u;

	for(u = leastid(us); u != nil; u = u->next)
		if(u->id == uid)
			return u->name;
	return nil;
}

int
adduser(User *us, int id, char *name, User **up)
{
	User *u, *prevu, *new;

	if(name == nil)
		return snprint(userserrmsg, Nuserserrmsg-1, "invalid name");
	if(baduname(name))
		return snprint(userserrmsg, Nuserserrmsg-1, "invalid name");

	for(u = leastid(us); u != nil; u = u->next){
		if(u->id == id){
			return snprint(userserrmsg, Nuserserrmsg-1,
					"mafs: duplicate user ID %d (name %q)\n", id, u->name);
		}else if(strcmp(u->name, name) == 0){
			return snprint(userserrmsg, Nuserserrmsg-1,
					"mafs: duplicate user name %q (id %d)\n", name, u->id);
		}
	}

	new = emalloc9p(sizeof(User));
	new->id = id;
	new->nmembers = 0;
	strncpy(new->name, name, Userlen);

	*up = new;
	if(us == nil)
		return 0;
	for(prevu = u = leastid(us); u != nil && u->id < id; u = u->next){
		prevu = u;
	}
	/* at the top/last */
	if(u == nil){
		prevu->next = new;
		new->prev = prevu;
		return 0;
	}
	if(u->prev != nil)
		u->prev->next = new;
	new->next = u;
	new->prev = u->prev;
	u->prev = new;
	return 0;
}

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

	if(us == nil || u == nil || memname == nil)
		return;
	x = lookforid(us, memname);
	if(x > 0){
		/* check for duplicate members */
		for(i=0; i<u->nmembers; i++)
			if(x == u->members[i])
				return;
		if(i == u->nmembers){
			if(u->members == nil){
				u->nalloc = 1;
				u->members = emalloc9p(u->nalloc*sizeof(s16));
			}else if(u->nmembers == u->nalloc){
				u->nalloc++;
				u->members = erealloc9p(u->members, u->nalloc*sizeof(s16));
			}
			u->members[u->nmembers++] = x;
		}
	}
}

int
addmembers(User *us, User *u, char *members)
{
	char *p, *ep;
	char memname[Userlen];

	if(u == nil)
		return snprint(userserrmsg, Nuserserrmsg-1, "invalid user");

	p = members;
	while((ep = strchr(p, ',')) != nil){
		if(ep-p >= Userlen)
			return snprint(userserrmsg, Nuserserrmsg-1, "memname too long");
		else if(ep-p > 0){
			*ep = '\0';
			strncpy(memname, p, ep-p);
			memname[ep-p] = '\0';
			if(memname != nil){
				 if(baduname(memname))
					return snprint(userserrmsg, Nuserserrmsg-1, "bad memname");
				addmember(us, u, memname);
			}
		}
		p = ep+1;
	}
	if(p != nil && p[0] != '\0'){
		if(baduname(p))
			return snprint(userserrmsg, Nuserserrmsg-1, "last memname bad");
		addmember(us, u, p);
	}
	return 0;
}

int
addleader(User *us, User *u, char *leader)
{
	if(u == nil)
		return snprint(userserrmsg, Nuserserrmsg-1, "invalid user");

	if(leader == nil || leader[0] == '\0' || baduname(leader))
		u->lid = 0;
	else if(strcmp(u->name, leader) == 0)
		u->lid = u->id;
	else
		u->lid = lookforid(us, leader);
	return 0;
}

/*
	(uid:name:leader:member1,member2,...\n)+
	first parse are the names, then, parse again
	to add the leader and members to the group.
 */
s32
parseusers(User **usp, s8 *raw, u64 usize)
{
	char *p, *ep, *line;
	char *flds[5];
	User *u, *us;
	s32 i;
	s32 rv;

	u = us = nil;
	p = raw;
	while(p-raw <= usize && (ep = strchr(p, '\n')) != nil){
		/* skip blank lines or empty lines of :::*/
		if(ep-p > 4){
			line = emalloc9p(ep-p+1);
			memcpy(line, p, ep-p);
			if(getfields(line, flds, 4, 0, ":") != 4){
				free(line);
				dprint("invalid user data");
				freeusers(us);
				return Einvusers;
			}
			if((rv = adduser(us, atoi(flds[0]), flds[1], &u)) != 0){
				free(line);
				dprint("invalid adduser");
				freeusers(us);
				return rv;
			
			}
			if(us == nil && u != nil)
				*usp = us = u;
			free(line);
		}
		p = ep+1;
	}

	if(us == nil)
		return Enousers;

	p = raw;
	while((ep = strchr(p, '\n')) != nil){
		/* skip blank lines or empty lines of ::::*/
		if(ep-p > 4){
			line = emalloc9p(ep-p+1);
			memcpy(line, p, ep-p);
			if(getfields(line, flds, 4, 0, ":") != 4){
				free(line);
				dprint("invalid user data");
				freeusers(us);
				return Einvusers;
			}
			u = lookforuser(us, atoi(flds[0]));
			if((rv = addleader(us, u, flds[2])) != 0){
				free(line);
				dprint("invalid addmembers");
				freeusers(us);
				return rv;
			
			}
			if((rv = addmembers(us, u, flds[3])) != 0){
				free(line);
				dprint("invalid addmembers %s\n", Nuserserrmsg);
				freeusers(us);
				return rv;
			
			}
			free(line);
		}
		p = ep+1;
	}
	if(chatty9p > 2)
		for(u = leastid(us); u != nil; u = u->next){
			dprint("%d:%s:%d(%s):", u->id, u->name, u->lid, lookforname(us, u->lid));
			for(i = 0; i<u->nmembers; i++)
				dprint("	%d(%s)", u->members[i], lookforname(us, u->lid));
			dprint("\n");
		}
	*usp = us;
	return 0;
}

void
usersinit(void)
{
	Iobuf *ubuf;
	s8 *buf;
	u64 usize;
	User *us;
	int rv;

	ubuf = egetmetachk(Bdusersinuse, Breadonly, Tdentry, Qpusersinuse);
	usize = ubuf->d->size+1;
	buf = emalloc9p(usize);
	putbuf(ubuf, 0);
	if(waserror()){
		free(buf);
		nexterror();
	}

	readfile(Bdusersinuse, Qpusersinuse, buf, usize, 0);
	if((rv=parseusers(&us, buf, usize)) != 0)
		error("could not parse /a/users/inuse rv %d\n", rv);
	poperror();
	free(buf);
	lruuser = us;
}

/*
	read from /a/user/staging
	check that the contents are valid
		check that -1 is the first entry TODO
		check that 0 is the next entry TODO
	write the contents to /a/users/inuse
	lruuser points to these new contents
 */
void
syncusers(void)
{
	Iobuf *ubuf;
	s8 *buf;
	u64 usize;
	User *us, *old;

	ubuf = egetmetachk(Bdusersstaging, Breadonly, Tdentry, Qpusersstaging);
	usize = ubuf->d->size+1;
	buf = emalloc9p(usize);
	putbuf(ubuf, 0);
	if(waserror()){
		free(buf);
		nexterror();
	}

	readfile(Bdusersstaging, Qpusersstaging, buf, usize, 0);
	if(parseusers(&us, buf, usize) != 0)
		error("could not parse /a/users/staging\n");

	wlock(&userslock);
	old = lruuser;
	if(waserror()){
		wunlock(&userslock);
		nexterror();
	}
	writefile(Bdusersinuse, Qpusersinuse, -1, buf, usize, 0);
	lruuser = us;
	poperror();
	wunlock(&userslock);

	freeusers(old);
	poperror();
	free(buf);
}