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