IPC/message queues: introduce msgctl_down

Currently, sys_msgctl is not easy to read.

This patch tries to improve that by introducing the msgctl_down function to
handle all commands requiring the rwmutex to be taken in write mode (ie
IPC_SET and IPC_RMID for now).  It is the equivalent function of semctl_down
for message queues.

This greatly changes the readability of sys_msgctl and also harmonizes the way
these commands are handled among all IPCs.

Signed-off-by: Pierre Peiffer <pierre.peiffer@bull.net>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: 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:
Pierre Peiffer 2008-04-29 01:00:48 -07:00 committed by Linus Torvalds
parent 8d4cc8b5c5
commit a0d092fc2d
1 changed files with 89 additions and 73 deletions

162
ipc/msg.c
View File

@ -436,10 +436,95 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
}
}
asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
/*
* This function handles some msgctl commands which require the rw_mutex
* to be held in write mode.
* NOTE: no locks must be held, the rw_mutex is taken inside this function.
*/
static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
struct msqid_ds __user *buf, int version)
{
struct kern_ipc_perm *ipcp;
struct msq_setbuf uninitialized_var(setbuf);
struct msq_setbuf setbuf;
struct msg_queue *msq;
int err;
if (cmd == IPC_SET) {
if (copy_msqid_from_user(&setbuf, buf, version))
return -EFAULT;
}
down_write(&msg_ids(ns).rw_mutex);
msq = msg_lock_check_down(ns, msqid);
if (IS_ERR(msq)) {
err = PTR_ERR(msq);
goto out_up;
}
ipcp = &msq->q_perm;
err = audit_ipc_obj(ipcp);
if (err)
goto out_unlock;
if (cmd == IPC_SET) {
err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
setbuf.mode);
if (err)
goto out_unlock;
}
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid &&
!capable(CAP_SYS_ADMIN)) {
/* We _could_ check for CAP_CHOWN above, but we don't */
err = -EPERM;
goto out_unlock;
}
err = security_msg_queue_msgctl(msq, cmd);
if (err)
goto out_unlock;
switch (cmd) {
case IPC_RMID:
freeque(ns, ipcp);
goto out_up;
case IPC_SET:
if (setbuf.qbytes > ns->msg_ctlmnb &&
!capable(CAP_SYS_RESOURCE)) {
err = -EPERM;
goto out_unlock;
}
msq->q_qbytes = setbuf.qbytes;
ipcp->uid = setbuf.uid;
ipcp->gid = setbuf.gid;
ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
(S_IRWXUGO & setbuf.mode);
msq->q_ctime = get_seconds();
/* sleeping receivers might be excluded by
* stricter permissions.
*/
expunge_all(msq, -EAGAIN);
/* sleeping senders might be able to send
* due to a larger queue size.
*/
ss_wakeup(&msq->q_senders, 0);
break;
default:
err = -EINVAL;
}
out_unlock:
msg_unlock(msq);
out_up:
up_write(&msg_ids(ns).rw_mutex);
return err;
}
asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
{
struct msg_queue *msq;
int err, version;
struct ipc_namespace *ns;
@ -535,82 +620,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
return success_return;
}
case IPC_SET:
if (!buf)
return -EFAULT;
if (copy_msqid_from_user(&setbuf, buf, version))
return -EFAULT;
break;
case IPC_RMID:
break;
err = msgctl_down(ns, msqid, cmd, buf, version);
return err;
default:
return -EINVAL;
}
down_write(&msg_ids(ns).rw_mutex);
msq = msg_lock_check_down(ns, msqid);
if (IS_ERR(msq)) {
err = PTR_ERR(msq);
goto out_up;
}
ipcp = &msq->q_perm;
err = audit_ipc_obj(ipcp);
if (err)
goto out_unlock_up;
if (cmd == IPC_SET) {
err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
setbuf.mode);
if (err)
goto out_unlock_up;
}
err = -EPERM;
if (current->euid != ipcp->cuid &&
current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
/* We _could_ check for CAP_CHOWN above, but we don't */
goto out_unlock_up;
err = security_msg_queue_msgctl(msq, cmd);
if (err)
goto out_unlock_up;
switch (cmd) {
case IPC_SET:
{
err = -EPERM;
if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
goto out_unlock_up;
msq->q_qbytes = setbuf.qbytes;
ipcp->uid = setbuf.uid;
ipcp->gid = setbuf.gid;
ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
(S_IRWXUGO & setbuf.mode);
msq->q_ctime = get_seconds();
/* sleeping receivers might be excluded by
* stricter permissions.
*/
expunge_all(msq, -EAGAIN);
/* sleeping senders might be able to send
* due to a larger queue size.
*/
ss_wakeup(&msq->q_senders, 0);
msg_unlock(msq);
break;
}
case IPC_RMID:
freeque(ns, &msq->q_perm);
break;
}
err = 0;
out_up:
up_write(&msg_ids(ns).rw_mutex);
return err;
out_unlock_up:
msg_unlock(msq);
goto out_up;
out_unlock:
msg_unlock(msq);
return err;