1
2
3
4
5
6
7
8
9
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
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
85 msgbytes += msgsz;
86 msghdrs++;
87 msq->msg_qnum++;
88 msq->msg_cbytes += msgsz;
89
90
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
153
154
155
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) {
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 {
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 }
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) {
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);
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
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