aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/shm.c
diff options
context:
space:
mode:
authorPierre Peiffer <pierre.peiffer@bull.net>2008-04-29 04:00:47 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-29 11:06:13 -0400
commit8d4cc8b5c5e5bac526618ee704f3cfdcad954e0c (patch)
tree21258b10175f63703ace0a88402e513c6046d2d8 /ipc/shm.c
parent6ff3797218ef41c248c83184101ce1aedc227333 (diff)
IPC/shared memory: introduce shmctl_down
Currently, the way the different commands are handled in sys_shmctl introduces some duplicated code. This patch introduces the shmctl_down function to handle all the 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 shared memory. This removes some duplicated code for handling these both commands and harmonizes the way they 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/shm.c')
-rw-r--r--ipc/shm.c160
1 files changed, 72 insertions, 88 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index 3e4aff982546..65a44bcc4ac2 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -616,10 +616,78 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
616 } 616 }
617} 617}
618 618
619asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) 619/*
620 * This function handles some shmctl commands which require the rw_mutex
621 * to be held in write mode.
622 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
623 */
624static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
625 struct shmid_ds __user *buf, int version)
620{ 626{
627 struct kern_ipc_perm *ipcp;
621 struct shm_setbuf setbuf; 628 struct shm_setbuf setbuf;
622 struct shmid_kernel *shp; 629 struct shmid_kernel *shp;
630 int err;
631
632 if (cmd == IPC_SET) {
633 if (copy_shmid_from_user(&setbuf, buf, version))
634 return -EFAULT;
635 }
636
637 down_write(&shm_ids(ns).rw_mutex);
638 shp = shm_lock_check_down(ns, shmid);
639 if (IS_ERR(shp)) {
640 err = PTR_ERR(shp);
641 goto out_up;
642 }
643
644 ipcp = &shp->shm_perm;
645
646 err = audit_ipc_obj(ipcp);
647 if (err)
648 goto out_unlock;
649
650 if (cmd == IPC_SET) {
651 err = audit_ipc_set_perm(0, setbuf.uid,
652 setbuf.gid, setbuf.mode);
653 if (err)
654 goto out_unlock;
655 }
656
657 if (current->euid != ipcp->uid &&
658 current->euid != ipcp->cuid &&
659 !capable(CAP_SYS_ADMIN)) {
660 err = -EPERM;
661 goto out_unlock;
662 }
663
664 err = security_shm_shmctl(shp, cmd);
665 if (err)
666 goto out_unlock;
667 switch (cmd) {
668 case IPC_RMID:
669 do_shm_rmid(ns, ipcp);
670 goto out_up;
671 case IPC_SET:
672 ipcp->uid = setbuf.uid;
673 ipcp->gid = setbuf.gid;
674 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
675 | (setbuf.mode & S_IRWXUGO);
676 shp->shm_ctim = get_seconds();
677 break;
678 default:
679 err = -EINVAL;
680 }
681out_unlock:
682 shm_unlock(shp);
683out_up:
684 up_write(&shm_ids(ns).rw_mutex);
685 return err;
686}
687
688asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
689{
690 struct shmid_kernel *shp;
623 int err, version; 691 int err, version;
624 struct ipc_namespace *ns; 692 struct ipc_namespace *ns;
625 693
@@ -775,97 +843,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
775 goto out; 843 goto out;
776 } 844 }
777 case IPC_RMID: 845 case IPC_RMID:
778 {
779 /*
780 * We cannot simply remove the file. The SVID states
781 * that the block remains until the last person
782 * detaches from it, then is deleted. A shmat() on
783 * an RMID segment is legal in older Linux and if
784 * we change it apps break...
785 *
786 * Instead we set a destroyed flag, and then blow
787 * the name away when the usage hits zero.
788 */
789 down_write(&shm_ids(ns).rw_mutex);
790 shp = shm_lock_check_down(ns, shmid);
791 if (IS_ERR(shp)) {
792 err = PTR_ERR(shp);
793 goto out_up;
794 }
795
796 err = audit_ipc_obj(&(shp->shm_perm));
797 if (err)
798 goto out_unlock_up;
799
800 if (current->euid != shp->shm_perm.uid &&
801 current->euid != shp->shm_perm.cuid &&
802 !capable(CAP_SYS_ADMIN)) {
803 err=-EPERM;
804 goto out_unlock_up;
805 }
806
807 err = security_shm_shmctl(shp, cmd);
808 if (err)
809 goto out_unlock_up;
810
811 do_shm_rmid(ns, &shp->shm_perm);
812 up_write(&shm_ids(ns).rw_mutex);
813 goto out;
814 }
815
816 case IPC_SET: 846 case IPC_SET:
817 { 847 err = shmctl_down(ns, shmid, cmd, buf, version);
818 if (!buf) { 848 return err;
819 err = -EFAULT;
820 goto out;
821 }
822
823 if (copy_shmid_from_user (&setbuf, buf, version)) {
824 err = -EFAULT;
825 goto out;
826 }
827 down_write(&shm_ids(ns).rw_mutex);
828 shp = shm_lock_check_down(ns, shmid);
829 if (IS_ERR(shp)) {
830 err = PTR_ERR(shp);
831 goto out_up;
832 }
833 err = audit_ipc_obj(&(shp->shm_perm));
834 if (err)
835 goto out_unlock_up;
836 err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
837 if (err)
838 goto out_unlock_up;
839 err=-EPERM;
840 if (current->euid != shp->shm_perm.uid &&
841 current->euid != shp->shm_perm.cuid &&
842 !capable(CAP_SYS_ADMIN)) {
843 goto out_unlock_up;
844 }
845
846 err = security_shm_shmctl(shp, cmd);
847 if (err)
848 goto out_unlock_up;
849
850 shp->shm_perm.uid = setbuf.uid;
851 shp->shm_perm.gid = setbuf.gid;
852 shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
853 | (setbuf.mode & S_IRWXUGO);
854 shp->shm_ctim = get_seconds();
855 break;
856 }
857
858 default: 849 default:
859 err = -EINVAL; 850 return -EINVAL;
860 goto out;
861 } 851 }
862 852
863 err = 0;
864out_unlock_up:
865 shm_unlock(shp);
866out_up:
867 up_write(&shm_ids(ns).rw_mutex);
868 goto out;
869out_unlock: 853out_unlock:
870 shm_unlock(shp); 854 shm_unlock(shp);
871out: 855out: