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