Import 2.3.9
[davej-history.git] / ipc / msg.c
blob86e5a9f26fcd530629a3533039419a80e69be178
1 /*
2 * linux/ipc/msg.c
3 * Copyright (C) 1992 Krishna Balasubramanian
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
11 * /proc/sysvipc/msg support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
14 #include <linux/config.h>
15 #include <linux/malloc.h>
16 #include <linux/msg.h>
17 #include <linux/interrupt.h>
18 #include <linux/smp_lock.h>
19 #include <linux/init.h>
20 #include <linux/proc_fs.h>
22 #include <asm/uaccess.h>
24 extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
26 static void freeque (int id);
27 static int newque (key_t key, int msgflg);
28 static int findkey (key_t key);
29 #ifdef CONFIG_PROC_FS
30 static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
31 #endif
33 static struct msqid_ds *msgque[MSGMNI];
34 static int msgbytes = 0;
35 static int msghdrs = 0;
36 static unsigned short msg_seq = 0;
37 static int used_queues = 0;
38 static int max_msqid = 0;
39 static DECLARE_WAIT_QUEUE_HEAD(msg_lock);
41 void __init msg_init (void)
43 int id;
44 #ifdef CONFIG_PROC_FS
45 struct proc_dir_entry *ent;
46 #endif
48 for (id = 0; id < MSGMNI; id++)
49 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
50 msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
51 init_waitqueue_head(&msg_lock);
52 #ifdef CONFIG_PROC_FS
53 ent = create_proc_entry("sysvipc/msg", 0, 0);
54 ent->read_proc = sysvipc_msg_read_proc;
55 #endif
56 return;
59 static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
61 int id;
62 struct msqid_ds *msq;
63 struct ipc_perm *ipcp;
64 struct msg *msgh;
65 long mtype;
67 if (msgsz > MSGMAX || (long) msgsz < 0 || msqid < 0)
68 return -EINVAL;
69 if (get_user(mtype, &msgp->mtype))
70 return -EFAULT;
71 if (mtype < 1)
72 return -EINVAL;
73 id = (unsigned int) msqid % MSGMNI;
74 msq = msgque [id];
75 if (msq == IPC_UNUSED || msq == IPC_NOID)
76 return -EINVAL;
77 ipcp = &msq->msg_perm;
79 slept:
80 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
81 return -EIDRM;
83 if (ipcperms(ipcp, S_IWUGO))
84 return -EACCES;
86 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
87 if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
88 /* still no space in queue */
89 if (msgflg & IPC_NOWAIT)
90 return -EAGAIN;
91 if (signal_pending(current))
92 return -EINTR;
93 interruptible_sleep_on (&msq->wwait);
94 goto slept;
98 /* allocate message header and text space*/
99 msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_KERNEL);
100 if (!msgh)
101 return -ENOMEM;
102 msgh->msg_spot = (char *) (msgh + 1);
104 if (copy_from_user(msgh->msg_spot, msgp->mtext, msgsz))
106 kfree(msgh);
107 return -EFAULT;
110 if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
111 || msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
112 kfree(msgh);
113 return -EIDRM;
116 msgh->msg_next = NULL;
117 msgh->msg_ts = msgsz;
118 msgh->msg_type = mtype;
119 msgh->msg_stime = CURRENT_TIME;
121 if (!msq->msg_first)
122 msq->msg_first = msq->msg_last = msgh;
123 else {
124 msq->msg_last->msg_next = msgh;
125 msq->msg_last = msgh;
127 msq->msg_cbytes += msgsz;
128 msgbytes += msgsz;
129 msghdrs++;
130 msq->msg_qnum++;
131 msq->msg_lspid = current->pid;
132 msq->msg_stime = CURRENT_TIME;
133 wake_up (&msq->rwait);
134 return 0;
137 static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
139 struct msqid_ds *msq;
140 struct ipc_perm *ipcp;
141 struct msg *tmsg, *leastp = NULL;
142 struct msg *nmsg = NULL;
143 int id;
145 if (msqid < 0 || (long) msgsz < 0)
146 return -EINVAL;
148 id = (unsigned int) msqid % MSGMNI;
149 msq = msgque [id];
150 if (msq == IPC_NOID || msq == IPC_UNUSED)
151 return -EINVAL;
152 ipcp = &msq->msg_perm;
155 * find message of correct type.
156 * msgtyp = 0 => get first.
157 * msgtyp > 0 => get first message of matching type.
158 * msgtyp < 0 => get message with least type must be < abs(msgtype).
160 while (!nmsg) {
161 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
162 return -EIDRM;
164 if (ipcperms (ipcp, S_IRUGO)) {
165 return -EACCES;
168 if (msgtyp == 0)
169 nmsg = msq->msg_first;
170 else if (msgtyp > 0) {
171 if (msgflg & MSG_EXCEPT) {
172 for (tmsg = msq->msg_first; tmsg;
173 tmsg = tmsg->msg_next)
174 if (tmsg->msg_type != msgtyp)
175 break;
176 nmsg = tmsg;
177 } else {
178 for (tmsg = msq->msg_first; tmsg;
179 tmsg = tmsg->msg_next)
180 if (tmsg->msg_type == msgtyp)
181 break;
182 nmsg = tmsg;
184 } else {
185 for (leastp = tmsg = msq->msg_first; tmsg;
186 tmsg = tmsg->msg_next)
187 if (tmsg->msg_type < leastp->msg_type)
188 leastp = tmsg;
189 if (leastp && leastp->msg_type <= - msgtyp)
190 nmsg = leastp;
193 if (nmsg) { /* done finding a message */
194 if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
195 return -E2BIG;
197 msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
198 if (nmsg == msq->msg_first)
199 msq->msg_first = nmsg->msg_next;
200 else {
201 for (tmsg = msq->msg_first; tmsg;
202 tmsg = tmsg->msg_next)
203 if (tmsg->msg_next == nmsg)
204 break;
205 tmsg->msg_next = nmsg->msg_next;
206 if (nmsg == msq->msg_last)
207 msq->msg_last = tmsg;
209 if (!(--msq->msg_qnum))
210 msq->msg_last = msq->msg_first = NULL;
212 msq->msg_rtime = CURRENT_TIME;
213 msq->msg_lrpid = current->pid;
214 msgbytes -= nmsg->msg_ts;
215 msghdrs--;
216 msq->msg_cbytes -= nmsg->msg_ts;
217 wake_up (&msq->wwait);
218 if (put_user (nmsg->msg_type, &msgp->mtype) ||
219 copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz))
220 msgsz = -EFAULT;
221 kfree(nmsg);
222 return msgsz;
223 } else { /* did not find a message */
224 if (msgflg & IPC_NOWAIT) {
225 return -ENOMSG;
227 if (signal_pending(current)) {
228 return -EINTR;
230 interruptible_sleep_on (&msq->rwait);
232 } /* end while */
233 return -1;
236 asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
238 int ret;
240 lock_kernel();
241 ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
242 unlock_kernel();
243 return ret;
246 asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
247 long msgtyp, int msgflg)
249 int ret;
251 lock_kernel();
252 ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
253 unlock_kernel();
254 return ret;
257 static int findkey (key_t key)
259 int id;
260 struct msqid_ds *msq;
262 for (id = 0; id <= max_msqid; id++) {
263 while ((msq = msgque[id]) == IPC_NOID)
264 interruptible_sleep_on (&msg_lock);
265 if (msq == IPC_UNUSED)
266 continue;
267 if (key == msq->msg_perm.key)
268 return id;
270 return -1;
273 static int newque (key_t key, int msgflg)
275 int id;
276 struct msqid_ds *msq;
277 struct ipc_perm *ipcp;
279 for (id = 0; id < MSGMNI; id++)
280 if (msgque[id] == IPC_UNUSED) {
281 msgque[id] = (struct msqid_ds *) IPC_NOID;
282 goto found;
284 return -ENOSPC;
286 found:
287 msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
288 if (!msq) {
289 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
290 wake_up (&msg_lock);
291 return -ENOMEM;
293 ipcp = &msq->msg_perm;
294 ipcp->mode = (msgflg & S_IRWXUGO);
295 ipcp->key = key;
296 ipcp->cuid = ipcp->uid = current->euid;
297 ipcp->gid = ipcp->cgid = current->egid;
298 msq->msg_perm.seq = msg_seq;
299 msq->msg_first = msq->msg_last = NULL;
300 init_waitqueue_head(&msq->wwait);
301 init_waitqueue_head(&msq->rwait);
302 msq->msg_cbytes = msq->msg_qnum = 0;
303 msq->msg_lspid = msq->msg_lrpid = 0;
304 msq->msg_stime = msq->msg_rtime = 0;
305 msq->msg_qbytes = MSGMNB;
306 msq->msg_ctime = CURRENT_TIME;
307 if (id > max_msqid)
308 max_msqid = id;
309 msgque[id] = msq;
310 used_queues++;
311 wake_up (&msg_lock);
312 return (unsigned int) msq->msg_perm.seq * MSGMNI + id;
315 asmlinkage int sys_msgget (key_t key, int msgflg)
317 int id, ret = -EPERM;
318 struct msqid_ds *msq;
320 lock_kernel();
321 if (key == IPC_PRIVATE)
322 ret = newque(key, msgflg);
323 else if ((id = findkey (key)) == -1) { /* key not used */
324 if (!(msgflg & IPC_CREAT))
325 ret = -ENOENT;
326 else
327 ret = newque(key, msgflg);
328 } else if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) {
329 ret = -EEXIST;
330 } else {
331 msq = msgque[id];
332 if (msq == IPC_UNUSED || msq == IPC_NOID)
333 ret = -EIDRM;
334 else if (ipcperms(&msq->msg_perm, msgflg))
335 ret = -EACCES;
336 else
337 ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
339 unlock_kernel();
340 return ret;
343 static void freeque (int id)
345 struct msqid_ds *msq = msgque[id];
346 struct msg *msgp, *msgh;
348 msq->msg_perm.seq++;
349 msg_seq = (msg_seq+1) % ((unsigned)(1<<31)/MSGMNI); /* increment, but avoid overflow */
350 msgbytes -= msq->msg_cbytes;
351 if (id == max_msqid)
352 while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
353 msgque[id] = (struct msqid_ds *) IPC_UNUSED;
354 used_queues--;
355 while (waitqueue_active(&msq->rwait) || waitqueue_active(&msq->wwait)) {
356 wake_up (&msq->rwait);
357 wake_up (&msq->wwait);
358 schedule();
360 for (msgp = msq->msg_first; msgp; msgp = msgh ) {
361 msgh = msgp->msg_next;
362 msghdrs--;
363 kfree(msgp);
365 kfree(msq);
368 asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
370 int id, err = -EINVAL;
371 struct msqid_ds *msq;
372 struct msqid_ds tbuf;
373 struct ipc_perm *ipcp;
375 lock_kernel();
376 if (msqid < 0 || cmd < 0)
377 goto out;
378 err = -EFAULT;
379 switch (cmd) {
380 case IPC_INFO:
381 case MSG_INFO:
382 if (!buf)
383 goto out;
385 struct msginfo msginfo;
386 msginfo.msgmni = MSGMNI;
387 msginfo.msgmax = MSGMAX;
388 msginfo.msgmnb = MSGMNB;
389 msginfo.msgmap = MSGMAP;
390 msginfo.msgpool = MSGPOOL;
391 msginfo.msgtql = MSGTQL;
392 msginfo.msgssz = MSGSSZ;
393 msginfo.msgseg = MSGSEG;
394 if (cmd == MSG_INFO) {
395 msginfo.msgpool = used_queues;
396 msginfo.msgmap = msghdrs;
397 msginfo.msgtql = msgbytes;
400 err = -EFAULT;
401 if (copy_to_user (buf, &msginfo, sizeof(struct msginfo)))
402 goto out;
403 err = max_msqid;
404 goto out;
406 case MSG_STAT:
407 if (!buf)
408 goto out;
409 err = -EINVAL;
410 if (msqid > max_msqid)
411 goto out;
412 msq = msgque[msqid];
413 if (msq == IPC_UNUSED || msq == IPC_NOID)
414 goto out;
415 err = -EACCES;
416 if (ipcperms (&msq->msg_perm, S_IRUGO))
417 goto out;
418 id = (unsigned int) msq->msg_perm.seq * MSGMNI + msqid;
419 tbuf.msg_perm = msq->msg_perm;
420 tbuf.msg_stime = msq->msg_stime;
421 tbuf.msg_rtime = msq->msg_rtime;
422 tbuf.msg_ctime = msq->msg_ctime;
423 tbuf.msg_cbytes = msq->msg_cbytes;
424 tbuf.msg_qnum = msq->msg_qnum;
425 tbuf.msg_qbytes = msq->msg_qbytes;
426 tbuf.msg_lspid = msq->msg_lspid;
427 tbuf.msg_lrpid = msq->msg_lrpid;
428 err = -EFAULT;
429 if (copy_to_user (buf, &tbuf, sizeof(*buf)))
430 goto out;
431 err = id;
432 goto out;
433 case IPC_SET:
434 if (!buf)
435 goto out;
436 err = -EFAULT;
437 if (!copy_from_user (&tbuf, buf, sizeof (*buf)))
438 err = 0;
439 break;
440 case IPC_STAT:
441 if (!buf)
442 goto out;
443 break;
446 id = (unsigned int) msqid % MSGMNI;
447 msq = msgque [id];
448 err = -EINVAL;
449 if (msq == IPC_UNUSED || msq == IPC_NOID)
450 goto out;
451 err = -EIDRM;
452 if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
453 goto out;
454 ipcp = &msq->msg_perm;
456 switch (cmd) {
457 case IPC_STAT:
458 err = -EACCES;
459 if (ipcperms (ipcp, S_IRUGO))
460 goto out;
461 tbuf.msg_perm = msq->msg_perm;
462 tbuf.msg_stime = msq->msg_stime;
463 tbuf.msg_rtime = msq->msg_rtime;
464 tbuf.msg_ctime = msq->msg_ctime;
465 tbuf.msg_cbytes = msq->msg_cbytes;
466 tbuf.msg_qnum = msq->msg_qnum;
467 tbuf.msg_qbytes = msq->msg_qbytes;
468 tbuf.msg_lspid = msq->msg_lspid;
469 tbuf.msg_lrpid = msq->msg_lrpid;
470 err = -EFAULT;
471 if (!copy_to_user (buf, &tbuf, sizeof (*buf)))
472 err = 0;
473 goto out;
474 case IPC_SET:
475 err = -EPERM;
476 if (current->euid != ipcp->cuid &&
477 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
478 /* We _could_ check for CAP_CHOWN above, but we don't */
479 goto out;
480 if (tbuf.msg_qbytes > MSGMNB && !capable(CAP_SYS_RESOURCE))
481 goto out;
482 msq->msg_qbytes = tbuf.msg_qbytes;
483 ipcp->uid = tbuf.msg_perm.uid;
484 ipcp->gid = tbuf.msg_perm.gid;
485 ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
486 (S_IRWXUGO & tbuf.msg_perm.mode);
487 msq->msg_ctime = CURRENT_TIME;
488 err = 0;
489 goto out;
490 case IPC_RMID:
491 err = -EPERM;
492 if (current->euid != ipcp->cuid &&
493 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN))
494 goto out;
496 freeque (id);
497 err = 0;
498 goto out;
499 default:
500 err = -EINVAL;
501 goto out;
503 out:
504 unlock_kernel();
505 return err;
508 #ifdef CONFIG_PROC_FS
509 static int sysvipc_msg_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
511 off_t pos = 0;
512 off_t begin = 0;
513 int i, len = 0;
515 len += sprintf(buffer, " key msqid perms cbytes qnum lspid lrpid uid gid cuid cgid stime rtime ctime\n");
517 for(i = 0; i < MSGMNI; i++)
518 if(msgque[i] != IPC_UNUSED) {
519 len += sprintf(buffer + len, "%10d %10d %4o %5u %5u %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n",
520 msgque[i]->msg_perm.key,
521 msgque[i]->msg_perm.seq * MSGMNI + i,
522 msgque[i]->msg_perm.mode,
523 msgque[i]->msg_cbytes,
524 msgque[i]->msg_qnum,
525 msgque[i]->msg_lspid,
526 msgque[i]->msg_lrpid,
527 msgque[i]->msg_perm.uid,
528 msgque[i]->msg_perm.gid,
529 msgque[i]->msg_perm.cuid,
530 msgque[i]->msg_perm.cgid,
531 msgque[i]->msg_stime,
532 msgque[i]->msg_rtime,
533 msgque[i]->msg_ctime);
535 pos += len;
536 if(pos < offset) {
537 len = 0;
538 begin = pos;
540 if(pos > offset + length)
541 goto done;
543 *eof = 1;
544 done:
545 *start = buffer + (offset - begin);
546 len -= (offset - begin);
547 if(len > length)
548 len = length;
549 if(len < 0)
550 len = 0;
551 return len;
553 #endif