All pastes #953658 Raw Edit

Stuff

public text v1 · immutable
#953658 ·published 2008-03-23 06:37 UTC
rendered paste body
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sx.h>
#include <sys/priv.h>
#include <sys/sysproto.h>
#include <sys/unistd.h>
#include <sys/condvar.h>
#include <sys/queue.h>

/* Maximum number of global mailbox groups allowed on the system */
#define MAXGLOBALMAILBOXES 	50

/* Each of these corresponds to 1 bit of the 9-bit permissions representation */
#define MPERM_OWNER_READ	0x100
#define MPERM_OWNER_WRITE	0x080
#define MPERM_OWNER_ADMIN	0x040
#define MPERM_GROUP_READ	0x020
#define MPERM_GROUP_WRITE	0x010
#define MPERM_GROUP_ADMIN	0x008
#define MPERM_OTHER_READ	0x004
#define MPERM_OTHER_WRITE	0x002
#define MPERM_OTHER_ADMIN	0x001

/* These can be used to check the return value of check_mperms() to see whether or not a process
   has sufficient privledges to read/write/admin a mailbox or mailbox group
 */
#define MPERM_READ_ALLOWED	0x4
#define MPERM_WRITE_ALLOWED	0x2
#define MPERM_ADMIN_ALLOWED	0x1

/* Declare M_MBOX to be a malloc_type */
MALLOC_DECLARE(M_MBOX);
MALLOC_DEFINE(M_MBOX, "mailbox_system", "Buffers to hold mboxgr and mbox structs and their associated data");

/* PRIVATE Function Prototypes (syscalls are prototyped in sys/sysproto.h)
 * =======================================================================
 */

/* Gets called during kernel startup to initialized linked lists, etc */
static void 	mboxinit(void *dummy);

/* Checks a process's permissions to read/write/admin */
static int 	check_mperms(struct proc *p, int perms, uid_t owner, int numGroups, gid_t *groups); 

/* Searches the linked list of GLOBAL mailbox groups by group name */
static struct mboxgr *find_gmboxgr(const char *name);

/* Searches the linked list of USER mailbox groups by uid# */
static struct mboxgr *find_umboxgr(const uid_t uid);


/* Global Data
 * ==========================
 */
LIST_HEAD(mboxgrlist, mboxgr);		/* Defines a new struct "mboxgrlist" to be a linked list of struct mboxgr */
struct sx 	all_gmboxgr_lock;	/* Shared/exclusive lock for the kernel's list of GLOBAL mailbox groups */
struct sx 	all_umboxgr_lock;	/* Shared/exclusive lock for the kernel's list of USER mailbox groups */
struct mboxgrlist all_gmboxgr;		/* List of all global mailbox groups on the system currently. 
					 * This list is protected by all_gmboxgr_lock 
					 */
struct mboxgrlist all_umboxgr;		/* List of all user mailbox groups on the system currently. 
					 * This list is protected by all_umboxgr_lock 
					 */
int 		numGlobalGroups;	/* Number of global mailbox groups on the system currently.
					 * This value is protected by all_gmboxgr_lock
					 */


/* Data Structures
 * ============================
 */

/* Mailbox Group data structure.  Can be either user or global.*/
struct mboxgr
{
	int 		type;		/* 0 for user, 1 for global */
	char 		name[9];	/* for a global mailbox group, this is the name of the group */
	int 		perms;		/* permissions for this mboxgr */
	uid_t 		owner;		/* uid of owner */
	gid_t		*groups;	/* Groups the owner belonged to when this mboxgr was created */
	int		numGroups;	/* Number of groups the owener belonged to when this mboxgr was created */
	LIST_ENTRY(mboxgr) mlist;	/* Node that allows this structure to be part of a mboxgrlist linked list */
	struct mbox 	*boxes[8];	/* Pointers to mailboxes */
	int 		numBoxes;	/* Number of mailboxs in the group currently */
	struct mtx 	m_mtx;		/* Lock for this struct */
};

/* Mailbox data structure.  Can belong to a user or a global mailbox group. */
struct mbox
{
	char 		name[256];	/* Name of mailbox */
	int 		perms;		/* permissions for this mbox */
	uid_t 		owner;		/* uid of owner */
	gid_t		*groups;	/* Groups the owner belonged to when this mboxgr was created */
	int		numGroups;	/* Number of groups the owener belonged to when this mboxgr was created */
	char 		message[256];	/* Message in this mailbox */
	int		hasMessage;	/* 0 if mbox is empty (or writable), 1 if there is a message (or readable) */
	struct cv	readable;	/* Threads waiting to read the mailbox message will wait on this */
	struct cv	writeable;	/* Threads waiting to write the mailbox message will wait on this */
	struct mtx	m_mtx;		/* Lock for this struct */
};



/* SUPPORT FUNCTIONS:
 * ======================
 */

/* Initialization function */
static void
mboxinit(dummy)
	void *dummy;	/* Not used */
{
	/* Print initialization message to dmesg during boot */
	printf("mailbox: Initializing CSE422 Project0 mailbox subsystem.\n");

	/* Initialize the locks that will be used to protect the 2 linked lists */
	sx_init(&all_gmboxgr_lock, "allgmboxgr");
	sx_init(&all_umboxgr_lock, "allumboxgr");

	numGlobalGroups = 0;

	/* Initialize 2 linked lists, one is the list of every global mailbox group, the other is for user mailbox groups */
	LIST_INIT(&all_gmboxgr);
	LIST_INIT(&all_umboxgr);
}
/* Causes the above funtion to get called during kernel startup */
SYSINIT(mbox, SI_SUB_MBOX, SI_ORDER_ANY, mboxinit, NULL)


/* Searches the linked list of GLOBAL mailbox groups by group name.
   Returns a pointer to the mailbox group if it exists, otherwise returns NULL
 */
static struct mboxgr *
find_gmboxgr(name)
	const char *name;	/* Target name */
{
	struct mboxgr 	*g;

	/* Ensure the calling thread has the list locked before trying to traverse list */
	sx_assert(&all_gmboxgr_lock, SA_LOCKED);
	LIST_FOREACH(g, &all_gmboxgr, mlist) {
		printf("Checking target %s against existing %s\n", name, g->name);
		if (strcmp(name, g->name) == 0)
			return (g);
	}
	return (NULL);
}


/* Searches the linked list of USER mailbox groups by uid #.
   Returns a pointer to the mailbox group if it exists, otherwise returns NULL
 */
static struct mboxgr *
find_umboxgr(uid)
	const uid_t uid;	/* Target uid */
{
	struct mboxgr 	*g;

	/* Ensure the calling thread has the list locked before trying to traverse list */
	sx_assert(&all_umboxgr_lock, SA_LOCKED);
	LIST_FOREACH(g, &all_umboxgr, mlist) {	
		printf("Checking target %d against existing %d\n", uid, g->owner);
		if (uid == g->owner)
			return (g);
	}
	return (NULL);
}


/* Checks a process'es permission to read, write, & admin a mailbox or mailbox group.  Returns an int of which only the
   lower 3 bits have any meaning.  Bit 2 means the process is allowed to read the resource, bit 1 means the process
   is allowed to write, and bit 0 means the process is allowed to admin
 */
static int
check_mperms(p,perms, owner, numGroups, groups)
	struct proc 	*p;			/* Process whose credentials will be checked */
	int 		perms;			/* Permissions against which the process'es credentials will be checked */
	uid_t 		owner;			/* Owner of the mailbox or mailbox group */
	int 		numGroups;		/* Number of groups the owner belongs to */
	gid_t 		*groups;		/* List of the groups the owner belongs to */
{
	int 		auth = 0;		/* This will be the return value */
	int 		haveCommonGroup = 0;	/* Whether or not the process and the owner of the share a common group. */

	/* Ensure the calling thread has the process' struct locked. */
	PROC_LOCK_ASSERT(p, MA_OWNED);

	printf("perms=%d, owner=%d, numgroups=%d, threaduid=%d\n",perms,owner,numGroups,p->p_ucred->cr_ruid);
	auth = perms & (MPERM_OTHER_READ | MPERM_OTHER_WRITE | MPERM_OTHER_ADMIN);
	printf("auth1=%d\n",auth);

	if (owner == p->p_ucred->cr_ruid)
		auth = auth | ((perms & (MPERM_OWNER_READ | MPERM_OWNER_WRITE | MPERM_OWNER_ADMIN)) >> 6);

	printf("auth2=%d\n",auth);
	for (int i = 0; i < p->p_ucred->cr_ngroups; i++) {
		for (int j = 0; j < numGroups; j++) {
			printf("Checking curthread's group %d against mbox owner group %d.\n",p->p_ucred->cr_groups[i],groups[j]);
			if (p->p_ucred->cr_groups[i] == groups[j]) {
				haveCommonGroup = 1;
				i = p->p_ucred->cr_ngroups;  /* do this to break out of outer loop. */
				break;
			}
		}
	}

	if (haveCommonGroup)
		auth = auth | ((perms & (MPERM_GROUP_READ | MPERM_GROUP_WRITE | MPERM_GROUP_ADMIN)) >> 3);
	printf("auth3=%d\n",auth);
	
	return (auth);
}



/* SYSTEM CALLS:
 * =============================
 */

/* Create a new mailbox group
 * UAP:(int type, char *name, uid_t uid, int perms)
 */
int
create_mboxgr(td, uap)
	struct thread *td;
	register struct create_mboxgr_args *uap;
{
	struct mboxgr 	*g;
	uid_t 		targetUID;	/* This will be the owner of the mbox group */
	struct mboxgrlist *myList;	/* Pointer to a mboxgrlist.  This will be set to all_gmboxgr or all_umboxgr */
	struct sx	*myListLock;	/* Pointer to a lock.  This will be set to all_gmboxgr_lock or all_umboxgr_lock */
	char		myName[9];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if ((uap->type == 0 && uap->name != NULL) || (uap->type == 1 && uap->name == NULL))
		return (EINVAL);

	targetUID = uap->uid;
	if (targetUID != 0) {
		if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) != 0)
			return (EACCES);
	} else 	{
		targetUID = td->td_ucred->cr_ruid;
	}

	if (uap->type == 1) {
		if ((copyin_error = copyinstr(uap->name, myName, 9, NULL)) != 0)
			return (copyin_error);

		if (myName[0] == 0)
			return (EINVAL);

		myList = &all_gmboxgr;
		myListLock = &all_gmboxgr_lock;
		sx_xlock(myListLock);
		if (numGlobalGroups >= MAXGLOBALMAILBOXES) {
			sx_xunlock(myListLock);
			return (EPERM);
		}
		g = find_gmboxgr(myName);
	} else 	{
		myList = &all_umboxgr;
		myListLock = &all_umboxgr_lock;
		sx_xlock(myListLock);
		g = find_umboxgr(targetUID);
	}	
	if (g != NULL) {
		/* a group by that name (or uid) already exists, so return a permission denied error */
		sx_xunlock(myListLock);
		return (EPERM);
	}

	/* Allocate memory for the new mboxgr structure and initialize its data */
	MALLOC(g, struct mboxgr *, sizeof(struct mboxgr), M_MBOX, M_WAITOK);
	mtx_init(&g->m_mtx, "struct mboxgr mutex", NULL, MTX_DEF);
	if (uap->type == 0) {
		g->name[0] = 0;
	} else {
		strcpy(g->name, myName);
		numGlobalGroups++;	
	}
	g->type = uap->type;
	g->perms = uap->perms;
	g->owner = targetUID;
	g->numBoxes = 0;
	g->numGroups = td->td_ucred->cr_ngroups;
	
	/* Allocate memory to hold the g->groups[] array */
	MALLOC(g->groups, gid_t*, sizeof(gid_t)*(g->numGroups), M_MBOX, M_WAITOK);
	for (int j = 0; j < g->numGroups; j++) {
		g->groups[j] = td->td_ucred->cr_groups[j];
	}
	LIST_INSERT_HEAD(myList, g, mlist);
	sx_xunlock(myListLock);

	return (0);
}


/* Destroy a mailbox group
 * UAP:(int type, char *name, uid_t uid)
 */
int
destroy_mboxgr(td, uap)
	register struct thread *td;
	struct destroy_mboxgr_args *uap;
{
	struct mboxgr 	*g;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs;	/* Flag to indicate if the calling therad has root privledges */
	struct mboxgrlist *myList;	/* Pointer to a mboxgrlist.  This will be set to all_gmboxgr or all_umboxgr */
	struct sx	*myListLock;	/* Pointer to a lock.  This will be set to all_gmboxgr_lock or all_umboxgr_lock */
	char		myName[9];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if ((uap->type == 0 && uap->name != NULL) || (uap->type == 1 && uap->name == NULL))
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if (uap->type == 1)
	{
		if ((copyin_error = copyinstr(uap->name, myName, 9, NULL)) != 0)
			return (copyin_error);

		if (myName[0] == 0)
			return (EINVAL);

		myList = &all_gmboxgr;
		myListLock = &all_gmboxgr_lock;
		sx_xlock(myListLock);
		g = find_gmboxgr(myName);
				
	} else {
		myList = &all_umboxgr;
		myListLock = &all_umboxgr_lock;
		sx_xlock(myListLock);
		g = find_umboxgr(uap->uid);
	}	
		
	if (g == NULL) {
		/* a group with the requested uid or name was not found: */
		sx_xunlock(myListLock);
		return (EINVAL);
	}
		
	mtx_lock(&g->m_mtx);
	if (!rootPrivs) {
		/* root can destroy any mailbox group (?) */
		PROC_LOCK(td->td_proc);
		authorization = check_mperms(td->td_proc, g->perms, g->owner, g->numGroups, g->groups);
		PROC_UNLOCK(td->td_proc);
		if ((authorization & MPERM_ADMIN_ALLOWED) == 0) {
			/* calling thread doesnt have admin privs on the group */
			mtx_unlock(&g->m_mtx);
			sx_xunlock(myListLock);
			return (EACCES);
		}
	}

	LIST_REMOVE(g, mlist);
	if (uap->type == 1)
		numGlobalGroups--;
	
	/* Destroy each mailbox in the mailbox group:
	 * per the proj specs it's not neccessary to check if we have perm. to delete each mbox (?)
	 */
	for (int i = 0; i < g->numBoxes; i++) {
		mtx_lock(&g->boxes[i]->m_mtx);
		cv_broadcast(&g->boxes[i]->readable);
		cv_broadcast(&g->boxes[i]->writeable);
		cv_destroy(&g->boxes[i]->readable);
		cv_destroy(&g->boxes[i]->writeable);
		FREE(g->boxes[i]->groups,M_MBOX);
		mtx_destroy(&g->boxes[i]->m_mtx);
		FREE(g->boxes[i],M_MBOX);
	}

	FREE(g->groups,M_MBOX);
	mtx_destroy(&g->m_mtx);
	FREE(g,M_MBOX);	
	sx_xunlock(myListLock);
	return (0);
}

/* Join a global mailbox group
 * UAP:(char *name, pid_t pid)
 */
int
join_mboxgr(td, uap)
	register struct thread *td;
	struct join_mboxgr_args *uap;
{
	struct mboxgr 	*g;
	struct proc 	*p;	
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	char		myName[9];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	p = NULL;

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if (uap->pid == 0) {
		PROC_LOCK(td->td_proc);
		p = td->td_proc;
	} else {
		if ((p = pfind(uap->pid)) == NULL)
			return (ESRCH);
	}	

	if (uap->name != NULL) {
		if ((copyin_error = copyinstr(uap->name, myName, 9, NULL)) != 0) {
			PROC_UNLOCK(p);
			return (copyin_error);
		}
		if (myName[0] == 0) {
			PROC_UNLOCK(p);
			return (EINVAL);
		}		

		sx_slock(&all_gmboxgr_lock);
			
		if ((g = find_gmboxgr(myName)) == NULL) {
			/* uap->name is a not valid global mailbox group name */
			PROC_UNLOCK(p);				
			sx_sunlock(&all_gmboxgr_lock);
			return (EINVAL);
		}

		if (!rootPrivs) {
			mtx_lock(&g->m_mtx);
			authorization = check_mperms(p, g->perms, g->owner, g->numGroups, g->groups);
			mtx_unlock(&g->m_mtx);
			if ((authorization & (MPERM_READ_ALLOWED | MPERM_WRITE_ALLOWED)) == 0) {
				PROC_UNLOCK(p);
				sx_sunlock(&all_gmboxgr_lock);
				return (EACCES);
			}
		}
		strcpy(p->p_gmboxgr,myName);
		p->p_gmboxgr[8] = 0;
	} else {
		if (rootPrivs || (p->p_ucred->cr_ruid == td->td_ucred->cr_ruid)) {
			p->p_gmboxgr[0] = 0;
		} else {
			PROC_UNLOCK(p);
			sx_sunlock(&all_gmboxgr_lock);
			return (EACCES);
		}
	}

	PROC_UNLOCK(p);
	sx_sunlock(&all_gmboxgr_lock);
	return (0);
}

/* Create a mailbox
 * UAP:(int type, char *name, uid_t uid, int perms)
 */
int
create_mbox(td, uap)
	register struct thread *td;
	struct create_mbox_args *uap;
{
	uid_t 		targetUID;
	struct 	mboxgr 	*g;
	struct 	mbox 	*b;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	char		myName[256];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if (uap->name == NULL)
		return (EINVAL);

	if ((uap->type == 1) && (td->td_proc->p_gmboxgr[0] == 0))
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if (uap->type == 1) {
		sx_slock(&all_gmboxgr_lock);
		g = find_gmboxgr(td->td_proc->p_gmboxgr);
		if (g == NULL) {
			sx_sunlock(&all_gmboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_sunlock(&all_gmboxgr_lock);
	} else {
		targetUID = uap->uid;
		if (targetUID == 0)
			targetUID = td->td_ucred->cr_ruid;

		sx_slock(&all_umboxgr_lock);
		g = find_umboxgr(targetUID);
		if (g == NULL) {
			sx_sunlock(&all_umboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_sunlock(&all_umboxgr_lock);
	}

	if (!rootPrivs) {
		PROC_LOCK(td->td_proc);
		authorization = check_mperms(td->td_proc, g->perms, g->owner, g->numGroups, g->groups);
		PROC_UNLOCK(td->td_proc);
		if ((authorization & MPERM_ADMIN_ALLOWED) == 0) {
			mtx_unlock(&g->m_mtx);
			return (EACCES);  /*  not allowed to create new mbox */
		}
	}

	if (g->numBoxes == 8) {
		mtx_unlock(&g->m_mtx);
		return (EPERM);  /*  not allowed to create any more mboxs */
	}
	if ((copyin_error = copyinstr(uap->name, myName, 256, NULL)) != 0) {
		mtx_unlock(&g->m_mtx);
		return (copyin_error);
	}
	if (myName[0] == 0) {
		mtx_unlock(&g->m_mtx);
		return (EINVAL);
	}		
;
	for (int i = 0; i < g->numBoxes; i++) {
		mtx_lock(&g->boxes[i]->m_mtx);
		if (strcmp(g->boxes[i]->name, myName) == 0) {
			mtx_unlock(&g->boxes[i]->m_mtx);
			mtx_unlock(&g->m_mtx);
			return (EINVAL);  /*  already a box with that name */
		}
		mtx_unlock(&g->boxes[i]->m_mtx);
	}

	MALLOC(b, struct mbox *, sizeof(struct mbox), M_MBOX, M_WAITOK);

	strcpy(b->name, myName);

	b->perms = uap->perms;
	b->owner = td->td_ucred->cr_ruid;
	b->hasMessage = 0;
	mtx_init(&b->m_mtx, "struct mbox mutex", NULL, MTX_DEF);
	cv_init(&b->readable, "struct mbox readable condition variable");
	cv_init(&b->writeable, "struct mbox writable condition variable");
	b->numGroups = td->td_ucred->cr_ngroups;
	MALLOC(b->groups, gid_t*, sizeof(gid_t)*(b->numGroups), M_MBOX, M_WAITOK);
	for (int j = 0; j < g->numGroups; j++) {
		b->groups[j] = td->td_ucred->cr_groups[j];
	}
	g->boxes[g->numBoxes++] = b;
	mtx_unlock(&g->m_mtx);
	return (0);
}

/* Destroy a mailbox
 * UAP: (int type, char *name, uid_t uid)
 */
int
destroy_mbox(td, uap)
	register struct thread *td;
	struct destroy_mbox_args *uap;
{
	struct mboxgr 	*g;
	struct mbox 	*b;
	uid_t 		targetUID;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	int 		i;
	int 		foundBox;
	char		myName[256];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if (uap->name == NULL)
		return (EINVAL);


	if ((uap->type == 1) && (td->td_proc->p_gmboxgr[0] == 0))
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if ((copyin_error = copyinstr(uap->name, myName, 256, NULL)) != 0)
		return (copyin_error);

	if (myName[0] == 0)
		return (EINVAL);

	if (uap->type == 1) {
		sx_slock(&all_gmboxgr_lock);
		g = find_gmboxgr(td->td_proc->p_gmboxgr);
		if (g == NULL)
		{
			sx_sunlock(&all_gmboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_sunlock(&all_gmboxgr_lock);
	} else {
		targetUID = uap->uid;
		if (targetUID == 0)
			targetUID = td->td_ucred->cr_ruid;
		
		sx_slock(&all_umboxgr_lock);
		g = find_umboxgr(targetUID);
		if (g == NULL) {
			sx_sunlock(&all_umboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_slock(&all_umboxgr_lock);
	}

	foundBox = 0;
	for (i = 0; i < g->numBoxes; i++) {
		mtx_lock(&g->boxes[i]->m_mtx);
		if (strcmp(g->boxes[i]->name, myName) == 0) {
			foundBox = 1;
			b = g->boxes[i];
			break;
		}
		mtx_unlock(&g->boxes[i]->m_mtx);
	}

	if (!foundBox) {
		mtx_unlock(&g->m_mtx);
		return (EINVAL);  /*  mailbox not found */
	}

	if (!rootPrivs) {
		PROC_LOCK(td->td_proc);
		authorization = check_mperms(td->td_proc, b->perms, b->owner, b->numGroups, b->groups);
		PROC_UNLOCK(td->td_proc);
		if ((authorization & MPERM_ADMIN_ALLOWED) == 0) {
			mtx_unlock(&b->m_mtx);
			mtx_unlock(&g->m_mtx);
			return (EACCES);  /*  not allowed to create new mbox */
		}
	}

	if (i != ((g->numBoxes)-1))
		g->boxes[i] = g->boxes[(g->numBoxes)-1];

	g->boxes[(g->numBoxes)-1] = NULL;
	g->numBoxes--;
	cv_broadcast(&b->readable);
	cv_broadcast(&b->writeable);
	cv_destroy(&b->readable);
	cv_destroy(&b->writeable);
	FREE(b->groups,M_MBOX);
	mtx_destroy(&b->m_mtx);
	FREE(b,M_MBOX);
	mtx_unlock(&g->m_mtx);
	return (0);
}

/* Write a message to a mailbox
 * UAP: (int type, char *name, uid_t uid, char *message, int flag)
 */
int
write_mbox(td, uap)
	register struct thread *td;
	struct write_mbox_args *uap;
{
	struct mbox 	*b;
	struct mboxgr 	*g;
	uid_t 		targetUID;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	int 		i;
	int 		foundBox;
	char		myName[256];	/* Buffer to hold name argument */
	char		myMessage[256];
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if (uap->name == NULL || uap->message == NULL)
		return (EINVAL);

	if ((uap->type == 1) && (td->td_proc->p_gmboxgr[0] == 0))
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if ((copyin_error = copyinstr(uap->name, myName, 256, NULL)) != 0)
		return (copyin_error);

	if (myName[0] == 0)
		return (EINVAL);

	if ((copyin_error = copyinstr(uap->message, myMessage, 256, NULL)) != 0)
		return (copyin_error);

	for (;;) {
		if (uap->type == 1) {
			sx_slock(&all_gmboxgr_lock);
			g = find_gmboxgr(td->td_proc->p_gmboxgr);
			if (g == NULL) {
				sx_sunlock(&all_gmboxgr_lock);
				return (EINVAL);
			}
			mtx_lock(&g->m_mtx);
			sx_sunlock(&all_gmboxgr_lock);
		} else {
			targetUID = uap->uid;
			if (targetUID == 0)
				targetUID = td->td_ucred->cr_ruid;
			
			sx_slock(&all_umboxgr_lock);
			g = find_umboxgr(targetUID);
			if (g == NULL) {
				sx_sunlock(&all_umboxgr_lock);
				return (EINVAL);
			}
			mtx_lock(&g->m_mtx);
			sx_sunlock(&all_umboxgr_lock);
		}
	
		foundBox = 0;
		b=NULL;
		for (i = 0; i < g->numBoxes; i++) {
			mtx_lock(&g->boxes[i]->m_mtx);
			if (strcmp(g->boxes[i]->name, myName) == 0) {
				foundBox = 1;
				b = g->boxes[i];
				break;
			}
			mtx_unlock(&g->boxes[i]->m_mtx);
		}
	
		mtx_unlock(&g->m_mtx);
	
		if (!foundBox)
			return (EINVAL);  /*  mailbox not found */
	
		if (!rootPrivs) {
			PROC_LOCK(td->td_proc);
			authorization = check_mperms(td->td_proc, b->perms, b->owner, b->numGroups, b->groups);
			PROC_UNLOCK(td->td_proc);
			if ((authorization & (MPERM_WRITE_ALLOWED)) == 0) {
				mtx_unlock(&b->m_mtx);
				return (EACCES);  /*  not allowed to create new mbox */
			}
		}
	
		if (b->hasMessage == 0) {
			/* write message */
			strcpy(b->message, myMessage);
			b->hasMessage = 1;
			cv_broadcast(&b->readable);
			mtx_unlock(&b->m_mtx);
			return (0);
		}
	
		if (uap->flag) {
			mtx_unlock(&b->m_mtx);
			return (EWOULDBLOCK);
		}
	
		/* Atomically release the lock on the mbox and get added to the sleep queue associated
		with the mbox's writable conditional variable.  When this thread gets awakened it will goto
		the top of the for(;;) loop and have to reacquire all the locks all the way down.  This
		way, if the box or group is deleted any threads waiting on one of its conditional variables
		will get awakened and then realize that there is no such group or box and return an error */
		cv_wait_unlock(&b->writeable, &b->m_mtx);
	}

	return (0);	/* NOTREACHED */
}


/* Read a message from a mailbox
 *(int type, char *name, uid_t uid, char *message, int len, int flag)
 */
int
read_mbox(td, uap)
	register struct thread *td;
	struct read_mbox_args *uap;
{
	struct mbox 	*b;
	struct mboxgr 	*g;
	uid_t 		targetUID;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	int 		i;
	int 		foundBox;
	char		myName[256];	/* Buffer to hold name argument */
	int		outBytes;
	int 		error;
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if (uap->name == NULL  || uap->message == NULL)
		return (EINVAL);

	if ((uap->type == 1) && (td->td_proc->p_gmboxgr[0] == 0))
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if ((copyin_error = copyinstr(uap->name, myName, 256, NULL)) != 0)
		return (copyin_error);

	if (myName[0] == 0)
		return (EINVAL);

	for (;;) {
		if (uap->type == 1) {
			sx_slock(&all_gmboxgr_lock);
			g = find_gmboxgr(td->td_proc->p_gmboxgr);
			if (g == NULL) {
				sx_sunlock(&all_gmboxgr_lock);
				return (EINVAL);
			}
			mtx_lock(&g->m_mtx);
			sx_sunlock(&all_gmboxgr_lock);
		} else {
			targetUID = uap->uid;
			if (targetUID == 0)
				targetUID = td->td_ucred->cr_ruid;
			
			sx_slock(&all_umboxgr_lock);
			g = find_umboxgr(targetUID);
			if (g == NULL) {
				sx_sunlock(&all_umboxgr_lock);
				return (EINVAL);
			}
			mtx_lock(&g->m_mtx);
			sx_sunlock(&all_umboxgr_lock);
		}
	
		foundBox = 0;
		b=NULL;
		for (i = 0; i < g->numBoxes; i++) {
			mtx_lock(&g->boxes[i]->m_mtx);
			if (strcmp(g->boxes[i]->name, myName) == 0) {
				foundBox = 1;
				b = g->boxes[i];
				break;
			}
			mtx_unlock(&g->boxes[i]->m_mtx);
		}
	
		mtx_unlock(&g->m_mtx);
	
		if (!foundBox)
			return (EINVAL);  /*  mailbox not found */

		if (!rootPrivs) {
			PROC_LOCK(td->td_proc);
			authorization = check_mperms(td->td_proc, b->perms, b->owner, b->numGroups, b->groups);
			PROC_UNLOCK(td->td_proc);
			if ((authorization & (MPERM_READ_ALLOWED)) == 0) {
				mtx_unlock(&b->m_mtx);
				return (EACCES);  /*  not allowed to create new mbox */
			}
		}
	
		if (b->hasMessage == 1) {
			/* read message */
			if (uap->len < (outBytes=strlen(b->message)+1)) {
				outBytes = uap->len;
				strncpy(myName, b->message, outBytes-1);
				myName[outBytes-1]=0;
			} else {
				strcpy(myName, b->message);
			}

			error = copyout(myName, uap->message, outBytes);
			if (error) {
				mtx_unlock(&b->m_mtx);
				return (error);
			}

			b->hasMessage = 0;
			cv_broadcast(&b->writeable);
			mtx_unlock(&b->m_mtx);
			td->td_retval[0]=outBytes;
			return (0);
		}
	
		if (uap->flag) {
			mtx_unlock(&b->m_mtx);
			return (EWOULDBLOCK);
		}
	
		/* Atomically release the lock on the mbox and get added to the sleep queue associated
		with the mbox's readable conditional variable.  When this thread gets awakened it will goto
		the top of the for(;;) loop and have to reacquire all the locks all the way down.  This
		way, if the box or group is deleted any threads waiting on one of its conditional variables
		will get awakened and then realize that there is no such group or box and return an error */
		cv_wait_unlock(&b->readable, &b->m_mtx);
	}

	return (0);	/* NOTREACHED */
}

/* Clears message from mailbox and restores default mailbox state
 * UAP:(int type, char *name, uid_t uid)
 */
int
clear_mbox(td, uap)
	register struct thread *td;
	struct clear_mbox_args *uap;
{
	struct mboxgr	*g;
	struct mbox 	*b;
	uid_t		targetUID;
	int 		authorization;	/* Holds the return value from a call to check_mperms() */
	int 		rootPrivs = 0;	/* Flag to indicate if the calling therad has root privledges */
	int 		i;
	int 		foundBox;
	char		myName[256];	/* Buffer to hold name argument */
	int		copyin_error;	/* Holds the result of a call to copyinstr() */

	if (uap->type < 0 || uap->type > 1)
		return (EINVAL);

	if (uap->name == NULL)
		return (EINVAL);

	if ((uap->type == 1) && (td->td_proc->p_gmboxgr[0] == 0))
		return (EINVAL);

	if ((copyin_error = copyinstr(uap->name, myName, 256, NULL)) != 0)
		return (copyin_error);

	if (myName[0] == 0)
		return (EINVAL);

	if (priv_check_cred(td->td_ucred, PRIV_ROOT, 0) == 0)
		rootPrivs = 1;
	else
		rootPrivs = 0;

	if (uap->type == 1) {
		sx_slock(&all_gmboxgr_lock);
		g = find_gmboxgr(td->td_proc->p_gmboxgr);
		if (g == NULL) {
			sx_sunlock(&all_gmboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_sunlock(&all_gmboxgr_lock);
	} else {
		targetUID = uap->uid;
		if (targetUID == 0)
			targetUID = td->td_ucred->cr_ruid;
		
		sx_slock(&all_umboxgr_lock);
		g = find_umboxgr(targetUID);
		if (g == NULL) {
			sx_sunlock(&all_umboxgr_lock);
			return (EINVAL);
		}
		mtx_lock(&g->m_mtx);
		sx_sunlock(&all_umboxgr_lock);
	}

	foundBox = 0;
	b=NULL;
	for (i = 0; i < g->numBoxes; i++) {
		mtx_lock(&g->boxes[i]->m_mtx);
		if (strcmp(g->boxes[i]->name, uap->name) == 0) {
			foundBox = 1;
			b = g->boxes[i];
			break;
		}
		mtx_unlock(&g->boxes[i]->m_mtx);
	}

	mtx_unlock(&g->m_mtx);

	if (!foundBox)
		return (EINVAL);  /*  mailbox not found */

	if (!rootPrivs) {
		PROC_LOCK(td->td_proc);
		authorization = check_mperms(td->td_proc, b->perms, b->owner, b->numGroups, b->groups);
		PROC_UNLOCK(td->td_proc);
		if ((authorization & (MPERM_ADMIN_ALLOWED|MPERM_READ_ALLOWED)) == 0) {
			mtx_unlock(&b->m_mtx);
			return (EACCES);  /*  not allowed to create new mbox */
		}
	}

	b->hasMessage = 0;

	/* Wake up any thread that may be sleeping on this box.  Probably only need to wakup the threads
	 * sleeping on writeable, but it may be required to wakeup the ones waiting on readable too, 
	 * and it wont hurt.
	 */
	cv_broadcast(&b->readable);
	cv_broadcast(&b->writeable);
	mtx_unlock(&b->m_mtx);
	return (0);
}