diff options
Diffstat (limited to 'ANDROID_3.4.5/ipc')
-rw-r--r-- | ANDROID_3.4.5/ipc/Makefile | 12 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/compat.c | 745 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/compat_mq.c | 151 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/ipc_sysctl.c | 248 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/ipcns_notifier.c | 92 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/mq_sysctl.c | 115 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/mqueue.c | 1302 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/msg.c | 944 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/msgutil.c | 144 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/namespace.c | 179 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/sem.c | 1695 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/shm.c | 1237 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/syscall.c | 99 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/util.c | 987 | ||||
-rw-r--r-- | ANDROID_3.4.5/ipc/util.h | 178 |
15 files changed, 0 insertions, 8128 deletions
diff --git a/ANDROID_3.4.5/ipc/Makefile b/ANDROID_3.4.5/ipc/Makefile deleted file mode 100644 index 9075e172..00000000 --- a/ANDROID_3.4.5/ipc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for the linux ipc. -# - -obj-$(CONFIG_SYSVIPC_COMPAT) += compat.o -obj-$(CONFIG_SYSVIPC) += util.o msgutil.o msg.o sem.o shm.o ipcns_notifier.o syscall.o -obj-$(CONFIG_SYSVIPC_SYSCTL) += ipc_sysctl.o -obj_mq-$(CONFIG_COMPAT) += compat_mq.o -obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y) -obj-$(CONFIG_IPC_NS) += namespace.o -obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o - diff --git a/ANDROID_3.4.5/ipc/compat.c b/ANDROID_3.4.5/ipc/compat.c deleted file mode 100644 index a6df704f..00000000 --- a/ANDROID_3.4.5/ipc/compat.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * 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/errno.h> -#include <linux/highuid.h> -#include <linux/init.h> -#include <linux/msg.h> -#include <linux/shm.h> -#include <linux/syscalls.h> -#include <linux/ptrace.h> - -#include <linux/mutex.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]) - -static inline int compat_ipc_parse_version(int *cmd) -{ -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC - 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; -#else - /* With the asm-generic APIs, we always use the 64-bit versions. */ - return IPC_64; -#endif -} - -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))) - return -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; -} - -static long do_compat_semctl(int first, int second, int third, u32 pad) -{ - union semun fourth; - int err, err2; - struct semid64_ds s64; - struct semid64_ds __user *up64; - int version = compat_ipc_parse_version(&third); - - memset(&s64, 0, sizeof(s64)); - - 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; -} - -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -long compat_sys_semctl(int first, int second, int third, void __user *uptr) -{ - u32 pad; - - if (!uptr) - return -EINVAL; - if (get_user(pad, (u32 __user *) uptr)) - return -EFAULT; - return do_compat_semctl(first, second, third, pad); -} - -long compat_sys_msgsnd(int first, int second, int third, void __user *uptr) -{ - struct compat_msgbuf __user *up = uptr; - long type; - - if (first < 0) - return -EINVAL; - if (second < 0) - return -EINVAL; - - if (get_user(type, &up->mtype)) - return -EFAULT; - - return do_msgsnd(first, type, up->mtext, second, third); -} - -long compat_sys_msgrcv(int first, int second, int msgtyp, int third, - int version, void __user *uptr) -{ - struct compat_msgbuf __user *up; - long type; - int err; - - if (first < 0) - return -EINVAL; - if (second < 0) - 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; - } - up = uptr; - err = do_msgrcv(first, &type, up->mtext, second, msgtyp, third); - if (err < 0) - goto out; - if (put_user(type, &up->mtype)) - err = -EFAULT; -out: - return err; -} -#else -long compat_sys_semctl(int semid, int semnum, int cmd, int arg) -{ - return do_compat_semctl(semid, semnum, cmd, arg); -} - -long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, int msgflg) -{ - compat_long_t mtype; - - if (get_user(mtype, &msgp->mtype)) - return -EFAULT; - return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); -} - -long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp, - size_t msgsz, long msgtyp, int msgflg) -{ - long err, mtype; - - err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); - if (err < 0) - goto out; - - if (put_user(mtype, &msgp->mtype)) - err = -EFAULT; - out: - return err; -} -#endif - -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; - - memset(&m64, 0, sizeof(m64)); - - 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; -} - -#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC -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); -} -#else -long compat_sys_shmat(int shmid, compat_uptr_t shmaddr, int shmflg) -{ - unsigned long ret; - long err; - - err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret); - if (err) - return err; - force_successful_syscall_return(); - return (long)ret; -} -#endif - -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; - if (smi->shmmax > INT_MAX) - smi->shmmax = INT_MAX; - 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; - if (smi->shmmax > INT_MAX) - smi->shmmax = INT_MAX; - 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); - return err; -} - -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); - - memset(&s64, 0, sizeof(s64)); - - 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/ANDROID_3.4.5/ipc/compat_mq.c b/ANDROID_3.4.5/ipc/compat_mq.c deleted file mode 100644 index 380ea4fe..00000000 --- a/ANDROID_3.4.5/ipc/compat_mq.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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; - - memset(&attr, 0, sizeof(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; - - memset(&mqstat, 0, sizeof(mqstat)); - - 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/ANDROID_3.4.5/ipc/ipc_sysctl.c b/ANDROID_3.4.5/ipc/ipc_sysctl.c deleted file mode 100644 index 00fba2ba..00000000 --- a/ANDROID_3.4.5/ipc/ipc_sysctl.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2007 - * - * Author: Eric Biederman <ebiederm@xmision.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - */ - -#include <linux/module.h> -#include <linux/ipc.h> -#include <linux/nsproxy.h> -#include <linux/sysctl.h> -#include <linux/uaccess.h> -#include <linux/ipc_namespace.h> -#include <linux/msg.h> -#include "util.h" - -static void *get_ipc(ctl_table *table) -{ - char *which = table->data; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns; - return which; -} - -#ifdef CONFIG_PROC_SYSCTL -static int proc_ipc_dointvec(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table ipc_table; - - memcpy(&ipc_table, table, sizeof(ipc_table)); - ipc_table.data = get_ipc(table); - - return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); -} - -static int proc_ipc_dointvec_minmax(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table ipc_table; - - memcpy(&ipc_table, table, sizeof(ipc_table)); - ipc_table.data = get_ipc(table); - - return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); -} - -static int proc_ipc_dointvec_minmax_orphans(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ipc_namespace *ns = current->nsproxy->ipc_ns; - int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos); - - if (err < 0) - return err; - if (ns->shm_rmid_forced) - shm_destroy_orphaned(ns); - return err; -} - -static int proc_ipc_callback_dointvec(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table ipc_table; - size_t lenp_bef = *lenp; - int rc; - - memcpy(&ipc_table, table, sizeof(ipc_table)); - ipc_table.data = get_ipc(table); - - rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos); - - if (write && !rc && lenp_bef == *lenp) - /* - * Tunable has successfully been changed by hand. Disable its - * automatic adjustment. This simply requires unregistering - * the notifiers that trigger recalculation. - */ - unregister_ipcns_notifier(current->nsproxy->ipc_ns); - - return rc; -} - -static int proc_ipc_doulongvec_minmax(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table ipc_table; - memcpy(&ipc_table, table, sizeof(ipc_table)); - ipc_table.data = get_ipc(table); - - return proc_doulongvec_minmax(&ipc_table, write, buffer, - lenp, ppos); -} - -/* - * Routine that is called when the file "auto_msgmni" has successfully been - * written. - * Two values are allowed: - * 0: unregister msgmni's callback routine from the ipc namespace notifier - * chain. This means that msgmni won't be recomputed anymore upon memory - * add/remove or ipc namespace creation/removal. - * 1: register back the callback routine. - */ -static void ipc_auto_callback(int val) -{ - if (!val) - unregister_ipcns_notifier(current->nsproxy->ipc_ns); - else { - /* - * Re-enable automatic recomputing only if not already - * enabled. - */ - recompute_msgmni(current->nsproxy->ipc_ns); - cond_register_ipcns_notifier(current->nsproxy->ipc_ns); - } -} - -static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table ipc_table; - size_t lenp_bef = *lenp; - int oldval; - int rc; - - memcpy(&ipc_table, table, sizeof(ipc_table)); - ipc_table.data = get_ipc(table); - oldval = *((int *)(ipc_table.data)); - - rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); - - if (write && !rc && lenp_bef == *lenp) { - int newval = *((int *)(ipc_table.data)); - /* - * The file "auto_msgmni" has correctly been set. - * React by (un)registering the corresponding tunable, if the - * value has changed. - */ - if (newval != oldval) - ipc_auto_callback(newval); - } - - return rc; -} - -#else -#define proc_ipc_doulongvec_minmax NULL -#define proc_ipc_dointvec NULL -#define proc_ipc_dointvec_minmax NULL -#define proc_ipc_dointvec_minmax_orphans NULL -#define proc_ipc_callback_dointvec NULL -#define proc_ipcauto_dointvec_minmax NULL -#endif - -static int zero; -static int one = 1; - -static struct ctl_table ipc_kern_table[] = { - { - .procname = "shmmax", - .data = &init_ipc_ns.shm_ctlmax, - .maxlen = sizeof (init_ipc_ns.shm_ctlmax), - .mode = 0644, - .proc_handler = proc_ipc_doulongvec_minmax, - }, - { - .procname = "shmall", - .data = &init_ipc_ns.shm_ctlall, - .maxlen = sizeof (init_ipc_ns.shm_ctlall), - .mode = 0644, - .proc_handler = proc_ipc_doulongvec_minmax, - }, - { - .procname = "shmmni", - .data = &init_ipc_ns.shm_ctlmni, - .maxlen = sizeof (init_ipc_ns.shm_ctlmni), - .mode = 0644, - .proc_handler = proc_ipc_dointvec, - }, - { - .procname = "shm_rmid_forced", - .data = &init_ipc_ns.shm_rmid_forced, - .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), - .mode = 0644, - .proc_handler = proc_ipc_dointvec_minmax_orphans, - .extra1 = &zero, - .extra2 = &one, - }, - { - .procname = "msgmax", - .data = &init_ipc_ns.msg_ctlmax, - .maxlen = sizeof (init_ipc_ns.msg_ctlmax), - .mode = 0644, - .proc_handler = proc_ipc_dointvec, - }, - { - .procname = "msgmni", - .data = &init_ipc_ns.msg_ctlmni, - .maxlen = sizeof (init_ipc_ns.msg_ctlmni), - .mode = 0644, - .proc_handler = proc_ipc_callback_dointvec, - }, - { - .procname = "msgmnb", - .data = &init_ipc_ns.msg_ctlmnb, - .maxlen = sizeof (init_ipc_ns.msg_ctlmnb), - .mode = 0644, - .proc_handler = proc_ipc_dointvec, - }, - { - .procname = "sem", - .data = &init_ipc_ns.sem_ctls, - .maxlen = 4*sizeof (int), - .mode = 0644, - .proc_handler = proc_ipc_dointvec, - }, - { - .procname = "auto_msgmni", - .data = &init_ipc_ns.auto_msgmni, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_ipcauto_dointvec_minmax, - .extra1 = &zero, - .extra2 = &one, - }, - {} -}; - -static struct ctl_table ipc_root_table[] = { - { - .procname = "kernel", - .mode = 0555, - .child = ipc_kern_table, - }, - {} -}; - -static int __init ipc_sysctl_init(void) -{ - register_sysctl_table(ipc_root_table); - return 0; -} - -__initcall(ipc_sysctl_init); diff --git a/ANDROID_3.4.5/ipc/ipcns_notifier.c b/ANDROID_3.4.5/ipc/ipcns_notifier.c deleted file mode 100644 index b9b31a4f..00000000 --- a/ANDROID_3.4.5/ipc/ipcns_notifier.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * linux/ipc/ipcns_notifier.c - * Copyright (C) 2007 BULL SA. Nadia Derbey - * - * Notification mechanism for ipc namespaces: - * The callback routine registered in the memory chain invokes the ipcns - * notifier chain with the IPCNS_MEMCHANGED event. - * Each callback routine registered in the ipcns namespace recomputes msgmni - * for the owning namespace. - */ - -#include <linux/msg.h> -#include <linux/rcupdate.h> -#include <linux/notifier.h> -#include <linux/nsproxy.h> -#include <linux/ipc_namespace.h> - -#include "util.h" - - - -static BLOCKING_NOTIFIER_HEAD(ipcns_chain); - - -static int ipcns_callback(struct notifier_block *self, - unsigned long action, void *arg) -{ - struct ipc_namespace *ns; - - switch (action) { - case IPCNS_MEMCHANGED: /* amount of lowmem has changed */ - case IPCNS_CREATED: - case IPCNS_REMOVED: - /* - * It's time to recompute msgmni - */ - ns = container_of(self, struct ipc_namespace, ipcns_nb); - /* - * No need to get a reference on the ns: the 1st job of - * free_ipc_ns() is to unregister the callback routine. - * blocking_notifier_chain_unregister takes the wr lock to do - * it. - * When this callback routine is called the rd lock is held by - * blocking_notifier_call_chain. - * So the ipc ns cannot be freed while we are here. - */ - recompute_msgmni(ns); - break; - default: - break; - } - - return NOTIFY_OK; -} - -int register_ipcns_notifier(struct ipc_namespace *ns) -{ - int rc; - - memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); - ns->ipcns_nb.notifier_call = ipcns_callback; - ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - rc = blocking_notifier_chain_register(&ipcns_chain, &ns->ipcns_nb); - if (!rc) - ns->auto_msgmni = 1; - return rc; -} - -int cond_register_ipcns_notifier(struct ipc_namespace *ns) -{ - int rc; - - memset(&ns->ipcns_nb, 0, sizeof(ns->ipcns_nb)); - ns->ipcns_nb.notifier_call = ipcns_callback; - ns->ipcns_nb.priority = IPCNS_CALLBACK_PRI; - rc = blocking_notifier_chain_cond_register(&ipcns_chain, - &ns->ipcns_nb); - if (!rc) - ns->auto_msgmni = 1; - return rc; -} - -void unregister_ipcns_notifier(struct ipc_namespace *ns) -{ - blocking_notifier_chain_unregister(&ipcns_chain, &ns->ipcns_nb); - ns->auto_msgmni = 0; -} - -int ipcns_notify(unsigned long val) -{ - return blocking_notifier_call_chain(&ipcns_chain, val, NULL); -} diff --git a/ANDROID_3.4.5/ipc/mq_sysctl.c b/ANDROID_3.4.5/ipc/mq_sysctl.c deleted file mode 100644 index 0c09366b..00000000 --- a/ANDROID_3.4.5/ipc/mq_sysctl.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2007 IBM Corporation - * - * Author: Cedric Le Goater <clg@fr.ibm.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. - */ - -#include <linux/nsproxy.h> -#include <linux/ipc_namespace.h> -#include <linux/sysctl.h> - -/* - * Define the ranges various user-specified maximum values can - * be set to. - */ -#define MIN_MSGMAX 1 /* min value for msg_max */ -#define MAX_MSGMAX HARD_MSGMAX /* max value for msg_max */ -#define MIN_MSGSIZEMAX 128 /* min value for msgsize_max */ -#define MAX_MSGSIZEMAX (8192*128) /* max value for msgsize_max */ - -#ifdef CONFIG_PROC_SYSCTL -static void *get_mq(ctl_table *table) -{ - char *which = table->data; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns; - return which; -} - -static int proc_mq_dointvec(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table mq_table; - memcpy(&mq_table, table, sizeof(mq_table)); - mq_table.data = get_mq(table); - - return proc_dointvec(&mq_table, write, buffer, lenp, ppos); -} - -static int proc_mq_dointvec_minmax(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - struct ctl_table mq_table; - memcpy(&mq_table, table, sizeof(mq_table)); - mq_table.data = get_mq(table); - - return proc_dointvec_minmax(&mq_table, write, buffer, - lenp, ppos); -} -#else -#define proc_mq_dointvec NULL -#define proc_mq_dointvec_minmax NULL -#endif - -static int msg_max_limit_min = MIN_MSGMAX; -static int msg_max_limit_max = MAX_MSGMAX; - -static int msg_maxsize_limit_min = MIN_MSGSIZEMAX; -static int msg_maxsize_limit_max = MAX_MSGSIZEMAX; - -static ctl_table mq_sysctls[] = { - { - .procname = "queues_max", - .data = &init_ipc_ns.mq_queues_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_mq_dointvec, - }, - { - .procname = "msg_max", - .data = &init_ipc_ns.mq_msg_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_mq_dointvec_minmax, - .extra1 = &msg_max_limit_min, - .extra2 = &msg_max_limit_max, - }, - { - .procname = "msgsize_max", - .data = &init_ipc_ns.mq_msgsize_max, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_mq_dointvec_minmax, - .extra1 = &msg_maxsize_limit_min, - .extra2 = &msg_maxsize_limit_max, - }, - {} -}; - -static ctl_table mq_sysctl_dir[] = { - { - .procname = "mqueue", - .mode = 0555, - .child = mq_sysctls, - }, - {} -}; - -static ctl_table mq_sysctl_root[] = { - { - .procname = "fs", - .mode = 0555, - .child = mq_sysctl_dir, - }, - {} -}; - -struct ctl_table_header *mq_register_sysctl_table(void) -{ - return register_sysctl_table(mq_sysctl_root); -} diff --git a/ANDROID_3.4.5/ipc/mqueue.c b/ANDROID_3.4.5/ipc/mqueue.c deleted file mode 100644 index 28bd64dd..00000000 --- a/ANDROID_3.4.5/ipc/mqueue.c +++ /dev/null @@ -1,1302 +0,0 @@ -/* - * POSIX message queues filesystem for Linux. - * - * Copyright (C) 2003,2004 Krzysztof Benedyczak (golbi@mat.uni.torun.pl) - * Michal Wronski (michal.wronski@gmail.com) - * - * Spinlocks: Mohamed Abbas (abbas.mohamed@intel.com) - * Lockless receive & send, fd based notify: - * Manfred Spraul (manfred@colorfullife.com) - * - * Audit: George Wilson (ltcgcw@us.ibm.com) - * - * This file is released under the GPL. - */ - -#include <linux/capability.h> -#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 <linux/audit.h> -#include <linux/signal.h> -#include <linux/mutex.h> -#include <linux/nsproxy.h> -#include <linux/pid.h> -#include <linux/ipc_namespace.h> -#include <linux/user_namespace.h> -#include <linux/slab.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 - -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; - struct pid* notify_owner; - struct user_struct *user; /* user who created, for accounting */ - 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 const struct inode_operations mqueue_dir_inode_operations; -static const struct file_operations mqueue_file_operations; -static const struct super_operations mqueue_super_ops; -static void remove_notification(struct mqueue_inode_info *info); - -static struct kmem_cache *mqueue_inode_cachep; - -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); -} - -/* - * This routine should be called with the mq_lock held. - */ -static inline struct ipc_namespace *__get_ns_from_inode(struct inode *inode) -{ - return get_ipc_ns(inode->i_sb->s_fs_info); -} - -static struct ipc_namespace *get_ns_from_inode(struct inode *inode) -{ - struct ipc_namespace *ns; - - spin_lock(&mq_lock); - ns = __get_ns_from_inode(inode); - spin_unlock(&mq_lock); - return ns; -} - -static struct inode *mqueue_get_inode(struct super_block *sb, - struct ipc_namespace *ipc_ns, umode_t mode, - struct mq_attr *attr) -{ - struct user_struct *u = current_user(); - struct inode *inode; - int ret = -ENOMEM; - - inode = new_inode(sb); - if (!inode) - goto err; - - inode->i_ino = get_next_ino(); - inode->i_mode = mode; - inode->i_uid = current_fsuid(); - inode->i_gid = current_fsgid(); - inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME; - - if (S_ISREG(mode)) { - struct mqueue_inode_info *info; - 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->notify_owner = NULL; - info->qsize = 0; - info->user = NULL; /* set when all is ok */ - memset(&info->attr, 0, sizeof(info->attr)); - info->attr.mq_maxmsg = ipc_ns->mq_msg_max; - info->attr.mq_msgsize = ipc_ns->mq_msgsize_max; - 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 *); - info->messages = kmalloc(mq_msg_tblsz, GFP_KERNEL); - if (!info->messages) - goto out_inode; - - 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 > rlimit(RLIMIT_MSGQUEUE)) { - spin_unlock(&mq_lock); - /* mqueue_evict_inode() releases info->messages */ - ret = -EMFILE; - goto out_inode; - } - u->mq_bytes += mq_bytes; - spin_unlock(&mq_lock); - - /* all is ok */ - info->user = get_uid(u); - } else if (S_ISDIR(mode)) { - inc_nlink(inode); - /* 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: - iput(inode); -err: - return ERR_PTR(ret); -} - -static int mqueue_fill_super(struct super_block *sb, void *data, int silent) -{ - struct inode *inode; - struct ipc_namespace *ns = data; - - 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, ns, S_IFDIR | S_ISVTX | S_IRWXUGO, NULL); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - sb->s_root = d_make_root(inode); - if (!sb->s_root) - return -ENOMEM; - return 0; -} - -static struct dentry *mqueue_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) -{ - if (!(flags & MS_KERNMOUNT)) - data = current->nsproxy->ipc_ns; - return mount_ns(fs_type, flags, data, mqueue_fill_super); -} - -static void init_once(void *foo) -{ - struct mqueue_inode_info *p = (struct mqueue_inode_info *) foo; - - 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, GFP_KERNEL); - if (!ei) - return NULL; - return &ei->vfs_inode; -} - -static void mqueue_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(mqueue_inode_cachep, MQUEUE_I(inode)); -} - -static void mqueue_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, mqueue_i_callback); -} - -static void mqueue_evict_inode(struct inode *inode) -{ - struct mqueue_inode_info *info; - struct user_struct *user; - unsigned long mq_bytes; - int i; - struct ipc_namespace *ipc_ns; - - end_writeback(inode); - - if (S_ISDIR(inode->i_mode)) - return; - - ipc_ns = get_ns_from_inode(inode); - info = MQUEUE_I(inode); - spin_lock(&info->lock); - for (i = 0; i < info->attr.mq_curmsgs; i++) - free_msg(info->messages[i]); - kfree(info->messages); - spin_unlock(&info->lock); - - /* Total amount of bytes accounted for the mqueue */ - mq_bytes = info->attr.mq_maxmsg * (sizeof(struct msg_msg *) - + info->attr.mq_msgsize); - user = info->user; - if (user) { - spin_lock(&mq_lock); - user->mq_bytes -= mq_bytes; - /* - * get_ns_from_inode() ensures that the - * (ipc_ns = sb->s_fs_info) is either a valid ipc_ns - * to which we now hold a reference, or it is NULL. - * We can't put it here under mq_lock, though. - */ - if (ipc_ns) - ipc_ns->mq_queues_count--; - spin_unlock(&mq_lock); - free_uid(user); - } - if (ipc_ns) - put_ipc_ns(ipc_ns); -} - -static int mqueue_create(struct inode *dir, struct dentry *dentry, - umode_t mode, struct nameidata *nd) -{ - struct inode *inode; - struct mq_attr *attr = dentry->d_fsdata; - int error; - struct ipc_namespace *ipc_ns; - - spin_lock(&mq_lock); - ipc_ns = __get_ns_from_inode(dir); - if (!ipc_ns) { - error = -EACCES; - goto out_unlock; - } - if (ipc_ns->mq_queues_count >= ipc_ns->mq_queues_max && - !capable(CAP_SYS_RESOURCE)) { - error = -ENOSPC; - goto out_unlock; - } - ipc_ns->mq_queues_count++; - spin_unlock(&mq_lock); - - inode = mqueue_get_inode(dir->i_sb, ipc_ns, mode, attr); - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - spin_lock(&mq_lock); - ipc_ns->mq_queues_count--; - goto out_unlock; - } - - put_ipc_ns(ipc_ns); - dir->i_size += DIRENT_SIZE; - dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; - - d_instantiate(dentry, inode); - dget(dentry); - return 0; -out_unlock: - spin_unlock(&mq_lock); - if (ipc_ns) - put_ipc_ns(ipc_ns); - return error; -} - -static int mqueue_unlink(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - - dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME; - dir->i_size -= DIRENT_SIZE; - drop_nlink(inode); - dput(dentry); - return 0; -} - -/* -* This is routine for system read from queue file. -* To avoid mess with doing here some sort of mq_receive we allow -* to read only queue size & notification info (the only values -* that are interesting from user point of view and aren't accessible -* through std routines) -*/ -static ssize_t mqueue_read_file(struct file *filp, char __user *u_data, - size_t count, loff_t *off) -{ - struct mqueue_inode_info *info = MQUEUE_I(filp->f_path.dentry->d_inode); - char buffer[FILENT_SIZE]; - ssize_t ret; - - spin_lock(&info->lock); - snprintf(buffer, sizeof(buffer), - "QSIZE:%-10lu NOTIFY:%-5d SIGNO:%-5d NOTIFY_PID:%-6d\n", - info->qsize, - info->notify_owner ? info->notify.sigev_notify : 0, - (info->notify_owner && - info->notify.sigev_notify == SIGEV_SIGNAL) ? - info->notify.sigev_signo : 0, - pid_vnr(info->notify_owner)); - spin_unlock(&info->lock); - buffer[sizeof(buffer)-1] = '\0'; - - ret = simple_read_from_buffer(u_data, count, off, buffer, - strlen(buffer)); - if (ret <= 0) - return ret; - - filp->f_path.dentry->d_inode->i_atime = filp->f_path.dentry->d_inode->i_ctime = CURRENT_TIME; - return ret; -} - -static int mqueue_flush_file(struct file *filp, fl_owner_t id) -{ - struct mqueue_inode_info *info = MQUEUE_I(filp->f_path.dentry->d_inode); - - spin_lock(&info->lock); - if (task_tgid(current) == info->notify_owner) - remove_notification(info); - - spin_unlock(&info->lock); - return 0; -} - -static unsigned int mqueue_poll_file(struct file *filp, struct poll_table_struct *poll_tab) -{ - struct mqueue_inode_info *info = MQUEUE_I(filp->f_path.dentry->d_inode); - int retval = 0; - - poll_wait(filp, &info->wait_q, poll_tab); - - spin_lock(&info->lock); - if (info->attr.mq_curmsgs) - retval = POLLIN | POLLRDNORM; - - if (info->attr.mq_curmsgs < info->attr.mq_maxmsg) - retval |= POLLOUT | POLLWRNORM; - spin_unlock(&info->lock); - - return retval; -} - -/* Adds current to info->e_wait_q[sr] before element with smaller prio */ -static void wq_add(struct mqueue_inode_info *info, int sr, - struct ext_wait_queue *ewp) -{ - struct ext_wait_queue *walk; - - ewp->task = current; - - list_for_each_entry(walk, &info->e_wait_q[sr].list, list) { - if (walk->task->static_prio <= current->static_prio) { - list_add_tail(&ewp->list, &walk->list); - return; - } - } - list_add_tail(&ewp->list, &info->e_wait_q[sr].list); -} - -/* - * Puts current task to sleep. Caller must hold queue lock. After return - * lock isn't held. - * sr: SEND or RECV - */ -static int wq_sleep(struct mqueue_inode_info *info, int sr, - ktime_t *timeout, struct ext_wait_queue *ewp) -{ - int retval; - signed long time; - - wq_add(info, sr, ewp); - - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - - spin_unlock(&info->lock); - time = schedule_hrtimeout_range_clock(timeout, 0, - HRTIMER_MODE_ABS, CLOCK_REALTIME); - - while (ewp->state == STATE_PENDING) - cpu_relax(); - - if (ewp->state == STATE_READY) { - retval = 0; - goto out; - } - spin_lock(&info->lock); - if (ewp->state == STATE_READY) { - retval = 0; - goto out_unlock; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - if (time == 0) { - retval = -ETIMEDOUT; - break; - } - } - list_del(&ewp->list); -out_unlock: - spin_unlock(&info->lock); -out: - return retval; -} - -/* - * Returns waiting task that should be serviced first or NULL if none exists - */ -static struct ext_wait_queue *wq_get_first_waiter( - struct mqueue_inode_info *info, int sr) -{ - struct list_head *ptr; - - ptr = info->e_wait_q[sr].list.prev; - if (ptr == &info->e_wait_q[sr].list) - return NULL; - return list_entry(ptr, struct ext_wait_queue, list); -} - -/* Auxiliary functions to manipulate messages' list */ -static void msg_insert(struct msg_msg *ptr, struct mqueue_inode_info *info) -{ - int k; - - k = info->attr.mq_curmsgs - 1; - while (k >= 0 && info->messages[k]->m_type >= ptr->m_type) { - info->messages[k + 1] = info->messages[k]; - k--; - } - info->attr.mq_curmsgs++; - info->qsize += ptr->m_ts; - info->messages[k + 1] = ptr; -} - -static inline struct msg_msg *msg_get(struct mqueue_inode_info *info) -{ - info->qsize -= info->messages[--info->attr.mq_curmsgs]->m_ts; - return info->messages[info->attr.mq_curmsgs]; -} - -static inline void set_cookie(struct sk_buff *skb, char code) -{ - ((char*)skb->data)[NOTIFY_COOKIE_LEN-1] = code; -} - -/* - * The next function is only to split too long sys_mq_timedsend - */ -static void __do_notify(struct mqueue_inode_info *info) -{ - /* notification - * invoked when there is registered process and there isn't process - * waiting synchronously for message AND state of queue changed from - * empty to not empty. Here we are sure that no one is waiting - * synchronously. */ - if (info->notify_owner && - info->attr.mq_curmsgs == 1) { - struct siginfo sig_i; - switch (info->notify.sigev_notify) { - case SIGEV_NONE: - break; - case SIGEV_SIGNAL: - /* sends signal */ - - sig_i.si_signo = info->notify.sigev_signo; - sig_i.si_errno = 0; - sig_i.si_code = SI_MESGQ; - sig_i.si_value = info->notify.sigev_value; - /* map current pid/uid into info->owner's namespaces */ - rcu_read_lock(); - sig_i.si_pid = task_tgid_nr_ns(current, - ns_of_pid(info->notify_owner)); - sig_i.si_uid = user_ns_map_uid(info->user->user_ns, - current_cred(), current_uid()); - rcu_read_unlock(); - - kill_pid_info(info->notify.sigev_signo, - &sig_i, info->notify_owner); - break; - case SIGEV_THREAD: - set_cookie(info->notify_cookie, NOTIFY_WOKENUP); - netlink_sendskb(info->notify_sock, info->notify_cookie); - break; - } - /* after notification unregisters process */ - put_pid(info->notify_owner); - info->notify_owner = NULL; - } - wake_up(&info->wait_q); -} - -static int prepare_timeout(const struct timespec __user *u_abs_timeout, - ktime_t *expires, struct timespec *ts) -{ - if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec))) - return -EFAULT; - if (!timespec_valid(ts)) - return -EINVAL; - - *expires = timespec_to_ktime(*ts); - return 0; -} - -static void remove_notification(struct mqueue_inode_info *info) -{ - if (info->notify_owner != NULL && - info->notify.sigev_notify == SIGEV_THREAD) { - set_cookie(info->notify_cookie, NOTIFY_REMOVED); - netlink_sendskb(info->notify_sock, info->notify_cookie); - } - put_pid(info->notify_owner); - info->notify_owner = NULL; -} - -static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr) -{ - if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) - return 0; - if (capable(CAP_SYS_RESOURCE)) { - if (attr->mq_maxmsg > HARD_MSGMAX) - return 0; - } else { - if (attr->mq_maxmsg > ipc_ns->mq_msg_max || - attr->mq_msgsize > ipc_ns->mq_msgsize_max) - return 0; - } - /* check for overflow */ - if (attr->mq_msgsize > ULONG_MAX/attr->mq_maxmsg) - return 0; - if ((unsigned long)(attr->mq_maxmsg * (attr->mq_msgsize - + sizeof (struct msg_msg *))) < - (unsigned long)(attr->mq_maxmsg * attr->mq_msgsize)) - return 0; - return 1; -} - -/* - * Invoked when creating a new queue via sys_mq_open - */ -static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir, - struct dentry *dentry, int oflag, umode_t mode, - struct mq_attr *attr) -{ - const struct cred *cred = current_cred(); - struct file *result; - int ret; - - if (attr) { - if (!mq_attr_ok(ipc_ns, attr)) { - ret = -EINVAL; - goto out; - } - /* store for use during create */ - dentry->d_fsdata = attr; - } - - mode &= ~current_umask(); - ret = mnt_want_write(ipc_ns->mq_mnt); - if (ret) - goto out; - ret = vfs_create(dir->d_inode, dentry, mode, NULL); - dentry->d_fsdata = NULL; - if (ret) - goto out_drop_write; - - result = dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred); - /* - * dentry_open() took a persistent mnt_want_write(), - * so we can now drop this one. - */ - mnt_drop_write(ipc_ns->mq_mnt); - return result; - -out_drop_write: - mnt_drop_write(ipc_ns->mq_mnt); -out: - dput(dentry); - mntput(ipc_ns->mq_mnt); - return ERR_PTR(ret); -} - -/* Opens existing queue */ -static struct file *do_open(struct ipc_namespace *ipc_ns, - struct dentry *dentry, int oflag) -{ - int ret; - const struct cred *cred = current_cred(); - - static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, - MAY_READ | MAY_WRITE }; - - if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) { - ret = -EINVAL; - goto err; - } - - if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { - ret = -EACCES; - goto err; - } - - return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred); - -err: - dput(dentry); - mntput(ipc_ns->mq_mnt); - return ERR_PTR(ret); -} - -SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, - struct mq_attr __user *, u_attr) -{ - struct dentry *dentry; - struct file *filp; - char *name; - struct mq_attr attr; - int fd, error; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - - if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) - return -EFAULT; - - audit_mq_open(oflag, mode, u_attr ? &attr : NULL); - - if (IS_ERR(name = getname(u_name))) - return PTR_ERR(name); - - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) - goto out_putname; - - mutex_lock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); - dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name)); - if (IS_ERR(dentry)) { - error = PTR_ERR(dentry); - goto out_putfd; - } - mntget(ipc_ns->mq_mnt); - - if (oflag & O_CREAT) { - if (dentry->d_inode) { /* entry already exists */ - audit_inode(name, dentry); - if (oflag & O_EXCL) { - error = -EEXIST; - goto out; - } - filp = do_open(ipc_ns, dentry, oflag); - } else { - filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root, - dentry, oflag, mode, - u_attr ? &attr : NULL); - } - } else { - if (!dentry->d_inode) { - error = -ENOENT; - goto out; - } - audit_inode(name, dentry); - filp = do_open(ipc_ns, dentry, oflag); - } - - if (IS_ERR(filp)) { - error = PTR_ERR(filp); - goto out_putfd; - } - - fd_install(fd, filp); - goto out_upsem; - -out: - dput(dentry); - mntput(ipc_ns->mq_mnt); -out_putfd: - put_unused_fd(fd); - fd = error; -out_upsem: - mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); -out_putname: - putname(name); - return fd; -} - -SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name) -{ - int err; - char *name; - struct dentry *dentry; - struct inode *inode = NULL; - struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; - - name = getname(u_name); - if (IS_ERR(name)) - return PTR_ERR(name); - - mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex, - I_MUTEX_PARENT); - dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name)); - if (IS_ERR(dentry)) { - err = PTR_ERR(dentry); - goto out_unlock; - } - - if (!dentry->d_inode) { - err = -ENOENT; - goto out_err; - } - - inode = dentry->d_inode; - if (inode) - ihold(inode); - err = mnt_want_write(ipc_ns->mq_mnt); - if (err) - goto out_err; - err = vfs_unlink(dentry->d_parent->d_inode, dentry); - mnt_drop_write(ipc_ns->mq_mnt); -out_err: - dput(dentry); - -out_unlock: - mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); - putname(name); - if (inode) - iput(inode); - - return err; -} - -/* Pipelined send and receive functions. - * - * If a receiver finds no waiting message, then it registers itself in the - * list of waiting receivers. A sender checks that list before adding the new - * message into the message array. If there is a waiting receiver, then it - * bypasses the message array and directly hands the message over to the - * receiver. - * The receiver accepts the message and returns without grabbing the queue - * spinlock. Therefore an intermediate STATE_PENDING state and memory barriers - * are necessary. The same algorithm is used for sysv semaphores, see - * ipc/sem.c for more details. - * - * The same algorithm is used for senders. - */ - -/* pipelined_send() - send a message directly to the task waiting in - * sys_mq_timedreceive() (without inserting message into a queue). - */ -static inline void pipelined_send(struct mqueue_inode_info *info, - struct msg_msg *message, - struct ext_wait_queue *receiver) -{ - receiver->msg = message; - list_del(&receiver->list); - receiver->state = STATE_PENDING; - wake_up_process(receiver->task); - smp_wmb(); - receiver->state = STATE_READY; -} - -/* pipelined_receive() - if there is task waiting in sys_mq_timedsend() - * gets its message and put to the queue (we have one free place for sure). */ -static inline void pipelined_receive(struct mqueue_inode_info *info) -{ - struct ext_wait_queue *sender = wq_get_first_waiter(info, SEND); - - if (!sender) { - /* for poll */ - wake_up_interruptible(&info->wait_q); - return; - } - msg_insert(sender->msg, info); - list_del(&sender->list); - sender->state = STATE_PENDING; - wake_up_process(sender->task); - smp_wmb(); - sender->state = STATE_READY; -} - -SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, - size_t, msg_len, unsigned int, msg_prio, - const struct timespec __user *, u_abs_timeout) -{ - struct file *filp; - struct inode *inode; - struct ext_wait_queue wait; - struct ext_wait_queue *receiver; - struct msg_msg *msg_ptr; - struct mqueue_inode_info *info; - ktime_t expires, *timeout = NULL; - struct timespec ts; - int ret; - - if (u_abs_timeout) { - int res = prepare_timeout(u_abs_timeout, &expires, &ts); - if (res) - return res; - timeout = &expires; - } - - if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) - return -EINVAL; - - audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); - - filp = fget(mqdes); - if (unlikely(!filp)) { - ret = -EBADF; - goto out; - } - - inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) { - ret = -EBADF; - goto out_fput; - } - info = MQUEUE_I(inode); - audit_inode(NULL, filp->f_path.dentry); - - if (unlikely(!(filp->f_mode & FMODE_WRITE))) { - ret = -EBADF; - goto out_fput; - } - - if (unlikely(msg_len > info->attr.mq_msgsize)) { - ret = -EMSGSIZE; - goto out_fput; - } - - /* First try to allocate memory, before doing anything with - * existing queues. */ - msg_ptr = load_msg(u_msg_ptr, msg_len); - if (IS_ERR(msg_ptr)) { - ret = PTR_ERR(msg_ptr); - goto out_fput; - } - msg_ptr->m_ts = msg_len; - msg_ptr->m_type = msg_prio; - - spin_lock(&info->lock); - - if (info->attr.mq_curmsgs == info->attr.mq_maxmsg) { - if (filp->f_flags & O_NONBLOCK) { - spin_unlock(&info->lock); - ret = -EAGAIN; - } else { - wait.task = current; - wait.msg = (void *) msg_ptr; - wait.state = STATE_NONE; - ret = wq_sleep(info, SEND, timeout, &wait); - } - if (ret < 0) - free_msg(msg_ptr); - } else { - receiver = wq_get_first_waiter(info, RECV); - if (receiver) { - pipelined_send(info, msg_ptr, receiver); - } else { - /* adds message to the queue */ - msg_insert(msg_ptr, info); - __do_notify(info); - } - inode->i_atime = inode->i_mtime = inode->i_ctime = - CURRENT_TIME; - spin_unlock(&info->lock); - ret = 0; - } -out_fput: - fput(filp); -out: - return ret; -} - -SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, - size_t, msg_len, unsigned int __user *, u_msg_prio, - const struct timespec __user *, u_abs_timeout) -{ - ssize_t ret; - struct msg_msg *msg_ptr; - struct file *filp; - struct inode *inode; - struct mqueue_inode_info *info; - struct ext_wait_queue wait; - ktime_t expires, *timeout = NULL; - struct timespec ts; - - if (u_abs_timeout) { - int res = prepare_timeout(u_abs_timeout, &expires, &ts); - if (res) - return res; - timeout = &expires; - } - - audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); - - filp = fget(mqdes); - if (unlikely(!filp)) { - ret = -EBADF; - goto out; - } - - inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) { - ret = -EBADF; - goto out_fput; - } - info = MQUEUE_I(inode); - audit_inode(NULL, filp->f_path.dentry); - - if (unlikely(!(filp->f_mode & FMODE_READ))) { - ret = -EBADF; - goto out_fput; - } - - /* checks if buffer is big enough */ - if (unlikely(msg_len < info->attr.mq_msgsize)) { - ret = -EMSGSIZE; - goto out_fput; - } - - spin_lock(&info->lock); - if (info->attr.mq_curmsgs == 0) { - if (filp->f_flags & O_NONBLOCK) { - spin_unlock(&info->lock); - ret = -EAGAIN; - } else { - wait.task = current; - wait.state = STATE_NONE; - ret = wq_sleep(info, RECV, timeout, &wait); - msg_ptr = wait.msg; - } - } else { - msg_ptr = msg_get(info); - - inode->i_atime = inode->i_mtime = inode->i_ctime = - CURRENT_TIME; - - /* There is now free space in queue. */ - pipelined_receive(info); - spin_unlock(&info->lock); - ret = 0; - } - if (ret == 0) { - ret = msg_ptr->m_ts; - - if ((u_msg_prio && put_user(msg_ptr->m_type, u_msg_prio)) || - store_msg(u_msg_ptr, msg_ptr, msg_ptr->m_ts)) { - ret = -EFAULT; - } - free_msg(msg_ptr); - } -out_fput: - fput(filp); -out: - return ret; -} - -/* - * Notes: the case when user wants us to deregister (with NULL as pointer) - * and he isn't currently owner of notification, will be silently discarded. - * It isn't explicitly defined in the POSIX. - */ -SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, - const struct sigevent __user *, u_notification) -{ - int ret; - struct file *filp; - struct sock *sock; - struct inode *inode; - struct sigevent notification; - struct mqueue_inode_info *info; - struct sk_buff *nc; - - if (u_notification) { - if (copy_from_user(¬ification, u_notification, - sizeof(struct sigevent))) - return -EFAULT; - } - - audit_mq_notify(mqdes, u_notification ? ¬ification : NULL); - - nc = NULL; - sock = NULL; - if (u_notification != NULL) { - if (unlikely(notification.sigev_notify != SIGEV_NONE && - notification.sigev_notify != SIGEV_SIGNAL && - notification.sigev_notify != SIGEV_THREAD)) - return -EINVAL; - if (notification.sigev_notify == SIGEV_SIGNAL && - !valid_signal(notification.sigev_signo)) { - return -EINVAL; - } - if (notification.sigev_notify == SIGEV_THREAD) { - long timeo; - - /* create the notify skb */ - nc = alloc_skb(NOTIFY_COOKIE_LEN, GFP_KERNEL); - if (!nc) { - ret = -ENOMEM; - goto out; - } - if (copy_from_user(nc->data, - notification.sigev_value.sival_ptr, - NOTIFY_COOKIE_LEN)) { - ret = -EFAULT; - goto out; - } - - /* TODO: add a header? */ - skb_put(nc, NOTIFY_COOKIE_LEN); - /* and attach it to the socket */ -retry: - filp = fget(notification.sigev_signo); - if (!filp) { - ret = -EBADF; - goto out; - } - sock = netlink_getsockbyfilp(filp); - fput(filp); - if (IS_ERR(sock)) { - ret = PTR_ERR(sock); - sock = NULL; - goto out; - } - - timeo = MAX_SCHEDULE_TIMEOUT; - ret = netlink_attachskb(sock, nc, &timeo, NULL); - if (ret == 1) - goto retry; - if (ret) { - sock = NULL; - nc = NULL; - goto out; - } - } - } - - filp = fget(mqdes); - if (!filp) { - ret = -EBADF; - goto out; - } - - inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) { - ret = -EBADF; - goto out_fput; - } - info = MQUEUE_I(inode); - - ret = 0; - spin_lock(&info->lock); - if (u_notification == NULL) { - if (info->notify_owner == task_tgid(current)) { - remove_notification(info); - inode->i_atime = inode->i_ctime = CURRENT_TIME; - } - } else if (info->notify_owner != NULL) { - ret = -EBUSY; - } else { - switch (notification.sigev_notify) { - case SIGEV_NONE: - info->notify.sigev_notify = SIGEV_NONE; - break; - case SIGEV_THREAD: - info->notify_sock = sock; - info->notify_cookie = nc; - sock = NULL; - nc = NULL; - info->notify.sigev_notify = SIGEV_THREAD; - break; - case SIGEV_SIGNAL: - info->notify.sigev_signo = notification.sigev_signo; - info->notify.sigev_value = notification.sigev_value; - info->notify.sigev_notify = SIGEV_SIGNAL; - break; - } - - info->notify_owner = get_pid(task_tgid(current)); - inode->i_atime = inode->i_ctime = CURRENT_TIME; - } - spin_unlock(&info->lock); -out_fput: - fput(filp); -out: - if (sock) { - netlink_detachskb(sock, nc); - } else if (nc) { - dev_kfree_skb(nc); - } - return ret; -} - -SYSCALL_DEFINE3(mq_getsetattr, mqd_t, mqdes, - const struct mq_attr __user *, u_mqstat, - struct mq_attr __user *, u_omqstat) -{ - int ret; - struct mq_attr mqstat, omqstat; - struct file *filp; - struct inode *inode; - struct mqueue_inode_info *info; - - if (u_mqstat != NULL) { - if (copy_from_user(&mqstat, u_mqstat, sizeof(struct mq_attr))) - return -EFAULT; - if (mqstat.mq_flags & (~O_NONBLOCK)) - return -EINVAL; - } - - filp = fget(mqdes); - if (!filp) { - ret = -EBADF; - goto out; - } - - inode = filp->f_path.dentry->d_inode; - if (unlikely(filp->f_op != &mqueue_file_operations)) { - ret = -EBADF; - goto out_fput; - } - info = MQUEUE_I(inode); - - spin_lock(&info->lock); - - omqstat = info->attr; - omqstat.mq_flags = filp->f_flags & O_NONBLOCK; - if (u_mqstat) { - audit_mq_getsetattr(mqdes, &mqstat); - spin_lock(&filp->f_lock); - if (mqstat.mq_flags & O_NONBLOCK) - filp->f_flags |= O_NONBLOCK; - else - filp->f_flags &= ~O_NONBLOCK; - spin_unlock(&filp->f_lock); - - inode->i_atime = inode->i_ctime = CURRENT_TIME; - } - - spin_unlock(&info->lock); - - ret = 0; - if (u_omqstat != NULL && copy_to_user(u_omqstat, &omqstat, - sizeof(struct mq_attr))) - ret = -EFAULT; - -out_fput: - fput(filp); -out: - return ret; -} - -static const struct inode_operations mqueue_dir_inode_operations = { - .lookup = simple_lookup, - .create = mqueue_create, - .unlink = mqueue_unlink, -}; - -static const struct file_operations mqueue_file_operations = { - .flush = mqueue_flush_file, - .poll = mqueue_poll_file, - .read = mqueue_read_file, - .llseek = default_llseek, -}; - -static const struct super_operations mqueue_super_ops = { - .alloc_inode = mqueue_alloc_inode, - .destroy_inode = mqueue_destroy_inode, - .evict_inode = mqueue_evict_inode, - .statfs = simple_statfs, -}; - -static struct file_system_type mqueue_fs_type = { - .name = "mqueue", - .mount = mqueue_mount, - .kill_sb = kill_litter_super, -}; - -int mq_init_ns(struct ipc_namespace *ns) -{ - ns->mq_queues_count = 0; - ns->mq_queues_max = DFLT_QUEUESMAX; - ns->mq_msg_max = DFLT_MSGMAX; - ns->mq_msgsize_max = DFLT_MSGSIZEMAX; - - ns->mq_mnt = kern_mount_data(&mqueue_fs_type, ns); - if (IS_ERR(ns->mq_mnt)) { - int err = PTR_ERR(ns->mq_mnt); - ns->mq_mnt = NULL; - return err; - } - return 0; -} - -void mq_clear_sbinfo(struct ipc_namespace *ns) -{ - ns->mq_mnt->mnt_sb->s_fs_info = NULL; -} - -void mq_put_mnt(struct ipc_namespace *ns) -{ - kern_unmount(ns->mq_mnt); -} - -static int __init init_mqueue_fs(void) -{ - int error; - - mqueue_inode_cachep = kmem_cache_create("mqueue_inode_cache", - sizeof(struct mqueue_inode_info), 0, - SLAB_HWCACHE_ALIGN, init_once); - if (mqueue_inode_cachep == NULL) - return -ENOMEM; - - /* ignore failures - they are not fatal */ - mq_sysctl_table = mq_register_sysctl_table(); - - error = register_filesystem(&mqueue_fs_type); - if (error) - goto out_sysctl; - - spin_lock_init(&mq_lock); - - error = mq_init_ns(&init_ipc_ns); - if (error) - goto out_filesystem; - - return 0; - -out_filesystem: - unregister_filesystem(&mqueue_fs_type); -out_sysctl: - if (mq_sysctl_table) - unregister_sysctl_table(mq_sysctl_table); - kmem_cache_destroy(mqueue_inode_cachep); - return error; -} - -__initcall(init_mqueue_fs); diff --git a/ANDROID_3.4.5/ipc/msg.c b/ANDROID_3.4.5/ipc/msg.c deleted file mode 100644 index 7385de25..00000000 --- a/ANDROID_3.4.5/ipc/msg.c +++ /dev/null @@ -1,944 +0,0 @@ -/* - * 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 <manfred@colorfullife.com> - * - * support for audit of ipc object properties and permission changes - * Dustin Kirkland <dustin.kirkland@us.ibm.com> - * - * namespaces support - * OpenVZ, SWsoft Inc. - * Pavel Emelianov <xemul@openvz.org> - */ - -#include <linux/capability.h> -#include <linux/msg.h> -#include <linux/spinlock.h> -#include <linux/init.h> -#include <linux/mm.h> -#include <linux/proc_fs.h> -#include <linux/list.h> -#include <linux/security.h> -#include <linux/sched.h> -#include <linux/syscalls.h> -#include <linux/audit.h> -#include <linux/seq_file.h> -#include <linux/rwsem.h> -#include <linux/nsproxy.h> -#include <linux/ipc_namespace.h> - -#include <asm/current.h> -#include <asm/uaccess.h> -#include "util.h" - -/* - * 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; -}; - -#define SEARCH_ANY 1 -#define SEARCH_EQUAL 2 -#define SEARCH_NOTEQUAL 3 -#define SEARCH_LESSEQUAL 4 - -#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) - -#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) - -static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); -static int newque(struct ipc_namespace *, struct ipc_params *); -#ifdef CONFIG_PROC_FS -static int sysvipc_msg_proc_show(struct seq_file *s, void *it); -#endif - -/* - * Scale msgmni with the available lowmem size: the memory dedicated to msg - * queues should occupy at most 1/MSG_MEM_SCALE of lowmem. - * Also take into account the number of nsproxies created so far. - * This should be done staying within the (MSGMNI , IPCMNI/nr_ipc_ns) range. - */ -void recompute_msgmni(struct ipc_namespace *ns) -{ - struct sysinfo i; - unsigned long allowed; - int nb_ns; - - si_meminfo(&i); - allowed = (((i.totalram - i.totalhigh) / MSG_MEM_SCALE) * i.mem_unit) - / MSGMNB; - nb_ns = atomic_read(&nr_ipc_ns); - allowed /= nb_ns; - - if (allowed < MSGMNI) { - ns->msg_ctlmni = MSGMNI; - return; - } - - if (allowed > IPCMNI / nb_ns) { - ns->msg_ctlmni = IPCMNI / nb_ns; - return; - } - - ns->msg_ctlmni = allowed; -} - -void msg_init_ns(struct ipc_namespace *ns) -{ - ns->msg_ctlmax = MSGMAX; - ns->msg_ctlmnb = MSGMNB; - - recompute_msgmni(ns); - - atomic_set(&ns->msg_bytes, 0); - atomic_set(&ns->msg_hdrs, 0); - ipc_init_ids(&ns->ids[IPC_MSG_IDS]); -} - -#ifdef CONFIG_IPC_NS -void msg_exit_ns(struct ipc_namespace *ns) -{ - free_ipcs(ns, &msg_ids(ns), freeque); - idr_destroy(&ns->ids[IPC_MSG_IDS].ipcs_idr); -} -#endif - -void __init msg_init(void) -{ - msg_init_ns(&init_ipc_ns); - - printk(KERN_INFO "msgmni has been set to %d\n", - init_ipc_ns.msg_ctlmni); - - ipc_init_proc_interface("sysvipc/msg", - " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n", - IPC_MSG_IDS, sysvipc_msg_proc_show); -} - -/* - * msg_lock_(check_) routines are called in the paths where the rw_mutex - * is not held. - */ -static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct msg_queue *)ipcp; - - return container_of(ipcp, struct msg_queue, q_perm); -} - -static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns, - int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct msg_queue *)ipcp; - - return container_of(ipcp, struct msg_queue, q_perm); -} - -static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) -{ - ipc_rmid(&msg_ids(ns), &s->q_perm); -} - -/** - * newque - Create a new msg queue - * @ns: namespace - * @params: ptr to the structure that contains the key and msgflg - * - * Called with msg_ids.rw_mutex held (writer) - */ -static int newque(struct ipc_namespace *ns, struct ipc_params *params) -{ - struct msg_queue *msq; - int id, retval; - key_t key = params->key; - int msgflg = params->flg; - - msq = ipc_rcu_alloc(sizeof(*msq)); - if (!msq) - return -ENOMEM; - - msq->q_perm.mode = msgflg & S_IRWXUGO; - msq->q_perm.key = key; - - msq->q_perm.security = NULL; - retval = security_msg_queue_alloc(msq); - if (retval) { - ipc_rcu_putref(msq); - return retval; - } - - /* - * ipc_addid() locks msq - */ - id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); - if (id < 0) { - security_msg_queue_free(msq); - ipc_rcu_putref(msq); - return id; - } - - msq->q_stime = msq->q_rtime = 0; - msq->q_ctime = get_seconds(); - msq->q_cbytes = msq->q_qnum = 0; - msq->q_qbytes = ns->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(msq); - - return msq->q_perm.id; -} - -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 = NULL; - wake_up_process(msr->r_tsk); - smp_mb(); - msr->r_msg = ERR_PTR(res); - } -} - -/* - * freeque() wakes up waiters on the sender and receiver waiting queue, - * removes the message queue from message queue ID IDR, and cleans up all the - * messages associated with this queue. - * - * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held - * before freeque() is called. msg_ids.rw_mutex remains locked on exit. - */ -static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) -{ - struct list_head *tmp; - struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); - - expunge_all(msq, -EIDRM); - ss_wakeup(&msq->q_senders, 1); - msg_rmid(ns, msq); - msg_unlock(msq); - - 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(&ns->msg_hdrs); - free_msg(msg); - } - atomic_sub(msq->q_cbytes, &ns->msg_bytes); - security_msg_queue_free(msq); - ipc_rcu_putref(msq); -} - -/* - * Called with msg_ids.rw_mutex and ipcp locked. - */ -static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) -{ - struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm); - - return security_msg_queue_associate(msq, msgflg); -} - -SYSCALL_DEFINE2(msgget, key_t, key, int, msgflg) -{ - struct ipc_namespace *ns; - struct ipc_ops msg_ops; - struct ipc_params msg_params; - - ns = current->nsproxy->ipc_ns; - - msg_ops.getnew = newque; - msg_ops.associate = msg_security; - msg_ops.more_checks = NULL; - - msg_params.key = key; - msg_params.flg = msgflg; - - return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params); -} - -static inline unsigned long -copy_msqid_to_user(void __user *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; - } -} - -static inline unsigned long -copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) -{ - switch(version) { - case IPC_64: - if (copy_from_user(out, buf, sizeof(*out))) - return -EFAULT; - return 0; - case IPC_OLD: - { - struct msqid_ds tbuf_old; - - if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) - return -EFAULT; - - out->msg_perm.uid = tbuf_old.msg_perm.uid; - out->msg_perm.gid = tbuf_old.msg_perm.gid; - out->msg_perm.mode = tbuf_old.msg_perm.mode; - - if (tbuf_old.msg_qbytes == 0) - out->msg_qbytes = tbuf_old.msg_lqbytes; - else - out->msg_qbytes = tbuf_old.msg_qbytes; - - return 0; - } - default: - return -EINVAL; - } -} - -/* - * This function handles some msgctl commands which require the rw_mutex - * to be held in write mode. - * NOTE: no locks must be held, the rw_mutex is taken inside this function. - */ -static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, - struct msqid_ds __user *buf, int version) -{ - struct kern_ipc_perm *ipcp; - struct msqid64_ds uninitialized_var(msqid64); - struct msg_queue *msq; - int err; - - if (cmd == IPC_SET) { - if (copy_msqid_from_user(&msqid64, buf, version)) - return -EFAULT; - } - - ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, - &msqid64.msg_perm, msqid64.msg_qbytes); - if (IS_ERR(ipcp)) - return PTR_ERR(ipcp); - - msq = container_of(ipcp, struct msg_queue, q_perm); - - err = security_msg_queue_msgctl(msq, cmd); - if (err) - goto out_unlock; - - switch (cmd) { - case IPC_RMID: - freeque(ns, ipcp); - goto out_up; - case IPC_SET: - if (msqid64.msg_qbytes > ns->msg_ctlmnb && - !capable(CAP_SYS_RESOURCE)) { - err = -EPERM; - goto out_unlock; - } - - msq->q_qbytes = msqid64.msg_qbytes; - - ipc_update_perm(&msqid64.msg_perm, ipcp); - msq->q_ctime = get_seconds(); - /* 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); - break; - default: - err = -EINVAL; - } -out_unlock: - msg_unlock(msq); -out_up: - up_write(&msg_ids(ns).rw_mutex); - return err; -} - -SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) -{ - struct msg_queue *msq; - int err, version; - struct ipc_namespace *ns; - - if (msqid < 0 || cmd < 0) - return -EINVAL; - - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; - - 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. - */ - err = security_msg_queue_msgctl(NULL, cmd); - if (err) - return err; - - memset(&msginfo, 0, sizeof(msginfo)); - msginfo.msgmni = ns->msg_ctlmni; - msginfo.msgmax = ns->msg_ctlmax; - msginfo.msgmnb = ns->msg_ctlmnb; - msginfo.msgssz = MSGSSZ; - msginfo.msgseg = MSGSEG; - down_read(&msg_ids(ns).rw_mutex); - if (cmd == MSG_INFO) { - msginfo.msgpool = msg_ids(ns).in_use; - msginfo.msgmap = atomic_read(&ns->msg_hdrs); - msginfo.msgtql = atomic_read(&ns->msg_bytes); - } else { - msginfo.msgmap = MSGMAP; - msginfo.msgpool = MSGPOOL; - msginfo.msgtql = MSGTQL; - } - max_id = ipc_get_maxid(&msg_ids(ns)); - up_read(&msg_ids(ns).rw_mutex); - if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) - return -EFAULT; - return (max_id < 0) ? 0 : max_id; - } - case MSG_STAT: /* msqid is an index rather than a msg queue id */ - case IPC_STAT: - { - struct msqid64_ds tbuf; - int success_return; - - if (!buf) - return -EFAULT; - - if (cmd == MSG_STAT) { - msq = msg_lock(ns, msqid); - if (IS_ERR(msq)) - return PTR_ERR(msq); - success_return = msq->q_perm.id; - } else { - msq = msg_lock_check(ns, msqid); - if (IS_ERR(msq)) - return PTR_ERR(msq); - success_return = 0; - } - err = -EACCES; - if (ipcperms(ns, &msq->q_perm, S_IRUGO)) - goto out_unlock; - - err = security_msg_queue_msgctl(msq, cmd); - if (err) - goto out_unlock; - - memset(&tbuf, 0, sizeof(tbuf)); - - 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(msq); - if (copy_msqid_to_user(buf, &tbuf, version)) - return -EFAULT; - return success_return; - } - case IPC_SET: - case IPC_RMID: - err = msgctl_down(ns, msqid, cmd, buf, version); - return err; - default: - return -EINVAL; - } - -out_unlock: - msg_unlock(msq); - 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 inline int 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) && - !security_msg_queue_msgrcv(msq, msg, msr->r_tsk, - msr->r_msgtype, msr->r_mode)) { - - list_del(&msr->r_list); - if (msr->r_maxsize < msg->m_ts) { - msr->r_msg = NULL; - wake_up_process(msr->r_tsk); - smp_mb(); - msr->r_msg = ERR_PTR(-E2BIG); - } else { - msr->r_msg = NULL; - msq->q_lrpid = task_pid_vnr(msr->r_tsk); - msq->q_rtime = get_seconds(); - wake_up_process(msr->r_tsk); - smp_mb(); - msr->r_msg = msg; - - return 1; - } - } - } - return 0; -} - -long do_msgsnd(int msqid, long mtype, void __user *mtext, - size_t msgsz, int msgflg) -{ - struct msg_queue *msq; - struct msg_msg *msg; - int err; - struct ipc_namespace *ns; - - ns = current->nsproxy->ipc_ns; - - if (msgsz > ns->msg_ctlmax || (long) msgsz < 0 || msqid < 0) - return -EINVAL; - if (mtype < 1) - return -EINVAL; - - msg = load_msg(mtext, msgsz); - if (IS_ERR(msg)) - return PTR_ERR(msg); - - msg->m_type = mtype; - msg->m_ts = msgsz; - - msq = msg_lock_check(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_free; - } - - for (;;) { - struct msg_sender s; - - err = -EACCES; - if (ipcperms(ns, &msq->q_perm, S_IWUGO)) - goto out_unlock_free; - - err = security_msg_queue_msgsnd(msq, msg, msgflg); - if (err) - goto out_unlock_free; - - if (msgsz + msq->q_cbytes <= msq->q_qbytes && - 1 + msq->q_qnum <= msq->q_qbytes) { - break; - } - - /* queue full, wait: */ - if (msgflg & IPC_NOWAIT) { - err = -EAGAIN; - goto out_unlock_free; - } - ss_add(msq, &s); - ipc_rcu_getref(msq); - msg_unlock(msq); - schedule(); - - ipc_lock_by_ptr(&msq->q_perm); - ipc_rcu_putref(msq); - if (msq->q_perm.deleted) { - err = -EIDRM; - goto out_unlock_free; - } - ss_del(&s); - - if (signal_pending(current)) { - err = -ERESTARTNOHAND; - goto out_unlock_free; - } - } - - msq->q_lspid = task_tgid_vnr(current); - msq->q_stime = get_seconds(); - - if (!pipelined_send(msq, msg)) { - /* no one 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, &ns->msg_bytes); - atomic_inc(&ns->msg_hdrs); - } - - err = 0; - msg = NULL; - -out_unlock_free: - msg_unlock(msq); -out_free: - if (msg != NULL) - free_msg(msg); - return err; -} - -SYSCALL_DEFINE4(msgsnd, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, - int, msgflg) -{ - long mtype; - - if (get_user(mtype, &msgp->mtype)) - return -EFAULT; - return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg); -} - -static inline int 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; -} - -long do_msgrcv(int msqid, long *pmtype, void __user *mtext, - size_t msgsz, long msgtyp, int msgflg) -{ - struct msg_queue *msq; - struct msg_msg *msg; - int mode; - struct ipc_namespace *ns; - - if (msqid < 0 || (long) msgsz < 0) - return -EINVAL; - mode = convert_mode(&msgtyp, msgflg); - ns = current->nsproxy->ipc_ns; - - msq = msg_lock_check(ns, msqid); - if (IS_ERR(msq)) - return PTR_ERR(msq); - - for (;;) { - struct msg_receiver msr_d; - struct list_head *tmp; - - msg = ERR_PTR(-EACCES); - if (ipcperms(ns, &msq->q_perm, S_IRUGO)) - goto out_unlock; - - msg = ERR_PTR(-EAGAIN); - tmp = msq->q_messages.next; - while (tmp != &msq->q_messages) { - struct msg_msg *walk_msg; - - walk_msg = list_entry(tmp, struct msg_msg, m_list); - if (testmsg(walk_msg, msgtyp, mode) && - !security_msg_queue_msgrcv(msq, walk_msg, current, - msgtyp, mode)) { - - msg = walk_msg; - if (mode == SEARCH_LESSEQUAL && - walk_msg->m_type != 1) { - msg = walk_msg; - msgtyp = walk_msg->m_type - 1; - } else { - msg = walk_msg; - break; - } - } - tmp = tmp->next; - } - if (!IS_ERR(msg)) { - /* - * Found a suitable message. - * Unlink it from the queue. - */ - if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { - msg = ERR_PTR(-E2BIG); - goto out_unlock; - } - list_del(&msg->m_list); - msq->q_qnum--; - msq->q_rtime = get_seconds(); - msq->q_lrpid = task_tgid_vnr(current); - msq->q_cbytes -= msg->m_ts; - atomic_sub(msg->m_ts, &ns->msg_bytes); - atomic_dec(&ns->msg_hdrs); - ss_wakeup(&msq->q_senders, 0); - msg_unlock(msq); - break; - } - /* No message waiting. Wait for a message */ - if (msgflg & IPC_NOWAIT) { - msg = ERR_PTR(-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(msq); - - schedule(); - - /* Lockless receive, part 1: - * Disable preemption. We don't hold a reference to the queue - * and getting a reference would defeat the idea of a lockless - * operation, thus the code relies on rcu to guarantee the - * existence of msq: - * Prior to destruction, expunge_all(-EIRDM) changes r_msg. - * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. - * rcu_read_lock() prevents preemption between reading r_msg - * and the spin_lock() inside ipc_lock_by_ptr(). - */ - rcu_read_lock(); - - /* Lockless receive, part 2: - * Wait until pipelined_send or expunge_all are outside of - * wake_up_process(). There is a race with exit(), see - * ipc/mqueue.c for the details. - */ - msg = (struct msg_msg*)msr_d.r_msg; - while (msg == NULL) { - cpu_relax(); - msg = (struct msg_msg *)msr_d.r_msg; - } - - /* Lockless receive, part 3: - * If there is a message or an error then accept it without - * locking. - */ - if (msg != ERR_PTR(-EAGAIN)) { - rcu_read_unlock(); - break; - } - - /* Lockless receive, part 3: - * Acquire the queue spinlock. - */ - ipc_lock_by_ptr(&msq->q_perm); - rcu_read_unlock(); - - /* Lockless receive, part 4: - * Repeat test after acquiring the spinlock. - */ - msg = (struct msg_msg*)msr_d.r_msg; - if (msg != ERR_PTR(-EAGAIN)) - goto out_unlock; - - list_del(&msr_d.r_list); - if (signal_pending(current)) { - msg = ERR_PTR(-ERESTARTNOHAND); -out_unlock: - msg_unlock(msq); - break; - } - } - if (IS_ERR(msg)) - return PTR_ERR(msg); - - msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz; - *pmtype = msg->m_type; - if (store_msg(mtext, msg, msgsz)) - msgsz = -EFAULT; - - free_msg(msg); - - return msgsz; -} - -SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, - long, msgtyp, int, msgflg) -{ - long err, mtype; - - err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); - if (err < 0) - goto out; - - if (put_user(mtype, &msgp->mtype)) - err = -EFAULT; -out: - return err; -} - -#ifdef CONFIG_PROC_FS -static int sysvipc_msg_proc_show(struct seq_file *s, void *it) -{ - struct msg_queue *msq = it; - - return seq_printf(s, - "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n", - msq->q_perm.key, - msq->q_perm.id, - 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); -} -#endif diff --git a/ANDROID_3.4.5/ipc/msgutil.c b/ANDROID_3.4.5/ipc/msgutil.c deleted file mode 100644 index 26143d37..00000000 --- a/ANDROID_3.4.5/ipc/msgutil.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * linux/ipc/msgutil.c - * Copyright (C) 1999, 2004 Manfred Spraul - * - * This file is released under GNU General Public Licence version 2 or - * (at your option) any later version. - * - * See the file COPYING for more details. - */ - -#include <linux/spinlock.h> -#include <linux/init.h> -#include <linux/security.h> -#include <linux/slab.h> -#include <linux/ipc.h> -#include <linux/msg.h> -#include <linux/ipc_namespace.h> -#include <linux/utsname.h> -#include <asm/uaccess.h> - -#include "util.h" - -DEFINE_SPINLOCK(mq_lock); - -/* - * The next 2 defines are here bc this is the only file - * compiled when either CONFIG_SYSVIPC and CONFIG_POSIX_MQUEUE - * and not CONFIG_IPC_NS. - */ -struct ipc_namespace init_ipc_ns = { - .count = ATOMIC_INIT(1), - .user_ns = &init_user_ns, -}; - -atomic_t nr_ipc_ns = ATOMIC_INIT(1); - -struct msg_msgseg { - struct msg_msgseg* next; - /* the next part of the message follows immediately */ -}; - -#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg)) -#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg)) - -struct msg_msg *load_msg(const void __user *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 = kmalloc(sizeof(*msg) + alen, GFP_KERNEL); - if (msg == NULL) - return ERR_PTR(-ENOMEM); - - msg->next = NULL; - msg->security = NULL; - - if (copy_from_user(msg + 1, src, alen)) { - err = -EFAULT; - goto out_err; - } - - len -= alen; - src = ((char __user *)src) + alen; - pseg = &msg->next; - while (len > 0) { - struct msg_msgseg *seg; - alen = len; - if (alen > DATALEN_SEG) - alen = DATALEN_SEG; - seg = 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 __user *)src) + alen; - } - - err = security_msg_msg_alloc(msg); - if (err) - goto out_err; - - return msg; - -out_err: - free_msg(msg); - return ERR_PTR(err); -} - -int store_msg(void __user *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 __user *)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 __user *)dest) + alen; - seg = seg->next; - } - return 0; -} - -void free_msg(struct msg_msg *msg) -{ - struct msg_msgseg *seg; - - security_msg_msg_free(msg); - - seg = msg->next; - kfree(msg); - while (seg != NULL) { - struct msg_msgseg *tmp = seg->next; - kfree(seg); - seg = tmp; - } -} diff --git a/ANDROID_3.4.5/ipc/namespace.c b/ANDROID_3.4.5/ipc/namespace.c deleted file mode 100644 index ce0a6478..00000000 --- a/ANDROID_3.4.5/ipc/namespace.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * linux/ipc/namespace.c - * Copyright (C) 2006 Pavel Emelyanov <xemul@openvz.org> OpenVZ, SWsoft Inc. - */ - -#include <linux/ipc.h> -#include <linux/msg.h> -#include <linux/ipc_namespace.h> -#include <linux/rcupdate.h> -#include <linux/nsproxy.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/mount.h> -#include <linux/user_namespace.h> -#include <linux/proc_fs.h> - -#include "util.h" - -static struct ipc_namespace *create_ipc_ns(struct task_struct *tsk, - struct ipc_namespace *old_ns) -{ - struct ipc_namespace *ns; - int err; - - ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL); - if (ns == NULL) - return ERR_PTR(-ENOMEM); - - atomic_set(&ns->count, 1); - err = mq_init_ns(ns); - if (err) { - kfree(ns); - return ERR_PTR(err); - } - atomic_inc(&nr_ipc_ns); - - sem_init_ns(ns); - msg_init_ns(ns); - shm_init_ns(ns); - - /* - * msgmni has already been computed for the new ipc ns. - * Thus, do the ipcns creation notification before registering that - * new ipcns in the chain. - */ - ipcns_notify(IPCNS_CREATED); - register_ipcns_notifier(ns); - - ns->user_ns = get_user_ns(task_cred_xxx(tsk, user)->user_ns); - - return ns; -} - -struct ipc_namespace *copy_ipcs(unsigned long flags, - struct task_struct *tsk) -{ - struct ipc_namespace *ns = tsk->nsproxy->ipc_ns; - - if (!(flags & CLONE_NEWIPC)) - return get_ipc_ns(ns); - return create_ipc_ns(tsk, ns); -} - -/* - * free_ipcs - free all ipcs of one type - * @ns: the namespace to remove the ipcs from - * @ids: the table of ipcs to free - * @free: the function called to free each individual ipc - * - * Called for each kind of ipc when an ipc_namespace exits. - */ -void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, - void (*free)(struct ipc_namespace *, struct kern_ipc_perm *)) -{ - struct kern_ipc_perm *perm; - int next_id; - int total, in_use; - - down_write(&ids->rw_mutex); - - in_use = ids->in_use; - - for (total = 0, next_id = 0; total < in_use; next_id++) { - perm = idr_find(&ids->ipcs_idr, next_id); - if (perm == NULL) - continue; - ipc_lock_by_ptr(perm); - free(ns, perm); - total++; - } - up_write(&ids->rw_mutex); -} - -static void free_ipc_ns(struct ipc_namespace *ns) -{ - /* - * Unregistering the hotplug notifier at the beginning guarantees - * that the ipc namespace won't be freed while we are inside the - * callback routine. Since the blocking_notifier_chain_XXX routines - * hold a rw lock on the notifier list, unregister_ipcns_notifier() - * won't take the rw lock before blocking_notifier_call_chain() has - * released the rd lock. - */ - unregister_ipcns_notifier(ns); - sem_exit_ns(ns); - msg_exit_ns(ns); - shm_exit_ns(ns); - atomic_dec(&nr_ipc_ns); - - /* - * Do the ipcns removal notification after decrementing nr_ipc_ns in - * order to have a correct value when recomputing msgmni. - */ - ipcns_notify(IPCNS_REMOVED); - put_user_ns(ns->user_ns); - kfree(ns); -} - -/* - * put_ipc_ns - drop a reference to an ipc namespace. - * @ns: the namespace to put - * - * If this is the last task in the namespace exiting, and - * it is dropping the refcount to 0, then it can race with - * a task in another ipc namespace but in a mounts namespace - * which has this ipcns's mqueuefs mounted, doing some action - * with one of the mqueuefs files. That can raise the refcount. - * So dropping the refcount, and raising the refcount when - * accessing it through the VFS, are protected with mq_lock. - * - * (Clearly, a task raising the refcount on its own ipc_ns - * needn't take mq_lock since it can't race with the last task - * in the ipcns exiting). - */ -void put_ipc_ns(struct ipc_namespace *ns) -{ - if (atomic_dec_and_lock(&ns->count, &mq_lock)) { - mq_clear_sbinfo(ns); - spin_unlock(&mq_lock); - mq_put_mnt(ns); - free_ipc_ns(ns); - } -} - -static void *ipcns_get(struct task_struct *task) -{ - struct ipc_namespace *ns = NULL; - struct nsproxy *nsproxy; - - rcu_read_lock(); - nsproxy = task_nsproxy(task); - if (nsproxy) - ns = get_ipc_ns(nsproxy->ipc_ns); - rcu_read_unlock(); - - return ns; -} - -static void ipcns_put(void *ns) -{ - return put_ipc_ns(ns); -} - -static int ipcns_install(struct nsproxy *nsproxy, void *ns) -{ - /* Ditch state from the old ipc namespace */ - exit_sem(current); - put_ipc_ns(nsproxy->ipc_ns); - nsproxy->ipc_ns = get_ipc_ns(ns); - return 0; -} - -const struct proc_ns_operations ipcns_operations = { - .name = "ipc", - .type = CLONE_NEWIPC, - .get = ipcns_get, - .put = ipcns_put, - .install = ipcns_install, -}; diff --git a/ANDROID_3.4.5/ipc/sem.c b/ANDROID_3.4.5/ipc/sem.c deleted file mode 100644 index 5215a814..00000000 --- a/ANDROID_3.4.5/ipc/sem.c +++ /dev/null @@ -1,1695 +0,0 @@ -/* - * linux/ipc/sem.c - * Copyright (C) 1992 Krishna Balasubramanian - * Copyright (C) 1995 Eric Schenk, Bruno Haible - * - * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com> - * - * SMP-threaded, sysctl's added - * (c) 1999 Manfred Spraul <manfred@colorfullife.com> - * Enforced range limit on SEM_UNDO - * (c) 2001 Red Hat Inc - * Lockless wakeup - * (c) 2003 Manfred Spraul <manfred@colorfullife.com> - * Further wakeup optimizations, documentation - * (c) 2010 Manfred Spraul <manfred@colorfullife.com> - * - * support for audit of ipc object properties and permission changes - * Dustin Kirkland <dustin.kirkland@us.ibm.com> - * - * namespaces support - * OpenVZ, SWsoft Inc. - * Pavel Emelianov <xemul@openvz.org> - * - * Implementation notes: (May 2010) - * This file implements System V semaphores. - * - * User space visible behavior: - * - FIFO ordering for semop() operations (just FIFO, not starvation - * protection) - * - multiple semaphore operations that alter the same semaphore in - * one semop() are handled. - * - sem_ctime (time of last semctl()) is updated in the IPC_SET, SETVAL and - * SETALL calls. - * - two Linux specific semctl() commands: SEM_STAT, SEM_INFO. - * - undo adjustments at process exit are limited to 0..SEMVMX. - * - namespace are supported. - * - SEMMSL, SEMMNS, SEMOPM and SEMMNI can be configured at runtine by writing - * to /proc/sys/kernel/sem. - * - statistics about the usage are reported in /proc/sysvipc/sem. - * - * Internals: - * - scalability: - * - all global variables are read-mostly. - * - semop() calls and semctl(RMID) are synchronized by RCU. - * - most operations do write operations (actually: spin_lock calls) to - * the per-semaphore array structure. - * Thus: Perfect SMP scaling between independent semaphore arrays. - * If multiple semaphores in one array are used, then cache line - * trashing on the semaphore array spinlock will limit the scaling. - * - semncnt and semzcnt are calculated on demand in count_semncnt() and - * count_semzcnt() - * - the task that performs a successful semop() scans the list of all - * sleeping tasks and completes any pending operations that can be fulfilled. - * Semaphores are actively given to waiting tasks (necessary for FIFO). - * (see update_queue()) - * - To improve the scalability, the actual wake-up calls are performed after - * dropping all locks. (see wake_up_sem_queue_prepare(), - * wake_up_sem_queue_do()) - * - All work is done by the waker, the woken up task does not have to do - * anything - not even acquiring a lock or dropping a refcount. - * - A woken up task may not even touch the semaphore array anymore, it may - * have been destroyed already by a semctl(RMID). - * - The synchronizations between wake-ups due to a timeout/signal and a - * wake-up due to a completed semaphore operation is achieved by using an - * intermediate state (IN_WAKEUP). - * - UNDO values are stored in an array (one per process and per - * semaphore array, lazily allocated). For backwards compatibility, multiple - * modes for the UNDO variables are supported (per process, per thread) - * (see copy_semundo, CLONE_SYSVSEM) - * - There are two lists of the pending operations: a per-array list - * and per-semaphore list (stored in the array). This allows to achieve FIFO - * ordering without always scanning all pending operations. - * The worst-case behavior is nevertheless O(N^2) for N wakeups. - */ - -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/init.h> -#include <linux/proc_fs.h> -#include <linux/time.h> -#include <linux/security.h> -#include <linux/syscalls.h> -#include <linux/audit.h> -#include <linux/capability.h> -#include <linux/seq_file.h> -#include <linux/rwsem.h> -#include <linux/nsproxy.h> -#include <linux/ipc_namespace.h> - -#include <asm/uaccess.h> -#include "util.h" - -/* One semaphore structure for each semaphore in the system. */ -struct sem { - int semval; /* current value */ - int sempid; /* pid of last operation */ - struct list_head sem_pending; /* pending single-sop operations */ -}; - -/* One queue for each sleeping process in the system. */ -struct sem_queue { - struct list_head simple_list; /* queue of pending operations */ - struct list_head list; /* queue of pending operations */ - struct task_struct *sleeper; /* this process */ - struct sem_undo *undo; /* undo structure */ - int pid; /* process id of requesting process */ - int status; /* completion status of operation */ - struct sembuf *sops; /* array of pending operations */ - int nsops; /* number of operations */ - int alter; /* does *sops alter the array? */ -}; - -/* Each task has a list of undo requests. They are executed automatically - * when the process exits. - */ -struct sem_undo { - struct list_head list_proc; /* per-process list: * - * all undos from one process - * rcu protected */ - struct rcu_head rcu; /* rcu struct for sem_undo */ - struct sem_undo_list *ulp; /* back ptr to sem_undo_list */ - struct list_head list_id; /* per semaphore array list: - * all undos for one array */ - int semid; /* semaphore set identifier */ - short *semadj; /* array of adjustments */ - /* one per semaphore */ -}; - -/* sem_undo_list controls shared access to the list of sem_undo structures - * that may be shared among all a CLONE_SYSVSEM task group. - */ -struct sem_undo_list { - atomic_t refcnt; - spinlock_t lock; - struct list_head list_proc; -}; - - -#define sem_ids(ns) ((ns)->ids[IPC_SEM_IDS]) - -#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm) -#define sem_checkid(sma, semid) ipc_checkid(&sma->sem_perm, semid) - -static int newary(struct ipc_namespace *, struct ipc_params *); -static void freeary(struct ipc_namespace *, struct kern_ipc_perm *); -#ifdef CONFIG_PROC_FS -static int sysvipc_sem_proc_show(struct seq_file *s, void *it); -#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. - * - */ - -#define sc_semmsl sem_ctls[0] -#define sc_semmns sem_ctls[1] -#define sc_semopm sem_ctls[2] -#define sc_semmni sem_ctls[3] - -void sem_init_ns(struct ipc_namespace *ns) -{ - ns->sc_semmsl = SEMMSL; - ns->sc_semmns = SEMMNS; - ns->sc_semopm = SEMOPM; - ns->sc_semmni = SEMMNI; - ns->used_sems = 0; - ipc_init_ids(&ns->ids[IPC_SEM_IDS]); -} - -#ifdef CONFIG_IPC_NS -void sem_exit_ns(struct ipc_namespace *ns) -{ - free_ipcs(ns, &sem_ids(ns), freeary); - idr_destroy(&ns->ids[IPC_SEM_IDS].ipcs_idr); -} -#endif - -void __init sem_init (void) -{ - sem_init_ns(&init_ipc_ns); - ipc_init_proc_interface("sysvipc/sem", - " key semid perms nsems uid gid cuid cgid otime ctime\n", - IPC_SEM_IDS, sysvipc_sem_proc_show); -} - -/* - * sem_lock_(check_) routines are called in the paths where the rw_mutex - * is not held. - */ -static inline struct sem_array *sem_lock(struct ipc_namespace *ns, int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock(&sem_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct sem_array *)ipcp; - - return container_of(ipcp, struct sem_array, sem_perm); -} - -static inline struct sem_array *sem_lock_check(struct ipc_namespace *ns, - int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock_check(&sem_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct sem_array *)ipcp; - - return container_of(ipcp, struct sem_array, sem_perm); -} - -static inline void sem_lock_and_putref(struct sem_array *sma) -{ - ipc_lock_by_ptr(&sma->sem_perm); - ipc_rcu_putref(sma); -} - -static inline void sem_getref_and_unlock(struct sem_array *sma) -{ - ipc_rcu_getref(sma); - ipc_unlock(&(sma)->sem_perm); -} - -static inline void sem_putref(struct sem_array *sma) -{ - ipc_lock_by_ptr(&sma->sem_perm); - ipc_rcu_putref(sma); - ipc_unlock(&(sma)->sem_perm); -} - -static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) -{ - ipc_rmid(&sem_ids(ns), &s->sem_perm); -} - -/* - * Lockless wakeup algorithm: - * Without the check/retry algorithm a lockless wakeup is possible: - * - queue.status is initialized to -EINTR before blocking. - * - wakeup is performed by - * * unlinking the queue entry from sma->sem_pending - * * setting queue.status to IN_WAKEUP - * This is the notification for the blocked thread that a - * result value is imminent. - * * call wake_up_process - * * set queue.status to the final value. - * - the previously blocked thread checks queue.status: - * * if it's IN_WAKEUP, then it must wait until the value changes - * * if it's not -EINTR, then the operation was completed by - * update_queue. semtimedop can return queue.status without - * performing any operation on the sem array. - * * otherwise it must acquire the spinlock and check what's up. - * - * The two-stage algorithm is necessary to protect against the following - * races: - * - if queue.status is set after wake_up_process, then the woken up idle - * thread could race forward and try (and fail) to acquire sma->lock - * before update_queue had a chance to set queue.status - * - if queue.status is written before wake_up_process and if the - * blocked process is woken up by a signal between writing - * queue.status and the wake_up_process, then the woken up - * process could return from semtimedop and die by calling - * sys_exit before wake_up_process is called. Then wake_up_process - * will oops, because the task structure is already invalid. - * (yes, this happened on s390 with sysv msg). - * - */ -#define IN_WAKEUP 1 - -/** - * newary - Create a new semaphore set - * @ns: namespace - * @params: ptr to the structure that contains key, semflg and nsems - * - * Called with sem_ids.rw_mutex held (as a writer) - */ - -static int newary(struct ipc_namespace *ns, struct ipc_params *params) -{ - int id; - int retval; - struct sem_array *sma; - int size; - key_t key = params->key; - int nsems = params->u.nsems; - int semflg = params->flg; - int i; - - if (!nsems) - return -EINVAL; - if (ns->used_sems + nsems > ns->sc_semmns) - return -ENOSPC; - - size = sizeof (*sma) + nsems * sizeof (struct sem); - sma = ipc_rcu_alloc(size); - if (!sma) { - return -ENOMEM; - } - memset (sma, 0, size); - - sma->sem_perm.mode = (semflg & S_IRWXUGO); - sma->sem_perm.key = key; - - sma->sem_perm.security = NULL; - retval = security_sem_alloc(sma); - if (retval) { - ipc_rcu_putref(sma); - return retval; - } - - id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); - if (id < 0) { - security_sem_free(sma); - ipc_rcu_putref(sma); - return id; - } - ns->used_sems += nsems; - - sma->sem_base = (struct sem *) &sma[1]; - - for (i = 0; i < nsems; i++) - INIT_LIST_HEAD(&sma->sem_base[i].sem_pending); - - sma->complex_count = 0; - INIT_LIST_HEAD(&sma->sem_pending); - INIT_LIST_HEAD(&sma->list_id); - sma->sem_nsems = nsems; - sma->sem_ctime = get_seconds(); - sem_unlock(sma); - - return sma->sem_perm.id; -} - - -/* - * Called with sem_ids.rw_mutex and ipcp locked. - */ -static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) -{ - struct sem_array *sma; - - sma = container_of(ipcp, struct sem_array, sem_perm); - return security_sem_associate(sma, semflg); -} - -/* - * Called with sem_ids.rw_mutex and ipcp locked. - */ -static inline int sem_more_checks(struct kern_ipc_perm *ipcp, - struct ipc_params *params) -{ - struct sem_array *sma; - - sma = container_of(ipcp, struct sem_array, sem_perm); - if (params->u.nsems > sma->sem_nsems) - return -EINVAL; - - return 0; -} - -SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg) -{ - struct ipc_namespace *ns; - struct ipc_ops sem_ops; - struct ipc_params sem_params; - - ns = current->nsproxy->ipc_ns; - - if (nsems < 0 || nsems > ns->sc_semmsl) - return -EINVAL; - - sem_ops.getnew = newary; - sem_ops.associate = sem_security; - sem_ops.more_checks = sem_more_checks; - - sem_params.key = key; - sem_params.flg = semflg; - sem_params.u.nsems = nsems; - - return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); -} - -/* - * 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 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; - result = curr->semval; - - if (!sem_op && result) - goto would_block; - - result += sem_op; - if (result < 0) - goto would_block; - if (result > SEMVMX) - goto out_of_range; - 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) - goto out_of_range; - } - curr->semval = result; - } - - sop--; - while (sop >= sops) { - sma->sem_base[sop->sem_num].sempid = pid; - if (sop->sem_flg & SEM_UNDO) - un->semadj[sop->sem_num] -= sop->sem_op; - sop--; - } - - return 0; - -out_of_range: - result = -ERANGE; - goto undo; - -would_block: - if (sop->sem_flg & IPC_NOWAIT) - result = -EAGAIN; - else - result = 1; - -undo: - sop--; - while (sop >= sops) { - sma->sem_base[sop->sem_num].semval -= sop->sem_op; - sop--; - } - - return result; -} - -/** wake_up_sem_queue_prepare(q, error): Prepare wake-up - * @q: queue entry that must be signaled - * @error: Error value for the signal - * - * Prepare the wake-up of the queue entry q. - */ -static void wake_up_sem_queue_prepare(struct list_head *pt, - struct sem_queue *q, int error) -{ - if (list_empty(pt)) { - /* - * Hold preempt off so that we don't get preempted and have the - * wakee busy-wait until we're scheduled back on. - */ - preempt_disable(); - } - q->status = IN_WAKEUP; - q->pid = error; - - list_add_tail(&q->simple_list, pt); -} - -/** - * wake_up_sem_queue_do(pt) - do the actual wake-up - * @pt: list of tasks to be woken up - * - * Do the actual wake-up. - * The function is called without any locks held, thus the semaphore array - * could be destroyed already and the tasks can disappear as soon as the - * status is set to the actual return code. - */ -static void wake_up_sem_queue_do(struct list_head *pt) -{ - struct sem_queue *q, *t; - int did_something; - - did_something = !list_empty(pt); - list_for_each_entry_safe(q, t, pt, simple_list) { - wake_up_process(q->sleeper); - /* q can disappear immediately after writing q->status. */ - smp_wmb(); - q->status = q->pid; - } - if (did_something) - preempt_enable(); -} - -static void unlink_queue(struct sem_array *sma, struct sem_queue *q) -{ - list_del(&q->list); - if (q->nsops == 1) - list_del(&q->simple_list); - else - sma->complex_count--; -} - -/** check_restart(sma, q) - * @sma: semaphore array - * @q: the operation that just completed - * - * update_queue is O(N^2) when it restarts scanning the whole queue of - * waiting operations. Therefore this function checks if the restart is - * really necessary. It is called after a previously waiting operation - * was completed. - */ -static int check_restart(struct sem_array *sma, struct sem_queue *q) -{ - struct sem *curr; - struct sem_queue *h; - - /* if the operation didn't modify the array, then no restart */ - if (q->alter == 0) - return 0; - - /* pending complex operations are too difficult to analyse */ - if (sma->complex_count) - return 1; - - /* we were a sleeping complex operation. Too difficult */ - if (q->nsops > 1) - return 1; - - curr = sma->sem_base + q->sops[0].sem_num; - - /* No-one waits on this queue */ - if (list_empty(&curr->sem_pending)) - return 0; - - /* the new semaphore value */ - if (curr->semval) { - /* It is impossible that someone waits for the new value: - * - q is a previously sleeping simple operation that - * altered the array. It must be a decrement, because - * simple increments never sleep. - * - The value is not 0, thus wait-for-zero won't proceed. - * - If there are older (higher priority) decrements - * in the queue, then they have observed the original - * semval value and couldn't proceed. The operation - * decremented to value - thus they won't proceed either. - */ - BUG_ON(q->sops[0].sem_op >= 0); - return 0; - } - /* - * semval is 0. Check if there are wait-for-zero semops. - * They must be the first entries in the per-semaphore simple queue - */ - h = list_first_entry(&curr->sem_pending, struct sem_queue, simple_list); - BUG_ON(h->nsops != 1); - BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num); - - /* Yes, there is a wait-for-zero semop. Restart */ - if (h->sops[0].sem_op == 0) - return 1; - - /* Again - no-one is waiting for the new value. */ - return 0; -} - - -/** - * update_queue(sma, semnum): Look for tasks that can be completed. - * @sma: semaphore array. - * @semnum: semaphore that was modified. - * @pt: list head for the tasks that must be woken up. - * - * update_queue must be called after a semaphore in a semaphore array - * was modified. If multiple semaphore were modified, then @semnum - * must be set to -1. - * The tasks that must be woken up are added to @pt. The return code - * is stored in q->pid. - * The function return 1 if at least one semop was completed successfully. - */ -static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) -{ - struct sem_queue *q; - struct list_head *walk; - struct list_head *pending_list; - int offset; - int semop_completed = 0; - - /* if there are complex operations around, then knowing the semaphore - * that was modified doesn't help us. Assume that multiple semaphores - * were modified. - */ - if (sma->complex_count) - semnum = -1; - - if (semnum == -1) { - pending_list = &sma->sem_pending; - offset = offsetof(struct sem_queue, list); - } else { - pending_list = &sma->sem_base[semnum].sem_pending; - offset = offsetof(struct sem_queue, simple_list); - } - -again: - walk = pending_list->next; - while (walk != pending_list) { - int error, restart; - - q = (struct sem_queue *)((char *)walk - offset); - walk = walk->next; - - /* If we are scanning the single sop, per-semaphore list of - * one semaphore and that semaphore is 0, then it is not - * necessary to scan the "alter" entries: simple increments - * that affect only one entry succeed immediately and cannot - * be in the per semaphore pending queue, and decrements - * cannot be successful if the value is already 0. - */ - if (semnum != -1 && sma->sem_base[semnum].semval == 0 && - q->alter) - break; - - error = try_atomic_semop(sma, q->sops, q->nsops, - q->undo, q->pid); - - /* Does q->sleeper still need to sleep? */ - if (error > 0) - continue; - - unlink_queue(sma, q); - - if (error) { - restart = 0; - } else { - semop_completed = 1; - restart = check_restart(sma, q); - } - - wake_up_sem_queue_prepare(pt, q, error); - if (restart) - goto again; - } - return semop_completed; -} - -/** - * do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue - * @sma: semaphore array - * @sops: operations that were performed - * @nsops: number of operations - * @otime: force setting otime - * @pt: list head of the tasks that must be woken up. - * - * do_smart_update() does the required called to update_queue, based on the - * actual changes that were performed on the semaphore array. - * Note that the function does not do the actual wake-up: the caller is - * responsible for calling wake_up_sem_queue_do(@pt). - * It is safe to perform this call after dropping all locks. - */ -static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsops, - int otime, struct list_head *pt) -{ - int i; - - if (sma->complex_count || sops == NULL) { - if (update_queue(sma, -1, pt)) - otime = 1; - goto done; - } - - for (i = 0; i < nsops; i++) { - if (sops[i].sem_op > 0 || - (sops[i].sem_op < 0 && - sma->sem_base[sops[i].sem_num].semval == 0)) - if (update_queue(sma, sops[i].sem_num, pt)) - otime = 1; - } -done: - if (otime) - sma->sem_otime = get_seconds(); -} - - -/* 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; - list_for_each_entry(q, &sma->sem_pending, list) { - 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; - list_for_each_entry(q, &sma->sem_pending, list) { - 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. freeary() is called with sem_ids.rw_mutex locked - * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex - * remains locked on exit. - */ -static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) -{ - struct sem_undo *un, *tu; - struct sem_queue *q, *tq; - struct sem_array *sma = container_of(ipcp, struct sem_array, sem_perm); - struct list_head tasks; - - /* Free the existing undo structures for this semaphore set. */ - assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { - list_del(&un->list_id); - spin_lock(&un->ulp->lock); - un->semid = -1; - list_del_rcu(&un->list_proc); - spin_unlock(&un->ulp->lock); - kfree_rcu(un, rcu); - } - - /* Wake up all pending processes and let them fail with EIDRM. */ - INIT_LIST_HEAD(&tasks); - list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { - unlink_queue(sma, q); - wake_up_sem_queue_prepare(&tasks, q, -EIDRM); - } - - /* Remove the semaphore set from the IDR */ - sem_rmid(ns, sma); - sem_unlock(sma); - - wake_up_sem_queue_do(&tasks); - ns->used_sems -= sma->sem_nsems; - security_sem_free(sma); - ipc_rcu_putref(sma); -} - -static unsigned long copy_semid_to_user(void __user *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; - - memset(&out, 0, sizeof(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(struct ipc_namespace *ns, int semid, - int cmd, int version, union semun arg) -{ - int err; - struct sem_array *sma; - - switch(cmd) { - case IPC_INFO: - case SEM_INFO: - { - struct seminfo seminfo; - int max_id; - - err = security_sem_semctl(NULL, cmd); - if (err) - return err; - - memset(&seminfo,0,sizeof(seminfo)); - seminfo.semmni = ns->sc_semmni; - seminfo.semmns = ns->sc_semmns; - seminfo.semmsl = ns->sc_semmsl; - seminfo.semopm = ns->sc_semopm; - seminfo.semvmx = SEMVMX; - seminfo.semmnu = SEMMNU; - seminfo.semmap = SEMMAP; - seminfo.semume = SEMUME; - down_read(&sem_ids(ns).rw_mutex); - if (cmd == SEM_INFO) { - seminfo.semusz = sem_ids(ns).in_use; - seminfo.semaem = ns->used_sems; - } else { - seminfo.semusz = SEMUSZ; - seminfo.semaem = SEMAEM; - } - max_id = ipc_get_maxid(&sem_ids(ns)); - up_read(&sem_ids(ns).rw_mutex); - if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) - return -EFAULT; - return (max_id < 0) ? 0: max_id; - } - case IPC_STAT: - case SEM_STAT: - { - struct semid64_ds tbuf; - int id; - - if (cmd == SEM_STAT) { - sma = sem_lock(ns, semid); - if (IS_ERR(sma)) - return PTR_ERR(sma); - id = sma->sem_perm.id; - } else { - sma = sem_lock_check(ns, semid); - if (IS_ERR(sma)) - return PTR_ERR(sma); - id = 0; - } - - err = -EACCES; - if (ipcperms(ns, &sma->sem_perm, S_IRUGO)) - goto out_unlock; - - err = security_sem_semctl(sma, cmd); - if (err) - goto out_unlock; - - 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(sma); - if (copy_semid_to_user (arg.buf, &tbuf, version)) - return -EFAULT; - return id; - } - default: - return -EINVAL; - } -out_unlock: - sem_unlock(sma); - return err; -} - -static int semctl_main(struct ipc_namespace *ns, 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; - struct list_head tasks; - - sma = sem_lock_check(ns, semid); - if (IS_ERR(sma)) - return PTR_ERR(sma); - - INIT_LIST_HEAD(&tasks); - nsems = sma->sem_nsems; - - err = -EACCES; - if (ipcperms(ns, &sma->sem_perm, - (cmd == SETVAL || cmd == SETALL) ? S_IWUGO : S_IRUGO)) - goto out_unlock; - - err = security_sem_semctl(sma, cmd); - if (err) - goto out_unlock; - - err = -EACCES; - switch (cmd) { - case GETALL: - { - ushort __user *array = arg.array; - int i; - - if(nsems > SEMMSL_FAST) { - sem_getref_and_unlock(sma); - - sem_io = ipc_alloc(sizeof(ushort)*nsems); - if(sem_io == NULL) { - sem_putref(sma); - return -ENOMEM; - } - - sem_lock_and_putref(sma); - if (sma->sem_perm.deleted) { - sem_unlock(sma); - err = -EIDRM; - goto out_free; - } - } - - for (i = 0; i < sma->sem_nsems; i++) - sem_io[i] = sma->sem_base[i].semval; - sem_unlock(sma); - 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_getref_and_unlock(sma); - - if(nsems > SEMMSL_FAST) { - sem_io = ipc_alloc(sizeof(ushort)*nsems); - if(sem_io == NULL) { - sem_putref(sma); - return -ENOMEM; - } - } - - if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) { - sem_putref(sma); - err = -EFAULT; - goto out_free; - } - - for (i = 0; i < nsems; i++) { - if (sem_io[i] > SEMVMX) { - sem_putref(sma); - err = -ERANGE; - goto out_free; - } - } - sem_lock_and_putref(sma); - if (sma->sem_perm.deleted) { - sem_unlock(sma); - err = -EIDRM; - goto out_free; - } - - for (i = 0; i < nsems; i++) - sma->sem_base[i].semval = sem_io[i]; - - assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry(un, &sma->list_id, list_id) { - for (i = 0; i < nsems; i++) - un->semadj[i] = 0; - } - sma->sem_ctime = get_seconds(); - /* maybe some queued-up processes were waiting for this */ - do_smart_update(sma, NULL, 0, 0, &tasks); - err = 0; - goto out_unlock; - } - /* 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; - 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; - - assert_spin_locked(&sma->sem_perm.lock); - list_for_each_entry(un, &sma->list_id, list_id) - un->semadj[semnum] = 0; - - curr->semval = val; - curr->sempid = task_tgid_vnr(current); - sma->sem_ctime = get_seconds(); - /* maybe some queued-up processes were waiting for this */ - do_smart_update(sma, NULL, 0, 0, &tasks); - err = 0; - goto out_unlock; - } - } -out_unlock: - sem_unlock(sma); - wake_up_sem_queue_do(&tasks); - -out_free: - if(sem_io != fast_sem_io) - ipc_free(sem_io, sizeof(ushort)*nsems); - return err; -} - -static inline unsigned long -copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) -{ - switch(version) { - case IPC_64: - if (copy_from_user(out, buf, sizeof(*out))) - return -EFAULT; - return 0; - case IPC_OLD: - { - struct semid_ds tbuf_old; - - if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) - return -EFAULT; - - out->sem_perm.uid = tbuf_old.sem_perm.uid; - out->sem_perm.gid = tbuf_old.sem_perm.gid; - out->sem_perm.mode = tbuf_old.sem_perm.mode; - - return 0; - } - default: - return -EINVAL; - } -} - -/* - * This function handles some semctl commands which require the rw_mutex - * to be held in write mode. - * NOTE: no locks must be held, the rw_mutex is taken inside this function. - */ -static int semctl_down(struct ipc_namespace *ns, int semid, - int cmd, int version, union semun arg) -{ - struct sem_array *sma; - int err; - struct semid64_ds semid64; - struct kern_ipc_perm *ipcp; - - if(cmd == IPC_SET) { - if (copy_semid_from_user(&semid64, arg.buf, version)) - return -EFAULT; - } - - ipcp = ipcctl_pre_down(ns, &sem_ids(ns), semid, cmd, - &semid64.sem_perm, 0); - if (IS_ERR(ipcp)) - return PTR_ERR(ipcp); - - sma = container_of(ipcp, struct sem_array, sem_perm); - - err = security_sem_semctl(sma, cmd); - if (err) - goto out_unlock; - - switch(cmd){ - case IPC_RMID: - freeary(ns, ipcp); - goto out_up; - case IPC_SET: - ipc_update_perm(&semid64.sem_perm, ipcp); - sma->sem_ctime = get_seconds(); - break; - default: - err = -EINVAL; - } - -out_unlock: - sem_unlock(sma); -out_up: - up_write(&sem_ids(ns).rw_mutex); - return err; -} - -SYSCALL_DEFINE(semctl)(int semid, int semnum, int cmd, union semun arg) -{ - int err = -EINVAL; - int version; - struct ipc_namespace *ns; - - if (semid < 0) - return -EINVAL; - - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; - - switch(cmd) { - case IPC_INFO: - case SEM_INFO: - case IPC_STAT: - case SEM_STAT: - err = semctl_nolock(ns, semid, cmd, version, arg); - return err; - case GETALL: - case GETVAL: - case GETPID: - case GETNCNT: - case GETZCNT: - case SETVAL: - case SETALL: - err = semctl_main(ns,semid,semnum,cmd,version,arg); - return err; - case IPC_RMID: - case IPC_SET: - err = semctl_down(ns, semid, cmd, version, arg); - return err; - default: - return -EINVAL; - } -} -#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS -asmlinkage long SyS_semctl(int semid, int semnum, int cmd, union semun arg) -{ - return SYSC_semctl((int) semid, (int) semnum, (int) cmd, arg); -} -SYSCALL_ALIAS(sys_semctl, SyS_semctl); -#endif - -/* If the task doesn't already have a undo_list, then allocate one - * here. We guarantee there is only one thread using this undo list, - * and current is THE ONE - * - * If this allocation and assignment succeeds, but later - * portions of this code fail, there is no need to free the sem_undo_list. - * Just let it stay associated with the task, and it'll be freed later - * at exit time. - * - * This can block, so callers must hold no locks. - */ -static inline int get_undo_list(struct sem_undo_list **undo_listp) -{ - struct sem_undo_list *undo_list; - - undo_list = current->sysvsem.undo_list; - if (!undo_list) { - undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL); - if (undo_list == NULL) - return -ENOMEM; - spin_lock_init(&undo_list->lock); - atomic_set(&undo_list->refcnt, 1); - INIT_LIST_HEAD(&undo_list->list_proc); - - current->sysvsem.undo_list = undo_list; - } - *undo_listp = undo_list; - return 0; -} - -static struct sem_undo *__lookup_undo(struct sem_undo_list *ulp, int semid) -{ - struct sem_undo *un; - - list_for_each_entry_rcu(un, &ulp->list_proc, list_proc) { - if (un->semid == semid) - return un; - } - return NULL; -} - -static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid) -{ - struct sem_undo *un; - - assert_spin_locked(&ulp->lock); - - un = __lookup_undo(ulp, semid); - if (un) { - list_del_rcu(&un->list_proc); - list_add_rcu(&un->list_proc, &ulp->list_proc); - } - return un; -} - -/** - * find_alloc_undo - Lookup (and if not present create) undo array - * @ns: namespace - * @semid: semaphore array id - * - * The function looks up (and if not present creates) the undo structure. - * The size of the undo structure depends on the size of the semaphore - * array, thus the alloc path is not that straightforward. - * Lifetime-rules: sem_undo is rcu-protected, on success, the function - * performs a rcu_read_lock(). - */ -static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) -{ - struct sem_array *sma; - struct sem_undo_list *ulp; - struct sem_undo *un, *new; - int nsems; - int error; - - error = get_undo_list(&ulp); - if (error) - return ERR_PTR(error); - - rcu_read_lock(); - spin_lock(&ulp->lock); - un = lookup_undo(ulp, semid); - spin_unlock(&ulp->lock); - if (likely(un!=NULL)) - goto out; - rcu_read_unlock(); - - /* no undo structure around - allocate one. */ - /* step 1: figure out the size of the semaphore array */ - sma = sem_lock_check(ns, semid); - if (IS_ERR(sma)) - return ERR_CAST(sma); - - nsems = sma->sem_nsems; - sem_getref_and_unlock(sma); - - /* step 2: allocate new undo structure */ - new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); - if (!new) { - sem_putref(sma); - return ERR_PTR(-ENOMEM); - } - - /* step 3: Acquire the lock on semaphore array */ - sem_lock_and_putref(sma); - if (sma->sem_perm.deleted) { - sem_unlock(sma); - kfree(new); - un = ERR_PTR(-EIDRM); - goto out; - } - spin_lock(&ulp->lock); - - /* - * step 4: check for races: did someone else allocate the undo struct? - */ - un = lookup_undo(ulp, semid); - if (un) { - kfree(new); - goto success; - } - /* step 5: initialize & link new undo structure */ - new->semadj = (short *) &new[1]; - new->ulp = ulp; - new->semid = semid; - assert_spin_locked(&ulp->lock); - list_add_rcu(&new->list_proc, &ulp->list_proc); - assert_spin_locked(&sma->sem_perm.lock); - list_add(&new->list_id, &sma->list_id); - un = new; - -success: - spin_unlock(&ulp->lock); - rcu_read_lock(); - sem_unlock(sma); -out: - return un; -} - - -/** - * get_queue_result - Retrieve the result code from sem_queue - * @q: Pointer to queue structure - * - * Retrieve the return code from the pending queue. If IN_WAKEUP is found in - * q->status, then we must loop until the value is replaced with the final - * value: This may happen if a task is woken up by an unrelated event (e.g. - * signal) and in parallel the task is woken up by another task because it got - * the requested semaphores. - * - * The function can be called with or without holding the semaphore spinlock. - */ -static int get_queue_result(struct sem_queue *q) -{ - int error; - - error = q->status; - while (unlikely(error == IN_WAKEUP)) { - cpu_relax(); - error = q->status; - } - - return error; -} - - -SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, - unsigned, nsops, const struct timespec __user *, timeout) -{ - 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, alter = 0, max; - struct sem_queue queue; - unsigned long jiffies_left = 0; - struct ipc_namespace *ns; - struct list_head tasks; - - ns = current->nsproxy->ipc_ns; - - if (nsops < 1 || semid < 0) - return -EINVAL; - if (nsops > ns->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; - } - if (timeout) { - struct timespec _timeout; - if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) { - error = -EFAULT; - goto out_free; - } - if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || - _timeout.tv_nsec >= 1000000000L) { - error = -EINVAL; - goto out_free; - } - jiffies_left = timespec_to_jiffies(&_timeout); - } - max = 0; - for (sop = sops; sop < sops + nsops; sop++) { - if (sop->sem_num >= max) - max = sop->sem_num; - if (sop->sem_flg & SEM_UNDO) - undos = 1; - if (sop->sem_op != 0) - alter = 1; - } - - if (undos) { - un = find_alloc_undo(ns, semid); - if (IS_ERR(un)) { - error = PTR_ERR(un); - goto out_free; - } - } else - un = NULL; - - INIT_LIST_HEAD(&tasks); - - sma = sem_lock_check(ns, semid); - if (IS_ERR(sma)) { - if (un) - rcu_read_unlock(); - error = PTR_ERR(sma); - goto out_free; - } - - /* - * semid identifiers are not unique - find_alloc_undo may have - * allocated an undo structure, it was invalidated by an RMID - * and now a new array with received the same id. Check and fail. - * This case can be detected checking un->semid. The existence of - * "un" itself is guaranteed by rcu. - */ - error = -EIDRM; - if (un) { - if (un->semid == -1) { - rcu_read_unlock(); - goto out_unlock_free; - } else { - /* - * rcu lock can be released, "un" cannot disappear: - * - sem_lock is acquired, thus IPC_RMID is - * impossible. - * - exit_sem is impossible, it always operates on - * current (or a dead task). - */ - - rcu_read_unlock(); - } - } - - error = -EFBIG; - if (max >= sma->sem_nsems) - goto out_unlock_free; - - error = -EACCES; - if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) - goto out_unlock_free; - - error = security_sem_semop(sma, sops, nsops, alter); - if (error) - goto out_unlock_free; - - error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current)); - if (error <= 0) { - if (alter && error == 0) - do_smart_update(sma, sops, nsops, 1, &tasks); - - goto out_unlock_free; - } - - /* We need to sleep on this operation, so we put the current - * task into the pending queue and go to sleep. - */ - - queue.sops = sops; - queue.nsops = nsops; - queue.undo = un; - queue.pid = task_tgid_vnr(current); - queue.alter = alter; - if (alter) - list_add_tail(&queue.list, &sma->sem_pending); - else - list_add(&queue.list, &sma->sem_pending); - - if (nsops == 1) { - struct sem *curr; - curr = &sma->sem_base[sops->sem_num]; - - if (alter) - list_add_tail(&queue.simple_list, &curr->sem_pending); - else - list_add(&queue.simple_list, &curr->sem_pending); - } else { - INIT_LIST_HEAD(&queue.simple_list); - sma->complex_count++; - } - - queue.status = -EINTR; - queue.sleeper = current; - -sleep_again: - current->state = TASK_INTERRUPTIBLE; - sem_unlock(sma); - - if (timeout) - jiffies_left = schedule_timeout(jiffies_left); - else - schedule(); - - error = get_queue_result(&queue); - - if (error != -EINTR) { - /* fast path: update_queue already obtained all requested - * resources. - * Perform a smp_mb(): User space could assume that semop() - * is a memory barrier: Without the mb(), the cpu could - * speculatively read in user space stale data that was - * overwritten by the previous owner of the semaphore. - */ - smp_mb(); - - goto out_free; - } - - sma = sem_lock(ns, semid); - - /* - * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing. - */ - error = get_queue_result(&queue); - - /* - * Array removed? If yes, leave without sem_unlock(). - */ - if (IS_ERR(sma)) { - goto out_free; - } - - - /* - * If queue.status != -EINTR we are woken up by another process. - * Leave without unlink_queue(), but with sem_unlock(). - */ - - if (error != -EINTR) { - goto out_unlock_free; - } - - /* - * If an interrupt occurred we have to clean up the queue - */ - if (timeout && jiffies_left == 0) - error = -EAGAIN; - - /* - * If the wakeup was spurious, just retry - */ - if (error == -EINTR && !signal_pending(current)) - goto sleep_again; - - unlink_queue(sma, &queue); - -out_unlock_free: - sem_unlock(sma); - - wake_up_sem_queue_do(&tasks); -out_free: - if(sops != fast_sops) - kfree(sops); - return error; -} - -SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, - unsigned, nsops) -{ - return sys_semtimedop(semid, tsops, nsops, NULL); -} - -/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between - * parent and child tasks. - */ - -int copy_semundo(unsigned long clone_flags, struct task_struct *tsk) -{ - struct sem_undo_list *undo_list; - int error; - - if (clone_flags & CLONE_SYSVSEM) { - error = get_undo_list(&undo_list); - if (error) - return error; - atomic_inc(&undo_list->refcnt); - tsk->sysvsem.undo_list = undo_list; - } else - tsk->sysvsem.undo_list = NULL; - - return 0; -} - -/* - * 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 exit_sem(struct task_struct *tsk) -{ - struct sem_undo_list *ulp; - - ulp = tsk->sysvsem.undo_list; - if (!ulp) - return; - tsk->sysvsem.undo_list = NULL; - - if (!atomic_dec_and_test(&ulp->refcnt)) - return; - - for (;;) { - struct sem_array *sma; - struct sem_undo *un; - struct list_head tasks; - int semid; - int i; - - rcu_read_lock(); - un = list_entry_rcu(ulp->list_proc.next, - struct sem_undo, list_proc); - if (&un->list_proc == &ulp->list_proc) - semid = -1; - else - semid = un->semid; - rcu_read_unlock(); - - if (semid == -1) - break; - - sma = sem_lock_check(tsk->nsproxy->ipc_ns, un->semid); - - /* exit_sem raced with IPC_RMID, nothing to do */ - if (IS_ERR(sma)) - continue; - - un = __lookup_undo(ulp, semid); - if (un == NULL) { - /* exit_sem raced with IPC_RMID+semget() that created - * exactly the same semid. Nothing to do. - */ - sem_unlock(sma); - continue; - } - - /* remove un from the linked lists */ - assert_spin_locked(&sma->sem_perm.lock); - list_del(&un->list_id); - - spin_lock(&ulp->lock); - list_del_rcu(&un->list_proc); - spin_unlock(&ulp->lock); - - /* perform adjustments registered in un */ - for (i = 0; i < sma->sem_nsems; i++) { - struct sem * semaphore = &sma->sem_base[i]; - if (un->semadj[i]) { - semaphore->semval += un->semadj[i]; - /* - * Range checks of the new semaphore value, - * not defined by sus: - * - Some unices ignore the undo entirely - * (e.g. HP UX 11i 11.22, Tru64 V5.1) - * - some cap the value (e.g. FreeBSD caps - * at 0, but doesn't enforce SEMVMX) - * - * Linux caps the semaphore value, both at 0 - * and at SEMVMX. - * - * Manfred <manfred@colorfullife.com> - */ - if (semaphore->semval < 0) - semaphore->semval = 0; - if (semaphore->semval > SEMVMX) - semaphore->semval = SEMVMX; - semaphore->sempid = task_tgid_vnr(current); - } - } - /* maybe some queued-up processes were waiting for this */ - INIT_LIST_HEAD(&tasks); - do_smart_update(sma, NULL, 0, 1, &tasks); - sem_unlock(sma); - wake_up_sem_queue_do(&tasks); - - kfree_rcu(un, rcu); - } - kfree(ulp); -} - -#ifdef CONFIG_PROC_FS -static int sysvipc_sem_proc_show(struct seq_file *s, void *it) -{ - struct sem_array *sma = it; - - return seq_printf(s, - "%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n", - sma->sem_perm.key, - sma->sem_perm.id, - 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); -} -#endif diff --git a/ANDROID_3.4.5/ipc/shm.c b/ANDROID_3.4.5/ipc/shm.c deleted file mode 100644 index 406c5b20..00000000 --- a/ANDROID_3.4.5/ipc/shm.c +++ /dev/null @@ -1,1237 +0,0 @@ -/* - * 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> - * - * support for audit of ipc object properties and permission changes - * Dustin Kirkland <dustin.kirkland@us.ibm.com> - * - * namespaces support - * OpenVZ, SWsoft Inc. - * Pavel Emelianov <xemul@openvz.org> - */ - -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/hugetlb.h> -#include <linux/shm.h> -#include <linux/init.h> -#include <linux/file.h> -#include <linux/mman.h> -#include <linux/shmem_fs.h> -#include <linux/security.h> -#include <linux/syscalls.h> -#include <linux/audit.h> -#include <linux/capability.h> -#include <linux/ptrace.h> -#include <linux/seq_file.h> -#include <linux/rwsem.h> -#include <linux/nsproxy.h> -#include <linux/mount.h> -#include <linux/ipc_namespace.h> - -#include <asm/uaccess.h> - -#include "util.h" - -struct shm_file_data { - int id; - struct ipc_namespace *ns; - struct file *file; - const struct vm_operations_struct *vm_ops; -}; - -#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data)) - -static const struct file_operations shm_file_operations; -static const struct vm_operations_struct shm_vm_ops; - -#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS]) - -#define shm_unlock(shp) \ - ipc_unlock(&(shp)->shm_perm) - -static int newseg(struct ipc_namespace *, struct ipc_params *); -static void shm_open(struct vm_area_struct *vma); -static void shm_close(struct vm_area_struct *vma); -static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp); -#ifdef CONFIG_PROC_FS -static int sysvipc_shm_proc_show(struct seq_file *s, void *it); -#endif - -void shm_init_ns(struct ipc_namespace *ns) -{ - ns->shm_ctlmax = SHMMAX; - ns->shm_ctlall = SHMALL; - ns->shm_ctlmni = SHMMNI; - ns->shm_rmid_forced = 0; - ns->shm_tot = 0; - ipc_init_ids(&shm_ids(ns)); -} - -/* - * Called with shm_ids.rw_mutex (writer) and the shp structure locked. - * Only shm_ids.rw_mutex remains locked on exit. - */ -static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) -{ - struct shmid_kernel *shp; - shp = container_of(ipcp, struct shmid_kernel, shm_perm); - - if (shp->shm_nattch){ - shp->shm_perm.mode |= SHM_DEST; - /* Do not find it any more */ - shp->shm_perm.key = IPC_PRIVATE; - shm_unlock(shp); - } else - shm_destroy(ns, shp); -} - -#ifdef CONFIG_IPC_NS -void shm_exit_ns(struct ipc_namespace *ns) -{ - free_ipcs(ns, &shm_ids(ns), do_shm_rmid); - idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr); -} -#endif - -static int __init ipc_ns_init(void) -{ - shm_init_ns(&init_ipc_ns); - return 0; -} - -pure_initcall(ipc_ns_init); - -void __init shm_init (void) -{ - ipc_init_proc_interface("sysvipc/shm", -#if BITS_PER_LONG <= 32 - " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", -#else - " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", -#endif - IPC_SHM_IDS, sysvipc_shm_proc_show); -} - -/* - * shm_lock_(check_) routines are called in the paths where the rw_mutex - * is not necessarily held. - */ -static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct shmid_kernel *)ipcp; - - return container_of(ipcp, struct shmid_kernel, shm_perm); -} - -static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) -{ - rcu_read_lock(); - spin_lock(&ipcp->shm_perm.lock); -} - -static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns, - int id) -{ - struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id); - - if (IS_ERR(ipcp)) - return (struct shmid_kernel *)ipcp; - - return container_of(ipcp, struct shmid_kernel, shm_perm); -} - -static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) -{ - ipc_rmid(&shm_ids(ns), &s->shm_perm); -} - - -/* This is called by fork, once for every shm attach. */ -static void shm_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct shm_file_data *sfd = shm_file_data(file); - struct shmid_kernel *shp; - - shp = shm_lock(sfd->ns, sfd->id); - BUG_ON(IS_ERR(shp)); - shp->shm_atim = get_seconds(); - shp->shm_lprid = task_tgid_vnr(current); - shp->shm_nattch++; - shm_unlock(shp); -} - -/* - * shm_destroy - free the struct shmid_kernel - * - * @ns: namespace - * @shp: struct to free - * - * It has to be called with shp and shm_ids.rw_mutex (writer) locked, - * but returns with shp unlocked and freed. - */ -static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) -{ - ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; - shm_rmid(ns, shp); - shm_unlock(shp); - if (!is_file_hugepages(shp->shm_file)) - shmem_lock(shp->shm_file, 0, shp->mlock_user); - else if (shp->mlock_user) - user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size, - shp->mlock_user); - fput (shp->shm_file); - security_shm_free(shp); - ipc_rcu_putref(shp); -} - -/* - * shm_may_destroy - identifies whether shm segment should be destroyed now - * - * Returns true if and only if there are no active users of the segment and - * one of the following is true: - * - * 1) shmctl(id, IPC_RMID, NULL) was called for this shp - * - * 2) sysctl kernel.shm_rmid_forced is set to 1. - */ -static bool shm_may_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) -{ - return (shp->shm_nattch == 0) && - (ns->shm_rmid_forced || - (shp->shm_perm.mode & SHM_DEST)); -} - -/* - * remove the attach descriptor vma. - * 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 *vma) -{ - struct file * file = vma->vm_file; - struct shm_file_data *sfd = shm_file_data(file); - struct shmid_kernel *shp; - struct ipc_namespace *ns = sfd->ns; - - down_write(&shm_ids(ns).rw_mutex); - /* remove from the list of attaches of the shm segment */ - shp = shm_lock(ns, sfd->id); - BUG_ON(IS_ERR(shp)); - shp->shm_lprid = task_tgid_vnr(current); - shp->shm_dtim = get_seconds(); - shp->shm_nattch--; - if (shm_may_destroy(ns, shp)) - shm_destroy(ns, shp); - else - shm_unlock(shp); - up_write(&shm_ids(ns).rw_mutex); -} - -/* Called with ns->shm_ids(ns).rw_mutex locked */ -static int shm_try_destroy_current(int id, void *p, void *data) -{ - struct ipc_namespace *ns = data; - struct kern_ipc_perm *ipcp = p; - struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); - - if (shp->shm_creator != current) - return 0; - - /* - * Mark it as orphaned to destroy the segment when - * kernel.shm_rmid_forced is changed. - * It is noop if the following shm_may_destroy() returns true. - */ - shp->shm_creator = NULL; - - /* - * Don't even try to destroy it. If shm_rmid_forced=0 and IPC_RMID - * is not set, it shouldn't be deleted here. - */ - if (!ns->shm_rmid_forced) - return 0; - - if (shm_may_destroy(ns, shp)) { - shm_lock_by_ptr(shp); - shm_destroy(ns, shp); - } - return 0; -} - -/* Called with ns->shm_ids(ns).rw_mutex locked */ -static int shm_try_destroy_orphaned(int id, void *p, void *data) -{ - struct ipc_namespace *ns = data; - struct kern_ipc_perm *ipcp = p; - struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); - - /* - * We want to destroy segments without users and with already - * exit'ed originating process. - * - * As shp->* are changed under rw_mutex, it's safe to skip shp locking. - */ - if (shp->shm_creator != NULL) - return 0; - - if (shm_may_destroy(ns, shp)) { - shm_lock_by_ptr(shp); - shm_destroy(ns, shp); - } - return 0; -} - -void shm_destroy_orphaned(struct ipc_namespace *ns) -{ - down_write(&shm_ids(ns).rw_mutex); - if (shm_ids(ns).in_use) - idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns); - up_write(&shm_ids(ns).rw_mutex); -} - - -void exit_shm(struct task_struct *task) -{ - struct ipc_namespace *ns = task->nsproxy->ipc_ns; - - if (shm_ids(ns).in_use == 0) - return; - - /* Destroy all already created segments, but not mapped yet */ - down_write(&shm_ids(ns).rw_mutex); - if (shm_ids(ns).in_use) - idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns); - up_write(&shm_ids(ns).rw_mutex); -} - -static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct file *file = vma->vm_file; - struct shm_file_data *sfd = shm_file_data(file); - - return sfd->vm_ops->fault(vma, vmf); -} - -#ifdef CONFIG_NUMA -static int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new) -{ - struct file *file = vma->vm_file; - struct shm_file_data *sfd = shm_file_data(file); - int err = 0; - if (sfd->vm_ops->set_policy) - err = sfd->vm_ops->set_policy(vma, new); - return err; -} - -static struct mempolicy *shm_get_policy(struct vm_area_struct *vma, - unsigned long addr) -{ - struct file *file = vma->vm_file; - struct shm_file_data *sfd = shm_file_data(file); - struct mempolicy *pol = NULL; - - if (sfd->vm_ops->get_policy) - pol = sfd->vm_ops->get_policy(vma, addr); - else if (vma->vm_policy) - pol = vma->vm_policy; - - return pol; -} -#endif - -static int shm_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct shm_file_data *sfd = shm_file_data(file); - int ret; - - ret = sfd->file->f_op->mmap(sfd->file, vma); - if (ret != 0) - return ret; - sfd->vm_ops = vma->vm_ops; -#ifdef CONFIG_MMU - BUG_ON(!sfd->vm_ops->fault); -#endif - vma->vm_ops = &shm_vm_ops; - shm_open(vma); - - return ret; -} - -static int shm_release(struct inode *ino, struct file *file) -{ - struct shm_file_data *sfd = shm_file_data(file); - - put_ipc_ns(sfd->ns); - shm_file_data(file) = NULL; - kfree(sfd); - return 0; -} - -static int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - struct shm_file_data *sfd = shm_file_data(file); - - if (!sfd->file->f_op->fsync) - return -EINVAL; - return sfd->file->f_op->fsync(sfd->file, start, end, datasync); -} - -static unsigned long shm_get_unmapped_area(struct file *file, - unsigned long addr, unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - struct shm_file_data *sfd = shm_file_data(file); - return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len, - pgoff, flags); -} - -static const struct file_operations shm_file_operations = { - .mmap = shm_mmap, - .fsync = shm_fsync, - .release = shm_release, -#ifndef CONFIG_MMU - .get_unmapped_area = shm_get_unmapped_area, -#endif - .llseek = noop_llseek, -}; - -static const struct file_operations shm_file_operations_huge = { - .mmap = shm_mmap, - .fsync = shm_fsync, - .release = shm_release, - .get_unmapped_area = shm_get_unmapped_area, - .llseek = noop_llseek, -}; - -int is_file_shm_hugepages(struct file *file) -{ - return file->f_op == &shm_file_operations_huge; -} - -static const 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 */ - .fault = shm_fault, -#if defined(CONFIG_NUMA) - .set_policy = shm_set_policy, - .get_policy = shm_get_policy, -#endif -}; - -/** - * newseg - Create a new shared memory segment - * @ns: namespace - * @params: ptr to the structure that contains key, size and shmflg - * - * Called with shm_ids.rw_mutex held as a writer. - */ - -static int newseg(struct ipc_namespace *ns, struct ipc_params *params) -{ - key_t key = params->key; - int shmflg = params->flg; - size_t size = params->u.size; - int error; - struct shmid_kernel *shp; - int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; - struct file * file; - char name[13]; - int id; - vm_flags_t acctflag = 0; - - if (size < SHMMIN || size > ns->shm_ctlmax) - return -EINVAL; - - if (ns->shm_tot + numpages > ns->shm_ctlall) - return -ENOSPC; - - shp = ipc_rcu_alloc(sizeof(*shp)); - if (!shp) - return -ENOMEM; - - shp->shm_perm.key = key; - shp->shm_perm.mode = (shmflg & S_IRWXUGO); - shp->mlock_user = NULL; - - shp->shm_perm.security = NULL; - error = security_shm_alloc(shp); - if (error) { - ipc_rcu_putref(shp); - return error; - } - - sprintf (name, "SYSV%08x", key); - if (shmflg & SHM_HUGETLB) { - /* hugetlb_file_setup applies strict accounting */ - if (shmflg & SHM_NORESERVE) - acctflag = VM_NORESERVE; - file = hugetlb_file_setup(name, 0, size, acctflag, - &shp->mlock_user, HUGETLB_SHMFS_INODE); - } else { - /* - * Do not allow no accounting for OVERCOMMIT_NEVER, even - * if it's asked for. - */ - if ((shmflg & SHM_NORESERVE) && - sysctl_overcommit_memory != OVERCOMMIT_NEVER) - acctflag = VM_NORESERVE; - file = shmem_file_setup(name, size, acctflag); - } - error = PTR_ERR(file); - if (IS_ERR(file)) - goto no_file; - - id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); - if (id < 0) { - error = id; - goto no_id; - } - - shp->shm_cprid = task_tgid_vnr(current); - shp->shm_lprid = 0; - shp->shm_atim = shp->shm_dtim = 0; - shp->shm_ctim = get_seconds(); - shp->shm_segsz = size; - shp->shm_nattch = 0; - shp->shm_file = file; - shp->shm_creator = current; - /* - * shmid gets reported as "inode#" in /proc/pid/maps. - * proc-ps tools use this. Changing this will break them. - */ - file->f_dentry->d_inode->i_ino = shp->shm_perm.id; - - ns->shm_tot += numpages; - error = shp->shm_perm.id; - shm_unlock(shp); - return error; - -no_id: - if (is_file_hugepages(file) && shp->mlock_user) - user_shm_unlock(size, shp->mlock_user); - fput(file); -no_file: - security_shm_free(shp); - ipc_rcu_putref(shp); - return error; -} - -/* - * Called with shm_ids.rw_mutex and ipcp locked. - */ -static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) -{ - struct shmid_kernel *shp; - - shp = container_of(ipcp, struct shmid_kernel, shm_perm); - return security_shm_associate(shp, shmflg); -} - -/* - * Called with shm_ids.rw_mutex and ipcp locked. - */ -static inline int shm_more_checks(struct kern_ipc_perm *ipcp, - struct ipc_params *params) -{ - struct shmid_kernel *shp; - - shp = container_of(ipcp, struct shmid_kernel, shm_perm); - if (shp->shm_segsz < params->u.size) - return -EINVAL; - - return 0; -} - -SYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg) -{ - struct ipc_namespace *ns; - struct ipc_ops shm_ops; - struct ipc_params shm_params; - - ns = current->nsproxy->ipc_ns; - - shm_ops.getnew = newseg; - shm_ops.associate = shm_security; - shm_ops.more_checks = shm_more_checks; - - shm_params.key = key; - shm_params.flg = shmflg; - shm_params.u.size = size; - - return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params); -} - -static inline unsigned long copy_shmid_to_user(void __user *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; - - memset(&out, 0, sizeof(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; - } -} - -static inline unsigned long -copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version) -{ - switch(version) { - case IPC_64: - if (copy_from_user(out, buf, sizeof(*out))) - return -EFAULT; - return 0; - case IPC_OLD: - { - struct shmid_ds tbuf_old; - - if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) - return -EFAULT; - - out->shm_perm.uid = tbuf_old.shm_perm.uid; - out->shm_perm.gid = tbuf_old.shm_perm.gid; - out->shm_perm.mode = tbuf_old.shm_perm.mode; - - return 0; - } - default: - return -EINVAL; - } -} - -static inline unsigned long copy_shminfo_to_user(void __user *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; - } -} - -/* - * Calculate and add used RSS and swap pages of a shm. - * Called with shm_ids.rw_mutex held as a reader - */ -static void shm_add_rss_swap(struct shmid_kernel *shp, - unsigned long *rss_add, unsigned long *swp_add) -{ - struct inode *inode; - - inode = shp->shm_file->f_path.dentry->d_inode; - - if (is_file_hugepages(shp->shm_file)) { - struct address_space *mapping = inode->i_mapping; - struct hstate *h = hstate_file(shp->shm_file); - *rss_add += pages_per_huge_page(h) * mapping->nrpages; - } else { -#ifdef CONFIG_SHMEM - struct shmem_inode_info *info = SHMEM_I(inode); - spin_lock(&info->lock); - *rss_add += inode->i_mapping->nrpages; - *swp_add += info->swapped; - spin_unlock(&info->lock); -#else - *rss_add += inode->i_mapping->nrpages; -#endif - } -} - -/* - * Called with shm_ids.rw_mutex held as a reader - */ -static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, - unsigned long *swp) -{ - int next_id; - int total, in_use; - - *rss = 0; - *swp = 0; - - in_use = shm_ids(ns).in_use; - - for (total = 0, next_id = 0; total < in_use; next_id++) { - struct kern_ipc_perm *ipc; - struct shmid_kernel *shp; - - ipc = idr_find(&shm_ids(ns).ipcs_idr, next_id); - if (ipc == NULL) - continue; - shp = container_of(ipc, struct shmid_kernel, shm_perm); - - shm_add_rss_swap(shp, rss, swp); - - total++; - } -} - -/* - * This function handles some shmctl commands which require the rw_mutex - * to be held in write mode. - * NOTE: no locks must be held, the rw_mutex is taken inside this function. - */ -static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, - struct shmid_ds __user *buf, int version) -{ - struct kern_ipc_perm *ipcp; - struct shmid64_ds shmid64; - struct shmid_kernel *shp; - int err; - - if (cmd == IPC_SET) { - if (copy_shmid_from_user(&shmid64, buf, version)) - return -EFAULT; - } - - ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, - &shmid64.shm_perm, 0); - if (IS_ERR(ipcp)) - return PTR_ERR(ipcp); - - shp = container_of(ipcp, struct shmid_kernel, shm_perm); - - err = security_shm_shmctl(shp, cmd); - if (err) - goto out_unlock; - switch (cmd) { - case IPC_RMID: - do_shm_rmid(ns, ipcp); - goto out_up; - case IPC_SET: - ipc_update_perm(&shmid64.shm_perm, ipcp); - shp->shm_ctim = get_seconds(); - break; - default: - err = -EINVAL; - } -out_unlock: - shm_unlock(shp); -out_up: - up_write(&shm_ids(ns).rw_mutex); - return err; -} - -SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) -{ - struct shmid_kernel *shp; - int err, version; - struct ipc_namespace *ns; - - if (cmd < 0 || shmid < 0) { - err = -EINVAL; - goto out; - } - - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; - - switch (cmd) { /* replace with proc interface ? */ - case IPC_INFO: - { - struct shminfo64 shminfo; - - err = security_shm_shmctl(NULL, cmd); - if (err) - return err; - - memset(&shminfo, 0, sizeof(shminfo)); - shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; - shminfo.shmmax = ns->shm_ctlmax; - shminfo.shmall = ns->shm_ctlall; - - shminfo.shmmin = SHMMIN; - if(copy_shminfo_to_user (buf, &shminfo, version)) - return -EFAULT; - - down_read(&shm_ids(ns).rw_mutex); - err = ipc_get_maxid(&shm_ids(ns)); - up_read(&shm_ids(ns).rw_mutex); - - if(err<0) - err = 0; - goto out; - } - case SHM_INFO: - { - struct shm_info shm_info; - - err = security_shm_shmctl(NULL, cmd); - if (err) - return err; - - memset(&shm_info, 0, sizeof(shm_info)); - down_read(&shm_ids(ns).rw_mutex); - shm_info.used_ids = shm_ids(ns).in_use; - shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); - shm_info.shm_tot = ns->shm_tot; - shm_info.swap_attempts = 0; - shm_info.swap_successes = 0; - err = ipc_get_maxid(&shm_ids(ns)); - up_read(&shm_ids(ns).rw_mutex); - if (copy_to_user(buf, &shm_info, sizeof(shm_info))) { - err = -EFAULT; - goto out; - } - - err = err < 0 ? 0 : err; - goto out; - } - case SHM_STAT: - case IPC_STAT: - { - struct shmid64_ds tbuf; - int result; - - if (cmd == SHM_STAT) { - shp = shm_lock(ns, shmid); - if (IS_ERR(shp)) { - err = PTR_ERR(shp); - goto out; - } - result = shp->shm_perm.id; - } else { - shp = shm_lock_check(ns, shmid); - if (IS_ERR(shp)) { - err = PTR_ERR(shp); - goto out; - } - result = 0; - } - err = -EACCES; - if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) - goto out_unlock; - err = security_shm_shmctl(shp, cmd); - if (err) - goto out_unlock; - memset(&tbuf, 0, sizeof(tbuf)); - 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(shp); - if(copy_shmid_to_user (buf, &tbuf, version)) - err = -EFAULT; - else - err = result; - goto out; - } - case SHM_LOCK: - case SHM_UNLOCK: - { - struct file *shm_file; - - shp = shm_lock_check(ns, shmid); - if (IS_ERR(shp)) { - err = PTR_ERR(shp); - goto out; - } - - audit_ipc_obj(&(shp->shm_perm)); - - if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { - uid_t euid = current_euid(); - err = -EPERM; - if (euid != shp->shm_perm.uid && - euid != shp->shm_perm.cuid) - goto out_unlock; - if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) - goto out_unlock; - } - - err = security_shm_shmctl(shp, cmd); - if (err) - goto out_unlock; - - shm_file = shp->shm_file; - if (is_file_hugepages(shm_file)) - goto out_unlock; - - if (cmd == SHM_LOCK) { - struct user_struct *user = current_user(); - err = shmem_lock(shm_file, 1, user); - if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { - shp->shm_perm.mode |= SHM_LOCKED; - shp->mlock_user = user; - } - goto out_unlock; - } - - /* SHM_UNLOCK */ - if (!(shp->shm_perm.mode & SHM_LOCKED)) - goto out_unlock; - shmem_lock(shm_file, 0, shp->mlock_user); - shp->shm_perm.mode &= ~SHM_LOCKED; - shp->mlock_user = NULL; - get_file(shm_file); - shm_unlock(shp); - shmem_unlock_mapping(shm_file->f_mapping); - fput(shm_file); - goto out; - } - case IPC_RMID: - case IPC_SET: - err = shmctl_down(ns, shmid, cmd, buf, version); - return err; - default: - return -EINVAL; - } - -out_unlock: - shm_unlock(shp); -out: - return err; -} - -/* - * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. - * - * NOTE! Despite the name, this is NOT a direct system call entrypoint. The - * "raddr" thing points to kernel space, and there has to be a wrapper around - * this. - */ -long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) -{ - struct shmid_kernel *shp; - unsigned long addr; - unsigned long size; - struct file * file; - int err; - unsigned long flags; - unsigned long prot; - int acc_mode; - unsigned long user_addr; - struct ipc_namespace *ns; - struct shm_file_data *sfd; - struct path path; - fmode_t f_mode; - - err = -EINVAL; - if (shmid < 0) - goto out; - else if ((addr = (ulong)shmaddr)) { - if (addr & (SHMLBA-1)) { - if (shmflg & SHM_RND) - addr &= ~(SHMLBA-1); /* round down */ - else -#ifndef __ARCH_FORCE_SHMLBA - if (addr & ~PAGE_MASK) -#endif - goto out; - } - flags = MAP_SHARED | MAP_FIXED; - } else { - if ((shmflg & SHM_REMAP)) - goto out; - - flags = MAP_SHARED; - } - - if (shmflg & SHM_RDONLY) { - prot = PROT_READ; - acc_mode = S_IRUGO; - f_mode = FMODE_READ; - } else { - prot = PROT_READ | PROT_WRITE; - acc_mode = S_IRUGO | S_IWUGO; - f_mode = FMODE_READ | FMODE_WRITE; - } - if (shmflg & SHM_EXEC) { - prot |= PROT_EXEC; - acc_mode |= S_IXUGO; - } - - /* - * We cannot rely on the fs check since SYSV IPC does have an - * additional creator id... - */ - ns = current->nsproxy->ipc_ns; - shp = shm_lock_check(ns, shmid); - if (IS_ERR(shp)) { - err = PTR_ERR(shp); - goto out; - } - - err = -EACCES; - if (ipcperms(ns, &shp->shm_perm, acc_mode)) - goto out_unlock; - - err = security_shm_shmat(shp, shmaddr, shmflg); - if (err) - goto out_unlock; - - path = shp->shm_file->f_path; - path_get(&path); - shp->shm_nattch++; - size = i_size_read(path.dentry->d_inode); - shm_unlock(shp); - - err = -ENOMEM; - sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); - if (!sfd) - goto out_put_dentry; - - file = alloc_file(&path, f_mode, - is_file_hugepages(shp->shm_file) ? - &shm_file_operations_huge : - &shm_file_operations); - if (!file) - goto out_free; - - file->private_data = sfd; - file->f_mapping = shp->shm_file->f_mapping; - sfd->id = shp->shm_perm.id; - sfd->ns = get_ipc_ns(ns); - sfd->file = shp->shm_file; - sfd->vm_ops = NULL; - - down_write(¤t->mm->mmap_sem); - if (addr && !(shmflg & SHM_REMAP)) { - err = -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 = do_mmap (file, addr, size, prot, flags, 0); - *raddr = user_addr; - err = 0; - if (IS_ERR_VALUE(user_addr)) - err = (long)user_addr; -invalid: - up_write(¤t->mm->mmap_sem); - - fput(file); - -out_nattch: - down_write(&shm_ids(ns).rw_mutex); - shp = shm_lock(ns, shmid); - BUG_ON(IS_ERR(shp)); - shp->shm_nattch--; - if (shm_may_destroy(ns, shp)) - shm_destroy(ns, shp); - else - shm_unlock(shp); - up_write(&shm_ids(ns).rw_mutex); - -out: - return err; - -out_unlock: - shm_unlock(shp); - goto out; - -out_free: - kfree(sfd); -out_put_dentry: - path_put(&path); - goto out_nattch; -} - -SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) -{ - unsigned long ret; - long err; - - err = do_shmat(shmid, shmaddr, shmflg, &ret); - if (err) - return err; - force_successful_syscall_return(); - return (long)ret; -} - -/* - * detach and kill segment if marked destroyed. - * The work is done in shm_close. - */ -SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long addr = (unsigned long)shmaddr; - int retval = -EINVAL; -#ifdef CONFIG_MMU - loff_t size = 0; - struct vm_area_struct *next; -#endif - - if (addr & ~PAGE_MASK) - return retval; - - down_write(&mm->mmap_sem); - - /* - * This function tries to be smart and unmap shm segments that - * were modified by partial mlock or munmap calls: - * - It first determines the size of the shm segment that should be - * unmapped: It searches for a vma that is backed by shm and that - * started at address shmaddr. It records it's size and then unmaps - * it. - * - Then it unmaps all shm vmas that started at shmaddr and that - * are within the initially determined size. - * Errors from do_munmap are ignored: the function only fails if - * it's called with invalid parameters or if it's called to unmap - * a part of a vma. Both calls in this function are for full vmas, - * the parameters are directly copied from the vma itself and always - * valid - therefore do_munmap cannot fail. (famous last words?) - */ - /* - * If it had been mremap()'d, the starting address would not - * match the usual checks anyway. So assume all vma's are - * above the starting address given. - */ - vma = find_vma(mm, addr); - -#ifdef CONFIG_MMU - while (vma) { - next = vma->vm_next; - - /* - * Check if the starting address would match, i.e. it's - * a fragment created by mprotect() and/or munmap(), or it - * otherwise it starts at this address with no hassles. - */ - if ((vma->vm_ops == &shm_vm_ops) && - (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) { - - - size = vma->vm_file->f_path.dentry->d_inode->i_size; - do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); - /* - * We discovered the size of the shm segment, so - * break out of here and fall through to the next - * loop that uses the size information to stop - * searching for matching vma's. - */ - retval = 0; - vma = next; - break; - } - vma = next; - } - - /* - * We need look no further than the maximum address a fragment - * could possibly have landed at. Also cast things to loff_t to - * prevent overflows and make comparisons vs. equal-width types. - */ - size = PAGE_ALIGN(size); - while (vma && (loff_t)(vma->vm_end - addr) <= size) { - next = vma->vm_next; - - /* finding a matching vma now does not alter retval */ - if ((vma->vm_ops == &shm_vm_ops) && - (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) - - do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); - vma = next; - } - -#else /* CONFIG_MMU */ - /* under NOMMU conditions, the exact address to be destroyed must be - * given */ - retval = -EINVAL; - if (vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) { - do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); - retval = 0; - } - -#endif - - up_write(&mm->mmap_sem); - return retval; -} - -#ifdef CONFIG_PROC_FS -static int sysvipc_shm_proc_show(struct seq_file *s, void *it) -{ - struct shmid_kernel *shp = it; - unsigned long rss = 0, swp = 0; - - shm_add_rss_swap(shp, &rss, &swp); - -#if BITS_PER_LONG <= 32 -#define SIZE_SPEC "%10lu" -#else -#define SIZE_SPEC "%21lu" -#endif - - return seq_printf(s, - "%10d %10d %4o " SIZE_SPEC " %5u %5u " - "%5lu %5u %5u %5u %5u %10lu %10lu %10lu " - SIZE_SPEC " " SIZE_SPEC "\n", - shp->shm_perm.key, - shp->shm_perm.id, - shp->shm_perm.mode, - 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, - rss * PAGE_SIZE, - swp * PAGE_SIZE); -} -#endif diff --git a/ANDROID_3.4.5/ipc/syscall.c b/ANDROID_3.4.5/ipc/syscall.c deleted file mode 100644 index 1d6f53f6..00000000 --- a/ANDROID_3.4.5/ipc/syscall.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * sys_ipc() is the old de-multiplexer for the SysV IPC calls. - * - * This is really horribly ugly, and new architectures should just wire up - * the individual syscalls instead. - */ -#include <linux/unistd.h> - -#ifdef __ARCH_WANT_SYS_IPC -#include <linux/errno.h> -#include <linux/ipc.h> -#include <linux/shm.h> -#include <linux/syscalls.h> -#include <linux/uaccess.h> - -SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, - unsigned long, third, void __user *, ptr, long, fifth) -{ - int version, ret; - - version = call >> 16; /* hack for backward compatibility */ - call &= 0xffff; - - switch (call) { - case SEMOP: - return sys_semtimedop(first, (struct sembuf __user *)ptr, - second, NULL); - case SEMTIMEDOP: - return sys_semtimedop(first, (struct sembuf __user *)ptr, - second, - (const struct timespec __user *)fifth); - - case SEMGET: - return sys_semget(first, second, third); - case SEMCTL: { - union semun fourth; - if (!ptr) - return -EINVAL; - if (get_user(fourth.__pad, (void __user * __user *) ptr)) - return -EFAULT; - return sys_semctl(first, second, third, fourth); - } - - case MSGSND: - return sys_msgsnd(first, (struct msgbuf __user *) ptr, - second, third); - case MSGRCV: - switch (version) { - case 0: { - struct ipc_kludge tmp; - if (!ptr) - return -EINVAL; - - if (copy_from_user(&tmp, - (struct ipc_kludge __user *) ptr, - sizeof(tmp))) - return -EFAULT; - return sys_msgrcv(first, tmp.msgp, second, - tmp.msgtyp, third); - } - default: - return sys_msgrcv(first, - (struct msgbuf __user *) ptr, - second, fifth, third); - } - case MSGGET: - return sys_msgget((key_t) first, second); - case MSGCTL: - return sys_msgctl(first, second, (struct msqid_ds __user *)ptr); - - case SHMAT: - switch (version) { - default: { - unsigned long raddr; - ret = do_shmat(first, (char __user *)ptr, - second, &raddr); - if (ret) - return ret; - return put_user(raddr, (unsigned long __user *) third); - } - case 1: - /* - * This was the entry point for kernel-originating calls - * from iBCS2 in 2.2 days. - */ - return -EINVAL; - } - case SHMDT: - return sys_shmdt((char __user *)ptr); - case SHMGET: - return sys_shmget(first, second, third); - case SHMCTL: - return sys_shmctl(first, second, - (struct shmid_ds __user *) ptr); - default: - return -ENOSYS; - } -} -#endif diff --git a/ANDROID_3.4.5/ipc/util.c b/ANDROID_3.4.5/ipc/util.c deleted file mode 100644 index 75261a31..00000000 --- a/ANDROID_3.4.5/ipc/util.c +++ /dev/null @@ -1,987 +0,0 @@ -/* - * 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 <manfred@colorfullife.com> - * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary(). - * Mingming Cao <cmm@us.ibm.com> - * Mar 2006 - support for audit of ipc object properties - * Dustin Kirkland <dustin.kirkland@us.ibm.com> - * Jun 2006 - namespaces ssupport - * OpenVZ, SWsoft Inc. - * Pavel Emelianov <xemul@openvz.org> - */ - -#include <linux/mm.h> -#include <linux/shm.h> -#include <linux/init.h> -#include <linux/msg.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/capability.h> -#include <linux/highuid.h> -#include <linux/security.h> -#include <linux/rcupdate.h> -#include <linux/workqueue.h> -#include <linux/seq_file.h> -#include <linux/proc_fs.h> -#include <linux/audit.h> -#include <linux/nsproxy.h> -#include <linux/rwsem.h> -#include <linux/memory.h> -#include <linux/ipc_namespace.h> - -#include <asm/unistd.h> - -#include "util.h" - -struct ipc_proc_iface { - const char *path; - const char *header; - int ids; - int (*show)(struct seq_file *, void *); -}; - -#ifdef CONFIG_MEMORY_HOTPLUG - -static void ipc_memory_notifier(struct work_struct *work) -{ - ipcns_notify(IPCNS_MEMCHANGED); -} - -static DECLARE_WORK(ipc_memory_wq, ipc_memory_notifier); - - -static int ipc_memory_callback(struct notifier_block *self, - unsigned long action, void *arg) -{ - switch (action) { - case MEM_ONLINE: /* memory successfully brought online */ - case MEM_OFFLINE: /* or offline: it's time to recompute msgmni */ - /* - * This is done by invoking the ipcns notifier chain with the - * IPC_MEMCHANGED event. - * In order not to keep the lock on the hotplug memory chain - * for too long, queue a work item that will, when waken up, - * activate the ipcns notification chain. - * No need to keep several ipc work items on the queue. - */ - if (!work_pending(&ipc_memory_wq)) - schedule_work(&ipc_memory_wq); - break; - case MEM_GOING_ONLINE: - case MEM_GOING_OFFLINE: - case MEM_CANCEL_ONLINE: - case MEM_CANCEL_OFFLINE: - default: - break; - } - - return NOTIFY_OK; -} - -#endif /* CONFIG_MEMORY_HOTPLUG */ - -/** - * ipc_init - initialise IPC subsystem - * - * The various system5 IPC resources (semaphores, messages and shared - * memory) are initialised - * A callback routine is registered into the memory hotplug notifier - * chain: since msgmni scales to lowmem this callback routine will be - * called upon successful memory add / remove to recompute msmgni. - */ - -static int __init ipc_init(void) -{ - sem_init(); - msg_init(); - shm_init(); - hotplug_memory_notifier(ipc_memory_callback, IPC_CALLBACK_PRI); - register_ipcns_notifier(&init_ipc_ns); - return 0; -} -__initcall(ipc_init); - -/** - * ipc_init_ids - initialise IPC identifiers - * @ids: Identifier set - * - * Set up the sequence range to use for the ipc identifier range (limited - * below IPCMNI) then initialise the ids idr. - */ - -void ipc_init_ids(struct ipc_ids *ids) -{ - init_rwsem(&ids->rw_mutex); - - ids->in_use = 0; - 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; - } - - idr_init(&ids->ipcs_idr); -} - -#ifdef CONFIG_PROC_FS -static const struct file_operations sysvipc_proc_fops; -/** - * ipc_init_proc_interface - Create a proc interface for sysipc types using a seq_file interface. - * @path: Path in procfs - * @header: Banner to be printed at the beginning of the file. - * @ids: ipc id table to iterate. - * @show: show routine. - */ -void __init ipc_init_proc_interface(const char *path, const char *header, - int ids, int (*show)(struct seq_file *, void *)) -{ - struct proc_dir_entry *pde; - struct ipc_proc_iface *iface; - - iface = kmalloc(sizeof(*iface), GFP_KERNEL); - if (!iface) - return; - iface->path = path; - iface->header = header; - iface->ids = ids; - iface->show = show; - - pde = proc_create_data(path, - S_IRUGO, /* world readable */ - NULL, /* parent dir */ - &sysvipc_proc_fops, - iface); - if (!pde) { - kfree(iface); - } -} -#endif - -/** - * ipc_findkey - find a key in an ipc identifier set - * @ids: Identifier set - * @key: The key to find - * - * Requires ipc_ids.rw_mutex locked. - * Returns the LOCKED pointer to the ipc structure if found or NULL - * if not. - * If key is found ipc points to the owning ipc structure - */ - -static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) -{ - struct kern_ipc_perm *ipc; - int next_id; - int total; - - for (total = 0, next_id = 0; total < ids->in_use; next_id++) { - ipc = idr_find(&ids->ipcs_idr, next_id); - - if (ipc == NULL) - continue; - - if (ipc->key != key) { - total++; - continue; - } - - ipc_lock_by_ptr(ipc); - return ipc; - } - - return NULL; -} - -/** - * ipc_get_maxid - get the last assigned id - * @ids: IPC identifier set - * - * Called with ipc_ids.rw_mutex held. - */ - -int ipc_get_maxid(struct ipc_ids *ids) -{ - struct kern_ipc_perm *ipc; - int max_id = -1; - int total, id; - - if (ids->in_use == 0) - return -1; - - if (ids->in_use == IPCMNI) - return IPCMNI - 1; - - /* Look for the last assigned id */ - total = 0; - for (id = 0; id < IPCMNI && total < ids->in_use; id++) { - ipc = idr_find(&ids->ipcs_idr, id); - if (ipc != NULL) { - max_id = id; - total++; - } - } - return max_id; -} - -/** - * ipc_addid - add an IPC identifier - * @ids: IPC identifier set - * @new: new IPC permission set - * @size: limit for the number of used ids - * - * Add an entry 'new' to the IPC ids idr. The permissions object is - * initialised and the first free entry is set up and the id assigned - * is returned. The 'new' entry is returned in a locked state on success. - * On failure the entry is not locked and a negative err-code is returned. - * - * Called with ipc_ids.rw_mutex held as a writer. - */ - -int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) -{ - uid_t euid; - gid_t egid; - int id, err; - - if (size > IPCMNI) - size = IPCMNI; - - if (ids->in_use >= size) - return -ENOSPC; - - spin_lock_init(&new->lock); - new->deleted = 0; - rcu_read_lock(); - spin_lock(&new->lock); - - err = idr_get_new(&ids->ipcs_idr, new, &id); - if (err) { - spin_unlock(&new->lock); - rcu_read_unlock(); - return err; - } - - ids->in_use++; - - current_euid_egid(&euid, &egid); - new->cuid = new->uid = euid; - new->gid = new->cgid = egid; - - new->seq = ids->seq++; - if(ids->seq > ids->seq_max) - ids->seq = 0; - - new->id = ipc_buildid(id, new->seq); - return id; -} - -/** - * ipcget_new - create a new ipc object - * @ns: namespace - * @ids: IPC identifer set - * @ops: the actual creation routine to call - * @params: its parameters - * - * This routine is called by sys_msgget, sys_semget() and sys_shmget() - * when the key is IPC_PRIVATE. - */ -static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, - struct ipc_ops *ops, struct ipc_params *params) -{ - int err; -retry: - err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); - - if (!err) - return -ENOMEM; - - down_write(&ids->rw_mutex); - err = ops->getnew(ns, params); - up_write(&ids->rw_mutex); - - if (err == -EAGAIN) - goto retry; - - return err; -} - -/** - * ipc_check_perms - check security and permissions for an IPC - * @ns: IPC namespace - * @ipcp: ipc permission set - * @ops: the actual security routine to call - * @params: its parameters - * - * This routine is called by sys_msgget(), sys_semget() and sys_shmget() - * when the key is not IPC_PRIVATE and that key already exists in the - * ids IDR. - * - * On success, the IPC id is returned. - * - * It is called with ipc_ids.rw_mutex and ipcp->lock held. - */ -static int ipc_check_perms(struct ipc_namespace *ns, - struct kern_ipc_perm *ipcp, - struct ipc_ops *ops, - struct ipc_params *params) -{ - int err; - - if (ipcperms(ns, ipcp, params->flg)) - err = -EACCES; - else { - err = ops->associate(ipcp, params->flg); - if (!err) - err = ipcp->id; - } - - return err; -} - -/** - * ipcget_public - get an ipc object or create a new one - * @ns: namespace - * @ids: IPC identifer set - * @ops: the actual creation routine to call - * @params: its parameters - * - * This routine is called by sys_msgget, sys_semget() and sys_shmget() - * when the key is not IPC_PRIVATE. - * It adds a new entry if the key is not found and does some permission - * / security checkings if the key is found. - * - * On success, the ipc id is returned. - */ -static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, - struct ipc_ops *ops, struct ipc_params *params) -{ - struct kern_ipc_perm *ipcp; - int flg = params->flg; - int err; -retry: - err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); - - /* - * Take the lock as a writer since we are potentially going to add - * a new entry + read locks are not "upgradable" - */ - down_write(&ids->rw_mutex); - ipcp = ipc_findkey(ids, params->key); - if (ipcp == NULL) { - /* key not used */ - if (!(flg & IPC_CREAT)) - err = -ENOENT; - else if (!err) - err = -ENOMEM; - else - err = ops->getnew(ns, params); - } else { - /* ipc object has been locked by ipc_findkey() */ - - if (flg & IPC_CREAT && flg & IPC_EXCL) - err = -EEXIST; - else { - err = 0; - if (ops->more_checks) - err = ops->more_checks(ipcp, params); - if (!err) - /* - * ipc_check_perms returns the IPC id on - * success - */ - err = ipc_check_perms(ns, ipcp, ops, params); - } - ipc_unlock(ipcp); - } - up_write(&ids->rw_mutex); - - if (err == -EAGAIN) - goto retry; - - return err; -} - - -/** - * ipc_rmid - remove an IPC identifier - * @ids: IPC identifier set - * @ipcp: ipc perm structure containing the identifier to remove - * - * ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held - * before this function is called, and remain locked on the exit. - */ - -void ipc_rmid(struct ipc_ids *ids, struct kern_ipc_perm *ipcp) -{ - int lid = ipcid_to_idx(ipcp->id); - - idr_remove(&ids->ipcs_idr, lid); - - ids->in_use--; - - ipcp->deleted = 1; - - return; -} - -/** - * 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); -} - -/* - * rcu allocations: - * There are three headers that are prepended to the actual allocation: - * - during use: ipc_rcu_hdr. - * - during the rcu grace period: ipc_rcu_grace. - * - [only if vmalloc]: ipc_rcu_sched. - * Their lifetime doesn't overlap, thus the headers share the same memory. - * Unlike a normal union, they are right-aligned, thus some container_of - * forward/backward casting is necessary: - */ -struct ipc_rcu_hdr -{ - int refcount; - int is_vmalloc; - void *data[0]; -}; - - -struct ipc_rcu_grace -{ - struct rcu_head rcu; - /* "void *" makes sure alignment of following data is sane. */ - void *data[0]; -}; - -struct ipc_rcu_sched -{ - struct work_struct work; - /* "void *" makes sure alignment of following data is sane. */ - void *data[0]; -}; - -#define HDRLEN_KMALLOC (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \ - sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr)) -#define HDRLEN_VMALLOC (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \ - sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC) - -static inline int rcu_use_vmalloc(int size) -{ - /* Too big for a single page? */ - if (HDRLEN_KMALLOC + size > PAGE_SIZE) - return 1; - return 0; -} - -/** - * ipc_rcu_alloc - allocate ipc and rcu space - * @size: size desired - * - * Allocate memory for the rcu header structure + the object. - * Returns the pointer to the object. - * NULL is returned if the allocation fails. - */ - -void* ipc_rcu_alloc(int size) -{ - void* out; - /* - * We prepend the allocation with the rcu struct, and - * workqueue if necessary (for vmalloc). - */ - if (rcu_use_vmalloc(size)) { - out = vmalloc(HDRLEN_VMALLOC + size); - if (out) { - out += HDRLEN_VMALLOC; - container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1; - container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; - } - } else { - out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); - if (out) { - out += HDRLEN_KMALLOC; - container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0; - container_of(out, struct ipc_rcu_hdr, data)->refcount = 1; - } - } - - return out; -} - -void ipc_rcu_getref(void *ptr) -{ - container_of(ptr, struct ipc_rcu_hdr, data)->refcount++; -} - -static void ipc_do_vfree(struct work_struct *work) -{ - vfree(container_of(work, struct ipc_rcu_sched, work)); -} - -/** - * ipc_schedule_free - free ipc + rcu space - * @head: RCU callback structure for queued work - * - * Since RCU callback function is called in bh, - * we need to defer the vfree to schedule_work(). - */ -static void ipc_schedule_free(struct rcu_head *head) -{ - struct ipc_rcu_grace *grace; - struct ipc_rcu_sched *sched; - - grace = container_of(head, struct ipc_rcu_grace, rcu); - sched = container_of(&(grace->data[0]), struct ipc_rcu_sched, - data[0]); - - INIT_WORK(&sched->work, ipc_do_vfree); - schedule_work(&sched->work); -} - -void ipc_rcu_putref(void *ptr) -{ - if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0) - return; - - if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) { - call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu, - ipc_schedule_free); - } else { - kfree_rcu(container_of(ptr, struct ipc_rcu_grace, data), rcu); - } -} - -/** - * ipcperms - check IPC permissions - * @ns: IPC namespace - * @ipcp: IPC permission set - * @flag: desired permission set. - * - * Check user, group, other permissions for access - * to ipc resources. return 0 if allowed - * - * @flag will most probably be 0 or S_...UGO from <linux/stat.h> - */ - -int ipcperms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, short flag) -{ - uid_t euid = current_euid(); - int requested_mode, granted_mode; - - audit_ipc_obj(ipcp); - requested_mode = (flag >> 6) | (flag >> 3) | flag; - granted_mode = ipcp->mode; - if (euid == ipcp->cuid || - 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) && - !ns_capable(ns->user_ns, CAP_IPC_OWNER)) - return -1; - - return security_ipc_permission(ipcp, flag); -} - -/* - * 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 new ipc permissions to old - * @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; - SET_UID(out->uid, in->uid); - SET_GID(out->gid, in->gid); - SET_UID(out->cuid, in->cuid); - SET_GID(out->cgid, in->cgid); - out->mode = in->mode; - out->seq = in->seq; -} - -/** - * ipc_lock - Lock an ipc structure without rw_mutex held - * @ids: IPC identifier set - * @id: ipc id to look for - * - * Look for an id in the ipc ids idr and lock the associated ipc object. - * - * The ipc object is locked on exit. - */ - -struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id) -{ - struct kern_ipc_perm *out; - int lid = ipcid_to_idx(id); - - rcu_read_lock(); - out = idr_find(&ids->ipcs_idr, lid); - if (out == NULL) { - rcu_read_unlock(); - return ERR_PTR(-EINVAL); - } - - spin_lock(&out->lock); - - /* ipc_rmid() may have already freed the ID while ipc_lock - * was spinning: here verify that the structure is still valid - */ - if (out->deleted) { - spin_unlock(&out->lock); - rcu_read_unlock(); - return ERR_PTR(-EINVAL); - } - - return out; -} - -struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id) -{ - struct kern_ipc_perm *out; - - out = ipc_lock(ids, id); - if (IS_ERR(out)) - return out; - - if (ipc_checkid(out, id)) { - ipc_unlock(out); - return ERR_PTR(-EIDRM); - } - - return out; -} - -/** - * ipcget - Common sys_*get() code - * @ns : namsepace - * @ids : IPC identifier set - * @ops : operations to be called on ipc object creation, permission checks - * and further checks - * @params : the parameters needed by the previous operations. - * - * Common routine called by sys_msgget(), sys_semget() and sys_shmget(). - */ -int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, - struct ipc_ops *ops, struct ipc_params *params) -{ - if (params->key == IPC_PRIVATE) - return ipcget_new(ns, ids, ops, params); - else - return ipcget_public(ns, ids, ops, params); -} - -/** - * ipc_update_perm - update the permissions of an IPC. - * @in: the permission given as input. - * @out: the permission of the ipc to set. - */ -void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) -{ - out->uid = in->uid; - out->gid = in->gid; - out->mode = (out->mode & ~S_IRWXUGO) - | (in->mode & S_IRWXUGO); -} - -/** - * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd - * @ns: the ipc namespace - * @ids: the table of ids where to look for the ipc - * @id: the id of the ipc to retrieve - * @cmd: the cmd to check - * @perm: the permission to set - * @extra_perm: one extra permission parameter used by msq - * - * This function does some common audit and permissions check for some IPC_XXX - * cmd and is called from semctl_down, shmctl_down and msgctl_down. - * It must be called without any lock held and - * - retrieves the ipc with the given id in the given table. - * - performs some audit and permission check, depending on the given cmd - * - returns the ipc with both ipc and rw_mutex locks held in case of success - * or an err-code without any lock held otherwise. - */ -struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, - struct ipc_ids *ids, int id, int cmd, - struct ipc64_perm *perm, int extra_perm) -{ - struct kern_ipc_perm *ipcp; - uid_t euid; - int err; - - down_write(&ids->rw_mutex); - ipcp = ipc_lock_check(ids, id); - if (IS_ERR(ipcp)) { - err = PTR_ERR(ipcp); - goto out_up; - } - - audit_ipc_obj(ipcp); - if (cmd == IPC_SET) - audit_ipc_set_perm(extra_perm, perm->uid, - perm->gid, perm->mode); - - euid = current_euid(); - if (euid == ipcp->cuid || euid == ipcp->uid || - ns_capable(ns->user_ns, CAP_SYS_ADMIN)) - return ipcp; - - err = -EPERM; - ipc_unlock(ipcp); -out_up: - up_write(&ids->rw_mutex); - return ERR_PTR(err); -} - -#ifdef __ARCH_WANT_IPC_PARSE_VERSION - - -/** - * 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) -{ - if (*cmd & IPC_64) { - *cmd ^= IPC_64; - return IPC_64; - } else { - return IPC_OLD; - } -} - -#endif /* __ARCH_WANT_IPC_PARSE_VERSION */ - -#ifdef CONFIG_PROC_FS -struct ipc_proc_iter { - struct ipc_namespace *ns; - struct ipc_proc_iface *iface; -}; - -/* - * This routine locks the ipc structure found at least at position pos. - */ -static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos, - loff_t *new_pos) -{ - struct kern_ipc_perm *ipc; - int total, id; - - total = 0; - for (id = 0; id < pos && total < ids->in_use; id++) { - ipc = idr_find(&ids->ipcs_idr, id); - if (ipc != NULL) - total++; - } - - if (total >= ids->in_use) - return NULL; - - for ( ; pos < IPCMNI; pos++) { - ipc = idr_find(&ids->ipcs_idr, pos); - if (ipc != NULL) { - *new_pos = pos + 1; - ipc_lock_by_ptr(ipc); - return ipc; - } - } - - /* Out of range - return NULL to terminate iteration */ - return NULL; -} - -static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos) -{ - struct ipc_proc_iter *iter = s->private; - struct ipc_proc_iface *iface = iter->iface; - struct kern_ipc_perm *ipc = it; - - /* If we had an ipc id locked before, unlock it */ - if (ipc && ipc != SEQ_START_TOKEN) - ipc_unlock(ipc); - - return sysvipc_find_ipc(&iter->ns->ids[iface->ids], *pos, pos); -} - -/* - * File positions: pos 0 -> header, pos n -> ipc id = n - 1. - * SeqFile iterator: iterator value locked ipc pointer or SEQ_TOKEN_START. - */ -static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) -{ - struct ipc_proc_iter *iter = s->private; - struct ipc_proc_iface *iface = iter->iface; - struct ipc_ids *ids; - - ids = &iter->ns->ids[iface->ids]; - - /* - * Take the lock - this will be released by the corresponding - * call to stop(). - */ - down_read(&ids->rw_mutex); - - /* pos < 0 is invalid */ - if (*pos < 0) - return NULL; - - /* pos == 0 means header */ - if (*pos == 0) - return SEQ_START_TOKEN; - - /* Find the (pos-1)th ipc */ - return sysvipc_find_ipc(ids, *pos - 1, pos); -} - -static void sysvipc_proc_stop(struct seq_file *s, void *it) -{ - struct kern_ipc_perm *ipc = it; - struct ipc_proc_iter *iter = s->private; - struct ipc_proc_iface *iface = iter->iface; - struct ipc_ids *ids; - - /* If we had a locked structure, release it */ - if (ipc && ipc != SEQ_START_TOKEN) - ipc_unlock(ipc); - - ids = &iter->ns->ids[iface->ids]; - /* Release the lock we took in start() */ - up_read(&ids->rw_mutex); -} - -static int sysvipc_proc_show(struct seq_file *s, void *it) -{ - struct ipc_proc_iter *iter = s->private; - struct ipc_proc_iface *iface = iter->iface; - - if (it == SEQ_START_TOKEN) - return seq_puts(s, iface->header); - - return iface->show(s, it); -} - -static const struct seq_operations sysvipc_proc_seqops = { - .start = sysvipc_proc_start, - .stop = sysvipc_proc_stop, - .next = sysvipc_proc_next, - .show = sysvipc_proc_show, -}; - -static int sysvipc_proc_open(struct inode *inode, struct file *file) -{ - int ret; - struct seq_file *seq; - struct ipc_proc_iter *iter; - - ret = -ENOMEM; - iter = kmalloc(sizeof(*iter), GFP_KERNEL); - if (!iter) - goto out; - - ret = seq_open(file, &sysvipc_proc_seqops); - if (ret) - goto out_kfree; - - seq = file->private_data; - seq->private = iter; - - iter->iface = PDE(inode)->data; - iter->ns = get_ipc_ns(current->nsproxy->ipc_ns); -out: - return ret; -out_kfree: - kfree(iter); - goto out; -} - -static int sysvipc_proc_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct ipc_proc_iter *iter = seq->private; - put_ipc_ns(iter->ns); - return seq_release_private(inode, file); -} - -static const struct file_operations sysvipc_proc_fops = { - .open = sysvipc_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = sysvipc_proc_release, -}; -#endif /* CONFIG_PROC_FS */ diff --git a/ANDROID_3.4.5/ipc/util.h b/ANDROID_3.4.5/ipc/util.h deleted file mode 100644 index 6f5c20be..00000000 --- a/ANDROID_3.4.5/ipc/util.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * linux/ipc/util.h - * Copyright (C) 1999 Christoph Rohland - * - * ipc helper functions (c) 1999 Manfred Spraul <manfred@colorfullife.com> - * namespaces support. 2006 OpenVZ, SWsoft Inc. - * Pavel Emelianov <xemul@openvz.org> - */ - -#ifndef _IPC_UTIL_H -#define _IPC_UTIL_H - -#include <linux/unistd.h> -#include <linux/err.h> - -#define SEQ_MULTIPLIER (IPCMNI) - -void sem_init (void); -void msg_init (void); -void shm_init (void); - -struct ipc_namespace; - -#ifdef CONFIG_POSIX_MQUEUE -extern void mq_clear_sbinfo(struct ipc_namespace *ns); -extern void mq_put_mnt(struct ipc_namespace *ns); -#else -static inline void mq_clear_sbinfo(struct ipc_namespace *ns) { } -static inline void mq_put_mnt(struct ipc_namespace *ns) { } -#endif - -#ifdef CONFIG_SYSVIPC -void sem_init_ns(struct ipc_namespace *ns); -void msg_init_ns(struct ipc_namespace *ns); -void shm_init_ns(struct ipc_namespace *ns); - -void sem_exit_ns(struct ipc_namespace *ns); -void msg_exit_ns(struct ipc_namespace *ns); -void shm_exit_ns(struct ipc_namespace *ns); -#else -static inline void sem_init_ns(struct ipc_namespace *ns) { } -static inline void msg_init_ns(struct ipc_namespace *ns) { } -static inline void shm_init_ns(struct ipc_namespace *ns) { } - -static inline void sem_exit_ns(struct ipc_namespace *ns) { } -static inline void msg_exit_ns(struct ipc_namespace *ns) { } -static inline void shm_exit_ns(struct ipc_namespace *ns) { } -#endif - -/* - * Structure that holds the parameters needed by the ipc operations - * (see after) - */ -struct ipc_params { - key_t key; - int flg; - union { - size_t size; /* for shared memories */ - int nsems; /* for semaphores */ - } u; /* holds the getnew() specific param */ -}; - -/* - * Structure that holds some ipc operations. This structure is used to unify - * the calls to sys_msgget(), sys_semget(), sys_shmget() - * . routine to call to create a new ipc object. Can be one of newque, - * newary, newseg - * . routine to call to check permissions for a new ipc object. - * Can be one of security_msg_associate, security_sem_associate, - * security_shm_associate - * . routine to call for an extra check if needed - */ -struct ipc_ops { - int (*getnew) (struct ipc_namespace *, struct ipc_params *); - int (*associate) (struct kern_ipc_perm *, int); - int (*more_checks) (struct kern_ipc_perm *, struct ipc_params *); -}; - -struct seq_file; -struct ipc_ids; - -void ipc_init_ids(struct ipc_ids *); -#ifdef CONFIG_PROC_FS -void __init ipc_init_proc_interface(const char *path, const char *header, - int ids, int (*show)(struct seq_file *, void *)); -#else -#define ipc_init_proc_interface(path, header, ids, show) do {} while (0) -#endif - -#define IPC_SEM_IDS 0 -#define IPC_MSG_IDS 1 -#define IPC_SHM_IDS 2 - -#define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER) - -/* must be called with ids->rw_mutex acquired for writing */ -int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); - -/* must be called with ids->rw_mutex acquired for reading */ -int ipc_get_maxid(struct ipc_ids *); - -/* must be called with both locks acquired. */ -void ipc_rmid(struct ipc_ids *, struct kern_ipc_perm *); - -/* must be called with ipcp locked */ -int ipcperms(struct ipc_namespace *ns, 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); - -/* - * For allocation that need to be freed by RCU. - * Objects are reference counted, they start with reference count 1. - * getref increases the refcount, the putref call that reduces the recount - * to 0 schedules the rcu destruction. Caller must guarantee locking. - */ -void* ipc_rcu_alloc(int size); -void ipc_rcu_getref(void *ptr); -void ipc_rcu_putref(void *ptr); - -struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); - -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); -void ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); -struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, - struct ipc_ids *ids, int id, int cmd, - struct ipc64_perm *perm, int extra_perm); - -#ifndef __ARCH_WANT_IPC_PARSE_VERSION - /* On IA-64, 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 - -extern void free_msg(struct msg_msg *msg); -extern struct msg_msg *load_msg(const void __user *src, int len); -extern int store_msg(void __user *dest, struct msg_msg *msg, int len); - -extern void recompute_msgmni(struct ipc_namespace *); - -static inline int ipc_buildid(int id, int seq) -{ - return SEQ_MULTIPLIER * seq + id; -} - -/* - * Must be called with ipcp locked - */ -static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid) -{ - if (uid / SEQ_MULTIPLIER != ipcp->seq) - return 1; - return 0; -} - -static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm) -{ - rcu_read_lock(); - spin_lock(&perm->lock); -} - -static inline void ipc_unlock(struct kern_ipc_perm *perm) -{ - spin_unlock(&perm->lock); - rcu_read_unlock(); -} - -struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id); -int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, - struct ipc_ops *ops, struct ipc_params *params); -void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, - void (*free)(struct ipc_namespace *, struct kern_ipc_perm *)); -#endif |