957 lines · 17.2 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "stdinc.h" #include "9.h" enum {	NUserHash	= 1009,}; typedef struct Ubox Ubox;typedef struct User User; struct User {	char*	uid;	char*	uname;	char*	leader;	char**	group;	int	ngroup; 	User*	next;			/* */	User*	ihash;			/* lookup by .uid */	User*	nhash;			/* lookup by .uname */}; #pragma varargck type "U"   User* struct Ubox {	User*	head;	User*	tail;	int	nuser;	int	len; 	User*	ihash[NUserHash];	/* lookup by .uid */	User*	nhash[NUserHash];	/* lookup by .uname */}; static struct {	VtLock*	lock; 	Ubox*	box;} ubox; static char usersDefault[] = {	"adm:adm:adm:sys\n"	"none:none::\n"	"noworld:noworld::\n"	"sys:sys::glenda\n"	"glenda:glenda:glenda:\n"}; static char* usersMandatory[] = {	"adm",	"none",	"noworld",	"sys",	nil,}; char* uidadm = "adm";char* unamenone = "none";char* uidnoworld = "noworld"; static u32intuserHash(char* s){	uchar *p;	u32int hash; 	hash = 0;	for(p = (uchar*)s; *p != '\0'; p++)		hash = hash*7 + *p; 	return hash % NUserHash;} static User*_userByUid(Ubox* box, char* uid){	User *u; 	if(box != nil){		for(u = box->ihash[userHash(uid)]; u != nil; u = u->ihash){			if(strcmp(u->uid, uid) == 0)				return u;		}	}	vtSetError("uname: uid '%s' not found", uid);	return nil;} char*unameByUid(char* uid){	User *u;	char *uname; 	vtRLock(ubox.lock);	if((u = _userByUid(ubox.box, uid)) == nil){		vtRUnlock(ubox.lock);		return nil;	}	uname = vtStrDup(u->uname);	vtRUnlock(ubox.lock); 	return uname;} static User*_userByUname(Ubox* box, char* uname){	User *u; 	if(box != nil){		for(u = box->nhash[userHash(uname)]; u != nil; u = u->nhash){			if(strcmp(u->uname, uname) == 0)				return u;		}	}	vtSetError("uname: uname '%s' not found", uname);	return nil;} char*uidByUname(char* uname){	User *u;	char *uid; 	vtRLock(ubox.lock);	if((u = _userByUname(ubox.box, uname)) == nil){		vtRUnlock(ubox.lock);		return nil;	}	uid = vtStrDup(u->uid);	vtRUnlock(ubox.lock); 	return uid;} static int_groupMember(Ubox* box, char* group, char* member, int whenNoGroup){	int i;	User *g, *m; 	/*	 * Is 'member' a member of 'group'?	 * Note that 'group' is a 'uid' and not a 'uname'.	 * A 'member' is automatically in their own group.	 */	if((g = _userByUid(box, group)) == nil)		return whenNoGroup;	if((m = _userByUname(box, member)) == nil)		return 0;	if(m == g)		return 1;	for(i = 0; i < g->ngroup; i++){		if(strcmp(g->group[i], member) == 0)			return 1;	}	return 0;} intgroupWriteMember(char* uname){	int ret; 	/*	 * If there is a ``write'' group, then only its members can write	 * to the file system, no matter what the permission bits say.	 *	 * To users not in the ``write'' group, the file system appears	 * read only.  This is used to serve sources.cs.bell-labs.com	 * to the world.	 *	 * Note that if there is no ``write'' group, then this routine	 * makes it look like everyone is a member -- the opposite	 * of what groupMember does.	 *	 * We use this for sources.cs.bell-labs.com.	 * If this slows things down too much on systems that don't	 * use this functionality, we could cache the write group lookup.	 */ 	vtRLock(ubox.lock);	ret = _groupMember(ubox.box, "write", uname, 1);	vtRUnlock(ubox.lock);	return ret;} static int_groupRemMember(Ubox* box, User* g, char* member){	int i; 	if(_userByUname(box, member) == nil)		return 0; 	for(i = 0; i < g->ngroup; i++){		if(strcmp(g->group[i], member) == 0)			break;	}	if(i >= g->ngroup){		if(strcmp(g->uname, member) == 0)			vtSetError("uname: '%s' always in own group", member);		else			vtSetError("uname: '%s' not in group '%s'",				member, g->uname);		return 0;	} 	vtMemFree(g->group[i]); 	box->len -= strlen(member);	if(g->ngroup > 1)		box->len--;	g->ngroup--;	switch(g->ngroup){	case 0:		vtMemFree(g->group);		g->group = nil;		break;	default:		for(; i < g->ngroup; i++)			g->group[i] = g->group[i+1];		g->group[i] = nil;		/* prevent accidents */		g->group = vtMemRealloc(g->group, g->ngroup * sizeof(char*));		break;	} 	return 1;} static int_groupAddMember(Ubox* box, User* g, char* member){	User *u; 	if((u = _userByUname(box, member)) == nil)		return 0;	if(_groupMember(box, g->uid, u->uname, 0)){		if(strcmp(g->uname, member) == 0)			vtSetError("uname: '%s' always in own group", member);		else			vtSetError("uname: '%s' already in group '%s'",				member, g->uname);		return 0;	} 	g->group = vtMemRealloc(g->group, (g->ngroup+1)*sizeof(char*));	g->group[g->ngroup] = vtStrDup(member);	box->len += strlen(member);	g->ngroup++;	if(g->ngroup > 1)		box->len++; 	return 1;} intgroupMember(char* group, char* member){	int r; 	if(group == nil)		return 0; 	vtRLock(ubox.lock);	r = _groupMember(ubox.box, group, member, 0);	vtRUnlock(ubox.lock); 	return r;} intgroupLeader(char* group, char* member){	int r;	User *g; 	/*	 * Is 'member' the leader of 'group'?	 * Note that 'group' is a 'uid' and not a 'uname'.	 * Uname 'none' cannot be a group leader.	 */	if(strcmp(member, unamenone) == 0 || group == nil)		return 0; 	vtRLock(ubox.lock);	if((g = _userByUid(ubox.box, group)) == nil){		vtRUnlock(ubox.lock);		return 0;	}	if(g->leader != nil){		if(strcmp(g->leader, member) == 0){			vtRUnlock(ubox.lock);			return 1;		}		r = 0;	}	else		r = _groupMember(ubox.box, group, member, 0);	vtRUnlock(ubox.lock); 	return r;} static voiduserFree(User* u){	int i; 	vtMemFree(u->uid);	vtMemFree(u->uname);	if(u->leader != nil)		vtMemFree(u->leader);	if(u->ngroup){		for(i = 0; i < u->ngroup; i++)			vtMemFree(u->group[i]);		vtMemFree(u->group);	}	vtMemFree(u);} static User*userAlloc(char* uid, char* uname){	User *u; 	u = vtMemAllocZ(sizeof(User));	u->uid = vtStrDup(uid);	u->uname = vtStrDup(uname); 	return u;} intvalidUserName(char* name){	Rune *r;	static Rune invalid[] = L"#:,()"; 	for(r = invalid; *r != '\0'; r++){		if(utfrune(name, *r))			return 0;	}	return 1;} static intuserFmt(Fmt* fmt){	User *u;	int i, r; 	u = va_arg(fmt->args, User*); 	r = fmtprint(fmt, "%s:%s:", u->uid, u->uname);	if(u->leader != nil)		r += fmtprint(fmt, u->leader);	r += fmtprint(fmt, ":");	if(u->ngroup){		r += fmtprint(fmt, u->group[0]);		for(i = 1; i < u->ngroup; i++)			r += fmtprint(fmt, ",%s", u->group[i]);	} 	return r;} static intusersFileWrite(Ubox* box){	Fs *fs;	User *u;	int i, r;	Fsys *fsys;	char *p, *q, *s;	File *dir, *file; 	if((fsys = fsysGet("main")) == nil)		return 0;	fsysFsRlock(fsys);	fs = fsysGetFs(fsys); 	/*	 * BUG:	 * 	the owner/group/permissions need to be thought out.	 */	r = 0;	if((dir = fileOpen(fs, "/active")) == nil)		goto tidy0;	if((file = fileWalk(dir, uidadm)) == nil)		file = fileCreate(dir, uidadm, ModeDir|0775, uidadm);	fileDecRef(dir);	if(file == nil)		goto tidy;	dir = file;	if((file = fileWalk(dir, "users")) == nil)		file = fileCreate(dir, "users", 0664, uidadm);	fileDecRef(dir);	if(file == nil)		goto tidy;	if(!fileTruncate(file, uidadm))		goto tidy; 	p = s = vtMemAlloc(box->len+1);	q = p + box->len+1;	for(u = box->head; u != nil; u = u->next){		p += snprint(p, q-p, "%s:%s:", u->uid, u->uname);		if(u->leader != nil)			p+= snprint(p, q-p, u->leader);		p += snprint(p, q-p, ":");		if(u->ngroup){			p += snprint(p, q-p, u->group[0]);			for(i = 1; i < u->ngroup; i++)				p += snprint(p, q-p, ",%s", u->group[i]);		}		p += snprint(p, q-p, "\n");	}	r = fileWrite(file, s, box->len, 0, uidadm);	vtMemFree(s); tidy:	if(file != nil)		fileDecRef(file);tidy0:	fsysFsRUnlock(fsys);	fsysPut(fsys); 	return r;} static voiduboxRemUser(Ubox* box, User *u){	User **h, *up; 	h = &box->ihash[userHash(u->uid)];	for(up = *h; up != nil && up != u; up = up->ihash)		h = &up->ihash;	assert(up == u);	*h = up->ihash;	box->len -= strlen(u->uid); 	h = &box->nhash[userHash(u->uname)];	for(up = *h; up != nil && up != u; up = up->nhash)		h = &up->nhash;	assert(up == u);	*h = up->nhash;	box->len -= strlen(u->uname); 	h = &box->head;	for(up = *h; up != nil && strcmp(up->uid, u->uid) != 0; up = up->next)		h = &up->next;	assert(up == u);	*h = u->next;	u->next = nil; 	box->len -= 4;	box->nuser--;} static voiduboxAddUser(Ubox* box, User* u){	User **h, *up; 	h = &box->ihash[userHash(u->uid)];	u->ihash = *h;	*h = u;	box->len += strlen(u->uid); 	h = &box->nhash[userHash(u->uname)];	u->nhash = *h;	*h = u;	box->len += strlen(u->uname); 	h = &box->head;	for(up = *h; up != nil && strcmp(up->uid, u->uid) < 0; up = up->next)		h = &up->next;	u->next = *h;	*h = u; 	box->len += 4;	box->nuser++;} static voiduboxDump(Ubox* box){

Lines 1–500 of 957.