diff options
Diffstat (limited to 'ipc/msg.c')
-rw-r--r-- | ipc/msg.c | 123 |
1 files changed, 96 insertions, 27 deletions
@@ -755,26 +755,91 @@ static inline int convert_mode(long *msgtyp, int msgflg) | |||
755 | return SEARCH_EQUAL; | 755 | return SEARCH_EQUAL; |
756 | } | 756 | } |
757 | 757 | ||
758 | long do_msgrcv(int msqid, long *pmtype, void __user *mtext, | 758 | static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) |
759 | size_t msgsz, long msgtyp, int msgflg) | 759 | { |
760 | struct msgbuf __user *msgp = dest; | ||
761 | size_t msgsz; | ||
762 | |||
763 | if (put_user(msg->m_type, &msgp->mtype)) | ||
764 | return -EFAULT; | ||
765 | |||
766 | msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; | ||
767 | if (store_msg(msgp->mtext, msg, msgsz)) | ||
768 | return -EFAULT; | ||
769 | return msgsz; | ||
770 | } | ||
771 | |||
772 | #ifdef CONFIG_CHECKPOINT_RESTORE | ||
773 | /* | ||
774 | * This function creates new kernel message structure, large enough to store | ||
775 | * bufsz message bytes. | ||
776 | */ | ||
777 | static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, | ||
778 | int msgflg, long *msgtyp, | ||
779 | unsigned long *copy_number) | ||
780 | { | ||
781 | struct msg_msg *copy; | ||
782 | |||
783 | *copy_number = *msgtyp; | ||
784 | *msgtyp = 0; | ||
785 | /* | ||
786 | * Create dummy message to copy real message to. | ||
787 | */ | ||
788 | copy = load_msg(buf, bufsz); | ||
789 | if (!IS_ERR(copy)) | ||
790 | copy->m_ts = bufsz; | ||
791 | return copy; | ||
792 | } | ||
793 | |||
794 | static inline void free_copy(struct msg_msg *copy) | ||
795 | { | ||
796 | if (copy) | ||
797 | free_msg(copy); | ||
798 | } | ||
799 | #else | ||
800 | static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, | ||
801 | int msgflg, long *msgtyp, | ||
802 | unsigned long *copy_number) | ||
803 | { | ||
804 | return ERR_PTR(-ENOSYS); | ||
805 | } | ||
806 | |||
807 | static inline void free_copy(struct msg_msg *copy) | ||
808 | { | ||
809 | } | ||
810 | #endif | ||
811 | |||
812 | long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, | ||
813 | int msgflg, | ||
814 | long (*msg_handler)(void __user *, struct msg_msg *, size_t)) | ||
760 | { | 815 | { |
761 | struct msg_queue *msq; | 816 | struct msg_queue *msq; |
762 | struct msg_msg *msg; | 817 | struct msg_msg *msg; |
763 | int mode; | 818 | int mode; |
764 | struct ipc_namespace *ns; | 819 | struct ipc_namespace *ns; |
820 | struct msg_msg *copy = NULL; | ||
821 | unsigned long copy_number = 0; | ||
765 | 822 | ||
766 | if (msqid < 0 || (long) msgsz < 0) | 823 | if (msqid < 0 || (long) bufsz < 0) |
767 | return -EINVAL; | 824 | return -EINVAL; |
825 | if (msgflg & MSG_COPY) { | ||
826 | copy = prepare_copy(buf, bufsz, msgflg, &msgtyp, ©_number); | ||
827 | if (IS_ERR(copy)) | ||
828 | return PTR_ERR(copy); | ||
829 | } | ||
768 | mode = convert_mode(&msgtyp, msgflg); | 830 | mode = convert_mode(&msgtyp, msgflg); |
769 | ns = current->nsproxy->ipc_ns; | 831 | ns = current->nsproxy->ipc_ns; |
770 | 832 | ||
771 | msq = msg_lock_check(ns, msqid); | 833 | msq = msg_lock_check(ns, msqid); |
772 | if (IS_ERR(msq)) | 834 | if (IS_ERR(msq)) { |
835 | free_copy(copy); | ||
773 | return PTR_ERR(msq); | 836 | return PTR_ERR(msq); |
837 | } | ||
774 | 838 | ||
775 | for (;;) { | 839 | for (;;) { |
776 | struct msg_receiver msr_d; | 840 | struct msg_receiver msr_d; |
777 | struct list_head *tmp; | 841 | struct list_head *tmp; |
842 | long msg_counter = 0; | ||
778 | 843 | ||
779 | msg = ERR_PTR(-EACCES); | 844 | msg = ERR_PTR(-EACCES); |
780 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) | 845 | if (ipcperms(ns, &msq->q_perm, S_IRUGO)) |
@@ -793,12 +858,21 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext, | |||
793 | msg = walk_msg; | 858 | msg = walk_msg; |
794 | if (mode == SEARCH_LESSEQUAL && | 859 | if (mode == SEARCH_LESSEQUAL && |
795 | walk_msg->m_type != 1) { | 860 | walk_msg->m_type != 1) { |
796 | msg = walk_msg; | ||
797 | msgtyp = walk_msg->m_type - 1; | 861 | msgtyp = walk_msg->m_type - 1; |
798 | } else { | 862 | } else if (msgflg & MSG_COPY) { |
799 | msg = walk_msg; | 863 | if (copy_number == msg_counter) { |
864 | /* | ||
865 | * Found requested message. | ||
866 | * Copy it. | ||
867 | */ | ||
868 | msg = copy_msg(msg, copy); | ||
869 | if (IS_ERR(msg)) | ||
870 | goto out_unlock; | ||
871 | break; | ||
872 | } | ||
873 | } else | ||
800 | break; | 874 | break; |
801 | } | 875 | msg_counter++; |
802 | } | 876 | } |
803 | tmp = tmp->next; | 877 | tmp = tmp->next; |
804 | } | 878 | } |
@@ -807,10 +881,16 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext, | |||
807 | * Found a suitable message. | 881 | * Found a suitable message. |
808 | * Unlink it from the queue. | 882 | * Unlink it from the queue. |
809 | */ | 883 | */ |
810 | if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { | 884 | if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { |
811 | msg = ERR_PTR(-E2BIG); | 885 | msg = ERR_PTR(-E2BIG); |
812 | goto out_unlock; | 886 | goto out_unlock; |
813 | } | 887 | } |
888 | /* | ||
889 | * If we are copying, then do not unlink message and do | ||
890 | * not update queue parameters. | ||
891 | */ | ||
892 | if (msgflg & MSG_COPY) | ||
893 | goto out_unlock; | ||
814 | list_del(&msg->m_list); | 894 | list_del(&msg->m_list); |
815 | msq->q_qnum--; | 895 | msq->q_qnum--; |
816 | msq->q_rtime = get_seconds(); | 896 | msq->q_rtime = get_seconds(); |
@@ -834,7 +914,7 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext, | |||
834 | if (msgflg & MSG_NOERROR) | 914 | if (msgflg & MSG_NOERROR) |
835 | msr_d.r_maxsize = INT_MAX; | 915 | msr_d.r_maxsize = INT_MAX; |
836 | else | 916 | else |
837 | msr_d.r_maxsize = msgsz; | 917 | msr_d.r_maxsize = bufsz; |
838 | msr_d.r_msg = ERR_PTR(-EAGAIN); | 918 | msr_d.r_msg = ERR_PTR(-EAGAIN); |
839 | current->state = TASK_INTERRUPTIBLE; | 919 | current->state = TASK_INTERRUPTIBLE; |
840 | msg_unlock(msq); | 920 | msg_unlock(msq); |
@@ -894,32 +974,21 @@ out_unlock: | |||
894 | break; | 974 | break; |
895 | } | 975 | } |
896 | } | 976 | } |
897 | if (IS_ERR(msg)) | 977 | if (IS_ERR(msg)) { |
978 | free_copy(copy); | ||
898 | return PTR_ERR(msg); | 979 | return PTR_ERR(msg); |
980 | } | ||
899 | 981 | ||
900 | msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz; | 982 | bufsz = msg_handler(buf, msg, bufsz); |
901 | *pmtype = msg->m_type; | ||
902 | if (store_msg(mtext, msg, msgsz)) | ||
903 | msgsz = -EFAULT; | ||
904 | |||
905 | free_msg(msg); | 983 | free_msg(msg); |
906 | 984 | ||
907 | return msgsz; | 985 | return bufsz; |
908 | } | 986 | } |
909 | 987 | ||
910 | SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, | 988 | SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz, |
911 | long, msgtyp, int, msgflg) | 989 | long, msgtyp, int, msgflg) |
912 | { | 990 | { |
913 | long err, mtype; | 991 | return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill); |
914 | |||
915 | err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); | ||
916 | if (err < 0) | ||
917 | goto out; | ||
918 | |||
919 | if (put_user(mtype, &msgp->mtype)) | ||
920 | err = -EFAULT; | ||
921 | out: | ||
922 | return err; | ||
923 | } | 992 | } |
924 | 993 | ||
925 | #ifdef CONFIG_PROC_FS | 994 | #ifdef CONFIG_PROC_FS |