mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 00:38:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
12
ipc/Makefile
Normal file
12
ipc/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
#
|
||||
# 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
|
||||
|
757
ipc/compat.c
Normal file
757
ipc/compat.c
Normal file
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
* 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 <linux/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;
|
||||
};
|
||||
|
||||
static inline int compat_ipc_parse_version(int *cmd)
|
||||
{
|
||||
#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION
|
||||
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 *uip)
|
||||
{
|
||||
int err;
|
||||
__compat_uid_t u;
|
||||
__compat_gid_t g;
|
||||
|
||||
err = __put_user(p->key, &uip->key);
|
||||
SET_UID(u, p->uid);
|
||||
err |= __put_user(u, &uip->uid);
|
||||
SET_GID(g, p->gid);
|
||||
err |= __put_user(g, &uip->gid);
|
||||
SET_UID(u, p->cuid);
|
||||
err |= __put_user(u, &uip->cuid);
|
||||
SET_GID(g, p->cgid);
|
||||
err |= __put_user(g, &uip->cgid);
|
||||
err |= __put_user(p->mode, &uip->mode);
|
||||
err |= __put_user(p->seq, &uip->seq);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int get_compat_semid64_ds(struct semid64_ds *sem64,
|
||||
struct compat_semid64_ds __user *up64)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
return __get_compat_ipc64_perm(&sem64->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 *sem64,
|
||||
struct compat_semid64_ds __user *up64)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm);
|
||||
err |= __put_user(sem64->sem_otime, &up64->sem_otime);
|
||||
err |= __put_user(sem64->sem_ctime, &up64->sem_ctime);
|
||||
err |= __put_user(sem64->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)
|
||||
{
|
||||
unsigned long fourth;
|
||||
int err, err2;
|
||||
struct semid64_ds sem64;
|
||||
struct semid64_ds __user *up64;
|
||||
int version = compat_ipc_parse_version(&third);
|
||||
|
||||
memset(&sem64, 0, sizeof(sem64));
|
||||
|
||||
if ((third & (~IPC_64)) == SETVAL)
|
||||
#ifdef __BIG_ENDIAN
|
||||
fourth = (unsigned long)pad << 32;
|
||||
#else
|
||||
fourth = pad;
|
||||
#endif
|
||||
else
|
||||
fourth = (unsigned long)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(sem64));
|
||||
fourth = (unsigned long)up64;
|
||||
err = sys_semctl(first, second, third, fourth);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&sem64, up64, sizeof(sem64)))
|
||||
err2 = -EFAULT;
|
||||
else if (version == IPC_64)
|
||||
err2 = put_compat_semid64_ds(&sem64, compat_ptr(pad));
|
||||
else
|
||||
err2 = put_compat_semid_ds(&sem64, compat_ptr(pad));
|
||||
if (err2)
|
||||
err = -EFAULT;
|
||||
break;
|
||||
|
||||
case IPC_SET:
|
||||
if (version == IPC_64)
|
||||
err = get_compat_semid64_ds(&sem64, compat_ptr(pad));
|
||||
else
|
||||
err = get_compat_semid_ds(&sem64, compat_ptr(pad));
|
||||
|
||||
up64 = compat_alloc_user_space(sizeof(sem64));
|
||||
if (copy_to_user(up64, &sem64, sizeof(sem64)))
|
||||
err = -EFAULT;
|
||||
if (err)
|
||||
break;
|
||||
|
||||
fourth = (unsigned long)up64;
|
||||
err = sys_semctl(first, second, third, fourth);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
|
||||
{
|
||||
struct compat_msgbuf __user *msgp = dest;
|
||||
size_t msgsz;
|
||||
|
||||
if (put_user(msg->m_type, &msgp->mtype))
|
||||
return -EFAULT;
|
||||
|
||||
msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz;
|
||||
if (store_msg(msgp->mtext, msg, msgsz))
|
||||
return -EFAULT;
|
||||
return msgsz;
|
||||
}
|
||||
|
||||
#ifndef COMPAT_SHMLBA
|
||||
#define COMPAT_SHMLBA SHMLBA
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
|
||||
COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
|
||||
u32, third, compat_uptr_t, ptr, u32, fifth)
|
||||
{
|
||||
int version;
|
||||
u32 pad;
|
||||
|
||||
version = call >> 16; /* hack for backward compatibility */
|
||||
call &= 0xffff;
|
||||
|
||||
switch (call) {
|
||||
case SEMOP:
|
||||
/* struct sembuf is the same on 32 and 64bit :)) */
|
||||
return sys_semtimedop(first, compat_ptr(ptr), second, NULL);
|
||||
case SEMTIMEDOP:
|
||||
return compat_sys_semtimedop(first, compat_ptr(ptr), second,
|
||||
compat_ptr(fifth));
|
||||
case SEMGET:
|
||||
return sys_semget(first, second, third);
|
||||
case SEMCTL:
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
|
||||
return -EFAULT;
|
||||
return do_compat_semctl(first, second, third, pad);
|
||||
|
||||
case MSGSND: {
|
||||
struct compat_msgbuf __user *up = compat_ptr(ptr);
|
||||
compat_long_t type;
|
||||
|
||||
if (first < 0 || second < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (get_user(type, &up->mtype))
|
||||
return -EFAULT;
|
||||
|
||||
return do_msgsnd(first, type, up->mtext, second, third);
|
||||
}
|
||||
case MSGRCV: {
|
||||
void __user *uptr = compat_ptr(ptr);
|
||||
|
||||
if (first < 0 || second < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!version) {
|
||||
struct compat_ipc_kludge ipck;
|
||||
if (!uptr)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&ipck, uptr, sizeof(ipck)))
|
||||
return -EFAULT;
|
||||
uptr = compat_ptr(ipck.msgp);
|
||||
fifth = ipck.msgtyp;
|
||||
}
|
||||
return do_msgrcv(first, uptr, second, (s32)fifth, third,
|
||||
compat_do_msg_fill);
|
||||
}
|
||||
case MSGGET:
|
||||
return sys_msgget(first, second);
|
||||
case MSGCTL:
|
||||
return compat_sys_msgctl(first, second, compat_ptr(ptr));
|
||||
|
||||
case SHMAT: {
|
||||
int err;
|
||||
unsigned long raddr;
|
||||
|
||||
if (version == 1)
|
||||
return -EINVAL;
|
||||
err = do_shmat(first, compat_ptr(ptr), second, &raddr,
|
||||
COMPAT_SHMLBA);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return put_user(raddr, (compat_ulong_t *)compat_ptr(third));
|
||||
}
|
||||
case SHMDT:
|
||||
return sys_shmdt(compat_ptr(ptr));
|
||||
case SHMGET:
|
||||
return sys_shmget(first, (unsigned)second, third);
|
||||
case SHMCTL:
|
||||
return compat_sys_shmctl(first, second, compat_ptr(ptr));
|
||||
}
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(semctl, int, semid, int, semnum, int, cmd, int, arg)
|
||||
{
|
||||
return do_compat_semctl(semid, semnum, cmd, arg);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(msgsnd, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, int, msgflg)
|
||||
{
|
||||
struct compat_msgbuf __user *up = compat_ptr(msgp);
|
||||
compat_long_t mtype;
|
||||
|
||||
if (get_user(mtype, &up->mtype))
|
||||
return -EFAULT;
|
||||
return do_msgsnd(msqid, mtype, up->mtext, (ssize_t)msgsz, msgflg);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(msgrcv, int, msqid, compat_uptr_t, msgp,
|
||||
compat_ssize_t, msgsz, compat_long_t, msgtyp, int, msgflg)
|
||||
{
|
||||
return do_msgrcv(msqid, compat_ptr(msgp), (ssize_t)msgsz, (long)msgtyp,
|
||||
msgflg, compat_do_msg_fill);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
|
||||
{
|
||||
unsigned long ret;
|
||||
long err;
|
||||
|
||||
err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
|
||||
if (err)
|
||||
return err;
|
||||
force_successful_syscall_return();
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline int get_compat_shmid64_ds(struct shmid64_ds *sem64,
|
||||
struct compat_shmid64_ds __user *up64)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
return __get_compat_ipc64_perm(&sem64->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 *sem64,
|
||||
struct compat_shmid64_ds __user *up64)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64)))
|
||||
return -EFAULT;
|
||||
err = __put_compat_ipc64_perm(&sem64->shm_perm, &up64->shm_perm);
|
||||
err |= __put_user(sem64->shm_atime, &up64->shm_atime);
|
||||
err |= __put_user(sem64->shm_dtime, &up64->shm_dtime);
|
||||
err |= __put_user(sem64->shm_ctime, &up64->shm_ctime);
|
||||
err |= __put_user(sem64->shm_segsz, &up64->shm_segsz);
|
||||
err |= __put_user(sem64->shm_nattch, &up64->shm_nattch);
|
||||
err |= __put_user(sem64->shm_cpid, &up64->shm_cpid);
|
||||
err |= __put_user(sem64->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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr)
|
||||
{
|
||||
void __user *p;
|
||||
struct shmid64_ds sem64;
|
||||
struct shminfo64 smi;
|
||||
int err, err2;
|
||||
int version = compat_ipc_parse_version(&second);
|
||||
|
||||
memset(&sem64, 0, sizeof(sem64));
|
||||
|
||||
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(&sem64, uptr);
|
||||
else
|
||||
err = get_compat_shmid_ds(&sem64, uptr);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
p = compat_alloc_user_space(sizeof(sem64));
|
||||
if (copy_to_user(p, &sem64, sizeof(sem64)))
|
||||
err = -EFAULT;
|
||||
else
|
||||
err = sys_shmctl(first, second, p);
|
||||
break;
|
||||
|
||||
case IPC_STAT:
|
||||
case SHM_STAT:
|
||||
p = compat_alloc_user_space(sizeof(sem64));
|
||||
err = sys_shmctl(first, second, p);
|
||||
if (err < 0)
|
||||
break;
|
||||
if (copy_from_user(&sem64, p, sizeof(sem64)))
|
||||
err2 = -EFAULT;
|
||||
else if (version == IPC_64)
|
||||
err2 = put_compat_shmid64_ds(&sem64, uptr);
|
||||
else
|
||||
err2 = put_compat_shmid_ds(&sem64, 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;
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems,
|
||||
unsigned, nsops,
|
||||
const struct compat_timespec __user *, timeout)
|
||||
{
|
||||
struct timespec __user *ts64;
|
||||
if (compat_convert_timespec(&ts64, timeout))
|
||||
return -EFAULT;
|
||||
return sys_semtimedop(semid, tsems, nsops, ts64);
|
||||
}
|
138
ipc/compat_mq.c
Normal file
138
ipc/compat_mq.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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 <linux/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);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE4(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);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes,
|
||||
const char __user *, u_msg_ptr,
|
||||
compat_size_t, msg_len, unsigned int, msg_prio,
|
||||
const struct compat_timespec __user *, u_abs_timeout)
|
||||
{
|
||||
struct timespec __user *u_ts;
|
||||
|
||||
if (compat_convert_timespec(&u_ts, u_abs_timeout))
|
||||
return -EFAULT;
|
||||
|
||||
return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len,
|
||||
msg_prio, u_ts);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes,
|
||||
char __user *, u_msg_ptr,
|
||||
compat_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_convert_timespec(&u_ts, u_abs_timeout))
|
||||
return -EFAULT;
|
||||
|
||||
return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len,
|
||||
u_msg_prio, u_ts);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE2(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);
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(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;
|
||||
}
|
283
ipc/ipc_sysctl.c
Normal file
283
ipc/ipc_sysctl.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* 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(struct 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(struct 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(struct 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(struct 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_minmax(struct 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_minmax(&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(struct 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(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct ctl_table ipc_table;
|
||||
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) {
|
||||
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_minmax NULL
|
||||
#define proc_ipcauto_dointvec_minmax NULL
|
||||
#endif
|
||||
|
||||
static int zero;
|
||||
static int one = 1;
|
||||
static int int_max = INT_MAX;
|
||||
|
||||
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_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
{
|
||||
.procname = "msgmni",
|
||||
.data = &init_ipc_ns.msg_ctlmni,
|
||||
.maxlen = sizeof(init_ipc_ns.msg_ctlmni),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_ipc_callback_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
{
|
||||
.procname = "msgmnb",
|
||||
.data = &init_ipc_ns.msg_ctlmnb,
|
||||
.maxlen = sizeof(init_ipc_ns.msg_ctlmnb),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_ipc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
{
|
||||
.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,
|
||||
},
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
{
|
||||
.procname = "sem_next_id",
|
||||
.data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
|
||||
.maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_ipc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
{
|
||||
.procname = "msg_next_id",
|
||||
.data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
|
||||
.maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_ipc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
{
|
||||
.procname = "shm_next_id",
|
||||
.data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
|
||||
.maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_ipc_dointvec_minmax,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max,
|
||||
},
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
device_initcall(ipc_sysctl_init);
|
92
ipc/ipcns_notifier.c
Normal file
92
ipc/ipcns_notifier.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
124
ipc/mq_sysctl.c
Normal file
124
ipc/mq_sysctl.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#ifdef CONFIG_PROC_SYSCTL
|
||||
static void *get_mq(struct 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(struct 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(struct 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 = HARD_MSGMAX;
|
||||
|
||||
static int msg_maxsize_limit_min = MIN_MSGSIZEMAX;
|
||||
static int msg_maxsize_limit_max = HARD_MSGSIZEMAX;
|
||||
|
||||
static struct 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,
|
||||
},
|
||||
{
|
||||
.procname = "msg_default",
|
||||
.data = &init_ipc_ns.mq_msg_default,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_mq_dointvec_minmax,
|
||||
.extra1 = &msg_max_limit_min,
|
||||
.extra2 = &msg_max_limit_max,
|
||||
},
|
||||
{
|
||||
.procname = "msgsize_default",
|
||||
.data = &init_ipc_ns.mq_msgsize_default,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_mq_dointvec_minmax,
|
||||
.extra1 = &msg_maxsize_limit_min,
|
||||
.extra2 = &msg_maxsize_limit_max,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table mq_sysctl_dir[] = {
|
||||
{
|
||||
.procname = "mqueue",
|
||||
.mode = 0555,
|
||||
.child = mq_sysctls,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct 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);
|
||||
}
|
1462
ipc/mqueue.c
Normal file
1462
ipc/mqueue.c
Normal file
File diff suppressed because it is too large
Load diff
182
ipc/msgutil.c
Normal file
182
ipc/msgutil.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* 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 <linux/proc_ns.h>
|
||||
#include <linux/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,
|
||||
.proc_inum = PROC_IPC_INIT_INO,
|
||||
};
|
||||
|
||||
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 ((size_t)PAGE_SIZE-sizeof(struct msg_msg))
|
||||
#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
|
||||
|
||||
|
||||
static struct msg_msg *alloc_msg(size_t len)
|
||||
{
|
||||
struct msg_msg *msg;
|
||||
struct msg_msgseg **pseg;
|
||||
size_t alen;
|
||||
|
||||
alen = min(len, DATALEN_MSG);
|
||||
msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
|
||||
if (msg == NULL)
|
||||
return NULL;
|
||||
|
||||
msg->next = NULL;
|
||||
msg->security = NULL;
|
||||
|
||||
len -= alen;
|
||||
pseg = &msg->next;
|
||||
while (len > 0) {
|
||||
struct msg_msgseg *seg;
|
||||
alen = min(len, DATALEN_SEG);
|
||||
seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL);
|
||||
if (seg == NULL)
|
||||
goto out_err;
|
||||
*pseg = seg;
|
||||
seg->next = NULL;
|
||||
pseg = &seg->next;
|
||||
len -= alen;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
out_err:
|
||||
free_msg(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct msg_msg *load_msg(const void __user *src, size_t len)
|
||||
{
|
||||
struct msg_msg *msg;
|
||||
struct msg_msgseg *seg;
|
||||
int err = -EFAULT;
|
||||
size_t alen;
|
||||
|
||||
msg = alloc_msg(len);
|
||||
if (msg == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
alen = min(len, DATALEN_MSG);
|
||||
if (copy_from_user(msg + 1, src, alen))
|
||||
goto out_err;
|
||||
|
||||
for (seg = msg->next; seg != NULL; seg = seg->next) {
|
||||
len -= alen;
|
||||
src = (char __user *)src + alen;
|
||||
alen = min(len, DATALEN_SEG);
|
||||
if (copy_from_user(seg + 1, src, alen))
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = security_msg_msg_alloc(msg);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
return msg;
|
||||
|
||||
out_err:
|
||||
free_msg(msg);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
#ifdef CONFIG_CHECKPOINT_RESTORE
|
||||
struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
|
||||
{
|
||||
struct msg_msgseg *dst_pseg, *src_pseg;
|
||||
size_t len = src->m_ts;
|
||||
size_t alen;
|
||||
|
||||
BUG_ON(dst == NULL);
|
||||
if (src->m_ts > dst->m_ts)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
alen = min(len, DATALEN_MSG);
|
||||
memcpy(dst + 1, src + 1, alen);
|
||||
|
||||
for (dst_pseg = dst->next, src_pseg = src->next;
|
||||
src_pseg != NULL;
|
||||
dst_pseg = dst_pseg->next, src_pseg = src_pseg->next) {
|
||||
|
||||
len -= alen;
|
||||
alen = min(len, DATALEN_SEG);
|
||||
memcpy(dst_pseg + 1, src_pseg + 1, alen);
|
||||
}
|
||||
|
||||
dst->m_type = src->m_type;
|
||||
dst->m_ts = src->m_ts;
|
||||
|
||||
return dst;
|
||||
}
|
||||
#else
|
||||
struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
#endif
|
||||
int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
|
||||
{
|
||||
size_t alen;
|
||||
struct msg_msgseg *seg;
|
||||
|
||||
alen = min(len, DATALEN_MSG);
|
||||
if (copy_to_user(dest, msg + 1, alen))
|
||||
return -1;
|
||||
|
||||
for (seg = msg->next; seg != NULL; seg = seg->next) {
|
||||
len -= alen;
|
||||
dest = (char __user *)dest + alen;
|
||||
alen = min(len, DATALEN_SEG);
|
||||
if (copy_to_user(dest, seg + 1, alen))
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
199
ipc/namespace.c
Normal file
199
ipc/namespace.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* 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_ns.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
|
||||
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);
|
||||
|
||||
err = proc_alloc_inum(&ns->proc_inum);
|
||||
if (err) {
|
||||
kfree(ns);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
atomic_set(&ns->count, 1);
|
||||
err = mq_init_ns(ns);
|
||||
if (err) {
|
||||
proc_free_inum(ns->proc_inum);
|
||||
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(user_ns);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
struct ipc_namespace *copy_ipcs(unsigned long flags,
|
||||
struct user_namespace *user_ns, struct ipc_namespace *ns)
|
||||
{
|
||||
if (!(flags & CLONE_NEWIPC))
|
||||
return get_ipc_ns(ns);
|
||||
return create_ipc_ns(user_ns, 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->rwsem);
|
||||
|
||||
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;
|
||||
rcu_read_lock();
|
||||
ipc_lock_object(perm);
|
||||
free(ns, perm);
|
||||
total++;
|
||||
}
|
||||
up_write(&ids->rwsem);
|
||||
}
|
||||
|
||||
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);
|
||||
proc_free_inum(ns->proc_inum);
|
||||
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;
|
||||
|
||||
task_lock(task);
|
||||
nsproxy = task->nsproxy;
|
||||
if (nsproxy)
|
||||
ns = get_ipc_ns(nsproxy->ipc_ns);
|
||||
task_unlock(task);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
static void ipcns_put(void *ns)
|
||||
{
|
||||
return put_ipc_ns(ns);
|
||||
}
|
||||
|
||||
static int ipcns_install(struct nsproxy *nsproxy, void *new)
|
||||
{
|
||||
struct ipc_namespace *ns = new;
|
||||
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
|
||||
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static unsigned int ipcns_inum(void *vp)
|
||||
{
|
||||
struct ipc_namespace *ns = vp;
|
||||
|
||||
return ns->proc_inum;
|
||||
}
|
||||
|
||||
const struct proc_ns_operations ipcns_operations = {
|
||||
.name = "ipc",
|
||||
.type = CLONE_NEWIPC,
|
||||
.get = ipcns_get,
|
||||
.put = ipcns_put,
|
||||
.install = ipcns_install,
|
||||
.inum = ipcns_inum,
|
||||
};
|
99
ipc/syscall.c
Normal file
99
ipc/syscall.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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: {
|
||||
unsigned long arg;
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(arg, (unsigned long __user *) ptr))
|
||||
return -EFAULT;
|
||||
return sys_semctl(first, second, third, arg);
|
||||
}
|
||||
|
||||
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, SHMLBA);
|
||||
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
|
921
ipc/util.c
Normal file
921
ipc/util.c
Normal file
|
@ -0,0 +1,921 @@
|
|||
/*
|
||||
* 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>
|
||||
*
|
||||
* General sysv ipc locking scheme:
|
||||
* rcu_read_lock()
|
||||
* obtain the ipc object (kern_ipc_perm) by looking up the id in an idr
|
||||
* tree.
|
||||
* - perform initial checks (capabilities, auditing and permission,
|
||||
* etc).
|
||||
* - perform read-only operations, such as STAT, INFO commands.
|
||||
* acquire the ipc lock (kern_ipc_perm.lock) through
|
||||
* ipc_lock_object()
|
||||
* - perform data updates, such as SET, RMID commands and
|
||||
* mechanism-specific operations (semop/semtimedop,
|
||||
* msgsnd/msgrcv, shmat/shmdt).
|
||||
* drop the ipc lock, through ipc_unlock_object().
|
||||
* rcu_read_unlock()
|
||||
*
|
||||
* The ids->rwsem must be taken when:
|
||||
* - creating, removing and iterating the existing entries in ipc
|
||||
* identifier sets.
|
||||
* - iterating through files under /proc/sysvipc/
|
||||
*
|
||||
* Note that sems have a special fast path that avoids kern_ipc_perm.lock -
|
||||
* see sem_lock().
|
||||
*/
|
||||
|
||||
#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/notifier.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 *);
|
||||
};
|
||||
|
||||
static void ipc_memory_notifier(struct work_struct *work)
|
||||
{
|
||||
ipcns_notify(IPCNS_MEMCHANGED);
|
||||
}
|
||||
|
||||
static int ipc_memory_callback(struct notifier_block *self,
|
||||
unsigned long action, void *arg)
|
||||
{
|
||||
static DECLARE_WORK(ipc_memory_wq, ipc_memory_notifier);
|
||||
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
static struct notifier_block ipc_memory_nb = {
|
||||
.notifier_call = ipc_memory_callback,
|
||||
.priority = IPC_CALLBACK_PRI,
|
||||
};
|
||||
|
||||
/**
|
||||
* ipc_init - initialise ipc subsystem
|
||||
*
|
||||
* The various sysv 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();
|
||||
register_hotmemory_notifier(&ipc_memory_nb);
|
||||
register_ipcns_notifier(&init_ipc_ns);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(ipc_init);
|
||||
|
||||
/**
|
||||
* ipc_init_ids - initialise ipc identifiers
|
||||
* @ids: ipc 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)
|
||||
{
|
||||
ids->in_use = 0;
|
||||
ids->seq = 0;
|
||||
ids->next_id = -1;
|
||||
init_rwsem(&ids->rwsem);
|
||||
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: ipc identifier set
|
||||
* @key: key to find
|
||||
*
|
||||
* Returns the locked pointer to the ipc structure if found or NULL
|
||||
* otherwise. If key is found ipc points to the owning ipc structure
|
||||
*
|
||||
* Called with ipc_ids.rwsem held.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
ipc_lock_object(ipc);
|
||||
return ipc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_get_maxid - get the last assigned id
|
||||
* @ids: ipc identifier set
|
||||
*
|
||||
* Called with ipc_ids.rwsem 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 writer ipc_ids.rwsem held.
|
||||
*/
|
||||
int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size)
|
||||
{
|
||||
kuid_t euid;
|
||||
kgid_t egid;
|
||||
int id;
|
||||
int next_id = ids->next_id;
|
||||
|
||||
if (size > IPCMNI)
|
||||
size = IPCMNI;
|
||||
|
||||
if (ids->in_use >= size)
|
||||
return -ENOSPC;
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
|
||||
spin_lock_init(&new->lock);
|
||||
new->deleted = false;
|
||||
rcu_read_lock();
|
||||
spin_lock(&new->lock);
|
||||
|
||||
id = idr_alloc(&ids->ipcs_idr, new,
|
||||
(next_id < 0) ? 0 : ipcid_to_idx(next_id), 0,
|
||||
GFP_NOWAIT);
|
||||
idr_preload_end();
|
||||
if (id < 0) {
|
||||
spin_unlock(&new->lock);
|
||||
rcu_read_unlock();
|
||||
return id;
|
||||
}
|
||||
|
||||
ids->in_use++;
|
||||
|
||||
current_euid_egid(&euid, &egid);
|
||||
new->cuid = new->uid = euid;
|
||||
new->gid = new->cgid = egid;
|
||||
|
||||
if (next_id < 0) {
|
||||
new->seq = ids->seq++;
|
||||
if (ids->seq > IPCID_SEQ_MAX)
|
||||
ids->seq = 0;
|
||||
} else {
|
||||
new->seq = ipcid_to_seqx(next_id);
|
||||
ids->next_id = -1;
|
||||
}
|
||||
|
||||
new->id = ipc_buildid(id, new->seq);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipcget_new - create a new ipc object
|
||||
* @ns: ipc namespace
|
||||
* @ids: ipc identifier 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,
|
||||
const struct ipc_ops *ops, struct ipc_params *params)
|
||||
{
|
||||
int err;
|
||||
|
||||
down_write(&ids->rwsem);
|
||||
err = ops->getnew(ns, params);
|
||||
up_write(&ids->rwsem);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_check_perms - check security and permissions for an ipc object
|
||||
* @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
|
||||
* ds IDR.
|
||||
*
|
||||
* On success, the ipc id is returned.
|
||||
*
|
||||
* It is called with ipc_ids.rwsem and ipcp->lock held.
|
||||
*/
|
||||
static int ipc_check_perms(struct ipc_namespace *ns,
|
||||
struct kern_ipc_perm *ipcp,
|
||||
const 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: ipc namespace
|
||||
* @ids: ipc identifier 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,
|
||||
const struct ipc_ops *ops, struct ipc_params *params)
|
||||
{
|
||||
struct kern_ipc_perm *ipcp;
|
||||
int flg = params->flg;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Take the lock as a writer since we are potentially going to add
|
||||
* a new entry + read locks are not "upgradable"
|
||||
*/
|
||||
down_write(&ids->rwsem);
|
||||
ipcp = ipc_findkey(ids, params->key);
|
||||
if (ipcp == NULL) {
|
||||
/* key not used */
|
||||
if (!(flg & IPC_CREAT))
|
||||
err = -ENOENT;
|
||||
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->rwsem);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ipc_rmid - remove an ipc identifier
|
||||
* @ids: ipc identifier set
|
||||
* @ipcp: ipc perm structure containing the identifier to remove
|
||||
*
|
||||
* ipc_ids.rwsem (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 = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 or NULL upon failure.
|
||||
*/
|
||||
void *ipc_rcu_alloc(int size)
|
||||
{
|
||||
/*
|
||||
* We prepend the allocation with the rcu struct
|
||||
*/
|
||||
struct ipc_rcu *out = ipc_alloc(sizeof(struct ipc_rcu) + size);
|
||||
if (unlikely(!out))
|
||||
return NULL;
|
||||
atomic_set(&out->refcount, 1);
|
||||
return out + 1;
|
||||
}
|
||||
|
||||
int ipc_rcu_getref(void *ptr)
|
||||
{
|
||||
struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
|
||||
|
||||
return atomic_inc_not_zero(&p->refcount);
|
||||
}
|
||||
|
||||
void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head))
|
||||
{
|
||||
struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1;
|
||||
|
||||
if (!atomic_dec_and_test(&p->refcount))
|
||||
return;
|
||||
|
||||
call_rcu(&p->rcu, func);
|
||||
}
|
||||
|
||||
void ipc_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu);
|
||||
|
||||
if (is_vmalloc_addr(p))
|
||||
vfree(p);
|
||||
else
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
kuid_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 (uid_eq(euid, ipcp->cuid) ||
|
||||
uid_eq(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 = from_kuid_munged(current_user_ns(), in->uid);
|
||||
out->gid = from_kgid_munged(current_user_ns(), in->gid);
|
||||
out->cuid = from_kuid_munged(current_user_ns(), in->cuid);
|
||||
out->cgid = from_kgid_munged(current_user_ns(), 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_obtain_object
|
||||
* @ids: ipc identifier set
|
||||
* @id: ipc id to look for
|
||||
*
|
||||
* Look for an id in the ipc ids idr and return associated ipc object.
|
||||
*
|
||||
* Call inside the RCU critical section.
|
||||
* The ipc object is *not* locked on exit.
|
||||
*/
|
||||
struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id)
|
||||
{
|
||||
struct kern_ipc_perm *out;
|
||||
int lid = ipcid_to_idx(id);
|
||||
|
||||
out = idr_find(&ids->ipcs_idr, lid);
|
||||
if (!out)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_lock - lock an ipc structure without rwsem 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 successful exit.
|
||||
*/
|
||||
struct kern_ipc_perm *ipc_lock(struct ipc_ids *ids, int id)
|
||||
{
|
||||
struct kern_ipc_perm *out;
|
||||
|
||||
rcu_read_lock();
|
||||
out = ipc_obtain_object(ids, id);
|
||||
if (IS_ERR(out))
|
||||
goto err1;
|
||||
|
||||
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 (ipc_valid_object(out))
|
||||
return out;
|
||||
|
||||
spin_unlock(&out->lock);
|
||||
out = ERR_PTR(-EINVAL);
|
||||
err1:
|
||||
rcu_read_unlock();
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_obtain_object_check
|
||||
* @ids: ipc identifier set
|
||||
* @id: ipc id to look for
|
||||
*
|
||||
* Similar to ipc_obtain_object() but also checks
|
||||
* the ipc object reference counter.
|
||||
*
|
||||
* Call inside the RCU critical section.
|
||||
* The ipc object is *not* locked on exit.
|
||||
*/
|
||||
struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id)
|
||||
{
|
||||
struct kern_ipc_perm *out = ipc_obtain_object(ids, id);
|
||||
|
||||
if (IS_ERR(out))
|
||||
goto out;
|
||||
|
||||
if (ipc_checkid(out, id))
|
||||
return ERR_PTR(-EIDRM);
|
||||
out:
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipcget - Common sys_*get() code
|
||||
* @ns: namespace
|
||||
* @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,
|
||||
const 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 object
|
||||
* @in: the permission given as input.
|
||||
* @out: the permission of the ipc to set.
|
||||
*/
|
||||
int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out)
|
||||
{
|
||||
kuid_t uid = make_kuid(current_user_ns(), in->uid);
|
||||
kgid_t gid = make_kgid(current_user_ns(), in->gid);
|
||||
if (!uid_valid(uid) || !gid_valid(gid))
|
||||
return -EINVAL;
|
||||
|
||||
out->uid = uid;
|
||||
out->gid = gid;
|
||||
out->mode = (out->mode & ~S_IRWXUGO)
|
||||
| (in->mode & S_IRWXUGO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipcctl_pre_down_nolock - retrieve an ipc and check permissions for some IPC_XXX cmd
|
||||
* @ns: 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 a pointer to the ipc object or otherwise, the corresponding error.
|
||||
*
|
||||
* Call holding the both the rwsem and the rcu read lock.
|
||||
*/
|
||||
struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
|
||||
struct ipc_ids *ids, int id, int cmd,
|
||||
struct ipc64_perm *perm, int extra_perm)
|
||||
{
|
||||
kuid_t euid;
|
||||
int err = -EPERM;
|
||||
struct kern_ipc_perm *ipcp;
|
||||
|
||||
ipcp = ipc_obtain_object_check(ids, id);
|
||||
if (IS_ERR(ipcp)) {
|
||||
err = PTR_ERR(ipcp);
|
||||
goto err;
|
||||
}
|
||||
|
||||
audit_ipc_obj(ipcp);
|
||||
if (cmd == IPC_SET)
|
||||
audit_ipc_set_perm(extra_perm, perm->uid,
|
||||
perm->gid, perm->mode);
|
||||
|
||||
euid = current_euid();
|
||||
if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) ||
|
||||
ns_capable(ns->user_ns, CAP_SYS_ADMIN))
|
||||
return ipcp; /* successful lookup */
|
||||
err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_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 /* CONFIG_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;
|
||||
rcu_read_lock();
|
||||
ipc_lock_object(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->rwsem);
|
||||
|
||||
/* 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->rwsem);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct ipc_proc_iter *iter;
|
||||
|
||||
iter = __seq_open_private(file, &sysvipc_proc_seqops, sizeof(*iter));
|
||||
if (!iter)
|
||||
return -ENOMEM;
|
||||
|
||||
iter->iface = PDE_DATA(inode);
|
||||
iter->ns = get_ipc_ns(current->nsproxy->ipc_ns);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 */
|
207
ipc/util.h
Normal file
207
ipc/util.h
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
struct ipc_rcu {
|
||||
struct rcu_head rcu;
|
||||
atomic_t refcount;
|
||||
} ____cacheline_aligned_in_smp;
|
||||
|
||||
#define ipc_rcu_to_struct(p) ((void *)(p+1))
|
||||
|
||||
/*
|
||||
* 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)
|
||||
#define ipcid_to_seqx(id) ((id) / SEQ_MULTIPLIER)
|
||||
#define IPCID_SEQ_MAX min_t(int, INT_MAX/SEQ_MULTIPLIER, USHRT_MAX)
|
||||
|
||||
/* must be called with ids->rwsem acquired for writing */
|
||||
int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
|
||||
|
||||
/* must be called with ids->rwsem 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);
|
||||
int ipc_rcu_getref(void *ptr);
|
||||
void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head));
|
||||
void ipc_rcu_free(struct rcu_head *head);
|
||||
|
||||
struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int);
|
||||
struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id);
|
||||
|
||||
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);
|
||||
int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out);
|
||||
struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns,
|
||||
struct ipc_ids *ids, int id, int cmd,
|
||||
struct ipc64_perm *perm, int extra_perm);
|
||||
|
||||
#ifndef CONFIG_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, size_t len);
|
||||
extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);
|
||||
extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
|
||||
|
||||
extern void recompute_msgmni(struct ipc_namespace *);
|
||||
|
||||
static inline int ipc_buildid(int id, int seq)
|
||||
{
|
||||
return SEQ_MULTIPLIER * seq + id;
|
||||
}
|
||||
|
||||
static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid)
|
||||
{
|
||||
return uid / SEQ_MULTIPLIER != ipcp->seq;
|
||||
}
|
||||
|
||||
static inline void ipc_lock_object(struct kern_ipc_perm *perm)
|
||||
{
|
||||
spin_lock(&perm->lock);
|
||||
}
|
||||
|
||||
static inline void ipc_unlock_object(struct kern_ipc_perm *perm)
|
||||
{
|
||||
spin_unlock(&perm->lock);
|
||||
}
|
||||
|
||||
static inline void ipc_assert_locked_object(struct kern_ipc_perm *perm)
|
||||
{
|
||||
assert_spin_locked(&perm->lock);
|
||||
}
|
||||
|
||||
static inline void ipc_unlock(struct kern_ipc_perm *perm)
|
||||
{
|
||||
ipc_unlock_object(perm);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/*
|
||||
* ipc_valid_object() - helper to sort out IPC_RMID races for codepaths
|
||||
* where the respective ipc_ids.rwsem is not being held down.
|
||||
* Checks whether the ipc object is still around or if it's gone already, as
|
||||
* ipc_rmid() may have already freed the ID while the ipc lock was spinning.
|
||||
* Needs to be called with kern_ipc_perm.lock held -- exception made for one
|
||||
* checkpoint case at sys_semtimedop() as noted in code commentary.
|
||||
*/
|
||||
static inline bool ipc_valid_object(struct kern_ipc_perm *perm)
|
||||
{
|
||||
return !perm->deleted;
|
||||
}
|
||||
|
||||
struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id);
|
||||
int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
|
||||
const 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
|
Loading…
Add table
Add a link
Reference in a new issue