/*
* linux/ipc/util.c
* Copyright (C) 1992 Krishna Balasubramanian
*
* Sep 1997 - Call suser() last after "normal" permission checks so we
* get BSD style process accounting right.
* Occurs in several places in the IPC code.
* Chris Evans, <chris@ferret.lmh.ox.ac.uk>
* Nov 1999 - ipc helper functions, unified SMP locking
* Manfred Spraul <manfred@colorfullife.com>
* Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
* Mingming Cao <cmm@us.ibm.com>
* Mar 2006 - support for audit of ipc object properties
* Dustin Kirkland <dustin.kirkland@us.ibm.com>
* Jun 2006 - namespaces ssupport
* OpenVZ, SWsoft Inc.
* Pavel Emelianov <xemul@openvz.org>
*/
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/init.h>
#include <linux/msg.h>
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/capability.h>
#include <linux/highuid.h>
#include <linux/security.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/audit.h>
#include <linux/nsproxy.h>
#include <asm/unistd.h>
#include "util.h"
struct ipc_proc_iface {
const char *path;
const char *header;
int ids;
int (*show)(struct seq_file *, void *);
};
struct ipc_namespace init_ipc_ns = {
.kref = {
.refcount = ATOMIC_INIT(2),
},
};
#ifdef CONFIG_IPC_NS
static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
{
int err;
struct ipc_namespace *ns;
err = -ENOMEM;
ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
if (ns == NULL)
goto err_mem;
err = sem_init_ns(ns);
if (err)
goto err_sem;
err = msg_init_ns(ns);
if (err)
goto err_msg;
err = shm_init_ns(ns);
if (err)
goto err_shm;
kref_init(&ns->kref);
return ns;
err_shm:
msg_exit_ns(ns);
err_msg:
sem_exit_ns(ns);
err_sem:
kfree(ns);
err_mem:
return ERR_PTR(err);
}
int unshare_ipcs(unsigned long unshare_flags, struct ipc_namespace **new_ipc)
{
struct ipc_namespace *new;
if (unshare_flags & CLONE_NEWIPC) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
new = clone_ipc_ns(current->nsproxy->ipc_ns);
if (IS_ERR(new))
return PTR_ERR(new);
*new_ipc = new;
}
return 0;
}
int copy_ipcs(unsigned long flags, struct task_struct *tsk)
{
struct ipc_namespace *old_ns = tsk->nsproxy->ipc_ns;
struct ipc_namespace *new_ns;
int err = 0;
if (!old_ns)
return 0;
get_ipc_ns(old_ns);
if (!(flags & CLONE_NEWIPC))
return 0;
if (!capable(CAP_SYS_ADMIN)) {
err = -EPERM;
goto out;
}
new_ns = clone_ipc_ns(old_ns);
if (!new_ns) {
err = -ENOMEM;
goto out;
}
tsk->nsproxy->ipc_ns = new_ns;
out:
put_ipc_ns(old_ns);
return err;
}
void free_ipc_ns(struct kref *kref)
{
struct ipc_namespace *ns;
ns = container_of(kref, struct ipc_namespace, kref);
sem_exit_ns(ns);
msg_exit_ns(ns);
shm_exit_ns(ns);
kfree(ns);
}
#else
int copy_ipcs(unsigned long flags, struct task_struct *tsk)
{
if (flags & CLONE_NEWIPC)
return -EINVAL;
return 0;
}
#endif
/**
* ipc_init - initialise IPC subsystem
*
* The various system5 IPC resources (semaphores, messages and shared
* memory) are initialised
*/
static int __init ipc_init(void)
{
sem_init();
msg_init();
shm_init();
return 0;
}
__initcall(ipc_init);
/**
* ipc_init_ids - initialise IPC identifiers
* @ids: Identifier set
* @size: Number of identifiers
*
* Given a size for the ipc identifier range (limited below IPCMNI)
* set up the sequence range to use then allocate and initialise the
* array itself.
*/
void __ipc_init ipc_init_ids(struct ipc_ids* ids, int size)
{
int i;
mutex_init(&ids->mutex);
if(size > IPCMNI)
size = IPCMNI;
ids->in_use = 0;
ids->max_id = -1;
ids->seq = 0;
{
int seq_limit = INT_MAX/SEQ_MULTIPLIER;
if(seq_limit > USHRT_MAX)
ids->seq_max = USHRT_MAX;
else
ids->seq_max = seq_limit;
}
ids->entries = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*size +
sizeof(struct ipc_id_ary));
if(ids->entries == NULL) {
printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
size = 0;
ids->entries = &ids->nullentry;
}
ids->entries->size = size;
for(i=0;i<size;i++)
ids->entries->p[i] = NULL;
}
#ifdef CONFIG_PROC_FS
static const