linux-old/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 */
  12
  13#include <linux/malloc.h>
  14#include <linux/msg.h>
  15#include <linux/interrupt.h>
  16#include <linux/smp_lock.h>
  17#include <linux/init.h>
  18
  19#include <asm/uaccess.h>
  20
  21extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
  22
  23static void freeque (int id);
  24static int newque (key_t key, int msgflg);
  25static int findkey (key_t key);
  26
  27static struct msqid_ds *msgque[MSGMNI];
  28static int msgbytes = 0;
  29static int msghdrs = 0;
  30static unsigned short msg_seq = 0;
  31static int used_queues = 0;
  32static int max_msqid = 0;
  33static struct wait_queue *msg_lock = NULL;
  34
  35void __init msg_init (void)
  36{
  37        int id;
  38        
  39        for (id = 0; id < MSGMNI; id++) 
  40                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
  41        msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
  42        msg_lock = NULL;
  43        return;
  44}
  45
  46static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
  47{
  48        int id;
  49        struct msqid_ds *msq;
  50        struct ipc_perm *ipcp;
  51        struct msg *msgh;
  52        long mtype;
  53        int err;
  54        
  55        if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
  56                return -EINVAL;
  57        if (get_user(mtype, &msgp->mtype))
  58                return -EFAULT; 
  59        if (mtype < 1)
  60                return -EINVAL;
  61        id = (unsigned int) msqid % MSGMNI;
  62        msq = msgque [id];
  63        if (msq == IPC_UNUSED || msq == IPC_NOID)
  64                return -EINVAL;
  65        ipcp = &msq->msg_perm; 
  66
  67 slept:
  68        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
  69                return -EIDRM;
  70
  71        if (ipcperms(ipcp, S_IWUGO)) 
  72                return -EACCES;
  73        
  74        if (msgsz + msq->msg_cbytes > msq->msg_qbytes || msq->msg_qnum>= MSGQNUM) { 
  75                /* still no space in queue */
  76                if (msgflg & IPC_NOWAIT)
  77                        return -EAGAIN;
  78                if (signal_pending(current))
  79                        return -EINTR;
  80                interruptible_sleep_on (&msq->wwait);
  81                goto slept;
  82        }
  83
  84        /* Charge first to avoid races */
  85        msgbytes  += msgsz;
  86        msghdrs++;
  87        msq->msg_qnum++;
  88        msq->msg_cbytes += msgsz;
  89
  90        /* allocate message header and text space */ 
  91        err = -ENOMEM;
  92        msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL);
  93        if (!msgh) 
  94                goto uncharge;
  95        msgh->msg_spot = (char *) (msgh + 1);
  96
  97        err = -EFAULT; 
  98        if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz)) {
  99                goto uncharge;
 100        }
 101        
 102        err = -EIDRM; 
 103        if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
 104                || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
 105                goto uncharge;
 106        }
 107
 108        msgh->msg_next = NULL;
 109        msgh->msg_ts = msgsz;
 110        msgh->msg_type = mtype;
 111        msgh->msg_stime = CURRENT_TIME;
 112
 113        if (!msq->msg_first)
 114                msq->msg_first = msq->msg_last = msgh;
 115        else {
 116                msq->msg_last->msg_next = msgh;
 117                msq->msg_last = msgh;
 118        }
 119        msq->msg_lspid = current->pid;
 120        msq->msg_stime = CURRENT_TIME;
 121        wake_up (&msq->rwait);
 122        return 0;
 123
 124uncharge:
 125        msgbytes  -= msgsz;
 126        msghdrs--;
 127        msq->msg_qnum--;
 128        msq->msg_cbytes -= msgsz;
 129        if (msgh)
 130                kfree(msgh); 
 131        return err;
 132}
 133
 134static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
 135{
 136        struct msqid_ds *msq;
 137        struct ipc_perm *ipcp;
 138        struct msg *tmsg, *leastp = NULL;
 139        struct msg *nmsg = NULL;
 140        int id;
 141
 142        if (msqid < 0 || (long) msgsz < 0)
 143                return -EINVAL;
 144
 145        id = (unsigned int) msqid % MSGMNI;
 146        msq = msgque [id];
 147        if (msq == IPC_NOID || msq == IPC_UNUSED)
 148                return -EINVAL;
 149        ipcp = &msq->msg_perm; 
 150
 151        /* 
 152         *  find message of correct type.
 153         *  msgtyp = 0 => get first.
 154         *  msgtyp > 0 => get first message of matching type.
 155         *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
 156         */
 157        while (!nmsg) {
 158                if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
 159                        return -EIDRM;
 160                }
 161                if (ipcperms (ipcp, S_IRUGO)) {
 162                        return -EACCES;
 163                }
 164
 165                if (msgtyp == 0) 
 166                        nmsg = msq->msg_first;
 167                else if (msgtyp > 0) {
 168                        if (msgflg & MSG_EXCEPT) { 
 169                                for (tmsg = msq->msg_first; tmsg; 
 170                                     tmsg = tmsg->msg_next)
 171                                        if (tmsg->msg_type != msgtyp)
 172                                                break;
 173                                nmsg = tmsg;
 174                        } else {
 175                                for (tmsg = msq->msg_first; tmsg; 
 176                                     tmsg = tmsg->msg_next)
 177                                        if (tmsg->msg_type == msgtyp)
 178                                                break;
 179                                nmsg = tmsg;
 180                        }
 181                } else {
 182                        for (leastp = tmsg = msq->msg_first; tmsg; 
 183                             tmsg = tmsg->msg_next) 
 184                                if (tmsg->msg_type < leastp->msg_type) 
 185                                        leastp = tmsg;
 186                        if (leastp && leastp->msg_type <= - msgtyp)
 187                                nmsg = leastp;
 188                }
 189                
 190                if (nmsg) { /* done finding a message */
 191                        if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
 192                                return -E2BIG;
 193                        }
 194                        msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
 195                        if (nmsg ==  msq->msg_first)
 196                                msq->msg_first = nmsg->msg_next;
 197                        else {
 198                                for (tmsg = msq->msg_first; tmsg; 
 199                                     tmsg = tmsg->msg_next)
 200                                        if (tmsg->msg_next == nmsg) 
 201                                                break;
 202                                tmsg->msg_next = nmsg->msg_next;
 203                                if (nmsg == msq->msg_last)
 204                                        msq->msg_last = tmsg;
 205                        }
 206                        if (!(--msq->msg_qnum))
 207                                msq->msg_last = msq->msg_first = NULL;
 208                        
 209                        msq->msg_rtime = CURRENT_TIME;
 210                        msq->msg_lrpid = current->pid;
 211                        msgbytes -= nmsg->msg_ts; 
 212                        msghdrs--; 
 213                        msq->msg_cbytes -= nmsg->msg_ts;
 214                        wake_up (&msq->wwait);
 215                        if (put_user (nmsg->msg_type, &msgp->mtype) ||
 216                            copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz))
 217                                msgsz = -EFAULT; 
 218                        kfree(nmsg);
 219                        return msgsz;
 220                } else {  /* did not find a message */
 221                        if (msgflg & IPC_NOWAIT) {
 222                                return -ENOMSG;
 223                        }
 224                        if (signal_pending(current)) {
 225                                return -EINTR; 
 226                        }
 227                        interruptible_sleep_on (&msq->rwait);
 228                }
 229        } /* end while */
 230        return -1;
 231}
 232
 233asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 234{
 235        int ret;
 236
 237        lock_kernel();
 238        ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
 239        unlock_kernel();
 240        return ret;
 241}
 242
 243asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
 244        long msgtyp, int msgflg)
 245{
 246        int ret;
 247
 248        lock_kernel();
 249        ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
 250        unlock_kernel();
 251        return ret;
 252}
 253
 254static int findkey (key_t key)
 255{
 256        int id;
 257        struct msqid_ds *msq;
 258        
 259        for (id = 0; id <= max_msqid; id++) {
 260                while ((msq = msgque[id]) == IPC_NOID) 
 261                        interruptible_sleep_on (&msg_lock);
 262                if (msq == IPC_UNUSED)
 263                        continue;
 264                if (key == msq->msg_perm.key)
 265                        return id;
 266        }
 267        return -1;
 268}
 269
 270static int newque (key_t key, int msgflg)
 271{
 272        int id;
 273        struct msqid_ds *msq;
 274        struct ipc_perm *ipcp;
 275
 276        for (id = 0; id < MSGMNI; id++) 
 277                if (msgque[id] == IPC_UNUSED) {
 278                        msgque[id] = (struct msqid_ds *) IPC_NOID;
 279                        goto found;
 280                }
 281        return -ENOSPC;
 282
 283found:
 284        msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
 285        if (!msq) {
 286                msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 287                wake_up (&msg_lock);
 288                return -ENOMEM;
 289        }
 290        ipcp = &msq->msg_perm;
 291        ipcp->mode = (msgflg & S_IRWXUGO);
 292        ipcp->key = key;
 293        ipcp->cuid = ipcp->uid = current->euid;
 294        ipcp->gid = ipcp->cgid = current->egid;
 295        msq->msg_perm.seq = msg_seq;
 296        msq->msg_first = msq->msg_last = NULL;
 297        msq->rwait = msq->wwait = NULL;
 298        msq->msg_cbytes = msq->msg_qnum = 0;
 299        msq->msg_lspid = msq->msg_lrpid = 0;
 300        msq->msg_stime = msq->msg_rtime = 0;
 301        msq->msg_qbytes = MSGMNB;
 302        msq->msg_ctime = CURRENT_TIME;
 303        if (id > max_msqid)
 304                max_msqid = id;
 305        msgque[id] = msq;
 306        used_queues++;
 307        wake_up (&msg_lock);
 308        return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 309}
 310
 311asmlinkage int sys_msgget (key_t key, int msgflg)
 312{
 313        int id, ret = -EPERM;
 314        struct msqid_ds *msq;
 315        
 316        lock_kernel();
 317        if (key == IPC_PRIVATE) 
 318                ret = newque(key, msgflg);
 319        else if ((id = findkey (key)) == -1) { /* key not used */
 320                if (!(msgflg & IPC_CREAT))
 321                        ret = -ENOENT;
 322                else
 323                        ret = newque(key, msgflg);
 324        } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
 325                ret = -EEXIST;
 326        } else {
 327                msq = msgque[id];
 328                if (msq == IPC_UNUSED || msq == IPC_NOID)
 329                        ret = -EIDRM;
 330                else if (ipcperms(&msq->msg_perm, msgflg))
 331                        ret = -EACCES;
 332                else
 333                        ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 334        }
 335        unlock_kernel();
 336        return ret;
 337} 
 338
 339static void freeque (int id)
 340{
 341        struct msqid_ds *msq = msgque[id];
 342        struct msg *msgp, *msgh;
 343
 344        msq->msg_perm.seq++;
 345        msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
 346        msgbytes -= msq->msg_cbytes;
 347        if (id == max_msqid)
 348                while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
 349        msgque[id] = (struct msqid_ds *) IPC_UNUSED;
 350        used_queues--;
 351        while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
 352                wake_up (&msq->rwait); 
 353                wake_up (&msq->wwait);
 354                schedule(); 
 355        }
 356        for (msgp = msq->msg_first; msgp; msgp = msgh ) {
 357                msgh = msgp->msg_next;
 358                msghdrs--;
 359                kfree(msgp);
 360        }
 361        kfree(msq);
 362}
 363
 364asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
 365{
 366        int id, err = -EINVAL;
 367        struct msqid_ds *msq;
 368        struct msqid_ds tbuf;
 369        struct ipc_perm *ipcp;
 370        
 371        lock_kernel();
 372        if (msqid < 0 || cmd < 0)
 373                goto out;
 374        err = -EFAULT;
 375        switch (cmd) {
 376        case IPC_INFO: 
 377        case MSG_INFO: 
 378                if (!buf)
 379                        goto out;
 380        { 
 381                struct msginfo msginfo;
 382                msginfo.msgmni = MSGMNI;
 383                msginfo.msgmax = MSGMAX;
 384                msginfo.msgmnb = MSGMNB;
 385                msginfo.msgmap = MSGMAP;
 386                msginfo.msgpool = MSGPOOL;
 387                msginfo.msgtql = MSGTQL;
 388                msginfo.msgssz = MSGSSZ;
 389                msginfo.msgseg = MSGSEG;
 390                if (cmd == MSG_INFO) {
 391                        msginfo.msgpool = used_queues;
 392                        msginfo.msgmap = msghdrs;
 393                        msginfo.msgtql = msgbytes;
 394                }
 395
 396                err = -EFAULT; 
 397                if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))
 398                        goto out; 
 399                err = max_msqid;
 400                goto out;
 401        }
 402        case MSG_STAT:
 403                if (!buf)
 404                        goto out;
 405                err = -EINVAL;
 406                if (msqid > max_msqid)
 407                        goto out;
 408                msq = msgque[msqid];
 409                if (msq == IPC_UNUSED || msq == IPC_NOID)
 410                        goto out;
 411                err = -EACCES;
 412                if (ipcperms (&msq->msg_perm, S_IRUGO))
 413                        goto out;
 414                id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
 415                tbuf.msg_perm   = msq->msg_perm;
 416                tbuf.msg_stime  = msq->msg_stime;
 417                tbuf.msg_rtime  = msq->msg_rtime;
 418                tbuf.msg_ctime  = msq->msg_ctime;
 419                tbuf.msg_cbytes = msq->msg_cbytes;
 420                tbuf.msg_qnum   = msq->msg_qnum;
 421                tbuf.msg_qbytes = msq->msg_qbytes;
 422                tbuf.msg_lspid  = msq->msg_lspid;
 423                tbuf.msg_lrpid  = msq->msg_lrpid;
 424                err = -EFAULT;
 425                if (copy_to_user (buf, &tbuf, sizeof(*buf)))
 426                        goto out; 
 427                err = id;
 428                goto out;
 429        case IPC_SET:
 430                if (!buf)
 431                        goto out;
 432                err = -EFAULT; 
 433                if (!copy_from_user (&tbuf, buf, sizeof (*buf)))
 434                        err = 0; 
 435                break;
 436        case IPC_STAT:
 437                if (!buf)
 438                        goto out;
 439                break;
 440        }
 441
 442        id = (unsigned int) msqid % MSGMNI;
 443        msq = msgque [id];
 444        err = -EINVAL;
 445        if (msq == IPC_UNUSED || msq == IPC_NOID)
 446                goto out;
 447        err = -EIDRM;
 448        if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
 449                goto out;
 450        ipcp = &msq->msg_perm;
 451
 452        switch (cmd) {
 453        case IPC_STAT:
 454                err = -EACCES;
 455                if (ipcperms (ipcp, S_IRUGO))
 456                        goto out;
 457                tbuf.msg_perm   = msq->msg_perm;
 458                tbuf.msg_stime  = msq->msg_stime;
 459                tbuf.msg_rtime  = msq->msg_rtime;
 460                tbuf.msg_ctime  = msq->msg_ctime;
 461                tbuf.msg_cbytes = msq->msg_cbytes;
 462                tbuf.msg_qnum   = msq->msg_qnum;
 463                tbuf.msg_qbytes = msq->msg_qbytes;
 464                tbuf.msg_lspid  = msq->msg_lspid;
 465                tbuf.msg_lrpid  = msq->msg_lrpid;
 466                err = -EFAULT;
 467                if (!copy_to_user (buf, &tbuf, sizeof (*buf)))
 468                        err = 0;
 469                goto out;
 470        case IPC_SET:
 471                err = -EPERM;
 472                if (current->euid != ipcp->cuid && 
 473                    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
 474                    /* We _could_ check for CAP_CHOWN above, but we don't */
 475                        goto out;
 476                if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE))
 477                        goto out;
 478                msq->msg_qbytes = tbuf.msg_qbytes;
 479                ipcp->uid = tbuf.msg_perm.uid;
 480                ipcp->gid =  tbuf.msg_perm.gid;
 481                ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | 
 482                        (S_IRWXUGO & tbuf.msg_perm.mode);
 483                msq->msg_ctime = CURRENT_TIME;
 484                err = 0;
 485                goto out;
 486        case IPC_RMID:
 487                err = -EPERM;
 488                if (current->euid != ipcp->cuid && 
 489                    current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
 490                        goto out;
 491
 492                freeque (id); 
 493                err = 0;
 494                goto out;
 495        default:
 496                err = -EINVAL;
 497                goto out;
 498        }
 499out:
 500        unlock_kernel();
 501        return err;
 502}
 503
 504
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.