RHEL4/ipc/msg.c
<<
>>
Prefs
   1/*
   2 * linux/ipc/msg.c
   3 * Copyright (C) 1992 Krishna Balasubramanian 
   4 *
   5 * Removed all the remaining kerneld mess
   6 * Catch the -EFAULT stuff properly
   7 * Use GFP_KERNEL for messages as in 1.2
   8 * Fixed up the unchecked user space derefs
   9 * Copyright (C) 1998 Alan Cox & Andi Kleen
  10 *
  11 * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
  12 *
  13 * mostly rewritten, threaded and wake-one semantics added
  14 * MSGMAX limit removed, sysctl's added
  15 * (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
  16 */
  17
  18#include <linux/config.h>
  19#include <linux/slab.h>
  20#include <linux/msg.h>
  21#include <linux/spinlock.h>
  22#include <linux/init.h>
  23#include <linux/proc_fs.h>
  24#include <linux/list.h>
  25#include <linux/security.h>
  26#include <linux/sched.h>
  27#include <linux/audit.h>
  28#include <asm/current.h>
  29#include <asm/uaccess.h>
  30#include "util.h"
  31
  32/* sysctl: */
  33int msg_ctlmax = MSGMAX;
  34int msg_ctlmnb = MSGMNB;
  35int msg_ctlmni = MSGMNI;
  36
  37/* one msg_receiver structure for each sleeping receiver */
  38struct msg_receiver {
  39        struct list_head r_list;
  40        struct task_struct* r_tsk;
  41
  42        int r_mode;
  43        long r_msgtype;
  44        long r_maxsize;
  45
  46        struct msg_msg* volatile r_msg;
  47};
  48
  49/* one msg_sender for each sleeping sender */
  50struct msg_sender {
  51        struct list_head list;
  52        struct task_struct* tsk;
  53};
  54
  55#define SEARCH_ANY              1
  56#define SEARCH_EQUAL            2
  57#define SEARCH_NOTEQUAL         3
  58#define SEARCH_LESSEQUAL        4
  59
  60static atomic_t msg_bytes = ATOMIC_INIT(0);
  61static atomic_t msg_hdrs = ATOMIC_INIT(0);
  62
  63static struct ipc_ids msg_ids;
  64
  65#define msg_lock(id)    ((struct msg_queue*)ipc_lock(&msg_ids,id))
  66#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm)
  67#define msg_rmid(id)    ((struct msg_queue*)ipc_rmid(&msg_ids,id))
  68#define msg_checkid(msq, msgid) \
  69        ipc_checkid(&msg_ids,&msq->q_perm,msgid)
  70#define msg_buildid(id, seq) \
  71        ipc_buildid(&msg_ids, id, seq)
  72
  73static void freeque (struct msg_queue *msq, int id);
  74static int newque (key_t key, int msgflg);
  75#ifdef CONFIG_PROC_FS
  76static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
  77#endif
  78
  79void __init msg_init (void)
  80{
  81        ipc_init_ids(&msg_ids,msg_ctlmni);
  82
  83#ifdef CONFIG_PROC_FS
  84        create_proc_read_entry("sysvipc/msg", 0, NULL, sysvipc_msg_read_proc, NULL);
  85#endif
  86}
  87
  88static int newque (key_t key, int msgflg)
  89{
  90        int id;
  91        int retval;
  92        struct msg_queue *msq;
  93
  94        msq  = ipc_rcu_alloc(sizeof(*msq));
  95        if (!msq) 
  96                return -ENOMEM;
  97
  98        msq->q_perm.mode = (msgflg & S_IRWXUGO);
  99        msq->q_perm.key = key;
 100
 101        msq->q_perm.security = NULL;
 102        retval = security_msg_queue_alloc(msq);
 103        if (retval) {
 104                ipc_rcu_putref(msq);
 105                return retval;
 106        }
 107
 108        id = ipc_addid(&msg_ids, &msq->q_perm, msg_ctlmni);
 109        if(id == -1) {
 110                security_msg_queue_free(msq);
 111                ipc_rcu_putref(msq);
 112                return -ENOSPC;
 113        }
 114
 115        msq->q_stime = msq->q_rtime = 0;
 116        msq->q_ctime = get_seconds();
 117        msq->q_cbytes = msq->q_qnum = 0;
 118        msq->q_qbytes = msg_ctlmnb;
 119        msq->q_lspid = msq->q_lrpid = 0;
 120        INIT_LIST_HEAD(&msq->q_messages);
 121        INIT_LIST_HEAD(&msq->q_receivers);
 122        INIT_LIST_HEAD(&msq->q_senders);
 123        msg_unlock(msq);
 124
 125        return msg_buildid(id,msq->q_perm.seq);
 126}
 127
 128static inline void ss_add(struct msg_queue* msq, struct msg_sender* mss)
 129{
 130        mss->tsk=current;
 131        current->state=TASK_INTERRUPTIBLE;
 132        list_add_tail(&mss->list,&msq->q_senders);
 133}
 134
 135static inline void ss_del(struct msg_sender* mss)
 136{
 137        if(mss->list.next != NULL)
 138                list_del(&mss->list);
 139}
 140
 141static void ss_wakeup(struct list_head* h, int kill)
 142{
 143        struct list_head *tmp;
 144
 145        tmp = h->next;
 146        while (tmp != h) {
 147                struct msg_sender* mss;
 148                
 149                mss = list_entry(tmp,struct msg_sender,list);
 150                tmp = tmp->next;
 151                if(kill)
 152                        mss->list.next=NULL;
 153                wake_up_process(mss->tsk);
 154        }
 155}
 156
 157static void expunge_all(struct msg_queue* msq, int res)
 158{
 159        struct list_head *tmp;
 160
 161        tmp = msq->q_receivers.next;
 162        while (tmp != &msq->q_receivers) {
 163                struct msg_receiver* msr;
 164                
 165                msr = list_entry(tmp,struct msg_receiver,r_list);
 166                tmp = tmp->next;
 167                msr->r_msg = NULL;
 168                wake_up_process(msr->r_tsk);
 169                smp_mb();
 170                msr->r_msg = ERR_PTR(res);
 171        }
 172}
 173/* 
 174 * freeque() wakes up waiters on the sender and receiver waiting queue, 
 175 * removes the message queue from message queue ID 
 176 * array, and cleans up all the messages associated with this queue.
 177 *
 178 * msg_ids.sem and the spinlock for this message queue is hold
 179 * before freeque() is called. msg_ids.sem remains locked on exit.
 180 */
 181static void freeque (struct msg_queue *msq, int id)
 182{
 183        struct list_head *tmp;
 184
 185        expunge_all(msq,-EIDRM);
 186        ss_wakeup(&msq->q_senders,1);
 187        msq = msg_rmid(id);
 188        msg_unlock(msq);
 189                
 190        tmp = msq->q_messages.next;
 191        while(tmp != &msq->q_messages) {
 192                struct msg_msg* msg = list_entry(tmp,struct msg_msg,m_list);
 193                tmp = tmp->next;
 194                atomic_dec(&msg_hdrs);
 195                free_msg(msg);
 196        }
 197        atomic_sub(msq->q_cbytes, &msg_bytes);
 198        security_msg_queue_free(msq);
 199        ipc_rcu_putref(msq);
 200}
 201
 202asmlinkage long sys_msgget (key_t key, int msgflg)
 203{
 204        int id, ret = -EPERM;
 205        struct msg_queue *msq;
 206        
 207        down(&msg_ids.sem);
 208        if (key == IPC_PRIVATE) 
 209                ret = newque(key, msgflg);
 210        else if ((id = ipc_findkey(&msg_ids, key)) == -1) { /* key not used */
 211                if (!(msgflg & IPC_CREAT))
 212                        ret = -ENOENT;
 213                else
 214                        ret = newque(key, msgflg);
 215        } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
 216                ret = -EEXIST;
 217        } else {
 218                msq = msg_lock(id);
 219                if(msq==NULL)
 220                        BUG();
 221                if (ipcperms(&msq->q_perm, msgflg))
 222                        ret = -EACCES;
 223                else {
 224                        int qid = msg_buildid(id, msq->q_perm.seq);
 225                        ret = security_msg_queue_associate(msq, msgflg);
 226                        if (!ret)
 227                                ret = qid;
 228                }
 229                msg_unlock(msq);
 230        }
 231        up(&msg_ids.sem);
 232        return ret;
 233}
 234
 235static inline unsigned long copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version)
 236{
 237        switch(version) {
 238        case IPC_64:
 239                return copy_to_user (buf, in, sizeof(*in));
 240        case IPC_OLD:
 241            {
 242                struct msqid_ds out;
 243
 244                memset(&out,0,sizeof(out));
 245
 246                ipc64_perm_to_ipc_perm(&in->msg_perm, &out.msg_perm);
 247
 248                out.msg_stime           = in->msg_stime;
 249                out.msg_rtime           = in->msg_rtime;
 250                out.msg_ctime           = in->msg_ctime;
 251
 252                if(in->msg_cbytes > USHRT_MAX)
 253                        out.msg_cbytes  = USHRT_MAX;
 254                else
 255                        out.msg_cbytes  = in->msg_cbytes;
 256                out.msg_lcbytes         = in->msg_cbytes;
 257
 258                if(in->msg_qnum > USHRT_MAX)
 259                        out.msg_qnum    = USHRT_MAX;
 260                else
 261                        out.msg_qnum    = in->msg_qnum;
 262
 263                if(in->msg_qbytes > USHRT_MAX)
 264                        out.msg_qbytes  = USHRT_MAX;
 265                else
 266                        out.msg_qbytes  = in->msg_qbytes;
 267                out.msg_lqbytes         = in->msg_qbytes;
 268
 269                out.msg_lspid           = in->msg_lspid;
 270                out.msg_lrpid           = in->msg_lrpid;
 271
 272                return copy_to_user (buf, &out, sizeof(out));
 273            }
 274        default:
 275                return -EINVAL;
 276        }
 277}
 278
 279struct msq_setbuf {
 280        unsigned long   qbytes;
 281        uid_t           uid;
 282        gid_t           gid;
 283        mode_t          mode;
 284};
 285
 286static inline unsigned long copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
 287{
 288        switch(version) {
 289        case IPC_64:
 290            {
 291                struct msqid64_ds tbuf;
 292
 293                if (copy_from_user (&tbuf, buf, sizeof (tbuf)))
 294                        return -EFAULT;
 295
 296                out->qbytes             = tbuf.msg_qbytes;
 297                out->uid                = tbuf.msg_perm.uid;
 298                out->gid                = tbuf.msg_perm.gid;
 299                out->mode               = tbuf.msg_perm.mode;
 300
 301                return 0;
 302            }
 303        case IPC_OLD:
 304            {
 305                struct msqid_ds tbuf_old;
 306
 307                if (copy_from_user (&tbuf_old, buf, sizeof (tbuf_old)))
 308                        return -EFAULT;
 309
 310                out->uid                = tbuf_old.msg_perm.uid;
 311                out->gid                = tbuf_old.msg_perm.gid;
 312                out->mode               = tbuf_old.msg_perm.mode;
 313
 314                if(tbuf_old.msg_qbytes == 0)
 315                        out->qbytes     = tbuf_old.msg_lqbytes;
 316                else
 317                        out->qbytes     = tbuf_old.msg_qbytes;
 318
 319                return 0;
 320            }
 321        default:
 322                return -EINVAL;
 323        }
 324}
 325
 326asmlinkage long sys_msgctl (int msqid, int cmd, struct msqid_ds __user *buf)
 327{
 328        int err, version;
 329        struct msg_queue *msq;
 330        struct msq_setbuf setbuf;
 331        struct kern_ipc_perm *ipcp;
 332        
 333        if (msqid < 0 || cmd < 0)
 334                return -EINVAL;
 335
 336        version = ipc_parse_version(&cmd);
 337
 338        switch (cmd) {
 339        case IPC_INFO: 
 340        case MSG_INFO: 
 341        { 
 342                struct msginfo msginfo;
 343                int max_id;
 344                if (!buf)
 345                        return -EFAULT;
 346                /* We must not return kernel stack data.
 347                 * due to padding, it's not enough
 348                 * to set all member fields.
 349                 */
 350
 351                err = security_msg_queue_msgctl(NULL, cmd);
 352                if (err)
 353                        return err;
 354
 355                memset(&msginfo,0,sizeof(msginfo));     
 356                msginfo.msgmni = msg_ctlmni;
 357                msginfo.msgmax = msg_ctlmax;
 358                msginfo.msgmnb = msg_ctlmnb;
 359                msginfo.msgssz = MSGSSZ;
 360                msginfo.msgseg = MSGSEG;
 361                down(&msg_ids.sem);
 362                if (cmd == MSG_INFO) {
 363                        msginfo.msgpool = msg_ids.in_use;
 364                        msginfo.msgmap = atomic_read(&msg_hdrs);
 365                        msginfo.msgtql = atomic_read(&msg_bytes);
 366                } else {
 367                        msginfo.msgmap = MSGMAP;
 368                        msginfo.msgpool = MSGPOOL;
 369                        msginfo.msgtql = MSGTQL;
 370                }
 371                max_id = msg_ids.max_id;
 372                up(&msg_ids.sem);
 373                if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))
 374                        return -EFAULT;
 375                return (max_id < 0) ? 0: max_id;
 376        }
 377        case MSG_STAT:
 378        case IPC_STAT:
 379        {
 380                struct msqid64_ds tbuf;
 381                int success_return;
 382                if (!buf)
 383                        return -EFAULT;
 384                if(cmd == MSG_STAT && msqid >= msg_ids.size)
 385                        return -EINVAL;
 386
 387                memset(&tbuf,0,sizeof(tbuf));
 388
 389                msq = msg_lock(msqid);
 390                if (msq == NULL)
 391                        return -EINVAL;
 392
 393                if(cmd == MSG_STAT) {
 394                        success_return = msg_buildid(msqid, msq->q_perm.seq);
 395                } else {
 396                        err = -EIDRM;
 397                        if (msg_checkid(msq,msqid))
 398                                goto out_unlock;
 399                        success_return = 0;
 400                }
 401                err = -EACCES;
 402                if (ipcperms (&msq->q_perm, S_IRUGO))
 403                        goto out_unlock;
 404
 405                err = security_msg_queue_msgctl(msq, cmd);
 406                if (err)
 407                        goto out_unlock;
 408
 409                kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm);
 410                tbuf.msg_stime  = msq->q_stime;
 411                tbuf.msg_rtime  = msq->q_rtime;
 412                tbuf.msg_ctime  = msq->q_ctime;
 413                tbuf.msg_cbytes = msq->q_cbytes;
 414                tbuf.msg_qnum   = msq->q_qnum;
 415                tbuf.msg_qbytes = msq->q_qbytes;
 416                tbuf.msg_lspid  = msq->q_lspid;
 417                tbuf.msg_lrpid  = msq->q_lrpid;
 418                msg_unlock(msq);
 419                if (copy_msqid_to_user(buf, &tbuf, version))
 420                        return -EFAULT;
 421                return success_return;
 422        }
 423        case IPC_SET:
 424                if (!buf)
 425                        return -EFAULT;
 426                if (copy_msqid_from_user (&setbuf, buf, version))
 427                        return -EFAULT;
 428                if ((err = audit_ipc_perms(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode)))
 429                        return err;
 430                break;
 431        case IPC_RMID:
 432                break;
 433        default:
 434                return  -EINVAL;
 435        }
 436
 437        down(&msg_ids.sem);
 438        msq = msg_lock(msqid);
 439        err=-EINVAL;
 440        if (msq == NULL)
 441                goto out_up;
 442
 443        err = -EIDRM;
 444        if (msg_checkid(msq,msqid))
 445                goto out_unlock_up;
 446        ipcp = &msq->q_perm;
 447        err = -EPERM;
 448        if (current->euid != ipcp->cuid && 
 449            current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
 450            /* We _could_ check for CAP_CHOWN above, but we don't */
 451                goto out_unlock_up;
 452
 453        err = security_msg_queue_msgctl(msq, cmd);
 454        if (err)
 455                goto out_unlock_up;
 456
 457        switch (cmd) {
 458        case IPC_SET:
 459        {
 460                err = -EPERM;
 461                if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE))
 462                        goto out_unlock_up;
 463
 464                msq->q_qbytes = setbuf.qbytes;
 465
 466                ipcp->uid = setbuf.uid;
 467                ipcp->gid = setbuf.gid;
 468                ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | 
 469                        (S_IRWXUGO & setbuf.mode);
 470                msq->q_ctime = get_seconds();
 471                /* sleeping receivers might be excluded by
 472                 * stricter permissions.
 473                 */
 474                expunge_all(msq,-EAGAIN);
 475                /* sleeping senders might be able to send
 476                 * due to a larger queue size.
 477                 */
 478                ss_wakeup(&msq->q_senders,0);
 479                msg_unlock(msq);
 480                break;
 481        }
 482        case IPC_RMID:
 483                freeque (msq, msqid); 
 484                break;
 485        }
 486        err = 0;
 487out_up:
 488        up(&msg_ids.sem);
 489        return err;
 490out_unlock_up:
 491        msg_unlock(msq);
 492        goto out_up;
 493out_unlock:
 494        msg_unlock(msq);
 495        return err;
 496}
 497
 498static int testmsg(struct msg_msg* msg,long type,int mode)
 499{
 500        switch(mode)
 501        {
 502                case SEARCH_ANY:
 503                        return 1;
 504                case SEARCH_LESSEQUAL:
 505                        if(msg->m_type <=type)
 506                                return 1;
 507                        break;
 508                case SEARCH_EQUAL:
 509                        if(msg->m_type == type)
 510                                return 1;
 511                        break;
 512                case SEARCH_NOTEQUAL:
 513                        if(msg->m_type != type)
 514                                return 1;
 515                        break;
 516        }
 517        return 0;
 518}
 519
 520static inline int pipelined_send(struct msg_queue* msq, struct msg_msg* msg)
 521{
 522        struct list_head* tmp;
 523
 524        tmp = msq->q_receivers.next;
 525        while (tmp != &msq->q_receivers) {
 526                struct msg_receiver* msr;
 527                msr = list_entry(tmp,struct msg_receiver,r_list);
 528                tmp = tmp->next;
 529                if(testmsg(msg,msr->r_msgtype,msr->r_mode) &&
 530                   !security_msg_queue_msgrcv(msq, msg, msr->r_tsk, msr->r_msgtype, msr->r_mode)) {
 531                        list_del(&msr->r_list);
 532                        if(msr->r_maxsize < msg->m_ts) {
 533                                msr->r_msg = NULL;
 534                                wake_up_process(msr->r_tsk);
 535                                smp_mb();
 536                                msr->r_msg = ERR_PTR(-E2BIG);
 537                        } else {
 538                                msr->r_msg = NULL;
 539                                msq->q_lrpid = msr->r_tsk->pid;
 540                                msq->q_rtime = get_seconds();
 541                                wake_up_process(msr->r_tsk);
 542                                smp_mb();
 543                                msr->r_msg = msg;
 544                                return 1;
 545                        }
 546                }
 547        }
 548        return 0;
 549}
 550
 551long do_msgsnd(int msqid, long mtype, void __user *mtext,
 552                size_t msgsz, int msgflg)
 553{
 554        struct msg_queue *msq;
 555        struct msg_msg *msg;
 556        int err;
 557        
 558        if (msgsz > msg_ctlmax || (long) msgsz < 0 || msqid < 0)
 559                return -EINVAL;
 560        if (mtype < 1)
 561                return -EINVAL;
 562
 563        msg = load_msg(mtext, msgsz);
 564        if(IS_ERR(msg))
 565                return PTR_ERR(msg);
 566
 567        msg->m_type = mtype;
 568        msg->m_ts = msgsz;
 569
 570        msq = msg_lock(msqid);
 571        err=-EINVAL;
 572        if(msq==NULL)
 573                goto out_free;
 574
 575        err= -EIDRM;
 576        if (msg_checkid(msq,msqid))
 577                goto out_unlock_free;
 578
 579        for (;;) {
 580                struct msg_sender s;
 581
 582                err=-EACCES;
 583                if (ipcperms(&msq->q_perm, S_IWUGO))
 584                        goto out_unlock_free;
 585
 586                err = security_msg_queue_msgsnd(msq, msg, msgflg);
 587                if (err)
 588                        goto out_unlock_free;
 589
 590                if(msgsz + msq->q_cbytes <= msq->q_qbytes &&
 591                                1 + msq->q_qnum <= msq->q_qbytes) {
 592                        break;
 593                }
 594
 595                /* queue full, wait: */
 596                if(msgflg&IPC_NOWAIT) {
 597                        err=-EAGAIN;
 598                        goto out_unlock_free;
 599                }
 600                ss_add(msq, &s);
 601                ipc_rcu_getref(msq);
 602                msg_unlock(msq);
 603                schedule();
 604
 605                ipc_lock_by_ptr(&msq->q_perm);
 606                ipc_rcu_putref(msq);
 607                if (msq->q_perm.deleted) {
 608                        err = -EIDRM;
 609                        goto out_unlock_free;
 610                }
 611                ss_del(&s);
 612                
 613                if (signal_pending(current)) {
 614                        err=-ERESTARTNOHAND;
 615                        goto out_unlock_free;
 616                }
 617        }
 618
 619        msq->q_lspid = current->tgid;
 620        msq->q_stime = get_seconds();
 621
 622        if(!pipelined_send(msq,msg)) {
 623                /* noone is waiting for this message, enqueue it */
 624                list_add_tail(&msg->m_list,&msq->q_messages);
 625                msq->q_cbytes += msgsz;
 626                msq->q_qnum++;
 627                atomic_add(msgsz,&msg_bytes);
 628                atomic_inc(&msg_hdrs);
 629        }
 630        
 631        err = 0;
 632        msg = NULL;
 633
 634out_unlock_free:
 635        msg_unlock(msq);
 636out_free:
 637        if(msg!=NULL)
 638                free_msg(msg);
 639        return err;
 640}
 641
 642asmlinkage long 
 643sys_msgsnd (int msqid, struct msgbuf __user *msgp, size_t msgsz, int msgflg)
 644{
 645        long mtype;
 646
 647        if (get_user(mtype, &msgp->mtype))
 648                return -EFAULT;
 649        return do_msgsnd(msqid, mtype, msgp->mtext, msgsz, msgflg);
 650}
 651
 652static inline int convert_mode(long* msgtyp, int msgflg)
 653{
 654        /* 
 655         *  find message of correct type.
 656         *  msgtyp = 0 => get first.
 657         *  msgtyp > 0 => get first message of matching type.
 658         *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
 659         */
 660        if(*msgtyp==0)
 661                return SEARCH_ANY;
 662        if(*msgtyp<0) {
 663                *msgtyp=-(*msgtyp);
 664                return SEARCH_LESSEQUAL;
 665        }
 666        if(msgflg & MSG_EXCEPT)
 667                return SEARCH_NOTEQUAL;
 668        return SEARCH_EQUAL;
 669}
 670
 671long do_msgrcv(int msqid, long *pmtype, void __user *mtext, size_t msgsz,
 672                long msgtyp, int msgflg)
 673{
 674        struct msg_queue *msq;
 675        struct msg_msg *msg;
 676        int mode;
 677
 678        if (msqid < 0 || (long) msgsz < 0)
 679                return -EINVAL;
 680        mode = convert_mode(&msgtyp,msgflg);
 681
 682        msq = msg_lock(msqid);
 683        if(msq==NULL)
 684                return -EINVAL;
 685
 686        msg = ERR_PTR(-EIDRM);
 687        if (msg_checkid(msq,msqid))
 688                goto out_unlock;
 689
 690        for (;;) {
 691                struct msg_receiver msr_d;
 692                struct list_head* tmp;
 693
 694                msg = ERR_PTR(-EACCES);
 695                if (ipcperms (&msq->q_perm, S_IRUGO))
 696                        goto out_unlock;
 697
 698                msg = ERR_PTR(-EAGAIN);
 699                tmp = msq->q_messages.next;
 700                while (tmp != &msq->q_messages) {
 701                        struct msg_msg *walk_msg;
 702                        walk_msg = list_entry(tmp,struct msg_msg,m_list);
 703                        if(testmsg(walk_msg,msgtyp,mode) &&
 704                           !security_msg_queue_msgrcv(msq, walk_msg, current, msgtyp, mode)) {
 705                                msg = walk_msg;
 706                                if(mode == SEARCH_LESSEQUAL && walk_msg->m_type != 1) {
 707                                        msg=walk_msg;
 708                                        msgtyp=walk_msg->m_type-1;
 709                                } else {
 710                                        msg=walk_msg;
 711                                        break;
 712                                }
 713                        }
 714                        tmp = tmp->next;
 715                }
 716                if(!IS_ERR(msg)) {
 717                        /* Found a suitable message. Unlink it from the queue. */
 718                        if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
 719                                msg = ERR_PTR(-E2BIG);
 720                                goto out_unlock;
 721                        }
 722                        list_del(&msg->m_list);
 723                        msq->q_qnum--;
 724                        msq->q_rtime = get_seconds();
 725                        msq->q_lrpid = current->tgid;
 726                        msq->q_cbytes -= msg->m_ts;
 727                        atomic_sub(msg->m_ts,&msg_bytes);
 728                        atomic_dec(&msg_hdrs);
 729                        ss_wakeup(&msq->q_senders,0);
 730                        msg_unlock(msq);
 731                        break;
 732                }
 733                /* No message waiting. Wait for a message */
 734                if (msgflg & IPC_NOWAIT) {
 735                        msg = ERR_PTR(-ENOMSG);
 736                        goto out_unlock;
 737                }
 738                list_add_tail(&msr_d.r_list,&msq->q_receivers);
 739                msr_d.r_tsk = current;
 740                msr_d.r_msgtype = msgtyp;
 741                msr_d.r_mode = mode;
 742                if(msgflg & MSG_NOERROR)
 743                        msr_d.r_maxsize = INT_MAX;
 744                 else
 745                        msr_d.r_maxsize = msgsz;
 746                msr_d.r_msg = ERR_PTR(-EAGAIN);
 747                current->state = TASK_INTERRUPTIBLE;
 748                msg_unlock(msq);
 749
 750                schedule();
 751
 752                /* Lockless receive, part 1:
 753                 * Disable preemption.  We don't hold a reference to the queue
 754                 * and getting a reference would defeat the idea of a lockless
 755                 * operation, thus the code relies on rcu to guarantee the
 756                 * existance of msq:
 757                 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
 758                 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
 759                 * rcu_read_lock() prevents preemption between reading r_msg
 760                 * and the spin_lock() inside ipc_lock_by_ptr().
 761                 */
 762                rcu_read_lock();
 763
 764                /* Lockless receive, part 2:
 765                 * Wait until pipelined_send or expunge_all are outside of
 766                 * wake_up_process(). There is a race with exit(), see
 767                 * ipc/mqueue.c for the details.
 768                 */
 769                msg = (struct msg_msg*) msr_d.r_msg;
 770                while (msg == NULL) {
 771                        cpu_relax();
 772                        msg = (struct msg_msg*) msr_d.r_msg;
 773                }
 774
 775                /* Lockless receive, part 3:
 776                 * If there is a message or an error then accept it without
 777                 * locking.
 778                 */
 779                if(msg != ERR_PTR(-EAGAIN)) {
 780                        rcu_read_unlock();
 781                        break;
 782                }
 783
 784                /* Lockless receive, part 3:
 785                 * Acquire the queue spinlock.
 786                 */
 787                ipc_lock_by_ptr(&msq->q_perm);
 788                rcu_read_unlock();
 789
 790                /* Lockless receive, part 4:
 791                 * Repeat test after acquiring the spinlock.
 792                 */
 793                msg = (struct msg_msg*)msr_d.r_msg;
 794                if(msg != ERR_PTR(-EAGAIN))
 795                        goto out_unlock;
 796
 797                list_del(&msr_d.r_list);
 798                if (signal_pending(current)) {
 799                        msg = ERR_PTR(-ERESTARTNOHAND);
 800out_unlock:
 801                        msg_unlock(msq);
 802                        break;
 803                }
 804        }
 805        if (IS_ERR(msg))
 806                return PTR_ERR(msg);
 807
 808        msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz;
 809        *pmtype = msg->m_type;
 810        if (store_msg(mtext, msg, msgsz)) 
 811                msgsz = -EFAULT;
 812        
 813        free_msg(msg);
 814        return msgsz;
 815}
 816
 817asmlinkage long sys_msgrcv (int msqid, struct msgbuf __user *msgp, size_t msgsz,
 818                            long msgtyp, int msgflg)
 819{
 820        long err, mtype;
 821        
 822        err = do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg);
 823        if (err < 0)
 824                goto out;
 825
 826        if (put_user(mtype, &msgp->mtype))
 827                err = -EFAULT;
 828out:
 829        return err;
 830}
 831
 832#ifdef CONFIG_PROC_FS
 833static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
 834{
 835        off_t pos = 0;
 836        off_t begin = 0;
 837        int i, len = 0;
 838
 839        down(&msg_ids.sem);
 840        len += sprintf(buffer, "       key      msqid perms      cbytes       qnum lspid lrpid   uid   gid  cuid  cgid      stime      rtime      ctime\n");
 841
 842        for(i = 0; i <= msg_ids.max_id; i++) {
 843                struct msg_queue * msq;
 844                msq = msg_lock(i);
 845                if(msq != NULL) {
 846                        len += sprintf(buffer + len, "%10d %10d  %4o  %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
 847                                msq->q_perm.key,
 848                                msg_buildid(i,msq->q_perm.seq),
 849                                msq->q_perm.mode,
 850                                msq->q_cbytes,
 851                                msq->q_qnum,
 852                                msq->q_lspid,
 853                                msq->q_lrpid,
 854                                msq->q_perm.uid,
 855                                msq->q_perm.gid,
 856                                msq->q_perm.cuid,
 857                                msq->q_perm.cgid,
 858                                msq->q_stime,
 859                                msq->q_rtime,
 860                                msq->q_ctime);
 861                        msg_unlock(msq);
 862
 863                        pos += len;
 864                        if(pos < offset) {
 865                                len = 0;
 866                                begin = pos;
 867                        }
 868                        if(pos > offset + length)
 869                                goto done;
 870                }
 871
 872        }
 873        *eof = 1;
 874done:
 875        up(&msg_ids.sem);
 876        *start = buffer + (offset - begin);
 877        len -= (offset - begin);
 878        if(len > length)
 879                len = length;
 880        if(len < 0)
 881                len = 0;
 882        return len;
 883}
 884#endif
 885