diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /ipc/msg.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'ipc/msg.c')
-rw-r--r-- | ipc/msg.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/ipc/msg.c b/ipc/msg.c new file mode 100644 index 000000000..04aa12328 --- /dev/null +++ b/ipc/msg.c @@ -0,0 +1,441 @@ +/* + * linux/ipc/msg.c + * Copyright (C) 1992 Krishna Balasubramanian + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/msg.h> +#include <linux/stat.h> +#include <linux/malloc.h> + +#include <asm/segment.h> + +extern int ipcperms (struct ipc_perm *ipcp, short msgflg); + +static void freeque (int id); +static int newque (key_t key, int msgflg); +static int findkey (key_t key); + +static struct msqid_ds *msgque[MSGMNI]; +static int msgbytes = 0; +static int msghdrs = 0; +static unsigned short msg_seq = 0; +static int used_queues = 0; +static int max_msqid = 0; +static struct wait_queue *msg_lock = NULL; + +void msg_init (void) +{ + int id; + + for (id = 0; id < MSGMNI; id++) + msgque[id] = (struct msqid_ds *) IPC_UNUSED; + msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0; + msg_lock = NULL; + return; +} + +int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg) +{ + int id, err; + struct msqid_ds *msq; + struct ipc_perm *ipcp; + struct msg *msgh; + long mtype; + + if (msgsz > MSGMAX || msgsz < 0 || msqid < 0) + return -EINVAL; + if (!msgp) + return -EFAULT; + err = verify_area (VERIFY_READ, msgp->mtext, msgsz); + if (err) + return err; + if ((mtype = get_fs_long (&msgp->mtype)) < 1) + return -EINVAL; + id = (unsigned int) msqid % MSGMNI; + msq = msgque [id]; + if (msq == IPC_UNUSED || msq == IPC_NOID) + return -EINVAL; + ipcp = &msq->msg_perm; + + slept: + if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) + return -EIDRM; + if (ipcperms(ipcp, S_IWUGO)) + return -EACCES; + + if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { + /* no space in queue */ + if (msgflg & IPC_NOWAIT) + return -EAGAIN; + if (current->signal & ~current->blocked) + return -EINTR; + interruptible_sleep_on (&msq->wwait); + goto slept; + } + + /* allocate message header and text space*/ + msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER); + if (!msgh) + return -ENOMEM; + msgh->msg_spot = (char *) (msgh + 1); + memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz); + + if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID + || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) { + kfree(msgh); + return -EIDRM; + } + + msgh->msg_next = NULL; + if (!msq->msg_first) + msq->msg_first = msq->msg_last = msgh; + else { + msq->msg_last->msg_next = msgh; + msq->msg_last = msgh; + } + msgh->msg_ts = msgsz; + msgh->msg_type = mtype; + msq->msg_cbytes += msgsz; + msgbytes += msgsz; + msghdrs++; + msq->msg_qnum++; + msq->msg_lspid = current->pid; + msq->msg_stime = CURRENT_TIME; + if (msq->rwait) + wake_up (&msq->rwait); + return msgsz; +} + +int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, + int msgflg) +{ + struct msqid_ds *msq; + struct ipc_perm *ipcp; + struct msg *tmsg, *leastp = NULL; + struct msg *nmsg = NULL; + int id, err; + + if (msqid < 0 || msgsz < 0) + return -EINVAL; + if (!msgp || !msgp->mtext) + return -EFAULT; + err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz); + if (err) + return err; + + id = (unsigned int) msqid % MSGMNI; + msq = msgque [id]; + if (msq == IPC_NOID || msq == IPC_UNUSED) + return -EINVAL; + ipcp = &msq->msg_perm; + + /* + * find message of correct type. + * msgtyp = 0 => get first. + * msgtyp > 0 => get first message of matching type. + * msgtyp < 0 => get message with least type must be < abs(msgtype). + */ + while (!nmsg) { + if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) + return -EIDRM; + if (ipcperms (ipcp, S_IRUGO)) + return -EACCES; + if (msgtyp == 0) + nmsg = msq->msg_first; + else if (msgtyp > 0) { + if (msgflg & MSG_EXCEPT) { + for (tmsg = msq->msg_first; tmsg; + tmsg = tmsg->msg_next) + if (tmsg->msg_type != msgtyp) + break; + nmsg = tmsg; + } else { + for (tmsg = msq->msg_first; tmsg; + tmsg = tmsg->msg_next) + if (tmsg->msg_type == msgtyp) + break; + nmsg = tmsg; + } + } else { + for (leastp = tmsg = msq->msg_first; tmsg; + tmsg = tmsg->msg_next) + if (tmsg->msg_type < leastp->msg_type) + leastp = tmsg; + if (leastp && leastp->msg_type <= - msgtyp) + nmsg = leastp; + } + + if (nmsg) { /* done finding a message */ + if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) + return -E2BIG; + msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz; + if (nmsg == msq->msg_first) + msq->msg_first = nmsg->msg_next; + else { + for (tmsg = msq->msg_first; tmsg; + tmsg = tmsg->msg_next) + if (tmsg->msg_next == nmsg) + break; + tmsg->msg_next = nmsg->msg_next; + if (nmsg == msq->msg_last) + msq->msg_last = tmsg; + } + if (!(--msq->msg_qnum)) + msq->msg_last = msq->msg_first = NULL; + + msq->msg_rtime = CURRENT_TIME; + msq->msg_lrpid = current->pid; + msgbytes -= nmsg->msg_ts; + msghdrs--; + msq->msg_cbytes -= nmsg->msg_ts; + if (msq->wwait) + wake_up (&msq->wwait); + put_fs_long (nmsg->msg_type, &msgp->mtype); + memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz); + kfree(nmsg); + return msgsz; + } else { /* did not find a message */ + if (msgflg & IPC_NOWAIT) + return -ENOMSG; + if (current->signal & ~current->blocked) + return -EINTR; + interruptible_sleep_on (&msq->rwait); + } + } /* end while */ + return -1; +} + + +static int findkey (key_t key) +{ + int id; + struct msqid_ds *msq; + + for (id = 0; id <= max_msqid; id++) { + while ((msq = msgque[id]) == IPC_NOID) + interruptible_sleep_on (&msg_lock); + if (msq == IPC_UNUSED) + continue; + if (key == msq->msg_perm.key) + return id; + } + return -1; +} + +static int newque (key_t key, int msgflg) +{ + int id; + struct msqid_ds *msq; + struct ipc_perm *ipcp; + + for (id = 0; id < MSGMNI; id++) + if (msgque[id] == IPC_UNUSED) { + msgque[id] = (struct msqid_ds *) IPC_NOID; + goto found; + } + return -ENOSPC; + +found: + msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL); + if (!msq) { + msgque[id] = (struct msqid_ds *) IPC_UNUSED; + if (msg_lock) + wake_up (&msg_lock); + return -ENOMEM; + } + ipcp = &msq->msg_perm; + ipcp->mode = (msgflg & S_IRWXUGO); + ipcp->key = key; + ipcp->cuid = ipcp->uid = current->euid; + ipcp->gid = ipcp->cgid = current->egid; + msq->msg_perm.seq = msg_seq; + msq->msg_first = msq->msg_last = NULL; + msq->rwait = msq->wwait = NULL; + msq->msg_cbytes = msq->msg_qnum = 0; + msq->msg_lspid = msq->msg_lrpid = 0; + msq->msg_stime = msq->msg_rtime = 0; + msq->msg_qbytes = MSGMNB; + msq->msg_ctime = CURRENT_TIME; + if (id > max_msqid) + max_msqid = id; + msgque[id] = msq; + used_queues++; + if (msg_lock) + wake_up (&msg_lock); + return (unsigned int) msq->msg_perm.seq * MSGMNI + id; +} + +int sys_msgget (key_t key, int msgflg) +{ + int id; + struct msqid_ds *msq; + + if (key == IPC_PRIVATE) + return newque(key, msgflg); + if ((id = findkey (key)) == -1) { /* key not used */ + if (!(msgflg & IPC_CREAT)) + return -ENOENT; + return newque(key, msgflg); + } + if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) + return -EEXIST; + msq = msgque[id]; + if (msq == IPC_UNUSED || msq == IPC_NOID) + return -EIDRM; + if (ipcperms(&msq->msg_perm, msgflg)) + return -EACCES; + return (unsigned int) msq->msg_perm.seq * MSGMNI + id; +} + +static void freeque (int id) +{ + struct msqid_ds *msq = msgque[id]; + struct msg *msgp, *msgh; + + msq->msg_perm.seq++; + msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */ + msgbytes -= msq->msg_cbytes; + if (id == max_msqid) + while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED)); + msgque[id] = (struct msqid_ds *) IPC_UNUSED; + used_queues--; + while (msq->rwait || msq->wwait) { + if (msq->rwait) + wake_up (&msq->rwait); + if (msq->wwait) + wake_up (&msq->wwait); + schedule(); + } + for (msgp = msq->msg_first; msgp; msgp = msgh ) { + msgh = msgp->msg_next; + msghdrs--; + kfree(msgp); + } + kfree(msq); +} + +int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf) +{ + int id, err; + struct msqid_ds *msq; + struct msqid_ds tbuf; + struct ipc_perm *ipcp; + + if (msqid < 0 || cmd < 0) + return -EINVAL; + switch (cmd) { + case IPC_INFO: + case MSG_INFO: + if (!buf) + return -EFAULT; + { + struct msginfo msginfo; + msginfo.msgmni = MSGMNI; + msginfo.msgmax = MSGMAX; + msginfo.msgmnb = MSGMNB; + msginfo.msgmap = MSGMAP; + msginfo.msgpool = MSGPOOL; + msginfo.msgtql = MSGTQL; + msginfo.msgssz = MSGSSZ; + msginfo.msgseg = MSGSEG; + if (cmd == MSG_INFO) { + msginfo.msgpool = used_queues; + msginfo.msgmap = msghdrs; + msginfo.msgtql = msgbytes; + } + err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo)); + if (err) + return err; + memcpy_tofs (buf, &msginfo, sizeof(struct msginfo)); + return max_msqid; + } + case MSG_STAT: + if (!buf) + return -EFAULT; + err = verify_area (VERIFY_WRITE, buf, sizeof (*buf)); + if (err) + return err; + if (msqid > max_msqid) + return -EINVAL; + msq = msgque[msqid]; + if (msq == IPC_UNUSED || msq == IPC_NOID) + return -EINVAL; + if (ipcperms (&msq->msg_perm, S_IRUGO)) + return -EACCES; + id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid; + tbuf.msg_perm = msq->msg_perm; + tbuf.msg_stime = msq->msg_stime; + tbuf.msg_rtime = msq->msg_rtime; + tbuf.msg_ctime = msq->msg_ctime; + tbuf.msg_cbytes = msq->msg_cbytes; + tbuf.msg_qnum = msq->msg_qnum; + tbuf.msg_qbytes = msq->msg_qbytes; + tbuf.msg_lspid = msq->msg_lspid; + tbuf.msg_lrpid = msq->msg_lrpid; + memcpy_tofs (buf, &tbuf, sizeof(*buf)); + return id; + case IPC_SET: + if (!buf) + return -EFAULT; + err = verify_area (VERIFY_READ, buf, sizeof (*buf)); + if (err) + return err; + memcpy_fromfs (&tbuf, buf, sizeof (*buf)); + break; + case IPC_STAT: + if (!buf) + return -EFAULT; + err = verify_area (VERIFY_WRITE, buf, sizeof(*buf)); + if (err) + return err; + break; + } + + id = (unsigned int) msqid % MSGMNI; + msq = msgque [id]; + if (msq == IPC_UNUSED || msq == IPC_NOID) + return -EINVAL; + if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) + return -EIDRM; + ipcp = &msq->msg_perm; + + switch (cmd) { + case IPC_STAT: + if (ipcperms (ipcp, S_IRUGO)) + return -EACCES; + tbuf.msg_perm = msq->msg_perm; + tbuf.msg_stime = msq->msg_stime; + tbuf.msg_rtime = msq->msg_rtime; + tbuf.msg_ctime = msq->msg_ctime; + tbuf.msg_cbytes = msq->msg_cbytes; + tbuf.msg_qnum = msq->msg_qnum; + tbuf.msg_qbytes = msq->msg_qbytes; + tbuf.msg_lspid = msq->msg_lspid; + tbuf.msg_lrpid = msq->msg_lrpid; + memcpy_tofs (buf, &tbuf, sizeof (*buf)); + return 0; + case IPC_SET: + if (!suser() && current->euid != ipcp->cuid && + current->euid != ipcp->uid) + return -EPERM; + if (tbuf.msg_qbytes > MSGMNB && !suser()) + return -EPERM; + msq->msg_qbytes = tbuf.msg_qbytes; + ipcp->uid = tbuf.msg_perm.uid; + ipcp->gid = tbuf.msg_perm.gid; + ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | + (S_IRWXUGO & tbuf.msg_perm.mode); + msq->msg_ctime = CURRENT_TIME; + return 0; + case IPC_RMID: + if (!suser() && current->euid != ipcp->cuid && + current->euid != ipcp->uid) + return -EPERM; + freeque (id); + return 0; + default: + return -EINVAL; + } +} |