ipc: unify the syscalls code

This patch introduces a change into the sys_msgget(), sys_semget() and
sys_shmget() routines: they now share a common code, which is better for
maintainability.

Signed-off-by: Nadia Derbey <Nadia.Derbey@bull.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Nadia Derbey 2007-10-18 23:40:49 -07:00 committed by Linus Torvalds
parent 7ca7e564e0
commit 7748dbfaa0
5 changed files with 215 additions and 139 deletions

View File

@ -81,7 +81,7 @@ static struct ipc_ids init_msg_ids;
ipc_buildid(&msg_ids(ns), id, seq)
static void freeque(struct ipc_namespace *, struct msg_queue *);
static int newque (struct ipc_namespace *ns, key_t key, int msgflg);
static int newque(struct ipc_namespace *, struct ipc_params *);
#ifdef CONFIG_PROC_FS
static int sysvipc_msg_proc_show(struct seq_file *s, void *it);
#endif
@ -144,10 +144,12 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s)
ipc_rmid(&msg_ids(ns), &s->q_perm);
}
static int newque (struct ipc_namespace *ns, key_t key, int msgflg)
static int newque(struct ipc_namespace *ns, struct ipc_params *params)
{
struct msg_queue *msq;
int id, retval;
key_t key = params->key;
int msgflg = params->flg;
msq = ipc_rcu_alloc(sizeof(*msq));
if (!msq)
@ -264,56 +266,27 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq)
ipc_rcu_putref(msq);
}
static inline int msg_security(void *msq, int msgflg)
{
return security_msg_queue_associate((struct msg_queue *) msq, msgflg);
}
asmlinkage long sys_msgget(key_t key, int msgflg)
{
struct msg_queue *msq;
int ret;
struct ipc_namespace *ns;
struct ipc_ops msg_ops;
struct ipc_params msg_params;
ns = current->nsproxy->ipc_ns;
ret = idr_pre_get(&msg_ids(ns).ipcs_idr, GFP_KERNEL);
msg_ops.getnew = newque;
msg_ops.associate = msg_security;
msg_ops.more_checks = NULL;
if (key == IPC_PRIVATE) {
if (!ret)
ret = -ENOMEM;
else {
mutex_lock(&msg_ids(ns).mutex);
ret = newque(ns, key, msgflg);
mutex_unlock(&msg_ids(ns).mutex);
}
} else {
mutex_lock(&msg_ids(ns).mutex);
msq = (struct msg_queue *) ipc_findkey(&msg_ids(ns), key);
if (msq == NULL) {
/* key not used */
if (!(msgflg & IPC_CREAT))
ret = -ENOENT;
else if (!ret)
ret = -ENOMEM;
else
ret = newque(ns, key, msgflg);
} else {
/* msq has been locked by ipc_findkey() */
msg_params.key = key;
msg_params.flg = msgflg;
if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
ret = -EEXIST;
else {
if (ipcperms(&msq->q_perm, msgflg))
ret = -EACCES;
else {
ret = security_msg_queue_associate(
msq, msgflg);
if (!ret)
ret = msq->q_perm.id;
}
}
msg_unlock(msq);
}
mutex_unlock(&msg_ids(ns).mutex);
}
return ret;
return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params);
}
static inline unsigned long

View File

@ -97,7 +97,7 @@
static struct ipc_ids init_sem_ids;
static int newary(struct ipc_namespace *, key_t, int, int);
static int newary(struct ipc_namespace *, struct ipc_params *);
static void freeary(struct ipc_namespace *, struct sem_array *);
#ifdef CONFIG_PROC_FS
static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
@ -214,12 +214,15 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
*/
#define IN_WAKEUP 1
static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
static int newary(struct ipc_namespace *ns, struct ipc_params *params)
{
int id;
int retval;
struct sem_array *sma;
int size;
key_t key = params->key;
int nsems = params->u.nsems;
int semflg = params->flg;
if (!nsems)
return -EINVAL;
@ -263,61 +266,40 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg)
return sma->sem_perm.id;
}
static inline int sem_security(void *sma, int semflg)
{
return security_sem_associate((struct sem_array *) sma, semflg);
}
static inline int sem_more_checks(void *sma, struct ipc_params *params)
{
if (params->u.nsems > ((struct sem_array *)sma)->sem_nsems)
return -EINVAL;
return 0;
}
asmlinkage long sys_semget(key_t key, int nsems, int semflg)
{
int err;
struct sem_array *sma;
struct ipc_namespace *ns;
struct ipc_ops sem_ops;
struct ipc_params sem_params;
ns = current->nsproxy->ipc_ns;
if (nsems < 0 || nsems > ns->sc_semmsl)
return -EINVAL;
err = idr_pre_get(&sem_ids(ns).ipcs_idr, GFP_KERNEL);
sem_ops.getnew = newary;
sem_ops.associate = sem_security;
sem_ops.more_checks = sem_more_checks;
if (key == IPC_PRIVATE) {
if (!err)
err = -ENOMEM;
else {
mutex_lock(&sem_ids(ns).mutex);
err = newary(ns, key, nsems, semflg);
mutex_unlock(&sem_ids(ns).mutex);
}
} else {
mutex_lock(&sem_ids(ns).mutex);
sma = (struct sem_array *) ipc_findkey(&sem_ids(ns), key);
if (sma == NULL) {
/* key not used */
if (!(semflg & IPC_CREAT))
err = -ENOENT;
else if (!err)
err = -ENOMEM;
else
err = newary(ns, key, nsems, semflg);
} else {
/* sma has been locked by ipc_findkey() */
sem_params.key = key;
sem_params.flg = semflg;
sem_params.u.nsems = nsems;
if (semflg & IPC_CREAT && semflg & IPC_EXCL)
err = -EEXIST;
else {
if (nsems > sma->sem_nsems)
err = -EINVAL;
else if (ipcperms(&sma->sem_perm, semflg))
err = -EACCES;
else {
err = security_sem_associate(sma,
semflg);
if (!err)
err = sma->sem_perm.id;
}
}
sem_unlock(sma);
}
mutex_unlock(&sem_ids(ns).mutex);
}
return err;
return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
}
/* Manage the doubly linked list sma->sem_pending as a FIFO:

View File

@ -68,8 +68,7 @@ static struct ipc_ids init_shm_ids;
#define shm_buildid(ns, id, seq) \
ipc_buildid(&shm_ids(ns), id, seq)
static int newseg (struct ipc_namespace *ns, key_t key,
int shmflg, size_t size);
static int newseg(struct ipc_namespace *, struct ipc_params *);
static void shm_open(struct vm_area_struct *vma);
static void shm_close(struct vm_area_struct *vma);
static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp);
@ -341,8 +340,11 @@ static struct vm_operations_struct shm_vm_ops = {
#endif
};
static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size)
static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
{
key_t key = params->key;
int shmflg = params->flg;
size_t size = params->u.size;
int error;
struct shmid_kernel *shp;
int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
@ -423,57 +425,36 @@ no_file:
return error;
}
static inline int shm_security(void *shp, int shmflg)
{
return security_shm_associate((struct shmid_kernel *) shp, shmflg);
}
static inline int shm_more_checks(void *shp, struct ipc_params *params)
{
if (((struct shmid_kernel *)shp)->shm_segsz < params->u.size)
return -EINVAL;
return 0;
}
asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
{
struct shmid_kernel *shp;
int err;
struct ipc_namespace *ns;
struct ipc_ops shm_ops;
struct ipc_params shm_params;
ns = current->nsproxy->ipc_ns;
err = idr_pre_get(&shm_ids(ns).ipcs_idr, GFP_KERNEL);
shm_ops.getnew = newseg;
shm_ops.associate = shm_security;
shm_ops.more_checks = shm_more_checks;
if (key == IPC_PRIVATE) {
if (!err)
err = -ENOMEM;
else {
mutex_lock(&shm_ids(ns).mutex);
err = newseg(ns, key, shmflg, size);
mutex_unlock(&shm_ids(ns).mutex);
}
} else {
mutex_lock(&shm_ids(ns).mutex);
shp = (struct shmid_kernel *) ipc_findkey(&shm_ids(ns), key);
if (shp == NULL) {
if (!(shmflg & IPC_CREAT))
err = -ENOENT;
else if (!err)
err = -ENOMEM;
else
err = newseg(ns, key, shmflg, size);
} else {
/* shp has been locked by ipc_findkey() */
shm_params.key = key;
shm_params.flg = shmflg;
shm_params.u.size = size;
if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
err = -EEXIST;
else {
if (shp->shm_segsz < size)
err = -EINVAL;
else if (ipcperms(&shp->shm_perm, shmflg))
err = -EACCES;
else {
err = security_shm_associate(shp,
shmflg);
if (!err)
err = shp->shm_perm.id;
}
}
shm_unlock(shp);
}
mutex_unlock(&shm_ids(ns).mutex);
}
return err;
return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
}
static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version)

View File

@ -197,7 +197,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
* If key is found ipc contains its ipc structure
*/
struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key)
{
struct kern_ipc_perm *ipc;
int next_id;
@ -300,6 +300,105 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
return id;
}
/**
* ipcget_new - create a new ipc object
* @ns: namespace
* @ids: identifer set
* @ops: the actual creation routine to call
* @params: its parameters
*
* This routine is called sys_msgget, sys_semget() and sys_shmget() when
* the key is IPC_PRIVATE
*/
int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids,
struct ipc_ops *ops, struct ipc_params *params)
{
int err;
err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL);
if (!err)
return -ENOMEM;
mutex_lock(&ids->mutex);
err = ops->getnew(ns, params);
mutex_unlock(&ids->mutex);
return err;
}
/**
* ipc_check_perms - check security and permissions for an IPC
* @ipcp: ipc permission set
* @ids: identifer set
* @ops: the actual security routine to call
* @params: its parameters
*/
static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops,
struct ipc_params *params)
{
int err;
if (ipcperms(ipcp, params->flg))
err = -EACCES;
else {
err = ops->associate(ipcp, params->flg);
if (!err)
err = ipcp->id;
}
return err;
}
/**
* ipcget_public - get an ipc object or create a new one
* @ns: namespace
* @ids: identifer set
* @ops: the actual creation routine to call
* @params: its parameters
*
* This routine is called sys_msgget, sys_semget() and sys_shmget() when
* the key is not IPC_PRIVATE
*/
int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids,
struct ipc_ops *ops, struct ipc_params *params)
{
struct kern_ipc_perm *ipcp;
int flg = params->flg;
int err;
err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL);
mutex_lock(&ids->mutex);
ipcp = ipc_findkey(ids, params->key);
if (ipcp == NULL) {
/* key not used */
if (!(flg & IPC_CREAT))
err = -ENOENT;
else if (!err)
err = -ENOMEM;
else
err = ops->getnew(ns, params);
} else {
/* ipc object has been locked by ipc_findkey() */
if (flg & IPC_CREAT && flg & IPC_EXCL)
err = -EEXIST;
else {
err = 0;
if (ops->more_checks)
err = ops->more_checks(ipcp, params);
if (!err)
err = ipc_check_perms(ipcp, ops, params);
}
ipc_unlock(ipcp);
}
mutex_unlock(&ids->mutex);
return err;
}
/**
* ipc_rmid - remove an IPC identifier
* @ids: identifier set

View File

@ -35,6 +35,35 @@ struct ipc_ids {
struct idr ipcs_idr;
};
/*
* 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 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) (void *, int);
int (*more_checks) (void *, struct ipc_params *);
};
struct seq_file;
void ipc_init_ids(struct ipc_ids *);
@ -50,7 +79,6 @@ void __init ipc_init_proc_interface(const char *path, const char *header,
#define IPC_SHM_IDS 2
/* must be called with ids->mutex acquired.*/
struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key);
int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int);
int ipc_get_maxid(struct ipc_ids *);
@ -95,5 +123,18 @@ int ipc_parse_version (int *cmd);
extern void free_msg(struct msg_msg *msg);
extern struct msg_msg *load_msg(const void __user *src, int len);
extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
extern int ipcget_new(struct ipc_namespace *, struct ipc_ids *,
struct ipc_ops *, struct ipc_params *);
extern int ipcget_public(struct ipc_namespace *, struct ipc_ids *,
struct ipc_ops *, struct ipc_params *);
static inline int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids,
struct ipc_ops *ops, struct ipc_params *params)
{
if (params->key == IPC_PRIVATE)
return ipcget_new(ns, ids, ops, params);
else
return ipcget_public(ns, ids, ops, params);
}
#endif