aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/msg.c
diff options
context:
space:
mode:
authorPierre Peiffer <pierre.peiffer@bull.net>2008-04-29 04:00:48 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 11:06:13 -0400
commita0d092fc2df845a43cc4847836818f49331d0a5c (patch)
treef47b94bfa2e18beadff39930c9386764e45bc4ec /ipc/msg.c
parent8d4cc8b5c5e5bac526618ee704f3cfdcad954e0c (diff)
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>
Diffstat (limited to 'ipc/msg.c')
-rw-r--r--ipc/msg.c162
1 files changed, 89 insertions, 73 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index 7d9b0694c743..9d868b3d3329 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -436,10 +436,95 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
436 } 436 }
437} 437}
438 438
439asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf) 439/*
440 * This function handles some msgctl commands which require the rw_mutex
441 * to be held in write mode.
442 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
443 */
444static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
445 struct msqid_ds __user *buf, int version)
440{ 446{
441 struct kern_ipc_perm *ipcp; 447 struct kern_ipc_perm *ipcp;
442 struct msq_setbuf uninitialized_var(setbuf); 448 struct msq_setbuf setbuf;
449 struct msg_queue *msq;
450 int err;
451
452 if (cmd == IPC_SET) {
453 if (copy_msqid_from_user(&setbuf, buf, version))
454 return -EFAULT;
455 }
456
457 down_write(&msg_ids(ns).rw_mutex);
458 msq = msg_lock_check_down(ns, msqid);
459 if (IS_ERR(msq)) {
460 err = PTR_ERR(msq);
461 goto out_up;
462 }
463
464 ipcp = &msq->q_perm;
465
466 err = audit_ipc_obj(ipcp);
467 if (err)
468 goto out_unlock;
469
470 if (cmd == IPC_SET) {
471 err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
472 setbuf.mode);
473 if (err)
474 goto out_unlock;
475 }
476
477 if (current->euid != ipcp->cuid &&
478 current->euid != ipcp->uid &&
479 !capable(CAP_SYS_ADMIN)) {
480 /* We _could_ check for CAP_CHOWN above, but we don't */
481 err = -EPERM;
482 goto out_unlock;
483 }
484
485 err = security_msg_queue_msgctl(msq, cmd);
486 if (err)
487 goto out_unlock;
488
489 switch (cmd) {
490 case IPC_RMID:
491 freeque(ns, ipcp);
492 goto out_up;
493 case IPC_SET:
494 if (setbuf.qbytes > ns->msg_ctlmnb &&
495 !capable(CAP_SYS_RESOURCE)) {
496 err = -EPERM;
497 goto out_unlock;
498 }
499
500 msq->q_qbytes = setbuf.qbytes;
501
502 ipcp->uid = setbuf.uid;
503 ipcp->gid = setbuf.gid;
504 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
505 (S_IRWXUGO & setbuf.mode);
506 msq->q_ctime = get_seconds();
507 /* sleeping receivers might be excluded by
508 * stricter permissions.
509 */
510 expunge_all(msq, -EAGAIN);
511 /* sleeping senders might be able to send
512 * due to a larger queue size.
513 */
514 ss_wakeup(&msq->q_senders, 0);
515 break;
516 default:
517 err = -EINVAL;
518 }
519out_unlock:
520 msg_unlock(msq);
521out_up:
522 up_write(&msg_ids(ns).rw_mutex);
523 return err;
524}
525
526asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
527{
443 struct msg_queue *msq; 528 struct msg_queue *msq;
444 int err, version; 529 int err, version;
445 struct ipc_namespace *ns; 530 struct ipc_namespace *ns;
@@ -535,82 +620,13 @@ asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
535 return success_return; 620 return success_return;
536 } 621 }
537 case IPC_SET: 622 case IPC_SET:
538 if (!buf)
539 return -EFAULT;
540 if (copy_msqid_from_user(&setbuf, buf, version))
541 return -EFAULT;
542 break;
543 case IPC_RMID: 623 case IPC_RMID:
544 break; 624 err = msgctl_down(ns, msqid, cmd, buf, version);
625 return err;
545 default: 626 default:
546 return -EINVAL; 627 return -EINVAL;
547 } 628 }
548 629
549 down_write(&msg_ids(ns).rw_mutex);
550 msq = msg_lock_check_down(ns, msqid);
551 if (IS_ERR(msq)) {
552 err = PTR_ERR(msq);
553 goto out_up;
554 }
555
556 ipcp = &msq->q_perm;
557
558 err = audit_ipc_obj(ipcp);
559 if (err)
560 goto out_unlock_up;
561 if (cmd == IPC_SET) {
562 err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid,
563 setbuf.mode);
564 if (err)
565 goto out_unlock_up;
566 }
567
568 err = -EPERM;
569 if (current->euid != ipcp->cuid &&
570 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
571 /* We _could_ check for CAP_CHOWN above, but we don't */
572 goto out_unlock_up;
573
574 err = security_msg_queue_msgctl(msq, cmd);
575 if (err)
576 goto out_unlock_up;
577
578 switch (cmd) {
579 case IPC_SET:
580 {
581 err = -EPERM;
582 if (setbuf.qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
583 goto out_unlock_up;
584
585 msq->q_qbytes = setbuf.qbytes;
586
587 ipcp->uid = setbuf.uid;
588 ipcp->gid = setbuf.gid;
589 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
590 (S_IRWXUGO & setbuf.mode);
591 msq->q_ctime = get_seconds();
592 /* sleeping receivers might be excluded by
593 * stricter permissions.
594 */
595 expunge_all(msq, -EAGAIN);
596 /* sleeping senders might be able to send
597 * due to a larger queue size.
598 */
599 ss_wakeup(&msq->q_senders, 0);
600 msg_unlock(msq);
601 break;
602 }
603 case IPC_RMID:
604 freeque(ns, &msq->q_perm);
605 break;
606 }
607 err = 0;
608out_up:
609 up_write(&msg_ids(ns).rw_mutex);
610 return err;
611out_unlock_up:
612 msg_unlock(msq);
613 goto out_up;
614out_unlock: 630out_unlock:
615 msg_unlock(msq); 631 msg_unlock(msq);
616 return err; 632 return err;