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