summaryrefslogtreecommitdiffstats
path: root/uClinux-2.4.20-uc1/ipc
diff options
context:
space:
mode:
authorOliver Schinagl <oliver@schinagl.nl>2011-03-01 13:19:47 (GMT)
committerOliver Schinagl <oliver@schinagl.nl>2011-03-01 13:19:47 (GMT)
commitc5ed56c50061cfaadb6dfa80f0107a605ae1f630 (patch)
tree704338dff4fe9575cafb9a86bfe335fd1dbf933c /uClinux-2.4.20-uc1/ipc
parenta00bc04c717debbeeaa3010605ac0318f8e4c98d (diff)
downloadopenipcam-c5ed56c50061cfaadb6dfa80f0107a605ae1f630.zip
openipcam-c5ed56c50061cfaadb6dfa80f0107a605ae1f630.tar.gz
openipcam-c5ed56c50061cfaadb6dfa80f0107a605ae1f630.tar.bz2
Bare uClinux-2.4.20-uc1 CVS pull
Diffstat (limited to 'uClinux-2.4.20-uc1/ipc')
-rw-r--r--uClinux-2.4.20-uc1/ipc/Makefile16
-rw-r--r--uClinux-2.4.20-uc1/ipc/msg.c903
-rw-r--r--uClinux-2.4.20-uc1/ipc/sem.c1104
-rw-r--r--uClinux-2.4.20-uc1/ipc/shm.c837
-rw-r--r--uClinux-2.4.20-uc1/ipc/util.c408
-rw-r--r--uClinux-2.4.20-uc1/ipc/util.h107
6 files changed, 3375 insertions, 0 deletions
diff --git a/uClinux-2.4.20-uc1/ipc/Makefile b/uClinux-2.4.20-uc1/ipc/Makefile
new file mode 100644
index 0000000..c358aa2
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux ipc.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := ipc.o
+
+obj-y := util.o
+
+obj-$(CONFIG_SYSVIPC) += msg.o sem.o shm.o
+
+include $(TOPDIR)/Rules.make
diff --git a/uClinux-2.4.20-uc1/ipc/msg.c b/uClinux-2.4.20-uc1/ipc/msg.c
new file mode 100644
index 0000000..b325d7f
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/msg.c
@@ -0,0 +1,903 @@
+/*
+ * linux/ipc/msg.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ *
+ * Removed all the remaining kerneld mess
+ * Catch the -EFAULT stuff properly
+ * Use GFP_KERNEL for messages as in 1.2
+ * Fixed up the unchecked user space derefs
+ * Copyright (C) 1998 Alan Cox & Andi Kleen
+ *
+ * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
+ *
+ * mostly rewritten, threaded and wake-one semantics added
+ * MSGMAX limit removed, sysctl's added
+ * (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/msg.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include "util.h"
+
+/* sysctl: */
+int msg_ctlmax = MSGMAX;
+int msg_ctlmnb = MSGMNB;
+int msg_ctlmni = MSGMNI;
+
+/* one msg_receiver structure for each sleeping receiver */
+struct msg_receiver {
+ struct list_head r_list;
+ struct task_struct* r_tsk;
+
+ int r_mode;
+ long r_msgtype;
+ long r_maxsize;
+
+ struct msg_msg* volatile r_msg;
+};
+
+/* one msg_sender for each sleeping sender */
+struct msg_sender {
+ struct list_head list;
+ struct task_struct* tsk;
+};
+
+struct msg_msgseg {
+ struct msg_msgseg* next;
+ /* the next part of the message follows immediately */
+};
+/* one msg_msg structure for each message */
+struct msg_msg {
+ struct list_head m_list;
+ long m_type;
+ int m_ts; /* message text size */
+ struct msg_msgseg* next;
+ /* the actual message follows immediately */
+};
+
+#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))
+#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))
+
+/* one msq_queue structure for each present queue on the system */
+struct msg_queue {
+ struct kern_ipc_perm q_perm;
+ time_t q_stime; /* last msgsnd time */
+ time_t q_rtime; /* last msgrcv time */
+ time_t q_ctime; /* last change time */
+ unsigned long q_cbytes; /* current number of bytes on queue */
+ unsigned long q_qnum; /* number of messages in queue */
+ unsigned long q_qbytes; /* max number of bytes on queue */
+ pid_t q_lspid; /* pid of last msgsnd */
+ pid_t q_lrpid; /* last receive pid */
+
+ struct list_head q_messages;
+ struct list_head q_receivers;
+ struct list_head q_senders;
+};
+
+#define SEARCH_ANY 1
+#define SEARCH_EQUAL 2
+#define SEARCH_NOTEQUAL 3
+#define SEARCH_LESSEQUAL 4
+
+static atomic_t msg_bytes = ATOMIC_INIT(0);
+static atomic_t msg_hdrs = ATOMIC_INIT(0);
+
+static struct ipc_ids msg_ids;
+
+#define msg_lock(id) ((struct msg_queue*)ipc_lock(&msg_ids,id))
+#define msg_unlock(id) ipc_unlock(&msg_ids,id)
+#define msg_rmid(id) ((struct msg_queue*)ipc_rmid(&msg_ids,id))
+#define msg_checkid(msq, msgid) \
+ ipc_checkid(&msg_ids,&msq->q_perm,msgid)
+#define msg_buildid(id, seq) \
+ ipc_buildid(&msg_ids, id, seq)
+
+static void freeque (int id);
+static int newque (key_t key, int msgflg);
+#ifdef CONFIG_PROC_FS
+static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
+
+void __init msg_init (void)
+{
+ ipc_init_ids(&msg_ids,msg_ctlmni);
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("sysvipc/msg", 0, 0, sysvipc_msg_read_proc, NULL);
+#endif
+}
+
+static int newque (key_t key, int msgflg)
+{
+ int id;
+ struct msg_queue *msq;
+
+ msq = (struct msg_queue *) kmalloc (sizeof (*msq), GFP_KERNEL);
+ if (!msq)
+ return -ENOMEM;
+ id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);
+ if(id == -1) {
+ kfree(msq);
+ return -ENOSPC;
+ }
+ msq->q_perm.mode = (msgflg & S_IRWXUGO);
+ msq->q_perm.key = key;
+
+ msq->q_stime = msq->q_rtime = 0;
+ msq->q_ctime = CURRENT_TIME;
+ msq->q_cbytes = msq->q_qnum = 0;
+ msq->q_qbytes = msg_ctlmnb;
+ msq->q_lspid = msq->q_lrpid = 0;
+ INIT_LIST_HEAD(&msq->q_messages);
+ INIT_LIST_HEAD(&msq->q_receivers);
+ INIT_LIST_HEAD(&msq->q_senders);
+ msg_unlock(id);
+
+ return msg_buildid(id,msq->q_perm.seq);
+}
+
+static void free_msg(struct msg_msg* msg)
+{
+ struct msg_msgseg* seg;
+ seg = msg->next;
+ kfree(msg);
+ while(seg != NULL) {
+ struct msg_msgseg* tmp = seg->next;
+ kfree(seg);
+ seg = tmp;
+ }
+}
+
+static struct msg_msg* load_msg(void* src, int len)
+{
+ struct msg_msg* msg;
+ struct msg_msgseg** pseg;
+ int err;
+ int alen;
+
+ alen = len;
+ if(alen > DATALEN_MSG)
+ alen = DATALEN_MSG;
+
+ msg = (struct msg_msg *) kmalloc (sizeof(*msg) + alen, GFP_KERNEL);
+ if(msg==NULL)
+ return ERR_PTR(-ENOMEM);
+
+ msg->next = NULL;
+
+ if (copy_from_user(msg+1, src, alen)) {
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ len -= alen;
+ src = ((char*)src)+alen;
+ pseg = &msg->next;
+ while(len > 0) {
+ struct msg_msgseg* seg;
+ alen = len;
+ if(alen > DATALEN_SEG)
+ alen = DATALEN_SEG;
+ seg = (struct msg_msgseg *) kmalloc (sizeof(*seg) + alen, GFP_KERNEL);
+ if(seg==NULL) {
+ err=-ENOMEM;
+ goto out_err;
+ }
+ *pseg = seg;
+ seg->next = NULL;
+ if(copy_from_user (seg+1, src, alen)) {
+ err = -EFAULT;
+ goto out_err;
+ }
+ pseg = &seg->next;
+ len -= alen;
+ src = ((char*)src)+alen;
+ }
+ return msg;
+
+out_err:
+ free_msg(msg);
+ return ERR_PTR(err);
+}
+
+static int store_msg(void* dest, struct msg_msg* msg, int len)
+{
+ int alen;
+ struct msg_msgseg *seg;
+
+ alen = len;
+ if(alen > DATALEN_MSG)
+ alen = DATALEN_MSG;
+ if(copy_to_user (dest, msg+1, alen))
+ return -1;
+
+ len -= alen;
+ dest = ((char*)dest)+alen;
+ seg = msg->next;
+ while(len > 0) {
+ alen = len;
+ if(alen > DATALEN_SEG)
+ alen = DATALEN_SEG;
+ if(copy_to_user (dest, seg+1, alen))
+ return -1;
+ len -= alen;
+ dest = ((char*)dest)+alen;
+ seg=seg->next;
+ }
+ return 0;
+}
+
+static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss)
+{
+ mss->tsk=current;
+ current->state=TASK_INTERRUPTIBLE;
+ list_add_tail(&mss->list,&msq->q_senders);
+}
+
+static inline void ss_del(struct msg_sender* mss)
+{
+ if(mss->list.next != NULL)
+ list_del(&mss->list);
+}
+
+static void ss_wakeup(struct list_head* h, int kill)
+{
+ struct list_head *tmp;
+
+ tmp = h->next;
+ while (tmp != h) {
+ struct msg_sender* mss;
+
+ mss = list_entry(tmp,struct msg_sender,list);
+ tmp = tmp->next;
+ if(kill)
+ mss->list.next=NULL;
+ wake_up_process(mss->tsk);
+ }
+}
+
+static void expunge_all(struct msg_queue* msq, int res)
+{
+ struct list_head *tmp;
+
+ tmp = msq->q_receivers.next;
+ while (tmp != &msq->q_receivers) {
+ struct msg_receiver* msr;
+
+ msr = list_entry(tmp,struct msg_receiver,r_list);
+ tmp = tmp->next;
+ msr->r_msg = ERR_PTR(res);
+ wake_up_process(msr->r_tsk);
+ }
+}
+
+static void freeque (int id)
+{
+ struct msg_queue *msq;
+ struct list_head *tmp;
+
+ msq = msg_rmid(id);
+
+ expunge_all(msq,-EIDRM);
+ ss_wakeup(&msq->q_senders,1);
+ msg_unlock(id);
+
+ tmp = msq->q_messages.next;
+ while(tmp != &msq->q_messages) {
+ struct msg_msg* msg = list_entry(tmp,struct msg_msg,m_list);
+ tmp = tmp->next;
+ atomic_dec(&msg_hdrs);
+ free_msg(msg);
+ }
+ atomic_sub(msq->q_cbytes, &msg_bytes);
+ kfree(msq);
+}
+
+asmlinkage long sys_msgget (key_t key, int msgflg)
+{
+ int id, ret = -EPERM;
+ struct msg_queue *msq;
+
+ down(&msg_ids.sem);
+ if (key == IPC_PRIVATE)
+ ret = newque(key, msgflg);
+ else if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key not used */
+ if (!(msgflg & IPC_CREAT))
+ ret = -ENOENT;
+ else
+ ret = newque(key, msgflg);
+ } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
+ ret = -EEXIST;
+ } else {
+ msq = msg_lock(id);
+ if(msq==NULL)
+ BUG();
+ if (ipcperms(&msq->q_perm, msgflg))
+ ret = -EACCES;
+ else
+ ret = msg_buildid(id, msq->q_perm.seq);
+ msg_unlock(id);
+ }
+ up(&msg_ids.sem);
+ return ret;
+}
+
+static inline unsigned long copy_msqid_to_user(void *buf, struct msqid64_ds *in, int version)
+{
+ switch(version) {
+ case IPC_64:
+ return copy_to_user (buf, in, sizeof(*in));
+ case IPC_OLD:
+ {
+ struct msqid_ds out;
+
+ memset(&out,0,sizeof(out));
+
+ ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm);
+
+ out.msg_stime = in->msg_stime;
+ out.msg_rtime = in->msg_rtime;
+ out.msg_ctime = in->msg_ctime;
+
+ if(in->msg_cbytes > USHRT_MAX)
+ out.msg_cbytes = USHRT_MAX;
+ else
+ out.msg_cbytes = in->msg_cbytes;
+ out.msg_lcbytes = in->msg_cbytes;
+
+ if(in->msg_qnum > USHRT_MAX)
+ out.msg_qnum = USHRT_MAX;
+ else
+ out.msg_qnum = in->msg_qnum;
+
+ if(in->msg_qbytes > USHRT_MAX)
+ out.msg_qbytes = USHRT_MAX;
+ else
+ out.msg_qbytes = in->msg_qbytes;
+ out.msg_lqbytes = in->msg_qbytes;
+
+ out.msg_lspid = in->msg_lspid;
+ out.msg_lrpid = in->msg_lrpid;
+
+ return copy_to_user (buf, &out, sizeof(out));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+struct msq_setbuf {
+ unsigned long qbytes;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+};
+
+static inline unsigned long copy_msqid_from_user(struct msq_setbuf *out, void *buf, int version)
+{
+ switch(version) {
+ case IPC_64:
+ {
+ struct msqid64_ds tbuf;
+
+ if (copy_from_user (&tbuf, buf, sizeof (tbuf)))
+ return -EFAULT;
+
+ out->qbytes = tbuf.msg_qbytes;
+ out->uid = tbuf.msg_perm.uid;
+ out->gid = tbuf.msg_perm.gid;
+ out->mode = tbuf.msg_perm.mode;
+
+ return 0;
+ }
+ case IPC_OLD:
+ {
+ struct msqid_ds tbuf_old;
+
+ if (copy_from_user (&tbuf_old, buf, sizeof (tbuf_old)))
+ return -EFAULT;
+
+ out->uid = tbuf_old.msg_perm.uid;
+ out->gid = tbuf_old.msg_perm.gid;
+ out->mode = tbuf_old.msg_perm.mode;
+
+ if(tbuf_old.msg_qbytes == 0)
+ out->qbytes = tbuf_old.msg_lqbytes;
+ else
+ out->qbytes = tbuf_old.msg_qbytes;
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
+{
+ int err, version;
+ struct msg_queue *msq;
+ struct msq_setbuf setbuf;
+ struct kern_ipc_perm *ipcp;
+
+ if (msqid < 0 || cmd < 0)
+ return -EINVAL;
+
+ version = ipc_parse_version(&cmd);
+
+ switch (cmd) {
+ case IPC_INFO:
+ case MSG_INFO:
+ {
+ struct msginfo msginfo;
+ int max_id;
+ if (!buf)
+ return -EFAULT;
+ /* We must not return kernel stack data.
+ * due to padding, it's not enough
+ * to set all member fields.
+ */
+ memset(&msginfo,0,sizeof(msginfo));
+ msginfo.msgmni = msg_ctlmni;
+ msginfo.msgmax = msg_ctlmax;
+ msginfo.msgmnb = msg_ctlmnb;
+ msginfo.msgssz = MSGSSZ;
+ msginfo.msgseg = MSGSEG;
+ down(&msg_ids.sem);
+ if (cmd == MSG_INFO) {
+ msginfo.msgpool = msg_ids.in_use;
+ msginfo.msgmap = atomic_read(&msg_hdrs);
+ msginfo.msgtql = atomic_read(&msg_bytes);
+ } else {
+ msginfo.msgmap = MSGMAP;
+ msginfo.msgpool = MSGPOOL;
+ msginfo.msgtql = MSGTQL;
+ }
+ max_id = msg_ids.max_id;
+ up(&msg_ids.sem);
+ if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))
+ return -EFAULT;
+ return (max_id < 0) ? 0: max_id;
+ }
+ case MSG_STAT:
+ case IPC_STAT:
+ {
+ struct msqid64_ds tbuf;
+ int success_return;
+ if (!buf)
+ return -EFAULT;
+ if(cmd == MSG_STAT && msqid >= msg_ids.size)
+ return -EINVAL;
+
+ memset(&tbuf,0,sizeof(tbuf));
+
+ msq = msg_lock(msqid);
+ if (msq == NULL)
+ return -EINVAL;
+
+ if(cmd == MSG_STAT) {
+ success_return = msg_buildid(msqid, msq->q_perm.seq);
+ } else {
+ err = -EIDRM;
+ if (msg_checkid(msq,msqid))
+ goto out_unlock;
+ success_return = 0;
+ }
+ err = -EACCES;
+ if (ipcperms (&msq->q_perm, S_IRUGO))
+ goto out_unlock;
+
+ kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
+ tbuf.msg_stime = msq->q_stime;
+ tbuf.msg_rtime = msq->q_rtime;
+ tbuf.msg_ctime = msq->q_ctime;
+ tbuf.msg_cbytes = msq->q_cbytes;
+ tbuf.msg_qnum = msq->q_qnum;
+ tbuf.msg_qbytes = msq->q_qbytes;
+ tbuf.msg_lspid = msq->q_lspid;
+ tbuf.msg_lrpid = msq->q_lrpid;
+ msg_unlock(msqid);
+ if (copy_msqid_to_user(buf, &tbuf, version))
+ return -EFAULT;
+ return success_return;
+ }
+ case IPC_SET:
+ if (!buf)
+ return -EFAULT;
+ if (copy_msqid_from_user (&setbuf, buf, version))
+ return -EFAULT;
+ break;
+ case IPC_RMID:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ down(&msg_ids.sem);
+ msq = msg_lock(msqid);
+ err=-EINVAL;
+ if (msq == NULL)
+ goto out_up;
+
+ err = -EIDRM;
+ if (msg_checkid(msq,msqid))
+ goto out_unlock_up;
+ ipcp = &msq->q_perm;
+ err = -EPERM;
+ if (current->euid != ipcp->cuid &&
+ current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
+ /* We _could_ check for CAP_CHOWN above, but we don't */
+ goto out_unlock_up;
+
+ switch (cmd) {
+ case IPC_SET:
+ {
+ if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
+ goto out_unlock_up;
+ msq->q_qbytes = setbuf.qbytes;
+
+ ipcp->uid = setbuf.uid;
+ ipcp->gid = setbuf.gid;
+ ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
+ (S_IRWXUGO & setbuf.mode);
+ msq->q_ctime = CURRENT_TIME;
+ /* sleeping receivers might be excluded by
+ * stricter permissions.
+ */
+ expunge_all(msq,-EAGAIN);
+ /* sleeping senders might be able to send
+ * due to a larger queue size.
+ */
+ ss_wakeup(&msq->q_senders,0);
+ msg_unlock(msqid);
+ break;
+ }
+ case IPC_RMID:
+ freeque (msqid);
+ break;
+ }
+ err = 0;
+out_up:
+ up(&msg_ids.sem);
+ return err;
+out_unlock_up:
+ msg_unlock(msqid);
+ goto out_up;
+out_unlock:
+ msg_unlock(msqid);
+ return err;
+}
+
+static int testmsg(struct msg_msg* msg,long type,int mode)
+{
+ switch(mode)
+ {
+ case SEARCH_ANY:
+ return 1;
+ case SEARCH_LESSEQUAL:
+ if(msg->m_type <=type)
+ return 1;
+ break;
+ case SEARCH_EQUAL:
+ if(msg->m_type == type)
+ return 1;
+ break;
+ case SEARCH_NOTEQUAL:
+ if(msg->m_type != type)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static int inline pipelined_send(struct msg_queue* msq, struct msg_msg* msg)
+{
+ struct list_head* tmp;
+
+ tmp = msq->q_receivers.next;
+ while (tmp != &msq->q_receivers) {
+ struct msg_receiver* msr;
+ msr = list_entry(tmp,struct msg_receiver,r_list);
+ tmp = tmp->next;
+ if(testmsg(msg,msr->r_msgtype,msr->r_mode)) {
+ list_del(&msr->r_list);
+ if(msr->r_maxsize < msg->m_ts) {
+ msr->r_msg = ERR_PTR(-E2BIG);
+ wake_up_process(msr->r_tsk);
+ } else {
+ msr->r_msg = msg;
+ msq->q_lrpid = msr->r_tsk->pid;
+ msq->q_rtime = CURRENT_TIME;
+ wake_up_process(msr->r_tsk);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+{
+ struct msg_queue *msq;
+ struct msg_msg *msg;
+ long mtype;
+ int err;
+
+ if (msgsz > msg_ctlmax || (long) msgsz < 0 || msqid < 0)
+ return -EINVAL;
+ if (get_user(mtype, &msgp->mtype))
+ return -EFAULT;
+ if (mtype < 1)
+ return -EINVAL;
+
+ msg = load_msg(msgp->mtext, msgsz);
+ if(IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->m_type = mtype;
+ msg->m_ts = msgsz;
+
+ msq = msg_lock(msqid);
+ err=-EINVAL;
+ if(msq==NULL)
+ goto out_free;
+retry:
+ err= -EIDRM;
+ if (msg_checkid(msq,msqid))
+ goto out_unlock_free;
+
+ err=-EACCES;
+ if (ipcperms(&msq->q_perm, S_IWUGO))
+ goto out_unlock_free;
+
+ if(msgsz + msq->q_cbytes > msq->q_qbytes ||
+ 1 + msq->q_qnum > msq->q_qbytes) {
+ struct msg_sender s;
+
+ if(msgflg&IPC_NOWAIT) {
+ err=-EAGAIN;
+ goto out_unlock_free;
+ }
+ ss_add(msq, &s);
+ msg_unlock(msqid);
+ schedule();
+ current->state= TASK_RUNNING;
+
+ msq = msg_lock(msqid);
+ err = -EIDRM;
+ if(msq==NULL)
+ goto out_free;
+ ss_del(&s);
+
+ if (signal_pending(current)) {
+ err=-EINTR;
+ goto out_unlock_free;
+ }
+ goto retry;
+ }
+
+ msq->q_lspid = current->pid;
+ msq->q_stime = CURRENT_TIME;
+
+ if(!pipelined_send(msq,msg)) {
+ /* noone is waiting for this message, enqueue it */
+ list_add_tail(&msg->m_list,&msq->q_messages);
+ msq->q_cbytes += msgsz;
+ msq->q_qnum++;
+ atomic_add(msgsz,&msg_bytes);
+ atomic_inc(&msg_hdrs);
+ }
+
+ err = 0;
+ msg = NULL;
+
+out_unlock_free:
+ msg_unlock(msqid);
+out_free:
+ if(msg!=NULL)
+ free_msg(msg);
+ return err;
+}
+
+static int inline convert_mode(long* msgtyp, int msgflg)
+{
+ /*
+ * 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).
+ */
+ if(*msgtyp==0)
+ return SEARCH_ANY;
+ if(*msgtyp<0) {
+ *msgtyp=-(*msgtyp);
+ return SEARCH_LESSEQUAL;
+ }
+ if(msgflg & MSG_EXCEPT)
+ return SEARCH_NOTEQUAL;
+ return SEARCH_EQUAL;
+}
+
+asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
+ long msgtyp, int msgflg)
+{
+ struct msg_queue *msq;
+ struct msg_receiver msr_d;
+ struct list_head* tmp;
+ struct msg_msg* msg, *found_msg;
+ int err;
+ int mode;
+
+ if (msqid < 0 || (long) msgsz < 0)
+ return -EINVAL;
+ mode = convert_mode(&msgtyp,msgflg);
+
+ msq = msg_lock(msqid);
+ if(msq==NULL)
+ return -EINVAL;
+retry:
+ err = -EIDRM;
+ if (msg_checkid(msq,msqid))
+ goto out_unlock;
+
+ err=-EACCES;
+ if (ipcperms (&msq->q_perm, S_IRUGO))
+ goto out_unlock;
+
+ tmp = msq->q_messages.next;
+ found_msg=NULL;
+ while (tmp != &msq->q_messages) {
+ msg = list_entry(tmp,struct msg_msg,m_list);
+ if(testmsg(msg,msgtyp,mode)) {
+ found_msg = msg;
+ if(mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
+ found_msg=msg;
+ msgtyp=msg->m_type-1;
+ } else {
+ found_msg=msg;
+ break;
+ }
+ }
+ tmp = tmp->next;
+ }
+ if(found_msg) {
+ msg=found_msg;
+ if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
+ err=-E2BIG;
+ goto out_unlock;
+ }
+ list_del(&msg->m_list);
+ msq->q_qnum--;
+ msq->q_rtime = CURRENT_TIME;
+ msq->q_lrpid = current->pid;
+ msq->q_cbytes -= msg->m_ts;
+ atomic_sub(msg->m_ts,&msg_bytes);
+ atomic_dec(&msg_hdrs);
+ ss_wakeup(&msq->q_senders,0);
+ msg_unlock(msqid);
+out_success:
+ msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz;
+ if (put_user (msg->m_type, &msgp->mtype) ||
+ store_msg(msgp->mtext, msg, msgsz)) {
+ msgsz = -EFAULT;
+ }
+ free_msg(msg);
+ return msgsz;
+ } else
+ {
+ struct msg_queue *t;
+ /* no message waiting. Prepare for pipelined
+ * receive.
+ */
+ if (msgflg & IPC_NOWAIT) {
+ err=-ENOMSG;
+ goto out_unlock;
+ }
+ list_add_tail(&msr_d.r_list,&msq->q_receivers);
+ msr_d.r_tsk = current;
+ msr_d.r_msgtype = msgtyp;
+ msr_d.r_mode = mode;
+ if(msgflg & MSG_NOERROR)
+ msr_d.r_maxsize = INT_MAX;
+ else
+ msr_d.r_maxsize = msgsz;
+ msr_d.r_msg = ERR_PTR(-EAGAIN);
+ current->state = TASK_INTERRUPTIBLE;
+ msg_unlock(msqid);
+
+ schedule();
+ current->state = TASK_RUNNING;
+
+ msg = (struct msg_msg*) msr_d.r_msg;
+ if(!IS_ERR(msg))
+ goto out_success;
+
+ t = msg_lock(msqid);
+ if(t==NULL)
+ msqid=-1;
+ msg = (struct msg_msg*)msr_d.r_msg;
+ if(!IS_ERR(msg)) {
+ /* our message arived while we waited for
+ * the spinlock. Process it.
+ */
+ if(msqid!=-1)
+ msg_unlock(msqid);
+ goto out_success;
+ }
+ err = PTR_ERR(msg);
+ if(err == -EAGAIN) {
+ if(msqid==-1)
+ BUG();
+ list_del(&msr_d.r_list);
+ if (signal_pending(current))
+ err=-EINTR;
+ else
+ goto retry;
+ }
+ }
+out_unlock:
+ if(msqid!=-1)
+ msg_unlock(msqid);
+ return err;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+ off_t pos = 0;
+ off_t begin = 0;
+ int i, len = 0;
+
+ down(&msg_ids.sem);
+ len += sprintf(buffer, " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n");
+
+ for(i = 0; i <= msg_ids.max_id; i++) {
+ struct msg_queue * msq;
+ msq = msg_lock(i);
+ if(msq != NULL) {
+ len += sprintf(buffer + len, "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
+ msq->q_perm.key,
+ msg_buildid(i,msq->q_perm.seq),
+ msq->q_perm.mode,
+ msq->q_cbytes,
+ msq->q_qnum,
+ msq->q_lspid,
+ msq->q_lrpid,
+ msq->q_perm.uid,
+ msq->q_perm.gid,
+ msq->q_perm.cuid,
+ msq->q_perm.cgid,
+ msq->q_stime,
+ msq->q_rtime,
+ msq->q_ctime);
+ msg_unlock(i);
+
+ pos += len;
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if(pos > offset + length)
+ goto done;
+ }
+
+ }
+ *eof = 1;
+done:
+ up(&msg_ids.sem);
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+ return len;
+}
+#endif
diff --git a/uClinux-2.4.20-uc1/ipc/sem.c b/uClinux-2.4.20-uc1/ipc/sem.c
new file mode 100644
index 0000000..a5bcfe1
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/sem.c
@@ -0,0 +1,1104 @@
+/*
+ * linux/ipc/sem.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ * Copyright (C) 1995 Eric Schenk, Bruno Haible
+ *
+ * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
+ * This code underwent a massive rewrite in order to solve some problems
+ * with the original code. In particular the original code failed to
+ * wake up processes that were waiting for semval to go to 0 if the
+ * value went to 0 and was then incremented rapidly enough. In solving
+ * this problem I have also modified the implementation so that it
+ * processes pending operations in a FIFO manner, thus give a guarantee
+ * that processes waiting for a lock on the semaphore won't starve
+ * unless another locking process fails to unlock.
+ * In addition the following two changes in behavior have been introduced:
+ * - The original implementation of semop returned the value
+ * last semaphore element examined on success. This does not
+ * match the manual page specifications, and effectively
+ * allows the user to read the semaphore even if they do not
+ * have read permissions. The implementation now returns 0
+ * on success as stated in the manual page.
+ * - There is some confusion over whether the set of undo adjustments
+ * to be performed at exit should be done in an atomic manner.
+ * That is, if we are attempting to decrement the semval should we queue
+ * up and wait until we can do so legally?
+ * The original implementation attempted to do this.
+ * The current implementation does not do so. This is because I don't
+ * think it is the right thing (TM) to do, and because I couldn't
+ * see a clean way to get the old behavior with the new design.
+ * The POSIX standard and SVID should be consulted to determine
+ * what behavior is mandated.
+ *
+ * Further notes on refinement (Christoph Rohland, December 1998):
+ * - The POSIX standard says, that the undo adjustments simply should
+ * redo. So the current implementation is o.K.
+ * - The previous code had two flaws:
+ * 1) It actively gave the semaphore to the next waiting process
+ * sleeping on the semaphore. Since this process did not have the
+ * cpu this led to many unnecessary context switches and bad
+ * performance. Now we only check which process should be able to
+ * get the semaphore and if this process wants to reduce some
+ * semaphore value we simply wake it up without doing the
+ * operation. So it has to try to get it later. Thus e.g. the
+ * running process may reacquire the semaphore during the current
+ * time slice. If it only waits for zero or increases the semaphore,
+ * we do the operation in advance and wake it up.
+ * 2) It did not wake up all zero waiting processes. We try to do
+ * better but only get the semops right which only wait for zero or
+ * increase. If there are decrement operations in the operations
+ * array we do the same as before.
+ *
+ * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
+ *
+ * SMP-threaded, sysctl's added
+ * (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
+ * Enforced range limit on SEM_UNDO
+ * (c) 2001 Red Hat Inc <alan@redhat.com>
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "util.h"
+
+
+#define sem_lock(id) ((struct sem_array*)ipc_lock(&sem_ids,id))
+#define sem_unlock(id) ipc_unlock(&sem_ids,id)
+#define sem_rmid(id) ((struct sem_array*)ipc_rmid(&sem_ids,id))
+#define sem_checkid(sma, semid) \
+ ipc_checkid(&sem_ids,&sma->sem_perm,semid)
+#define sem_buildid(id, seq) \
+ ipc_buildid(&sem_ids, id, seq)
+static struct ipc_ids sem_ids;
+
+static int newary (key_t, int, int);
+static void freeary (int id);
+#ifdef CONFIG_PROC_FS
+static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
+
+#define SEMMSL_FAST 256 /* 512 bytes on stack */
+#define SEMOPM_FAST 64 /* ~ 372 bytes on stack */
+
+/*
+ * linked list protection:
+ * sem_undo.id_next,
+ * sem_array.sem_pending{,last},
+ * sem_array.sem_undo: sem_lock() for read/write
+ * sem_undo.proc_next: only "current" is allowed to read/write that field.
+ *
+ */
+
+int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
+#define sc_semmsl (sem_ctls[0])
+#define sc_semmns (sem_ctls[1])
+#define sc_semopm (sem_ctls[2])
+#define sc_semmni (sem_ctls[3])
+
+static int used_sems;
+
+void __init sem_init (void)
+{
+ used_sems = 0;
+ ipc_init_ids(&sem_ids,sc_semmni);
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL);
+#endif
+}
+
+static int newary (key_t key, int nsems, int semflg)
+{
+ int id;
+ struct sem_array *sma;
+ int size;
+
+ if (!nsems)
+ return -EINVAL;
+ if (used_sems + nsems > sc_semmns)
+ return -ENOSPC;
+
+ size = sizeof (*sma) + nsems * sizeof (struct sem);
+ sma = (struct sem_array *) ipc_alloc(size);
+ if (!sma) {
+ return -ENOMEM;
+ }
+ memset (sma, 0, size);
+ id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
+ if(id == -1) {
+ ipc_free(sma, size);
+ return -ENOSPC;
+ }
+ used_sems += nsems;
+
+ sma->sem_perm.mode = (semflg & S_IRWXUGO);
+ sma->sem_perm.key = key;
+
+ sma->sem_base = (struct sem *) &sma[1];
+ /* sma->sem_pending = NULL; */
+ sma->sem_pending_last = &sma->sem_pending;
+ /* sma->undo = NULL; */
+ sma->sem_nsems = nsems;
+ sma->sem_ctime = CURRENT_TIME;
+ sem_unlock(id);
+
+ return sem_buildid(id, sma->sem_perm.seq);
+}
+
+asmlinkage long sys_semget (key_t key, int nsems, int semflg)
+{
+ int id, err = -EINVAL;
+ struct sem_array *sma;
+
+ if (nsems < 0 || nsems > sc_semmsl)
+ return -EINVAL;
+ down(&sem_ids.sem);
+
+ if (key == IPC_PRIVATE) {
+ err = newary(key, nsems, semflg);
+ } else if ((id = ipc_findkey(&sem_ids, key)) == -1) { /* key not used */
+ if (!(semflg & IPC_CREAT))
+ err = -ENOENT;
+ else
+ err = newary(key, nsems, semflg);
+ } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
+ err = -EEXIST;
+ } else {
+ sma = sem_lock(id);
+ if(sma==NULL)
+ BUG();
+ if (nsems > sma->sem_nsems)
+ err = -EINVAL;
+ else if (ipcperms(&sma->sem_perm, semflg))
+ err = -EACCES;
+ else
+ err = sem_buildid(id, sma->sem_perm.seq);
+ sem_unlock(id);
+ }
+
+ up(&sem_ids.sem);
+ return err;
+}
+
+/* doesn't acquire the sem_lock on error! */
+static int sem_revalidate(int semid, struct sem_array* sma, int nsems, short flg)
+{
+ struct sem_array* smanew;
+
+ smanew = sem_lock(semid);
+ if(smanew==NULL)
+ return -EIDRM;
+ if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) {
+ sem_unlock(semid);
+ return -EIDRM;
+ }
+
+ if (ipcperms(&sma->sem_perm, flg)) {
+ sem_unlock(semid);
+ return -EACCES;
+ }
+ return 0;
+}
+/* Manage the doubly linked list sma->sem_pending as a FIFO:
+ * insert new queue elements at the tail sma->sem_pending_last.
+ */
+static inline void append_to_queue (struct sem_array * sma,
+ struct sem_queue * q)
+{
+ *(q->prev = sma->sem_pending_last) = q;
+ *(sma->sem_pending_last = &q->next) = NULL;
+}
+
+static inline void prepend_to_queue (struct sem_array * sma,
+ struct sem_queue * q)
+{
+ q->next = sma->sem_pending;
+ *(q->prev = &sma->sem_pending) = q;
+ if (q->next)
+ q->next->prev = &q->next;
+ else /* sma->sem_pending_last == &sma->sem_pending */
+ sma->sem_pending_last = &q->next;
+}
+
+static inline void remove_from_queue (struct sem_array * sma,
+ struct sem_queue * q)
+{
+ *(q->prev) = q->next;
+ if (q->next)
+ q->next->prev = q->prev;
+ else /* sma->sem_pending_last == &q->next */
+ sma->sem_pending_last = q->prev;
+ q->prev = NULL; /* mark as removed */
+}
+
+/*
+ * Determine whether a sequence of semaphore operations would succeed
+ * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
+ */
+
+static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
+ int nsops, struct sem_undo *un, int pid,
+ int do_undo)
+{
+ int result, sem_op;
+ struct sembuf *sop;
+ struct sem * curr;
+
+ for (sop = sops; sop < sops + nsops; sop++) {
+ curr = sma->sem_base + sop->sem_num;
+ sem_op = sop->sem_op;
+
+ if (!sem_op && curr->semval)
+ goto would_block;
+
+ curr->sempid = (curr->sempid << 16) | pid;
+ curr->semval += sem_op;
+ if (sop->sem_flg & SEM_UNDO)
+ {
+ int undo = un->semadj[sop->sem_num] - sem_op;
+ /*
+ * Exceeding the undo range is an error.
+ */
+ if (undo < (-SEMAEM - 1) || undo > SEMAEM)
+ {
+ /* Don't undo the undo */
+ sop->sem_flg &= ~SEM_UNDO;
+ goto out_of_range;
+ }
+ un->semadj[sop->sem_num] = undo;
+ }
+ if (curr->semval < 0)
+ goto would_block;
+ if (curr->semval > SEMVMX)
+ goto out_of_range;
+ }
+
+ if (do_undo)
+ {
+ sop--;
+ result = 0;
+ goto undo;
+ }
+
+ sma->sem_otime = CURRENT_TIME;
+ return 0;
+
+out_of_range:
+ result = -ERANGE;
+ goto undo;
+
+would_block:
+ if (sop->sem_flg & IPC_NOWAIT)
+ result = -EAGAIN;
+ else
+ result = 1;
+
+undo:
+ while (sop >= sops) {
+ curr = sma->sem_base + sop->sem_num;
+ curr->semval -= sop->sem_op;
+ curr->sempid >>= 16;
+
+ if (sop->sem_flg & SEM_UNDO)
+ un->semadj[sop->sem_num] += sop->sem_op;
+ sop--;
+ }
+
+ return result;
+}
+
+/* Go through the pending queue for the indicated semaphore
+ * looking for tasks that can be completed.
+ */
+static void update_queue (struct sem_array * sma)
+{
+ int error;
+ struct sem_queue * q;
+
+ for (q = sma->sem_pending; q; q = q->next) {
+
+ if (q->status == 1)
+ continue; /* this one was woken up before */
+
+ error = try_atomic_semop(sma, q->sops, q->nsops,
+ q->undo, q->pid, q->alter);
+
+ /* Does q->sleeper still need to sleep? */
+ if (error <= 0) {
+ /* Found one, wake it up */
+ wake_up_process(q->sleeper);
+ if (error == 0 && q->alter) {
+ /* if q-> alter let it self try */
+ q->status = 1;
+ return;
+ }
+ q->status = error;
+ remove_from_queue(sma,q);
+ }
+ }
+}
+
+/* The following counts are associated to each semaphore:
+ * semncnt number of tasks waiting on semval being nonzero
+ * semzcnt number of tasks waiting on semval being zero
+ * This model assumes that a task waits on exactly one semaphore.
+ * Since semaphore operations are to be performed atomically, tasks actually
+ * wait on a whole sequence of semaphores simultaneously.
+ * The counts we return here are a rough approximation, but still
+ * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
+ */
+static int count_semncnt (struct sem_array * sma, ushort semnum)
+{
+ int semncnt;
+ struct sem_queue * q;
+
+ semncnt = 0;
+ for (q = sma->sem_pending; q; q = q->next) {
+ struct sembuf * sops = q->sops;
+ int nsops = q->nsops;
+ int i;
+ for (i = 0; i < nsops; i++)
+ if (sops[i].sem_num == semnum
+ && (sops[i].sem_op < 0)
+ && !(sops[i].sem_flg & IPC_NOWAIT))
+ semncnt++;
+ }
+ return semncnt;
+}
+static int count_semzcnt (struct sem_array * sma, ushort semnum)
+{
+ int semzcnt;
+ struct sem_queue * q;
+
+ semzcnt = 0;
+ for (q = sma->sem_pending; q; q = q->next) {
+ struct sembuf * sops = q->sops;
+ int nsops = q->nsops;
+ int i;
+ for (i = 0; i < nsops; i++)
+ if (sops[i].sem_num == semnum
+ && (sops[i].sem_op == 0)
+ && !(sops[i].sem_flg & IPC_NOWAIT))
+ semzcnt++;
+ }
+ return semzcnt;
+}
+
+/* Free a semaphore set. */
+static void freeary (int id)
+{
+ struct sem_array *sma;
+ struct sem_undo *un;
+ struct sem_queue *q;
+ int size;
+
+ sma = sem_rmid(id);
+
+ /* Invalidate the existing undo structures for this semaphore set.
+ * (They will be freed without any further action in sem_exit()
+ * or during the next semop.)
+ */
+ for (un = sma->undo; un; un = un->id_next)
+ un->semid = -1;
+
+ /* Wake up all pending processes and let them fail with EIDRM. */
+ for (q = sma->sem_pending; q; q = q->next) {
+ q->status = -EIDRM;
+ q->prev = NULL;
+ wake_up_process(q->sleeper); /* doesn't sleep */
+ }
+ sem_unlock(id);
+
+ used_sems -= sma->sem_nsems;
+ size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
+ ipc_free(sma, size);
+}
+
+static unsigned long copy_semid_to_user(void *buf, struct semid64_ds *in, int version)
+{
+ switch(version) {
+ case IPC_64:
+ return copy_to_user(buf, in, sizeof(*in));
+ case IPC_OLD:
+ {
+ struct semid_ds out;
+
+ ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
+
+ out.sem_otime = in->sem_otime;
+ out.sem_ctime = in->sem_ctime;
+ out.sem_nsems = in->sem_nsems;
+
+ return copy_to_user(buf, &out, sizeof(out));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
+{
+ int err = -EINVAL;
+
+ switch(cmd) {
+ case IPC_INFO:
+ case SEM_INFO:
+ {
+ struct seminfo seminfo;
+ int max_id;
+
+ memset(&seminfo,0,sizeof(seminfo));
+ seminfo.semmni = sc_semmni;
+ seminfo.semmns = sc_semmns;
+ seminfo.semmsl = sc_semmsl;
+ seminfo.semopm = sc_semopm;
+ seminfo.semvmx = SEMVMX;
+ seminfo.semmnu = SEMMNU;
+ seminfo.semmap = SEMMAP;
+ seminfo.semume = SEMUME;
+ down(&sem_ids.sem);
+ if (cmd == SEM_INFO) {
+ seminfo.semusz = sem_ids.in_use;
+ seminfo.semaem = used_sems;
+ } else {
+ seminfo.semusz = SEMUSZ;
+ seminfo.semaem = SEMAEM;
+ }
+ max_id = sem_ids.max_id;
+ up(&sem_ids.sem);
+ if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo)))
+ return -EFAULT;
+ return (max_id < 0) ? 0: max_id;
+ }
+ case SEM_STAT:
+ {
+ struct sem_array *sma;
+ struct semid64_ds tbuf;
+ int id;
+
+ if(semid >= sem_ids.size)
+ return -EINVAL;
+
+ memset(&tbuf,0,sizeof(tbuf));
+
+ sma = sem_lock(semid);
+ if(sma == NULL)
+ return -EINVAL;
+
+ err = -EACCES;
+ if (ipcperms (&sma->sem_perm, S_IRUGO))
+ goto out_unlock;
+ id = sem_buildid(semid, sma->sem_perm.seq);
+
+ kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
+ tbuf.sem_otime = sma->sem_otime;
+ tbuf.sem_ctime = sma->sem_ctime;
+ tbuf.sem_nsems = sma->sem_nsems;
+ sem_unlock(semid);
+ if (copy_semid_to_user (arg.buf, &tbuf, version))
+ return -EFAULT;
+ return id;
+ }
+ default:
+ return -EINVAL;
+ }
+ return err;
+out_unlock:
+ sem_unlock(semid);
+ return err;
+}
+
+static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
+{
+ struct sem_array *sma;
+ struct sem* curr;
+ int err;
+ ushort fast_sem_io[SEMMSL_FAST];
+ ushort* sem_io = fast_sem_io;
+ int nsems;
+
+ sma = sem_lock(semid);
+ if(sma==NULL)
+ return -EINVAL;
+
+ nsems = sma->sem_nsems;
+
+ err=-EIDRM;
+ if (sem_checkid(sma,semid))
+ goto out_unlock;
+
+ err = -EACCES;
+ if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
+ goto out_unlock;
+
+ switch (cmd) {
+ case GETALL:
+ {
+ ushort *array = arg.array;
+ int i;
+
+ if(nsems > SEMMSL_FAST) {
+ sem_unlock(semid);
+ sem_io = ipc_alloc(sizeof(ushort)*nsems);
+ if(sem_io == NULL)
+ return -ENOMEM;
+ err = sem_revalidate(semid, sma, nsems, S_IRUGO);
+ if(err)
+ goto out_free;
+ }
+
+ for (i = 0; i < sma->sem_nsems; i++)
+ sem_io[i] = sma->sem_base[i].semval;
+ sem_unlock(semid);
+ err = 0;
+ if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
+ err = -EFAULT;
+ goto out_free;
+ }
+ case SETALL:
+ {
+ int i;
+ struct sem_undo *un;
+
+ sem_unlock(semid);
+
+ if(nsems > SEMMSL_FAST) {
+ sem_io = ipc_alloc(sizeof(ushort)*nsems);
+ if(sem_io == NULL)
+ return -ENOMEM;
+ }
+
+ if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
+ err = -EFAULT;
+ goto out_free;
+ }
+
+ for (i = 0; i < nsems; i++) {
+ if (sem_io[i] > SEMVMX) {
+ err = -ERANGE;
+ goto out_free;
+ }
+ }
+ err = sem_revalidate(semid, sma, nsems, S_IWUGO);
+ if(err)
+ goto out_free;
+
+ for (i = 0; i < nsems; i++)
+ sma->sem_base[i].semval = sem_io[i];
+ for (un = sma->undo; un; un = un->id_next)
+ for (i = 0; i < nsems; i++)
+ un->semadj[i] = 0;
+ sma->sem_ctime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
+ err = 0;
+ goto out_unlock;
+ }
+ case IPC_STAT:
+ {
+ struct semid64_ds tbuf;
+ memset(&tbuf,0,sizeof(tbuf));
+ kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
+ tbuf.sem_otime = sma->sem_otime;
+ tbuf.sem_ctime = sma->sem_ctime;
+ tbuf.sem_nsems = sma->sem_nsems;
+ sem_unlock(semid);
+ if (copy_semid_to_user (arg.buf, &tbuf, version))
+ return -EFAULT;
+ return 0;
+ }
+ /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */
+ }
+ err = -EINVAL;
+ if(semnum < 0 || semnum >= nsems)
+ goto out_unlock;
+
+ curr = &sma->sem_base[semnum];
+
+ switch (cmd) {
+ case GETVAL:
+ err = curr->semval;
+ goto out_unlock;
+ case GETPID:
+ err = curr->sempid & 0xffff;
+ goto out_unlock;
+ case GETNCNT:
+ err = count_semncnt(sma,semnum);
+ goto out_unlock;
+ case GETZCNT:
+ err = count_semzcnt(sma,semnum);
+ goto out_unlock;
+ case SETVAL:
+ {
+ int val = arg.val;
+ struct sem_undo *un;
+ err = -ERANGE;
+ if (val > SEMVMX || val < 0)
+ goto out_unlock;
+
+ for (un = sma->undo; un; un = un->id_next)
+ un->semadj[semnum] = 0;
+ curr->semval = val;
+ curr->sempid = current->pid;
+ sma->sem_ctime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
+ err = 0;
+ goto out_unlock;
+ }
+ }
+out_unlock:
+ sem_unlock(semid);
+out_free:
+ if(sem_io != fast_sem_io)
+ ipc_free(sem_io, sizeof(ushort)*nsems);
+ return err;
+}
+
+struct sem_setbuf {
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+};
+
+static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void *buf, int version)
+{
+ switch(version) {
+ case IPC_64:
+ {
+ struct semid64_ds tbuf;
+
+ if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
+ return -EFAULT;
+
+ out->uid = tbuf.sem_perm.uid;
+ out->gid = tbuf.sem_perm.gid;
+ out->mode = tbuf.sem_perm.mode;
+
+ return 0;
+ }
+ case IPC_OLD:
+ {
+ struct semid_ds tbuf_old;
+
+ if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
+ return -EFAULT;
+
+ out->uid = tbuf_old.sem_perm.uid;
+ out->gid = tbuf_old.sem_perm.gid;
+ out->mode = tbuf_old.sem_perm.mode;
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
+{
+ struct sem_array *sma;
+ int err;
+ struct sem_setbuf setbuf;
+ struct kern_ipc_perm *ipcp;
+
+ if(cmd == IPC_SET) {
+ if(copy_semid_from_user (&setbuf, arg.buf, version))
+ return -EFAULT;
+ }
+ sma = sem_lock(semid);
+ if(sma==NULL)
+ return -EINVAL;
+
+ if (sem_checkid(sma,semid)) {
+ err=-EIDRM;
+ goto out_unlock;
+ }
+ ipcp = &sma->sem_perm;
+
+ if (current->euid != ipcp->cuid &&
+ current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
+ err=-EPERM;
+ goto out_unlock;
+ }
+
+ switch(cmd){
+ case IPC_RMID:
+ freeary(semid);
+ err = 0;
+ break;
+ case IPC_SET:
+ ipcp->uid = setbuf.uid;
+ ipcp->gid = setbuf.gid;
+ ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
+ | (setbuf.mode & S_IRWXUGO);
+ sma->sem_ctime = CURRENT_TIME;
+ sem_unlock(semid);
+ err = 0;
+ break;
+ default:
+ sem_unlock(semid);
+ err = -EINVAL;
+ break;
+ }
+ return err;
+
+out_unlock:
+ sem_unlock(semid);
+ return err;
+}
+
+asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
+{
+ int err = -EINVAL;
+ int version;
+
+ if (semid < 0)
+ return -EINVAL;
+
+ version = ipc_parse_version(&cmd);
+
+ switch(cmd) {
+ case IPC_INFO:
+ case SEM_INFO:
+ case SEM_STAT:
+ err = semctl_nolock(semid,semnum,cmd,version,arg);
+ return err;
+ case GETALL:
+ case GETVAL:
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case IPC_STAT:
+ case SETVAL:
+ case SETALL:
+ err = semctl_main(semid,semnum,cmd,version,arg);
+ return err;
+ case IPC_RMID:
+ case IPC_SET:
+ down(&sem_ids.sem);
+ err = semctl_down(semid,semnum,cmd,version,arg);
+ up(&sem_ids.sem);
+ return err;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct sem_undo* freeundos(struct sem_array *sma, struct sem_undo* un)
+{
+ struct sem_undo* u;
+ struct sem_undo** up;
+
+ for(up = &current->semundo;(u=*up);up=&u->proc_next) {
+ if(un==u) {
+ un=u->proc_next;
+ *up=un;
+ kfree(u);
+ return un;
+ }
+ }
+ printk ("freeundos undo list error id=%d\n", un->semid);
+ return un->proc_next;
+}
+
+/* returns without sem_lock on error! */
+static int alloc_undo(struct sem_array *sma, struct sem_undo** unp, int semid, int alter)
+{
+ int size, nsems, error;
+ struct sem_undo *un;
+
+ nsems = sma->sem_nsems;
+ size = sizeof(struct sem_undo) + sizeof(short)*nsems;
+ sem_unlock(semid);
+
+ un = (struct sem_undo *) kmalloc(size, GFP_KERNEL);
+ if (!un)
+ return -ENOMEM;
+
+ memset(un, 0, size);
+ error = sem_revalidate(semid, sma, nsems, alter ? S_IWUGO : S_IRUGO);
+ if(error) {
+ kfree(un);
+ return error;
+ }
+
+ un->semadj = (short *) &un[1];
+ un->semid = semid;
+ un->proc_next = current->semundo;
+ current->semundo = un;
+ un->id_next = sma->undo;
+ sma->undo = un;
+ *unp = un;
+ return 0;
+}
+
+asmlinkage long sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
+{
+ int error = -EINVAL;
+ struct sem_array *sma;
+ struct sembuf fast_sops[SEMOPM_FAST];
+ struct sembuf* sops = fast_sops, *sop;
+ struct sem_undo *un;
+ int undos = 0, decrease = 0, alter = 0;
+ struct sem_queue queue;
+
+ if (nsops < 1 || semid < 0)
+ return -EINVAL;
+ if (nsops > sc_semopm)
+ return -E2BIG;
+ if(nsops > SEMOPM_FAST) {
+ sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
+ if(sops==NULL)
+ return -ENOMEM;
+ }
+ if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
+ error=-EFAULT;
+ goto out_free;
+ }
+ sma = sem_lock(semid);
+ error=-EINVAL;
+ if(sma==NULL)
+ goto out_free;
+ error = -EIDRM;
+ if (sem_checkid(sma,semid))
+ goto out_unlock_free;
+ error = -EFBIG;
+ for (sop = sops; sop < sops + nsops; sop++) {
+ if (sop->sem_num >= sma->sem_nsems)
+ goto out_unlock_free;
+ if (sop->sem_flg & SEM_UNDO)
+ undos++;
+ if (sop->sem_op < 0)
+ decrease = 1;
+ if (sop->sem_op > 0)
+ alter = 1;
+ }
+ alter |= decrease;
+
+ error = -EACCES;
+ if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
+ goto out_unlock_free;
+ if (undos) {
+ /* Make sure we have an undo structure
+ * for this process and this semaphore set.
+ */
+ un=current->semundo;
+ while(un != NULL) {
+ if(un->semid==semid)
+ break;
+ if(un->semid==-1)
+ un=freeundos(sma,un);
+ else
+ un=un->proc_next;
+ }
+ if (!un) {
+ error = alloc_undo(sma,&un,semid,alter);
+ if(error)
+ goto out_free;
+ }
+ } else
+ un = NULL;
+
+ error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0);
+ if (error <= 0)
+ goto update;
+
+ /* We need to sleep on this operation, so we put the current
+ * task into the pending queue and go to sleep.
+ */
+
+ queue.sma = sma;
+ queue.sops = sops;
+ queue.nsops = nsops;
+ queue.undo = un;
+ queue.pid = current->pid;
+ queue.alter = decrease;
+ queue.id = semid;
+ if (alter)
+ append_to_queue(sma ,&queue);
+ else
+ prepend_to_queue(sma ,&queue);
+ current->semsleeping = &queue;
+
+ for (;;) {
+ struct sem_array* tmp;
+ queue.status = -EINTR;
+ queue.sleeper = current;
+ current->state = TASK_INTERRUPTIBLE;
+ sem_unlock(semid);
+
+ schedule();
+
+ tmp = sem_lock(semid);
+ if(tmp==NULL) {
+ if(queue.prev != NULL)
+ BUG();
+ current->semsleeping = NULL;
+ error = -EIDRM;
+ goto out_free;
+ }
+ /*
+ * If queue.status == 1 we where woken up and
+ * have to retry else we simply return.
+ * If an interrupt occurred we have to clean up the
+ * queue
+ *
+ */
+ if (queue.status == 1)
+ {
+ error = try_atomic_semop (sma, sops, nsops, un,
+ current->pid,0);
+ if (error <= 0)
+ break;
+ } else {
+ error = queue.status;
+ if (queue.prev) /* got Interrupt */
+ break;
+ /* Everything done by update_queue */
+ current->semsleeping = NULL;
+ goto out_unlock_free;
+ }
+ }
+ current->semsleeping = NULL;
+ remove_from_queue(sma,&queue);
+update:
+ if (alter)
+ update_queue (sma);
+out_unlock_free:
+ sem_unlock(semid);
+out_free:
+ if(sops != fast_sops)
+ kfree(sops);
+ return error;
+}
+
+/*
+ * add semadj values to semaphores, free undo structures.
+ * undo structures are not freed when semaphore arrays are destroyed
+ * so some of them may be out of date.
+ * IMPLEMENTATION NOTE: There is some confusion over whether the
+ * set of adjustments that needs to be done should be done in an atomic
+ * manner or not. That is, if we are attempting to decrement the semval
+ * should we queue up and wait until we can do so legally?
+ * The original implementation attempted to do this (queue and wait).
+ * The current implementation does not do so. The POSIX standard
+ * and SVID should be consulted to determine what behavior is mandated.
+ */
+void sem_exit (void)
+{
+ struct sem_queue *q;
+ struct sem_undo *u, *un = NULL, **up, **unp;
+ struct sem_array *sma;
+ int nsems, i;
+
+ /* If the current process was sleeping for a semaphore,
+ * remove it from the queue.
+ */
+ if ((q = current->semsleeping)) {
+ int semid = q->id;
+ sma = sem_lock(semid);
+ current->semsleeping = NULL;
+
+ if (q->prev) {
+ if(sma==NULL)
+ BUG();
+ remove_from_queue(q->sma,q);
+ }
+ if(sma!=NULL)
+ sem_unlock(semid);
+ }
+
+ for (up = &current->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
+ int semid = u->semid;
+ if(semid == -1)
+ continue;
+ sma = sem_lock(semid);
+ if (sma == NULL)
+ continue;
+
+ if (u->semid == -1)
+ goto next_entry;
+
+ if (sem_checkid(sma,u->semid))
+ goto next_entry;
+
+ /* remove u from the sma->undo list */
+ for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
+ if (u == un)
+ goto found;
+ }
+ printk ("sem_exit undo list error id=%d\n", u->semid);
+ goto next_entry;
+found:
+ *unp = un->id_next;
+ /* perform adjustments registered in u */
+ nsems = sma->sem_nsems;
+ for (i = 0; i < nsems; i++) {
+ struct sem * sem = &sma->sem_base[i];
+ sem->semval += u->semadj[i];
+ if (sem->semval < 0)
+ sem->semval = 0; /* shouldn't happen */
+ sem->sempid = current->pid;
+ }
+ sma->sem_otime = CURRENT_TIME;
+ /* maybe some queued-up processes were waiting for this */
+ update_queue(sma);
+next_entry:
+ sem_unlock(semid);
+ }
+ current->semundo = NULL;
+}
+
+#ifdef CONFIG_PROC_FS
+static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+ off_t pos = 0;
+ off_t begin = 0;
+ int i, len = 0;
+
+ len += sprintf(buffer, " key semid perms nsems uid gid cuid cgid otime ctime\n");
+ down(&sem_ids.sem);
+
+ for(i = 0; i <= sem_ids.max_id; i++) {
+ struct sem_array *sma;
+ sma = sem_lock(i);
+ if(sma) {
+ len += sprintf(buffer + len, "%10d %10d %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
+ sma->sem_perm.key,
+ sem_buildid(i,sma->sem_perm.seq),
+ sma->sem_perm.mode,
+ sma->sem_nsems,
+ sma->sem_perm.uid,
+ sma->sem_perm.gid,
+ sma->sem_perm.cuid,
+ sma->sem_perm.cgid,
+ sma->sem_otime,
+ sma->sem_ctime);
+ sem_unlock(i);
+
+ pos += len;
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if(pos > offset + length)
+ goto done;
+ }
+ }
+ *eof = 1;
+done:
+ up(&sem_ids.sem);
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+ return len;
+}
+#endif
diff --git a/uClinux-2.4.20-uc1/ipc/shm.c b/uClinux-2.4.20-uc1/ipc/shm.c
new file mode 100644
index 0000000..214c9a1
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/shm.c
@@ -0,0 +1,837 @@
+/*
+ * linux/ipc/shm.c
+ * Copyright (C) 1992, 1993 Krishna Balasubramanian
+ * Many improvements/fixes by Bruno Haible.
+ * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
+ * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
+ *
+ * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
+ * BIGMEM support, Andrea Arcangeli <andrea@suse.de>
+ * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr>
+ * HIGHMEM support, Ingo Molnar <mingo@redhat.com>
+ * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
+ * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
+ * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
+ *
+ */
+
+ /*
+ * uClinux revisions for NO_MM
+ *
+ * A simplistic attempt to get shared memory under a mmu-less system. Uses
+ * the shm_file pointer as a pointer to a chunk of shared memory instead of
+ * a "struct file".
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/shm.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+
+#include "util.h"
+
+struct shmid_kernel /* private to the kernel */
+{
+ struct kern_ipc_perm shm_perm;
+ struct file * shm_file;
+ int id;
+ unsigned long shm_nattch;
+ unsigned long shm_segsz;
+ time_t shm_atim;
+ time_t shm_dtim;
+ time_t shm_ctim;
+ pid_t shm_cprid;
+ pid_t shm_lprid;
+};
+
+#define shm_flags shm_perm.mode
+
+#ifndef NO_MM
+static struct file_operations shm_file_operations;
+static struct vm_operations_struct shm_vm_ops;
+#endif /* NO_MM */
+
+static struct ipc_ids shm_ids;
+
+#define shm_lock(id) ((struct shmid_kernel*)ipc_lock(&shm_ids,id))
+#define shm_unlock(id) ipc_unlock(&shm_ids,id)
+#define shm_lockall() ipc_lockall(&shm_ids)
+#define shm_unlockall() ipc_unlockall(&shm_ids)
+#define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id))
+#define shm_buildid(id, seq) \
+ ipc_buildid(&shm_ids, id, seq)
+
+static int newseg (key_t key, int shmflg, size_t size);
+#ifndef NO_MM
+static void shm_open (struct vm_area_struct *shmd);
+static void shm_close (struct vm_area_struct *shmd);
+#endif /* NO_MM */
+#ifdef CONFIG_PROC_FS
+static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
+#endif
+
+size_t shm_ctlmax = SHMMAX;
+size_t shm_ctlall = SHMALL;
+int shm_ctlmni = SHMMNI;
+
+static int shm_tot; /* total number of shared memory pages */
+
+void __init shm_init (void)
+{
+ ipc_init_ids(&shm_ids, 1);
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("sysvipc/shm", 0, 0, sysvipc_shm_read_proc, NULL);
+#endif
+}
+
+static inline int shm_checkid(struct shmid_kernel *s, int id)
+{
+ if (ipc_checkid(&shm_ids,&s->shm_perm,id))
+ return -EIDRM;
+ return 0;
+}
+
+static inline struct shmid_kernel *shm_rmid(int id)
+{
+ return (struct shmid_kernel *)ipc_rmid(&shm_ids,id);
+}
+
+static inline int shm_addid(struct shmid_kernel *shp)
+{
+ return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
+}
+
+
+
+static inline void shm_inc (int id) {
+ struct shmid_kernel *shp;
+
+ if(!(shp = shm_lock(id)))
+ BUG();
+ shp->shm_atim = CURRENT_TIME;
+ shp->shm_lprid = current->pid;
+ shp->shm_nattch++;
+ shm_unlock(id);
+}
+
+#ifndef NO_MM
+/* This is called by fork, once for every shm attach. */
+static void shm_open (struct vm_area_struct *shmd)
+{
+ shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino);
+}
+#endif /* NO_MM */
+
+/*
+ * shm_destroy - free the struct shmid_kernel
+ *
+ * @shp: struct to free
+ *
+ * It has to be called with shp and shm_ids.sem locked,
+ * but returns with shp unlocked and freed.
+ */
+static void shm_destroy (struct shmid_kernel *shp)
+{
+ shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ shm_rmid (shp->id);
+#ifndef NO_MM
+ shm_unlock(shp->id);
+ shmem_lock(shp->shm_file, 0);
+ fput (shp->shm_file);
+#else
+ kfree (shp->shm_file);
+#endif /* NO_MM */
+ kfree (shp);
+}
+
+#ifndef NO_MM
+/*
+ * remove the attach descriptor shmd.
+ * free memory for segment if it is marked destroyed.
+ * The descriptor has already been removed from the current->mm->mmap list
+ * and will later be kfree()d.
+ */
+static void shm_close (struct vm_area_struct *shmd)
+{
+ struct file * file = shmd->vm_file;
+ int id = file->f_dentry->d_inode->i_ino;
+ struct shmid_kernel *shp;
+
+ down (&shm_ids.sem);
+ /* remove from the list of attaches of the shm segment */
+ if(!(shp = shm_lock(id)))
+ BUG();
+ shp->shm_lprid = current->pid;
+ shp->shm_dtim = CURRENT_TIME;
+ shp->shm_nattch--;
+ if(shp->shm_nattch == 0 &&
+ shp->shm_flags & SHM_DEST)
+ shm_destroy (shp);
+ else
+ shm_unlock(id);
+ up (&shm_ids.sem);
+}
+
+static int shm_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ UPDATE_ATIME(file->f_dentry->d_inode);
+ vma->vm_ops = &shm_vm_ops;
+ shm_inc(file->f_dentry->d_inode->i_ino);
+ return 0;
+}
+
+static struct file_operations shm_file_operations = {
+ mmap: shm_mmap
+};
+
+static struct vm_operations_struct shm_vm_ops = {
+ open: shm_open, /* callback for a new vm-area open */
+ close: shm_close, /* callback for when the vm-area is released */
+ nopage: shmem_nopage,
+};
+#else
+struct file *shmem_file_setup(char * name, loff_t size)
+{
+ void * f = kmalloc(size, GFP_KERNEL);
+
+ return (struct file *)(PTR_ERR(f) ? f : (void *)-ENOMEM);
+}
+#endif /* NO_MM */
+
+static int newseg (key_t key, int shmflg, size_t size)
+{
+ int error;
+ struct shmid_kernel *shp;
+ int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
+ struct file * file;
+ char name[13];
+ int id;
+
+ if (size < SHMMIN || size > shm_ctlmax)
+ return -EINVAL;
+
+ if (shm_tot + numpages >= shm_ctlall)
+ return -ENOSPC;
+
+ shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_USER);
+ if (!shp)
+ return -ENOMEM;
+ sprintf (name, "SYSV%08x", key);
+ file = shmem_file_setup(name, size);
+ error = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto no_file;
+
+ error = -ENOSPC;
+ id = shm_addid(shp);
+ if(id == -1)
+ goto no_id;
+ shp->shm_perm.key = key;
+ shp->shm_flags = (shmflg & S_IRWXUGO);
+ shp->shm_cprid = current->pid;
+ shp->shm_lprid = 0;
+ shp->shm_atim = shp->shm_dtim = 0;
+ shp->shm_ctim = CURRENT_TIME;
+ shp->shm_segsz = size;
+ shp->shm_nattch = 0;
+ shp->id = shm_buildid(id,shp->shm_perm.seq);
+ shp->shm_file = file;
+#ifndef NO_MM
+ file->f_dentry->d_inode->i_ino = shp->id;
+ file->f_op = &shm_file_operations;
+#endif /* NO_MM */
+ shm_tot += numpages;
+ shm_unlock (id);
+ return shp->id;
+
+no_id:
+#ifndef NO_MM
+ fput(file);
+#else
+ kfree(file);
+#endif
+
+no_file:
+ kfree(shp);
+ return error;
+}
+
+asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
+{
+ struct shmid_kernel *shp;
+ int err, id = 0;
+
+ down(&shm_ids.sem);
+ if (key == IPC_PRIVATE) {
+ err = newseg(key, shmflg, size);
+ } else if ((id = ipc_findkey(&shm_ids, key)) == -1) {
+ if (!(shmflg & IPC_CREAT))
+ err = -ENOENT;
+ else
+ err = newseg(key, shmflg, size);
+ } else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
+ err = -EEXIST;
+ } else {
+ shp = shm_lock(id);
+ if(shp==NULL)
+ BUG();
+ if (shp->shm_segsz < size)
+ err = -EINVAL;
+ else if (ipcperms(&shp->shm_perm, shmflg))
+ err = -EACCES;
+ else
+ err = shm_buildid(id, shp->shm_perm.seq);
+ shm_unlock(id);
+ }
+ up(&shm_ids.sem);
+ return err;
+}
+
+static inline unsigned long copy_shmid_to_user(void *buf, struct shmid64_ds *in, int version)
+{
+ switch(version) {
+ case IPC_64:
+ return copy_to_user(buf, in, sizeof(*in));
+ case IPC_OLD:
+ {
+ struct shmid_ds out;
+
+ ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm);
+ out.shm_segsz = in->shm_segsz;
+ out.shm_atime = in->shm_atime;
+ out.shm_dtime = in->shm_dtime;
+ out.shm_ctime = in->shm_ctime;
+ out.shm_cpid = in->shm_cpid;
+ out.shm_lpid = in->shm_lpid;
+ out.shm_nattch = in->shm_nattch;
+
+ return copy_to_user(buf, &out, sizeof(out));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+struct shm_setbuf {
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+};
+
+static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void *buf, int version)
+{
+ switch(version) {
+ case IPC_64:
+ {
+ struct shmid64_ds tbuf;
+
+ if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
+ return -EFAULT;
+
+ out->uid = tbuf.shm_perm.uid;
+ out->gid = tbuf.shm_perm.gid;
+ out->mode = tbuf.shm_flags;
+
+ return 0;
+ }
+ case IPC_OLD:
+ {
+ struct shmid_ds tbuf_old;
+
+ if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
+ return -EFAULT;
+
+ out->uid = tbuf_old.shm_perm.uid;
+ out->gid = tbuf_old.shm_perm.gid;
+ out->mode = tbuf_old.shm_flags;
+
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline unsigned long copy_shminfo_to_user(void *buf, struct shminfo64 *in, int version)
+{
+ switch(version) {
+ case IPC_64:
+ return copy_to_user(buf, in, sizeof(*in));
+ case IPC_OLD:
+ {
+ struct shminfo out;
+
+ if(in->shmmax > INT_MAX)
+ out.shmmax = INT_MAX;
+ else
+ out.shmmax = (int)in->shmmax;
+
+ out.shmmin = in->shmmin;
+ out.shmmni = in->shmmni;
+ out.shmseg = in->shmseg;
+ out.shmall = in->shmall;
+
+ return copy_to_user(buf, &out, sizeof(out));
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static void shm_get_stat (unsigned long *rss, unsigned long *swp)
+{
+#ifndef NO_MM
+ struct shmem_inode_info *info;
+#endif
+ int i;
+
+ *rss = 0;
+ *swp = 0;
+
+ for(i = 0; i <= shm_ids.max_id; i++) {
+ struct shmid_kernel* shp;
+#ifndef NO_MM
+ struct inode * inode;
+#endif
+
+ shp = shm_get(i);
+ if(shp == NULL)
+ continue;
+#ifndef NO_MM
+ inode = shp->shm_file->f_dentry->d_inode;
+ info = SHMEM_I(inode);
+ spin_lock (&info->lock);
+ *rss += inode->i_mapping->nrpages;
+ *swp += info->swapped;
+ spin_unlock (&info->lock);
+#else
+ *rss += (shp->shm_segsz + PAGE_SIZE -1) >> PAGE_SHIFT;
+#endif
+ }
+}
+
+asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
+{
+ struct shm_setbuf setbuf;
+ struct shmid_kernel *shp;
+ int err, version;
+
+ if (cmd < 0 || shmid < 0)
+ return -EINVAL;
+
+ version = ipc_parse_version(&cmd);
+
+ switch (cmd) { /* replace with proc interface ? */
+ case IPC_INFO:
+ {
+ struct shminfo64 shminfo;
+
+ memset(&shminfo,0,sizeof(shminfo));
+ shminfo.shmmni = shminfo.shmseg = shm_ctlmni;
+ shminfo.shmmax = shm_ctlmax;
+ shminfo.shmall = shm_ctlall;
+
+ shminfo.shmmin = SHMMIN;
+ if(copy_shminfo_to_user (buf, &shminfo, version))
+ return -EFAULT;
+ /* reading a integer is always atomic */
+ err= shm_ids.max_id;
+ if(err<0)
+ err = 0;
+ return err;
+ }
+ case SHM_INFO:
+ {
+ struct shm_info shm_info;
+
+ memset(&shm_info,0,sizeof(shm_info));
+ down(&shm_ids.sem);
+ shm_lockall();
+ shm_info.used_ids = shm_ids.in_use;
+ shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp);
+ shm_info.shm_tot = shm_tot;
+ shm_info.swap_attempts = 0;
+ shm_info.swap_successes = 0;
+ err = shm_ids.max_id;
+ shm_unlockall();
+ up(&shm_ids.sem);
+ if(copy_to_user (buf, &shm_info, sizeof(shm_info)))
+ return -EFAULT;
+
+ return err < 0 ? 0 : err;
+ }
+ case SHM_STAT:
+ case IPC_STAT:
+ {
+ struct shmid64_ds tbuf;
+ int result;
+ memset(&tbuf, 0, sizeof(tbuf));
+ shp = shm_lock(shmid);
+ if(shp==NULL)
+ return -EINVAL;
+ if(cmd==SHM_STAT) {
+ err = -EINVAL;
+ if (shmid > shm_ids.max_id)
+ goto out_unlock;
+ result = shm_buildid(shmid, shp->shm_perm.seq);
+ } else {
+ err = shm_checkid(shp,shmid);
+ if(err)
+ goto out_unlock;
+ result = 0;
+ }
+ err=-EACCES;
+ if (ipcperms (&shp->shm_perm, S_IRUGO))
+ goto out_unlock;
+ kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
+ tbuf.shm_segsz = shp->shm_segsz;
+ tbuf.shm_atime = shp->shm_atim;
+ tbuf.shm_dtime = shp->shm_dtim;
+ tbuf.shm_ctime = shp->shm_ctim;
+ tbuf.shm_cpid = shp->shm_cprid;
+ tbuf.shm_lpid = shp->shm_lprid;
+ tbuf.shm_nattch = shp->shm_nattch;
+ shm_unlock(shmid);
+ if(copy_shmid_to_user (buf, &tbuf, version))
+ return -EFAULT;
+ return result;
+ }
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ {
+/* Allow superuser to lock segment in memory */
+/* Should the pages be faulted in here or leave it to user? */
+/* need to determine interaction with current->swappable */
+ if (!capable(CAP_IPC_LOCK))
+ return -EPERM;
+
+ shp = shm_lock(shmid);
+ if(shp==NULL)
+ return -EINVAL;
+ err = shm_checkid(shp,shmid);
+ if(err)
+ goto out_unlock;
+ if(cmd==SHM_LOCK) {
+#ifndef NO_MM
+ shmem_lock(shp->shm_file, 1);
+#endif /* NO_MM */
+ shp->shm_flags |= SHM_LOCKED;
+ } else {
+#ifndef NO_MM
+ shmem_lock(shp->shm_file, 0);
+#endif /* NO_MM */
+ shp->shm_flags &= ~SHM_LOCKED;
+ }
+ shm_unlock(shmid);
+ return err;
+ }
+ case IPC_RMID:
+ {
+ /*
+ * We cannot simply remove the file. The SVID states
+ * that the block remains until the last person
+ * detaches from it, then is deleted. A shmat() on
+ * an RMID segment is legal in older Linux and if
+ * we change it apps break...
+ *
+ * Instead we set a destroyed flag, and then blow
+ * the name away when the usage hits zero.
+ */
+ down(&shm_ids.sem);
+ shp = shm_lock(shmid);
+ err = -EINVAL;
+ if (shp == NULL)
+ goto out_up;
+ err = shm_checkid(shp, shmid);
+ if(err)
+ goto out_unlock_up;
+ if (current->euid != shp->shm_perm.uid &&
+ current->euid != shp->shm_perm.cuid &&
+ !capable(CAP_SYS_ADMIN)) {
+ err=-EPERM;
+ goto out_unlock_up;
+ }
+ if (shp->shm_nattch){
+ shp->shm_flags |= SHM_DEST;
+ /* Do not find it any more */
+ shp->shm_perm.key = IPC_PRIVATE;
+ shm_unlock(shmid);
+ } else
+ shm_destroy (shp);
+ up(&shm_ids.sem);
+ return err;
+ }
+
+ case IPC_SET:
+ {
+ if(copy_shmid_from_user (&setbuf, buf, version))
+ return -EFAULT;
+ down(&shm_ids.sem);
+ shp = shm_lock(shmid);
+ err=-EINVAL;
+ if(shp==NULL)
+ goto out_up;
+ err = shm_checkid(shp,shmid);
+ if(err)
+ goto out_unlock_up;
+ err=-EPERM;
+ if (current->euid != shp->shm_perm.uid &&
+ current->euid != shp->shm_perm.cuid &&
+ !capable(CAP_SYS_ADMIN)) {
+ goto out_unlock_up;
+ }
+
+ shp->shm_perm.uid = setbuf.uid;
+ shp->shm_perm.gid = setbuf.gid;
+ shp->shm_flags = (shp->shm_flags & ~S_IRWXUGO)
+ | (setbuf.mode & S_IRWXUGO);
+ shp->shm_ctim = CURRENT_TIME;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ err = 0;
+out_unlock_up:
+ shm_unlock(shmid);
+out_up:
+ up(&shm_ids.sem);
+ return err;
+out_unlock:
+ shm_unlock(shmid);
+ return err;
+}
+
+/*
+ * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
+ */
+asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
+{
+ struct shmid_kernel *shp;
+ unsigned long addr;
+#ifndef NO_MM
+ unsigned long size;
+ struct file * file;
+#endif /* NO_MM */
+ int err;
+ unsigned long flags;
+ unsigned long prot;
+ unsigned long o_flags;
+ int acc_mode;
+ void *user_addr;
+
+ if (shmid < 0)
+ return -EINVAL;
+
+ if ((addr = (ulong)shmaddr)) {
+ if (addr & (SHMLBA-1)) {
+ if (shmflg & SHM_RND)
+ addr &= ~(SHMLBA-1); /* round down */
+ else
+ return -EINVAL;
+ }
+ flags = MAP_SHARED | MAP_FIXED;
+ } else {
+ if ((shmflg & SHM_REMAP))
+ return -EINVAL;
+
+ flags = MAP_SHARED;
+ }
+
+ if (shmflg & SHM_RDONLY) {
+ prot = PROT_READ;
+ o_flags = O_RDONLY;
+ acc_mode = S_IRUGO;
+ } else {
+ prot = PROT_READ | PROT_WRITE;
+ o_flags = O_RDWR;
+ acc_mode = S_IRUGO | S_IWUGO;
+ }
+
+ /*
+ * We cannot rely on the fs check since SYSV IPC does have an
+ * additional creator id...
+ */
+ shp = shm_lock(shmid);
+ if(shp == NULL)
+ return -EINVAL;
+ err = shm_checkid(shp,shmid);
+ if (err) {
+ shm_unlock(shmid);
+ return err;
+ }
+ if (ipcperms(&shp->shm_perm, acc_mode)) {
+ shm_unlock(shmid);
+ return -EACCES;
+ }
+#ifndef NO_MM
+ file = shp->shm_file;
+ size = file->f_dentry->d_inode->i_size;
+#endif /* NO_MM */
+ shp->shm_nattch++;
+ shm_unlock(shmid);
+
+ down_write(&current->mm->mmap_sem);
+#ifndef NO_MM
+ if (addr && !(shmflg & SHM_REMAP)) {
+ user_addr = ERR_PTR(-EINVAL);
+ if (find_vma_intersection(current->mm, addr, addr + size))
+ goto invalid;
+ /*
+ * If shm segment goes below stack, make sure there is some
+ * space left for the stack to grow (at least 4 pages).
+ */
+ if (addr < current->mm->start_stack &&
+ addr > current->mm->start_stack - size - PAGE_SIZE * 5)
+ goto invalid;
+ }
+
+ user_addr = (void*) do_mmap (file, addr, size, prot, flags, 0);
+#else
+ if (addr) {
+ user_addr = ERR_PTR(-EINVAL);
+ goto invalid;
+ }
+
+ user_addr = (void*)shp->shm_file;
+ if (user_addr)
+ shm_inc(shmid);
+#endif /* NO_MM */
+
+invalid:
+ up_write(&current->mm->mmap_sem);
+
+ down (&shm_ids.sem);
+ if(!(shp = shm_lock(shmid)))
+ BUG();
+ shp->shm_nattch--;
+ if(shp->shm_nattch == 0 &&
+ shp->shm_flags & SHM_DEST)
+ shm_destroy (shp);
+ else
+ shm_unlock(shmid);
+ up (&shm_ids.sem);
+
+ *raddr = (unsigned long) user_addr;
+ err = 0;
+ if (IS_ERR(user_addr))
+ err = PTR_ERR(user_addr);
+ return err;
+
+}
+
+/*
+ * detach and kill segment if marked destroyed.
+ * The work is done in shm_close.
+ */
+asmlinkage long sys_shmdt (char *shmaddr)
+{
+#ifndef NO_MM
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *shmd, *shmdnext;
+ int retval = -EINVAL;
+
+ down_write(&mm->mmap_sem);
+ for (shmd = mm->mmap; shmd; shmd = shmdnext) {
+ shmdnext = shmd->vm_next;
+ if (shmd->vm_ops == &shm_vm_ops
+ && shmd->vm_start - (shmd->vm_pgoff << PAGE_SHIFT) == (ulong) shmaddr) {
+ do_munmap(mm, shmd->vm_start, shmd->vm_end - shmd->vm_start);
+ retval = 0;
+ }
+ }
+ up_write(&mm->mmap_sem);
+ return retval;
+#else
+ int i;
+
+ down (&shm_ids.sem);
+ for(i= 0; i <= shm_ids.max_id; i++) {
+ struct shmid_kernel* shp;
+
+ shp = shm_lock(i);
+ if(shp!=NULL) {
+ if (shp->shm_file == (struct file *)shmaddr) {
+ shp->shm_lprid = current->pid;
+ shp->shm_dtim = CURRENT_TIME;
+ shp->shm_nattch--;
+ if(shp->shm_nattch == 0 &&
+ shp->shm_flags & SHM_DEST)
+ shm_destroy (shp);
+ i = shm_ids.max_id+1; /* Make this the last iteration */
+ }
+ shm_unlock(i);
+ }
+ }
+ up (&shm_ids.sem);
+ return 0;
+#endif /* NO_MM */
+}
+
+#ifdef CONFIG_PROC_FS
+static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
+{
+ off_t pos = 0;
+ off_t begin = 0;
+ int i, len = 0;
+
+ down(&shm_ids.sem);
+ len += sprintf(buffer, " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime\n");
+
+ for(i = 0; i <= shm_ids.max_id; i++) {
+ struct shmid_kernel* shp;
+
+ shp = shm_lock(i);
+ if(shp!=NULL) {
+#define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
+#define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
+ char *format;
+
+ if (sizeof(size_t) <= sizeof(int))
+ format = SMALL_STRING;
+ else
+ format = BIG_STRING;
+ len += sprintf(buffer + len, format,
+ shp->shm_perm.key,
+ shm_buildid(i, shp->shm_perm.seq),
+ shp->shm_flags,
+ shp->shm_segsz,
+ shp->shm_cprid,
+ shp->shm_lprid,
+ shp->shm_nattch,
+ shp->shm_perm.uid,
+ shp->shm_perm.gid,
+ shp->shm_perm.cuid,
+ shp->shm_perm.cgid,
+ shp->shm_atim,
+ shp->shm_dtim,
+ shp->shm_ctim);
+ shm_unlock(i);
+
+ pos += len;
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if(pos > offset + length)
+ goto done;
+ }
+ }
+ *eof = 1;
+done:
+ up(&shm_ids.sem);
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+ return len;
+}
+#endif
diff --git a/uClinux-2.4.20-uc1/ipc/util.c b/uClinux-2.4.20-uc1/ipc/util.c
new file mode 100644
index 0000000..64aa6cf
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/util.c
@@ -0,0 +1,408 @@
+/*
+ * linux/ipc/util.c
+ * Copyright (C) 1992 Krishna Balasubramanian
+ *
+ * Sep 1997 - Call suser() last after "normal" permission checks so we
+ * get BSD style process accounting right.
+ * Occurs in several places in the IPC code.
+ * Chris Evans, <chris@ferret.lmh.ox.ac.uk>
+ * Nov 1999 - ipc helper functions, unified SMP locking
+ * Manfred Spraul <manfreds@colorfullife.com>
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/init.h>
+#include <linux/msg.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/highuid.h>
+
+#if defined(CONFIG_SYSVIPC)
+
+#include "util.h"
+
+/**
+ * ipc_init - initialise IPC subsystem
+ *
+ * The various system5 IPC resources (semaphores, messages and shared
+ * memory are initialised
+ */
+
+void __init ipc_init (void)
+{
+ sem_init();
+ msg_init();
+ shm_init();
+ return;
+}
+
+/**
+ * ipc_init_ids - initialise IPC identifiers
+ * @ids: Identifier set
+ * @size: Number of identifiers
+ *
+ * Given a size for the ipc identifier range (limited below IPCMNI)
+ * set up the sequence range to use then allocate and initialise the
+ * array itself.
+ */
+
+void __init ipc_init_ids(struct ipc_ids* ids, int size)
+{
+ int i;
+ sema_init(&ids->sem,1);
+
+ if(size > IPCMNI)
+ size = IPCMNI;
+ ids->size = size;
+ ids->in_use = 0;
+ ids->max_id = -1;
+ ids->seq = 0;
+ {
+ int seq_limit = INT_MAX/SEQ_MULTIPLIER;
+ if(seq_limit > USHRT_MAX)
+ ids->seq_max = USHRT_MAX;
+ else
+ ids->seq_max = seq_limit;
+ }
+
+ ids->entries = ipc_alloc(sizeof(struct ipc_id)*size);
+
+ if(ids->entries == NULL) {
+ printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
+ ids->size = 0;
+ }
+ ids->ary = SPIN_LOCK_UNLOCKED;
+ for(i=0;i<ids->size;i++)
+ ids->entries[i].p = NULL;
+}
+
+/**
+ * ipc_findkey - find a key in an ipc identifier set
+ * @ids: Identifier set
+ * @key: The key to find
+ *
+ * Returns the identifier if found or -1 if not.
+ */
+
+int ipc_findkey(struct ipc_ids* ids, key_t key)
+{
+ int id;
+ struct kern_ipc_perm* p;
+
+ for (id = 0; id <= ids->max_id; id++) {
+ p = ids->entries[id].p;
+ if(p==NULL)
+ continue;
+ if (key == p->key)
+ return id;
+ }
+ return -1;
+}
+
+static int grow_ary(struct ipc_ids* ids, int newsize)
+{
+ struct ipc_id* new;
+ struct ipc_id* old;
+ int i;
+
+ if(newsize > IPCMNI)
+ newsize = IPCMNI;
+ if(newsize <= ids->size)
+ return newsize;
+
+ new = ipc_alloc(sizeof(struct ipc_id)*newsize);
+ if(new == NULL)
+ return ids->size;
+ memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size);
+ for(i=ids->size;i<newsize;i++) {
+ new[i].p = NULL;
+ }
+ spin_lock(&ids->ary);
+
+ old = ids->entries;
+ ids->entries = new;
+ i = ids->size;
+ ids->size = newsize;
+ spin_unlock(&ids->ary);
+ ipc_free(old, sizeof(struct ipc_id)*i);
+ return ids->size;
+}
+
+/**
+ * ipc_addid - add an IPC identifier
+ * @ids: IPC identifier set
+ * @new: new IPC permission set
+ * @size: new size limit for the id array
+ *
+ * Add an entry 'new' to the IPC arrays. The permissions object is
+ * initialised and the first free entry is set up and the id assigned
+ * is returned. The list is returned in a locked state on success.
+ * On failure the list is not locked and -1 is returned.
+ */
+
+int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
+{
+ int id;
+
+ size = grow_ary(ids,size);
+ for (id = 0; id < size; id++) {
+ if(ids->entries[id].p == NULL)
+ goto found;
+ }
+ return -1;
+found:
+ ids->in_use++;
+ if (id > ids->max_id)
+ ids->max_id = id;
+
+ new->cuid = new->uid = current->euid;
+ new->gid = new->cgid = current->egid;
+
+ new->seq = ids->seq++;
+ if(ids->seq > ids->seq_max)
+ ids->seq = 0;
+
+ spin_lock(&ids->ary);
+ ids->entries[id].p = new;
+ return id;
+}
+
+/**
+ * ipc_rmid - remove an IPC identifier
+ * @ids: identifier set
+ * @id: Identifier to remove
+ *
+ * The identifier must be valid, and in use. The kernel will panic if
+ * fed an invalid identifier. The entry is removed and internal
+ * variables recomputed. The object associated with the identifier
+ * is returned.
+ */
+
+struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
+{
+ struct kern_ipc_perm* p;
+ int lid = id % SEQ_MULTIPLIER;
+ if(lid >= ids->size)
+ BUG();
+ p = ids->entries[lid].p;
+ ids->entries[lid].p = NULL;
+ if(p==NULL)
+ BUG();
+ ids->in_use--;
+
+ if (lid == ids->max_id) {
+ do {
+ lid--;
+ if(lid == -1)
+ break;
+ } while (ids->entries[lid].p == NULL);
+ ids->max_id = lid;
+ }
+ return p;
+}
+
+/**
+ * ipc_alloc - allocate ipc space
+ * @size: size desired
+ *
+ * Allocate memory from the appropriate pools and return a pointer to it.
+ * NULL is returned if the allocation fails
+ */
+
+void* ipc_alloc(int size)
+{
+ void* out;
+ if(size > PAGE_SIZE)
+ out = vmalloc(size);
+ else
+ out = kmalloc(size, GFP_KERNEL);
+ return out;
+}
+
+/**
+ * ipc_free - free ipc space
+ * @ptr: pointer returned by ipc_alloc
+ * @size: size of block
+ *
+ * Free a block created with ipc_alloc. The caller must know the size
+ * used in the allocation call.
+ */
+
+void ipc_free(void* ptr, int size)
+{
+ if(size > PAGE_SIZE)
+ vfree(ptr);
+ else
+ kfree(ptr);
+}
+
+/**
+ * ipcperms - check IPC permissions
+ * @ipcp: IPC permission set
+ * @flag: desired permission set.
+ *
+ * Check user, group, other permissions for access
+ * to ipc resources. return 0 if allowed
+ */
+
+int ipcperms (struct kern_ipc_perm *ipcp, short flag)
+{ /* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
+ int requested_mode, granted_mode;
+
+ requested_mode = (flag >> 6) | (flag >> 3) | flag;
+ granted_mode = ipcp->mode;
+ if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
+ granted_mode >>= 6;
+ else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
+ granted_mode >>= 3;
+ /* is there some bit set in requested_mode but not in granted_mode? */
+ if ((requested_mode & ~granted_mode & 0007) &&
+ !capable(CAP_IPC_OWNER))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Functions to convert between the kern_ipc_perm structure and the
+ * old/new ipc_perm structures
+ */
+
+/**
+ * kernel_to_ipc64_perm - convert kernel ipc permissions to user
+ * @in: kernel permissions
+ * @out: new style IPC permissions
+ *
+ * Turn the kernel object 'in' into a set of permissions descriptions
+ * for returning to userspace (out).
+ */
+
+
+void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
+{
+ out->key = in->key;
+ out->uid = in->uid;
+ out->gid = in->gid;
+ out->cuid = in->cuid;
+ out->cgid = in->cgid;
+ out->mode = in->mode;
+ out->seq = in->seq;
+}
+
+/**
+ * ipc64_perm_to_ipc_perm - convert old ipc permissions to new
+ * @in: new style IPC permissions
+ * @out: old style IPC permissions
+ *
+ * Turn the new style permissions object in into a compatibility
+ * object and store it into the 'out' pointer.
+ */
+
+void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
+{
+ out->key = in->key;
+ out->uid = NEW_TO_OLD_UID(in->uid);
+ out->gid = NEW_TO_OLD_GID(in->gid);
+ out->cuid = NEW_TO_OLD_UID(in->cuid);
+ out->cgid = NEW_TO_OLD_GID(in->cgid);
+ out->mode = in->mode;
+ out->seq = in->seq;
+}
+
+#if !defined(__ia64__) && !defined(__hppa__)
+
+/**
+ * ipc_parse_version - IPC call version
+ * @cmd: pointer to command
+ *
+ * Return IPC_64 for new style IPC and IPC_OLD for old style IPC.
+ * The cmd value is turned from an encoding command and version into
+ * just the command code.
+ */
+
+int ipc_parse_version (int *cmd)
+{
+#ifdef __x86_64__
+ if (!(current->thread.flags & THREAD_IA32))
+ return IPC_64;
+#endif
+ if (*cmd & IPC_64) {
+ *cmd ^= IPC_64;
+ return IPC_64;
+ } else {
+ return IPC_OLD;
+ }
+}
+
+#endif /* __ia64__ */
+
+#else
+/*
+ * Dummy functions when SYSV IPC isn't configured
+ */
+
+void sem_exit (void)
+{
+ return;
+}
+
+asmlinkage long sys_semget (key_t key, int nsems, int semflg)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_semop (int semid, struct sembuf *sops, unsigned nsops)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_msgget (key_t key, int msgflg)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
+ int msgflg)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_shmget (key_t key, size_t size, int shmflag)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *addr)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_shmdt (char *shmaddr)
+{
+ return -ENOSYS;
+}
+
+asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_SYSVIPC */
diff --git a/uClinux-2.4.20-uc1/ipc/util.h b/uClinux-2.4.20-uc1/ipc/util.h
new file mode 100644
index 0000000..c9f6422
--- /dev/null
+++ b/uClinux-2.4.20-uc1/ipc/util.h
@@ -0,0 +1,107 @@
+/*
+ * linux/ipc/util.h
+ * Copyright (C) 1999 Christoph Rohland
+ *
+ * ipc helper functions (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
+ */
+
+#define USHRT_MAX 0xffff
+#define SEQ_MULTIPLIER (IPCMNI)
+
+void sem_init (void);
+void msg_init (void);
+void shm_init (void);
+
+struct ipc_ids {
+ int size;
+ int in_use;
+ int max_id;
+ unsigned short seq;
+ unsigned short seq_max;
+ struct semaphore sem;
+ spinlock_t ary;
+ struct ipc_id* entries;
+};
+
+struct ipc_id {
+ struct kern_ipc_perm* p;
+};
+
+
+void __init ipc_init_ids(struct ipc_ids* ids, int size);
+
+/* must be called with ids->sem acquired.*/
+int ipc_findkey(struct ipc_ids* ids, key_t key);
+int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size);
+
+/* must be called with both locks acquired. */
+struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id);
+
+int ipcperms (struct kern_ipc_perm *ipcp, short flg);
+
+/* for rare, potentially huge allocations.
+ * both function can sleep
+ */
+void* ipc_alloc(int size);
+void ipc_free(void* ptr, int size);
+
+extern inline void ipc_lockall(struct ipc_ids* ids)
+{
+ spin_lock(&ids->ary);
+}
+
+extern inline struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
+{
+ struct kern_ipc_perm* out;
+ int lid = id % SEQ_MULTIPLIER;
+ if(lid >= ids->size)
+ return NULL;
+
+ out = ids->entries[lid].p;
+ return out;
+}
+
+extern inline void ipc_unlockall(struct ipc_ids* ids)
+{
+ spin_unlock(&ids->ary);
+}
+extern inline struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
+{
+ struct kern_ipc_perm* out;
+ int lid = id % SEQ_MULTIPLIER;
+ if(lid >= ids->size)
+ return NULL;
+
+ spin_lock(&ids->ary);
+ out = ids->entries[lid].p;
+ if(out==NULL)
+ spin_unlock(&ids->ary);
+ return out;
+}
+
+extern inline void ipc_unlock(struct ipc_ids* ids, int id)
+{
+ spin_unlock(&ids->ary);
+}
+
+extern inline int ipc_buildid(struct ipc_ids* ids, int id, int seq)
+{
+ return SEQ_MULTIPLIER*seq + id;
+}
+
+extern inline int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
+{
+ if(uid/SEQ_MULTIPLIER != ipcp->seq)
+ return 1;
+ return 0;
+}
+
+void kernel_to_ipc64_perm(struct kern_ipc_perm *in, struct ipc64_perm *out);
+void ipc64_perm_to_ipc_perm(struct ipc64_perm *in, struct ipc_perm *out);
+
+#if defined(__ia64__) || defined(__hppa__)
+ /* On IA-64 and PA-RISC, we always use the "64-bit version" of the IPC structures. */
+# define ipc_parse_version(cmd) IPC_64
+#else
+int ipc_parse_version (int *cmd);
+#endif