diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /ipc |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'ipc')
-rw-r--r-- | ipc/Makefile | 9 | ||||
-rw-r--r-- | ipc/compat.c | 687 | ||||
-rw-r--r-- | ipc/compat_mq.c | 146 | ||||
-rw-r--r-- | ipc/mqueue.c | 1252 | ||||
-rw-r--r-- | ipc/msg.c | 862 | ||||
-rw-r--r-- | ipc/msgutil.c | 127 | ||||
-rw-r--r-- | ipc/sem.c | 1384 | ||||
-rw-r--r-- | ipc/shm.c | 917 | ||||
-rw-r--r-- | ipc/util.c | 580 | ||||
-rw-r--r-- | ipc/util.h | 81 |
10 files changed, 6045 insertions, 0 deletions
diff --git a/ipc/Makefile b/ipc/Makefile new file mode 100644 index 00000000000..0a6d626cd79 --- /dev/null +++ b/ipc/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the linux ipc. +# + +obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o +obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o +obj_mq-$(CONFIG_COMPAT) += compat_mq.o +obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y) + diff --git a/ipc/compat.c b/ipc/compat.c new file mode 100644 index 00000000000..70e4e4e10fd --- /dev/null +++ b/ipc/compat.c @@ -0,0 +1,687 @@ +/* + * 32 bit compatibility code for System V IPC + * + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com> + * Copyright (C) 2000 VA Linux Co + * Copyright (C) 2000 Don Dugger <n0ano@valinux.com> + * Copyright (C) 2000 Hewlett-Packard Co. + * Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com> + * Copyright (C) 2000 Gerhard Tonn (ton@de.ibm.com) + * Copyright (C) 2000-2002 Andi Kleen, SuSE Labs (x86-64 port) + * Copyright (C) 2000 Silicon Graphics, Inc. + * Copyright (C) 2001 IBM + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 2004 Arnd Bergmann (arnd@arndb.de) + * + * This code is collected from the versions for sparc64, mips64, s390x, ia64, + * ppc64 and x86_64, all of which are based on the original sparc64 version + * by Jakub Jelinek. + * + */ +#include <linux/compat.h> +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/highuid.h> +#include <linux/init.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/slab.h> +#include <linux/syscalls.h> + +#include <asm/semaphore.h> +#include <asm/uaccess.h> + +#include "util.h" + +struct compat_msgbuf { + compat_long_t mtype; + char mtext[1]; +}; + +struct compat_ipc_perm { + key_t key; + compat_uid_t uid; + compat_gid_t gid; + compat_uid_t cuid; + compat_gid_t cgid; + compat_mode_t mode; + unsigned short seq; +}; + +struct compat_semid_ds { + struct compat_ipc_perm sem_perm; + compat_time_t sem_otime; + compat_time_t sem_ctime; + compat_uptr_t sem_base; + compat_uptr_t sem_pending; + compat_uptr_t sem_pending_last; + compat_uptr_t undo; + unsigned short sem_nsems; +}; + +struct compat_msqid_ds { + struct compat_ipc_perm msg_perm; + compat_uptr_t msg_first; + compat_uptr_t msg_last; + compat_time_t msg_stime; + compat_time_t msg_rtime; + compat_time_t msg_ctime; + compat_ulong_t msg_lcbytes; + compat_ulong_t msg_lqbytes; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + compat_ipc_pid_t msg_lspid; + compat_ipc_pid_t msg_lrpid; +}; + +struct compat_shmid_ds { + struct compat_ipc_perm shm_perm; + int shm_segsz; + compat_time_t shm_atime; + compat_time_t shm_dtime; + compat_time_t shm_ctime; + compat_ipc_pid_t shm_cpid; + compat_ipc_pid_t shm_lpid; + unsigned short shm_nattch; + unsigned short shm_unused; + compat_uptr_t shm_unused2; + compat_uptr_t shm_unused3; +}; + +struct compat_ipc_kludge { + compat_uptr_t msgp; + compat_long_t msgtyp; +}; + +struct compat_shminfo64 { + compat_ulong_t shmmax; + compat_ulong_t shmmin; + compat_ulong_t shmmni; + compat_ulong_t shmseg; + compat_ulong_t shmall; + compat_ulong_t __unused1; + compat_ulong_t __unused2; + compat_ulong_t __unused3; + compat_ulong_t __unused4; +}; + +struct compat_shm_info { + compat_int_t used_ids; + compat_ulong_t shm_tot, shm_rss, shm_swp; + compat_ulong_t swap_attempts, swap_successes; +}; + +extern int sem_ctls[]; +#define sc_semopm (sem_ctls[2]) +#define MAXBUF (64*1024) + +static inline int compat_ipc_parse_version(int *cmd) +{ + int version = *cmd & IPC_64; + + /* this is tricky: architectures that have support for the old + * ipc structures in 64 bit binaries need to have IPC_64 set + * in cmd, the others need to have it cleared */ +#ifndef ipc_parse_version + *cmd |= IPC_64; +#else + *cmd &= ~IPC_64; +#endif + return version; +} + +static inline int __get_compat_ipc64_perm(struct ipc64_perm *p64, + struct compat_ipc64_perm __user *up64) +{ + int err; + + err = __get_user(p64->uid, &up64->uid); + err |= __get_user(p64->gid, &up64->gid); + err |= __get_user(p64->mode, &up64->mode); + return err; +} + +static inline int __get_compat_ipc_perm(struct ipc64_perm *p, + struct compat_ipc_perm __user *up) +{ + int err; + + err = __get_user(p->uid, &up->uid); + err |= __get_user(p->gid, &up->gid); + err |= __get_user(p->mode, &up->mode); + return err; +} + +static inline int __put_compat_ipc64_perm(struct ipc64_perm *p64, + struct compat_ipc64_perm __user *up64) +{ + int err; + + err = __put_user(p64->key, &up64->key); + err |= __put_user(p64->uid, &up64->uid); + err |= __put_user(p64->gid, &up64->gid); + err |= __put_user(p64->cuid, &up64->cuid); + err |= __put_user(p64->cgid, &up64->cgid); + err |= __put_user(p64->mode, &up64->mode); + err |= __put_user(p64->seq, &up64->seq); + return err; +} + +static inline int __put_compat_ipc_perm(struct ipc64_perm *p, + struct compat_ipc_perm __user *up) +{ + int err; + compat_uid_t u; + compat_gid_t g; + + err = __put_user(p->key, &up->key); + SET_UID(u, p->uid); + err |= __put_user(u, &up->uid); + SET_GID(g, p->gid); + err |= __put_user(g, &up->gid); + SET_UID(u, p->cuid); + err |= __put_user(u, &up->cuid); + SET_GID(g, p->cgid); + err |= __put_user(g, &up->cgid); + err |= __put_user(p->mode, &up->mode); + err |= __put_user(p->seq, &up->seq); + return err; +} + +static inline int get_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds __user *up64) +{ + if (!access_ok (VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + return __get_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); +} + +static inline int get_compat_semid_ds(struct semid64_ds *s, + struct compat_semid_ds __user *up) +{ + if (!access_ok (VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->sem_perm, &up->sem_perm); +} + +static inline int put_compat_semid64_ds(struct semid64_ds *s64, + struct compat_semid64_ds __user *up64) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->sem_perm, &up64->sem_perm); + err |= __put_user(s64->sem_otime, &up64->sem_otime); + err |= __put_user(s64->sem_ctime, &up64->sem_ctime); + err |= __put_user(s64->sem_nsems, &up64->sem_nsems); + return err; +} + +static inline int put_compat_semid_ds(struct semid64_ds *s, + struct compat_semid_ds __user *up) +{ + int err; + + if (!access_ok (VERIFY_WRITE, up, sizeof(*up))) + err = -EFAULT; + err = __put_compat_ipc_perm(&s->sem_perm, &up->sem_perm); + err |= __put_user(s->sem_otime, &up->sem_otime); + err |= __put_user(s->sem_ctime, &up->sem_ctime); + err |= __put_user(s->sem_nsems, &up->sem_nsems); + return err; +} + +long compat_sys_semctl(int first, int second, int third, void __user *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + struct semid64_ds s64; + struct semid64_ds __user *up64; + int version = compat_ipc_parse_version(&third); + + if (!uptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) uptr)) + return -EFAULT; + if ((third & (~IPC_64)) == SETVAL) + fourth.val = (int) pad; + else + fourth.__pad = compat_ptr(pad); + switch (third & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETVAL: + case SETALL: + err = sys_semctl(first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + up64 = compat_alloc_user_space(sizeof(s64)); + fourth.__pad = up64; + err = sys_semctl(first, second, third, fourth); + if (err < 0) + break; + if (copy_from_user(&s64, up64, sizeof(s64))) + err2 = -EFAULT; + else if (version == IPC_64) + err2 = put_compat_semid64_ds(&s64, compat_ptr(pad)); + else + err2 = put_compat_semid_ds(&s64, compat_ptr(pad)); + if (err2) + err = -EFAULT; + break; + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_semid64_ds(&s64, compat_ptr(pad)); + } else { + err = get_compat_semid_ds(&s64, compat_ptr(pad)); + } + up64 = compat_alloc_user_space(sizeof(s64)); + if (copy_to_user(up64, &s64, sizeof(s64))) + err = -EFAULT; + if (err) + break; + + fourth.__pad = up64; + err = sys_semctl(first, second, third, fourth); + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) +{ + struct msgbuf __user *p; + struct compat_msgbuf __user *up = uptr; + long type; + + if (first < 0) + return -EINVAL; + if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) + return -EINVAL; + + p = compat_alloc_user_space(second + sizeof(struct msgbuf)); + if (get_user(type, &up->mtype) || + put_user(type, &p->mtype) || + copy_in_user(p->mtext, up->mtext, second)) + return -EFAULT; + + return sys_msgsnd(first, p, second, third); +} + +long compat_sys_msgrcv(int first, int second, int msgtyp, int third, + int version, void __user *uptr) +{ + struct msgbuf __user *p; + struct compat_msgbuf __user *up; + long type; + int err; + + if (first < 0) + return -EINVAL; + if (second < 0 || (second >= MAXBUF - sizeof(struct msgbuf))) + return -EINVAL; + + if (!version) { + struct compat_ipc_kludge ipck; + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uptr, sizeof(ipck))) + goto out; + uptr = compat_ptr(ipck.msgp); + msgtyp = ipck.msgtyp; + } + p = compat_alloc_user_space(second + sizeof(struct msgbuf)); + err = sys_msgrcv(first, p, second, msgtyp, third); + if (err < 0) + goto out; + up = uptr; + if (get_user(type, &p->mtype) || + put_user(type, &up->mtype) || + copy_in_user(up->mtext, p->mtext, err)) + err = -EFAULT; +out: + return err; +} + +static inline int get_compat_msqid64(struct msqid64_ds *m64, + struct compat_msqid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + err = __get_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __get_user(m64->msg_qbytes, &up64->msg_qbytes); + return err; +} + +static inline int get_compat_msqid(struct msqid64_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + err = __get_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __get_user(m->msg_qbytes, &up->msg_qbytes); + return err; +} + +static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, + struct compat_msqid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __put_user(m64->msg_stime, &up64->msg_stime); + err |= __put_user(m64->msg_rtime, &up64->msg_rtime); + err |= __put_user(m64->msg_ctime, &up64->msg_ctime); + err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); + err |= __put_user(m64->msg_qnum, &up64->msg_qnum); + err |= __put_user(m64->msg_qbytes, &up64->msg_qbytes); + err |= __put_user(m64->msg_lspid, &up64->msg_lspid); + err |= __put_user(m64->msg_lrpid, &up64->msg_lrpid); + return err; +} + +static inline int put_compat_msqid_ds(struct msqid64_ds *m, + struct compat_msqid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&m->msg_perm, &up->msg_perm); + err |= __put_user(m->msg_stime, &up->msg_stime); + err |= __put_user(m->msg_rtime, &up->msg_rtime); + err |= __put_user(m->msg_ctime, &up->msg_ctime); + err |= __put_user(m->msg_cbytes, &up->msg_cbytes); + err |= __put_user(m->msg_qnum, &up->msg_qnum); + err |= __put_user(m->msg_qbytes, &up->msg_qbytes); + err |= __put_user(m->msg_lspid, &up->msg_lspid); + err |= __put_user(m->msg_lrpid, &up->msg_lrpid); + return err; +} + +long compat_sys_msgctl(int first, int second, void __user *uptr) +{ + int err, err2; + struct msqid64_ds m64; + int version = compat_ipc_parse_version(&second); + void __user *p; + + switch (second & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl(first, second, uptr); + break; + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_msqid64(&m64, uptr); + } else { + err = get_compat_msqid(&m64, uptr); + } + if (err) + break; + p = compat_alloc_user_space(sizeof(m64)); + if (copy_to_user(p, &m64, sizeof(m64))) + err = -EFAULT; + else + err = sys_msgctl(first, second, p); + break; + + case IPC_STAT: + case MSG_STAT: + p = compat_alloc_user_space(sizeof(m64)); + err = sys_msgctl(first, second, p); + if (err < 0) + break; + if (copy_from_user(&m64, p, sizeof(m64))) + err2 = -EFAULT; + else if (version == IPC_64) + err2 = put_compat_msqid64_ds(&m64, uptr); + else + err2 = put_compat_msqid_ds(&m64, uptr); + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_shmat(int first, int second, compat_uptr_t third, int version, + void __user *uptr) +{ + int err; + unsigned long raddr; + compat_ulong_t __user *uaddr; + + if (version == 1) + return -EINVAL; + err = do_shmat(first, uptr, second, &raddr); + if (err < 0) + return err; + uaddr = compat_ptr(third); + return put_user(raddr, uaddr); +} + +static inline int get_compat_shmid64_ds(struct shmid64_ds *s64, + struct compat_shmid64_ds __user *up64) +{ + if (!access_ok(VERIFY_READ, up64, sizeof(*up64))) + return -EFAULT; + return __get_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); +} + +static inline int get_compat_shmid_ds(struct shmid64_ds *s, + struct compat_shmid_ds __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(*up))) + return -EFAULT; + return __get_compat_ipc_perm(&s->shm_perm, &up->shm_perm); +} + +static inline int put_compat_shmid64_ds(struct shmid64_ds *s64, + struct compat_shmid64_ds __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_compat_ipc64_perm(&s64->shm_perm, &up64->shm_perm); + err |= __put_user(s64->shm_atime, &up64->shm_atime); + err |= __put_user(s64->shm_dtime, &up64->shm_dtime); + err |= __put_user(s64->shm_ctime, &up64->shm_ctime); + err |= __put_user(s64->shm_segsz, &up64->shm_segsz); + err |= __put_user(s64->shm_nattch, &up64->shm_nattch); + err |= __put_user(s64->shm_cpid, &up64->shm_cpid); + err |= __put_user(s64->shm_lpid, &up64->shm_lpid); + return err; +} + +static inline int put_compat_shmid_ds(struct shmid64_ds *s, + struct compat_shmid_ds __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_compat_ipc_perm(&s->shm_perm, &up->shm_perm); + err |= __put_user(s->shm_atime, &up->shm_atime); + err |= __put_user(s->shm_dtime, &up->shm_dtime); + err |= __put_user(s->shm_ctime, &up->shm_ctime); + err |= __put_user(s->shm_segsz, &up->shm_segsz); + err |= __put_user(s->shm_nattch, &up->shm_nattch); + err |= __put_user(s->shm_cpid, &up->shm_cpid); + err |= __put_user(s->shm_lpid, &up->shm_lpid); + return err; +} + +static inline int put_compat_shminfo64(struct shminfo64 *smi, + struct compat_shminfo64 __user *up64) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) + return -EFAULT; + err = __put_user(smi->shmmax, &up64->shmmax); + err |= __put_user(smi->shmmin, &up64->shmmin); + err |= __put_user(smi->shmmni, &up64->shmmni); + err |= __put_user(smi->shmseg, &up64->shmseg); + err |= __put_user(smi->shmall, &up64->shmall); + return err; +} + +static inline int put_compat_shminfo(struct shminfo64 *smi, + struct shminfo __user *up) +{ + int err; + + if (!access_ok(VERIFY_WRITE, up, sizeof(*up))) + return -EFAULT; + err = __put_user(smi->shmmax, &up->shmmax); + err |= __put_user(smi->shmmin, &up->shmmin); + err |= __put_user(smi->shmmni, &up->shmmni); + err |= __put_user(smi->shmseg, &up->shmseg); + err |= __put_user(smi->shmall, &up->shmall); +} + +static inline int put_compat_shm_info(struct shm_info __user *ip, + struct compat_shm_info __user *uip) +{ + int err; + struct shm_info si; + + if (!access_ok(VERIFY_WRITE, uip, sizeof(*uip)) || + copy_from_user(&si, ip, sizeof(si))) + return -EFAULT; + err = __put_user(si.used_ids, &uip->used_ids); + err |= __put_user(si.shm_tot, &uip->shm_tot); + err |= __put_user(si.shm_rss, &uip->shm_rss); + err |= __put_user(si.shm_swp, &uip->shm_swp); + err |= __put_user(si.swap_attempts, &uip->swap_attempts); + err |= __put_user(si.swap_successes, &uip->swap_successes); + return err; +} + +long compat_sys_shmctl(int first, int second, void __user *uptr) +{ + void __user *p; + struct shmid64_ds s64; + struct shminfo64 smi; + int err, err2; + int version = compat_ipc_parse_version(&second); + + switch (second & (~IPC_64)) { + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl(first, second, uptr); + break; + + case IPC_INFO: + p = compat_alloc_user_space(sizeof(smi)); + err = sys_shmctl(first, second, p); + if (err < 0) + break; + if (copy_from_user(&smi, p, sizeof(smi))) + err2 = -EFAULT; + else if (version == IPC_64) + err2 = put_compat_shminfo64(&smi, uptr); + else + err2 = put_compat_shminfo(&smi, uptr); + if (err2) + err = -EFAULT; + break; + + + case IPC_SET: + if (version == IPC_64) { + err = get_compat_shmid64_ds(&s64, uptr); + } else { + err = get_compat_shmid_ds(&s64, uptr); + } + if (err) + break; + p = compat_alloc_user_space(sizeof(s64)); + if (copy_to_user(p, &s64, sizeof(s64))) + err = -EFAULT; + else + err = sys_shmctl(first, second, p); + break; + + case IPC_STAT: + case SHM_STAT: + p = compat_alloc_user_space(sizeof(s64)); + err = sys_shmctl(first, second, p); + if (err < 0) + break; + if (copy_from_user(&s64, p, sizeof(s64))) + err2 = -EFAULT; + else if (version == IPC_64) + err2 = put_compat_shmid64_ds(&s64, uptr); + else + err2 = put_compat_shmid_ds(&s64, uptr); + if (err2) + err = -EFAULT; + break; + + case SHM_INFO: + p = compat_alloc_user_space(sizeof(struct shm_info)); + err = sys_shmctl(first, second, p); + if (err < 0) + break; + err2 = put_compat_shm_info(p, uptr); + if (err2) + err = -EFAULT; + break; + + default: + err = -EINVAL; + break; + } + return err; +} + +long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsops, const struct compat_timespec __user *timeout) +{ + struct timespec __user *ts64 = NULL; + if (timeout) { + struct timespec ts; + ts64 = compat_alloc_user_space(sizeof(*ts64)); + if (get_compat_timespec(&ts, timeout)) + return -EFAULT; + if (copy_to_user(ts64, &ts, sizeof(ts))) + return -EFAULT; + } + return sys_semtimedop(semid, tsems, nsops, ts64); +} diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c new file mode 100644 index 00000000000..d8d1e9ff4e8 --- /dev/null +++ b/ipc/compat_mq.c @@ -0,0 +1,146 @@ +/* + * ipc/compat_mq.c + * 32 bit emulation for POSIX message queue system calls + * + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author: Arnd Bergmann <arnd@arndb.de> + */ + +#include <linux/compat.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/mqueue.h> +#include <linux/syscalls.h> + +#include <asm/uaccess.h> + +struct compat_mq_attr { + compat_long_t mq_flags; /* message queue flags */ + compat_long_t mq_maxmsg; /* maximum number of messages */ + compat_long_t mq_msgsize; /* maximum message size */ + compat_long_t mq_curmsgs; /* number of messages currently queued */ + compat_long_t __reserved[4]; /* ignored for input, zeroed for output */ +}; + +static inline int get_compat_mq_attr(struct mq_attr *attr, + const struct compat_mq_attr __user *uattr) +{ + if (!access_ok(VERIFY_READ, uattr, sizeof *uattr)) + return -EFAULT; + + return __get_user(attr->mq_flags, &uattr->mq_flags) + | __get_user(attr->mq_maxmsg, &uattr->mq_maxmsg) + | __get_user(attr->mq_msgsize, &uattr->mq_msgsize) + | __get_user(attr->mq_curmsgs, &uattr->mq_curmsgs); +} + +static inline int put_compat_mq_attr(const struct mq_attr *attr, + struct compat_mq_attr __user *uattr) +{ + if (clear_user(uattr, sizeof *uattr)) + return -EFAULT; + + return __put_user(attr->mq_flags, &uattr->mq_flags) + | __put_user(attr->mq_maxmsg, &uattr->mq_maxmsg) + | __put_user(attr->mq_msgsize, &uattr->mq_msgsize) + | __put_user(attr->mq_curmsgs, &uattr->mq_curmsgs); +} + +asmlinkage long compat_sys_mq_open(const char __user *u_name, + int oflag, compat_mode_t mode, + struct compat_mq_attr __user *u_attr) +{ + void __user *p = NULL; + if (u_attr && oflag & O_CREAT) { + struct mq_attr attr; + p = compat_alloc_user_space(sizeof(attr)); + if (get_compat_mq_attr(&attr, u_attr) || + copy_to_user(p, &attr, sizeof(attr))) + return -EFAULT; + } + return sys_mq_open(u_name, oflag, mode, p); +} + +static int compat_prepare_timeout(struct timespec __user * *p, + const struct compat_timespec __user *u) +{ + struct timespec ts; + if (!u) { + *p = NULL; + return 0; + } + *p = compat_alloc_user_space(sizeof(ts)); + if (get_compat_timespec(&ts, u) || copy_to_user(*p, &ts, sizeof(ts))) + return -EFAULT; + return 0; +} + +asmlinkage long compat_sys_mq_timedsend(mqd_t mqdes, + const char __user *u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + const struct compat_timespec __user *u_abs_timeout) +{ + struct timespec __user *u_ts; + + if (compat_prepare_timeout(&u_ts, u_abs_timeout)) + return -EFAULT; + + return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len, + msg_prio, u_ts); +} + +asmlinkage ssize_t compat_sys_mq_timedreceive(mqd_t mqdes, + char __user *u_msg_ptr, + size_t msg_len, unsigned int __user *u_msg_prio, + const struct compat_timespec __user *u_abs_timeout) +{ + struct timespec __user *u_ts; + if (compat_prepare_timeout(&u_ts, u_abs_timeout)) + return -EFAULT; + + return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len, + u_msg_prio, u_ts); +} + +asmlinkage long compat_sys_mq_notify(mqd_t mqdes, + const struct compat_sigevent __user *u_notification) +{ + struct sigevent __user *p = NULL; + if (u_notification) { + struct sigevent n; + p = compat_alloc_user_space(sizeof(*p)); + if (get_compat_sigevent(&n, u_notification)) + return -EFAULT; + if (n.sigev_notify == SIGEV_THREAD) + n.sigev_value.sival_ptr = compat_ptr(n.sigev_value.sival_int); + if (copy_to_user(p, &n, sizeof(*p))) + return -EFAULT; + } + return sys_mq_notify(mqdes, p); +} + +asmlinkage long compat_sys_mq_getsetattr(mqd_t mqdes, + const struct compat_mq_attr __user *u_mqstat, + struct compat_mq_attr __user *u_omqstat) +{ + struct mq_attr mqstat; + struct mq_attr __user *p = compat_alloc_user_space(2 * sizeof(*p)); + long ret; + + if (u_mqstat) { + if (get_compat_mq_attr(&mqstat, u_mqstat) || + copy_to_user(p, &mqstat, sizeof(mqstat))) + return -EFAULT; + } + ret = sys_mq_getsetattr(mqdes, + u_mqstat ? p : NULL, + u_omqstat ? p + 1 : NULL); + if (ret) + return ret; + if (u_omqstat) { + if (copy_from_user(&mqstat, p + 1, sizeof(mqstat)) || + put_compat_mq_attr(&mqstat, u_omqstat)) + return -EFAULT; + } + return 0; +} diff --git a/ipc/mqueue.c b/ipc/mqueue.c new file mode 100644 index 00000000000..cb0cd3cf3b5 --- /dev/null +++ b/ipc/mqueue.c @@ -0,0 +1,1252 @@ +/* + * POSIX message queues filesystem for Linux. + * + * Copyright (C) 2003,2004 Krzysztof Benedyczak (golbi@mat.uni.torun.pl) + * Michal Wronski (wrona@mat.uni.torun.pl) + * + * Spinlocks: Mohamed Abbas (abbas.mohamed@intel.com) + * Lockless receive & send, fd based notify: + * Manfred Spraul (manfred@colorfullife.com) + * + * This file is released under the GPL. + */ + +#include <linux/init.h> +#include <linux/pagemap.h> +#include <linux/file.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/sysctl.h> +#include <linux/poll.h> +#include <linux/mqueue.h> +#include <linux/msg.h> +#include <linux/skbuff.h> +#include <linux/netlink.h> +#include <linux/syscalls.h> +#include <net/sock.h> +#include "util.h" + +#define MQUEUE_MAGIC 0x19800202 +#define DIRENT_SIZE 20 +#define FILENT_SIZE 80 + +#define SEND 0 +#define RECV 1 + +#define STATE_NONE 0 +#define STATE_PENDING 1 +#define STATE_READY 2 + +/* used by sysctl */ +#define FS_MQUEUE 1 +#define CTL_QUEUESMAX 2 +#define CTL_MSGMAX 3 +#define CTL_MSGSIZEMAX 4 + +/* default values */ +#define DFLT_QUEUESMAX 256 /* max number of message queues */ +#define DFLT_MSGMAX 10 /* max number of messages in each queue */ +#define HARD_MSGMAX (131072/sizeof(void*)) +#define DFLT_MSGSIZEMAX 8192 /* max message size */ + +#define NOTIFY_COOKIE_LEN 32 + +struct ext_wait_queue { /* queue of sleeping tasks */ + struct task_struct *task; + struct list_head list; + struct msg_msg *msg; /* ptr of loaded message */ + int state; /* one of STATE_* values */ +}; + +struct mqueue_inode_info { + spinlock_t lock; + struct inode vfs_inode; + wait_queue_head_t wait_q; + + struct msg_msg **messages; + struct mq_attr attr; + + struct sigevent notify; + pid_t notify_owner; + struct user_struct *user; /* user who created, for accouting */ + struct sock *notify_sock; + struct sk_buff *notify_cookie; + + /* for tasks waiting for free space and messages, respectively */ + struct ext_wait_queue e_wait_q[2]; + + unsigned long qsize; /* size of queue in memory (sum of all msgs) */ +}; + +static struct inode_operations mqueue_dir_inode_operations; +static struct file_operations mqueue_file_operations; +static struct super_operations mqueue_super_ops; +static void remove_notification(struct mqueue_inode_info *info); + +static spinlock_t mq_lock; +static kmem_cache_t *mqueue_inode_cachep; +static struct vfsmount *mqueue_mnt; + +static unsigned int queues_count; +static unsigned int queues_max = DFLT_QUEUESMAX; +static unsigned int msg_max = DFLT_MSGMAX; +static unsigned int msgsize_max = DFLT_MSGSIZEMAX; + +static struct ctl_table_header * mq_sysctl_table; + +static inline struct mqueue_inode_info *MQUEUE_I(struct inode *inode) +{ + return container_of(inode, struct mqueue_inode_info, vfs_inode); +} + +static struct inode *mqueue_get_inode(struct super_block *sb, int mode, + struct mq_attr *attr) +{ + struct inode *inode; + + inode = new_inode(sb); + if (inode) { + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_mtime = inode->i_ctime = inode->i_atime = + CURRENT_TIME; + + if (S_ISREG(mode)) { + struct mqueue_inode_info *info; + struct task_struct *p = current; + struct user_struct *u = p->user; + unsigned long mq_bytes, mq_msg_tblsz; + + inode->i_fop = &mqueue_file_operations; + inode->i_size = FILENT_SIZE; + /* mqueue specific info */ + info = MQUEUE_I(inode); + spin_lock_init(&info->lock); + init_waitqueue_head(&info->wait_q); + INIT_LIST_HEAD(&info->e_wait_q[0].list); + INIT_LIST_HEAD(&info->e_wait_q[1].list); + info->messages = NULL; + info->notify_owner = 0; + info->qsize = 0; + info->user = NULL; /* set when all is ok */ + memset(&info->attr, 0, sizeof(info->attr)); + info->attr.mq_maxmsg = DFLT_MSGMAX; + info->attr.mq_msgsize = DFLT_MSGSIZEMAX; + if (attr) { + info->attr.mq_maxmsg = attr->mq_maxmsg; + info->attr.mq_msgsize = attr->mq_msgsize; + } + mq_msg_tblsz = info->attr.mq_maxmsg * sizeof(struct msg_msg *); + mq_bytes = (mq_msg_tblsz + + (info->attr.mq_maxmsg * info->attr.mq_msgsize)); + + spin_lock(&mq_lock); + if (u->mq_bytes + mq_bytes < u->mq_bytes || + u->mq_bytes + mq_bytes > + p->signal->rlim[RLIMIT_MSGQUEUE].rlim_cur) { + spin_unlock(&mq_lock); + goto out_inode; + } + u->mq_bytes += mq_bytes; + spin_unlock(&mq_lock); + + info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); + if (!info->messages) { + spin_lock(&mq_lock); + u->mq_bytes -= mq_bytes; + spin_unlock(&mq_lock); + goto out_inode; + } + /* all is ok */ + info->user = get_uid(u); + } else if (S_ISDIR(mode)) { + inode->i_nlink++; + /* Some things misbehave if size == 0 on a directory */ + inode->i_size = 2 * DIRENT_SIZE; + inode->i_op = &mqueue_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + } + } + return inode; +out_inode: + make_bad_inode(inode); + iput(inode); + return NULL; +} + +static int mqueue_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *inode; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = MQUEUE_MAGIC; + sb->s_op = &mqueue_super_ops; + + inode = mqueue_get_inode(sb, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL); + if (!inode) + return -ENOMEM; + + sb->s_root = d_alloc_root(inode); + if (!sb->s_root) { + iput(inode); + return -ENOMEM; + } + + return 0; +} + +static struct super_block *mqueue_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(fs_type, flags, data, mqueue_fill_super); +} + +static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&p->vfs_inode); +} + +static struct inode *mqueue_alloc_inode(struct super_block *sb) +{ + struct mqueue_inode_info *ei; + + ei = kmem_cache_alloc(mqueue_inode_cachep, SLAB_KERNEL); + if (!ei) + return NULL; + return &ei->vfs_inode; +} + +static void mqueue_destroy_inode(struct inode *inode) +{ + |