1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
32int msg_ctlmax = MSGMAX;
33int msg_ctlmnb = MSGMNB;
34int msg_ctlmni = MSGMNI;
35
36
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
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
172
173
174
175
176
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) {
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
344
345
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
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
467
468
469 expunge_all(msq,-EAGAIN);
470
471
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
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
633
634
635
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
717
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
739
740
741
742
743
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
755
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