1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66#include <linux/config.h>
67#include <linux/slab.h>
68#include <linux/spinlock.h>
69#include <linux/init.h>
70#include <linux/proc_fs.h>
71#include <linux/time.h>
72#include <linux/smp_lock.h>
73#include <linux/security.h>
74#include <asm/uaccess.h>
75#include "util.h"
76
77
78#define sem_lock(id) ((struct sem_array*)ipc_lock(&sem_ids,id))
79#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
80#define sem_rmid(id) ((struct sem_array*)ipc_rmid(&sem_ids,id))
81#define sem_checkid(sma, semid) \
82 ipc_checkid(&sem_ids,&sma->sem_perm,semid)
83#define sem_buildid(id, seq) \
84 ipc_buildid(&sem_ids, id, seq)
85static struct ipc_ids sem_ids;
86
87static int newary (key_t, int, int);
88static void freeary (struct sem_array *sma, int id);
89#ifdef CONFIG_PROC_FS
90static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
91#endif
92
93#define SEMMSL_FAST 256
94#define SEMOPM_FAST 64
95
96
97
98
99
100
101
102
103
104
105int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
106#define sc_semmsl (sem_ctls[0])
107#define sc_semmns (sem_ctls[1])
108#define sc_semopm (sem_ctls[2])
109#define sc_semmni (sem_ctls[3])
110
111static int used_sems;
112
113void __init sem_init (void)
114{
115 used_sems = 0;
116 ipc_init_ids(&sem_ids,sc_semmni);
117
118#ifdef CONFIG_PROC_FS
119 create_proc_read_entry("sysvipc/sem", 0, NULL, sysvipc_sem_read_proc, NULL);
120#endif
121}
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155#define IN_WAKEUP 1
156
157static int newary (key_t key, int nsems, int semflg)
158{
159 int id;
160 int retval;
161 struct sem_array *sma;
162 int size;
163
164 if (!nsems)
165 return -EINVAL;
166 if (used_sems + nsems > sc_semmns)
167 return -ENOSPC;
168
169 size = sizeof (*sma) + nsems * sizeof (struct sem);
170 sma = ipc_rcu_alloc(size);
171 if (!sma) {
172 return -ENOMEM;
173 }
174 memset (sma, 0, size);
175
176 sma->sem_perm.mode = (semflg & S_IRWXUGO);
177 sma->sem_perm.key = key;
178
179 sma->sem_perm.security = NULL;
180 retval = security_sem_alloc(sma);
181 if (retval) {
182 ipc_rcu_free(sma, size);
183 return retval;
184 }
185
186 id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
187 if(id == -1) {
188 security_sem_free(sma);
189 ipc_rcu_free(sma, size);
190 return -ENOSPC;
191 }
192 used_sems += nsems;
193
194 sma->sem_base = (struct sem *) &sma[1];
195
196 sma->sem_pending_last = &sma->sem_pending;
197
198 sma->sem_nsems = nsems;
199 sma->sem_ctime = get_seconds();
200 sem_unlock(sma);
201
202 return sem_buildid(id, sma->sem_perm.seq);
203}
204
205asmlinkage long sys_semget (key_t key, int nsems, int semflg)
206{
207 int id, err = -EINVAL;
208 struct sem_array *sma;
209
210 if (nsems < 0 || nsems > sc_semmsl)
211 return -EINVAL;
212 down(&sem_ids.sem);
213
214 if (key == IPC_PRIVATE) {
215 err = newary(key, nsems, semflg);
216 } else if ((id = ipc_findkey(&sem_ids, key)) == -1) {
217 if (!(semflg & IPC_CREAT))
218 err = -ENOENT;
219 else
220 err = newary(key, nsems, semflg);
221 } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
222 err = -EEXIST;
223 } else {
224 sma = sem_lock(id);
225 if(sma==NULL)
226 BUG();
227 if (nsems > sma->sem_nsems)
228 err = -EINVAL;
229 else if (ipcperms(&sma->sem_perm, semflg))
230 err = -EACCES;
231 else {
232 int semid = sem_buildid(id, sma->sem_perm.seq);
233 err = security_sem_associate(sma, semflg);
234 if (!err)
235 err = semid;
236 }
237 sem_unlock(sma);
238 }
239
240 up(&sem_ids.sem);
241 return err;
242}
243
244
245static int sem_revalidate(int semid, struct sem_array* sma, int nsems, short flg)
246{
247 struct sem_array* smanew;
248
249 smanew = sem_lock(semid);
250 if(smanew==NULL)
251 return -EIDRM;
252 if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) {
253 sem_unlock(smanew);
254 return -EIDRM;
255 }
256
257 if (flg && ipcperms(&sma->sem_perm, flg)) {
258 sem_unlock(smanew);
259 return -EACCES;
260 }
261 return 0;
262}
263
264
265
266static inline void append_to_queue (struct sem_array * sma,
267 struct sem_queue * q)
268{
269 *(q->prev = sma->sem_pending_last) = q;
270 *(sma->sem_pending_last = &q->next) = NULL;
271}
272
273static inline void prepend_to_queue (struct sem_array * sma,
274 struct sem_queue * q)
275{
276 q->next = sma->sem_pending;
277 *(q->prev = &sma->sem_pending) = q;
278 if (q->next)
279 q->next->prev = &q->next;
280 else
281 sma->sem_pending_last = &q->next;
282}
283
284static inline void remove_from_queue (struct sem_array * sma,
285 struct sem_queue * q)
286{
287 *(q->prev) = q->next;
288 if (q->next)
289 q->next->prev = q->prev;
290 else
291 sma->sem_pending_last = q->prev;
292 q->prev = NULL;
293}
294
295
296
297
298
299
300static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
301 int nsops, struct sem_undo *un, int pid)
302{
303 int result, sem_op;
304 struct sembuf *sop;
305 struct sem * curr;
306
307 for (sop = sops; sop < sops + nsops; sop++) {
308 curr = sma->sem_base + sop->sem_num;
309 sem_op = sop->sem_op;
310 result = curr->semval;
311
312 if (!sem_op && result)
313 goto would_block;
314
315 result += sem_op;
316 if (result < 0)
317 goto would_block;
318 if (result > SEMVMX)
319 goto out_of_range;
320 if (sop->sem_flg & SEM_UNDO) {
321 int undo = un->semadj[sop->sem_num] - sem_op;
322
323
324
325 if (undo < (-SEMAEM - 1) || undo > SEMAEM)
326 goto out_of_range;
327 }
328 curr->semval = result;
329 }
330
331 sop--;
332 while (sop >= sops) {
333 sma->sem_base[sop->sem_num].sempid = pid;
334 if (sop->sem_flg & SEM_UNDO)
335 un->semadj[sop->sem_num] -= sop->sem_op;
336 sop--;
337 }
338
339 sma->sem_otime = get_seconds();
340 return 0;
341
342out_of_range:
343 result = -ERANGE;
344 goto undo;
345
346would_block:
347 if (sop->sem_flg & IPC_NOWAIT)
348 result = -EAGAIN;
349 else
350 result = 1;
351
352undo:
353 sop--;
354 while (sop >= sops) {
355 sma->sem_base[sop->sem_num].semval -= sop->sem_op;
356 sop--;
357 }
358
359 return result;
360}
361
362
363
364
365static void update_queue (struct sem_array * sma)
366{
367 int error;
368 struct sem_queue * q;
369
370 q = sma->sem_pending;
371 while(q) {
372 error = try_atomic_semop(sma, q->sops, q->nsops,
373 q->undo, q->pid);
374
375
376 if (error <= 0) {
377 struct sem_queue *n;
378 remove_from_queue(sma,q);
379 n = q->next;
380 q->status = IN_WAKEUP;
381 wake_up_process(q->sleeper);
382
383
384
385 q->status = error;
386 q = n;
387 } else {
388 q = q->next;
389 }
390 }
391}
392
393
394
395
396
397
398
399
400
401
402static int count_semncnt (struct sem_array * sma, ushort semnum)
403{
404 int semncnt;
405 struct sem_queue * q;
406
407 semncnt = 0;
408 for (q = sma->sem_pending; q; q = q->next) {
409 struct sembuf * sops = q->sops;
410 int nsops = q->nsops;
411 int i;
412 for (i = 0; i < nsops; i++)
413 if (sops[i].sem_num == semnum
414 && (sops[i].sem_op < 0)
415 && !(sops[i].sem_flg & IPC_NOWAIT))
416 semncnt++;
417 }
418 return semncnt;
419}
420static int count_semzcnt (struct sem_array * sma, ushort semnum)
421{
422 int semzcnt;
423 struct sem_queue * q;
424
425 semzcnt = 0;
426 for (q = sma->sem_pending; q; q = q->next) {
427 struct sembuf * sops = q->sops;
428 int nsops = q->nsops;
429 int i;
430 for (i = 0; i < nsops; i++)
431 if (sops[i].sem_num == semnum
432 && (sops[i].sem_op == 0)
433 && !(sops[i].sem_flg & IPC_NOWAIT))
434 semzcnt++;
435 }
436 return semzcnt;
437}
438
439
440
441
442
443static void freeary (struct sem_array *sma, int id)
444{
445 struct sem_undo *un;
446 struct sem_queue *q;
447 int size;
448
449
450
451
452
453 for (un = sma->undo; un; un = un->id_next)
454 un->semid = -1;
455
456
457 q = sma->sem_pending;
458 while(q) {
459 struct sem_queue *n;
460
461 q->prev = NULL;
462 n = q->next;
463 q->status = IN_WAKEUP;
464 wake_up_process(q->sleeper);
465 q->status = -EIDRM;
466 q = n;
467 }
468
469
470 sma = sem_rmid(id);
471 sem_unlock(sma);
472
473 used_sems -= sma->sem_nsems;
474 size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
475 security_sem_free(sma);
476 ipc_rcu_free(sma, size);
477}
478
479static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
480{
481 switch(version) {
482 case IPC_64:
483 return copy_to_user(buf, in, sizeof(*in));
484 case IPC_OLD:
485 {
486 struct semid_ds out;
487
488 ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
489
490 out.sem_otime = in->sem_otime;
491 out.sem_ctime = in->sem_ctime;
492 out.sem_nsems = in->sem_nsems;
493
494 return copy_to_user(buf, &out, sizeof(out));
495 }
496 default:
497 return -EINVAL;
498 }
499}
500
501static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
502{
503 int err = -EINVAL;
504 struct sem_array *sma;
505
506 switch(cmd) {
507 case IPC_INFO:
508 case SEM_INFO:
509 {
510 struct seminfo seminfo;
511 int max_id;
512
513 err = security_sem_semctl(NULL, cmd);
514 if (err)
515 return err;
516
517 memset(&seminfo,0,sizeof(seminfo));
518 seminfo.semmni = sc_semmni;
519 seminfo.semmns = sc_semmns;
520 seminfo.semmsl = sc_semmsl;
521 seminfo.semopm = sc_semopm;
522 seminfo.semvmx = SEMVMX;
523 seminfo.semmnu = SEMMNU;
524 seminfo.semmap = SEMMAP;
525 seminfo.semume = SEMUME;
526 down(&sem_ids.sem);
527 if (cmd == SEM_INFO) {
528 seminfo.semusz = sem_ids.in_use;
529 seminfo.semaem = used_sems;
530 } else {
531 seminfo.semusz = SEMUSZ;
532 seminfo.semaem = SEMAEM;
533 }
534 max_id = sem_ids.max_id;
535 up(&sem_ids.sem);
536 if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo)))
537 return -EFAULT;
538 return (max_id < 0) ? 0: max_id;
539 }
540 case SEM_STAT:
541 {
542 struct semid64_ds tbuf;
543 int id;
544
545 if(semid >= sem_ids.size)
546 return -EINVAL;
547
548 memset(&tbuf,0,sizeof(tbuf));
549
550 sma = sem_lock(semid);
551 if(sma == NULL)
552 return -EINVAL;
553
554 err = -EACCES;
555 if (ipcperms (&sma->sem_perm, S_IRUGO))
556 goto out_unlock;
557
558 err = security_sem_semctl(sma, cmd);
559 if (err)
560 goto out_unlock;
561
562 id = sem_buildid(semid, sma->sem_perm.seq);
563
564 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
565 tbuf.sem_otime = sma->sem_otime;
566 tbuf.sem_ctime = sma->sem_ctime;
567 tbuf.sem_nsems = sma->sem_nsems;
568 sem_unlock(sma);
569 if (copy_semid_to_user (arg.buf, &tbuf, version))
570 return -EFAULT;
571 return id;
572 }
573 default:
574 return -EINVAL;
575 }
576 return err;
577out_unlock:
578 sem_unlock(sma);
579 return err;
580}
581
582static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
583{
584 struct sem_array *sma;
585 struct sem* curr;
586 int err;
587 ushort fast_sem_io[SEMMSL_FAST];
588 ushort* sem_io = fast_sem_io;
589 int nsems;
590
591 sma = sem_lock(semid);
592 if(sma==NULL)
593 return -EINVAL;
594
595 nsems = sma->sem_nsems;
596
597 err=-EIDRM;
598 if (sem_checkid(sma,semid))
599 goto out_unlock;
600
601 err = -EACCES;
602 if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
603 goto out_unlock;
604
605 err = security_sem_semctl(sma, cmd);
606 if (err)
607 goto out_unlock;
608
609 err = -EACCES;
610 switch (cmd) {
611 case GETALL:
612 {
613 ushort __user *array = arg.array;
614 int i;
615
616 if(nsems > SEMMSL_FAST) {
617 sem_unlock(sma);
618 sem_io = ipc_alloc(sizeof(ushort)*nsems);
619 if(sem_io == NULL)
620 return -ENOMEM;
621 err = sem_revalidate(semid, sma, nsems, S_IRUGO);
622 if(err)
623 goto out_free;
624 }
625
626 for (i = 0; i < sma->sem_nsems; i++)
627 sem_io[i] = sma->sem_base[i].semval;
628 sem_unlock(sma);
629 err = 0;
630 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
631 err = -EFAULT;
632 goto out_free;
633 }
634 case SETALL:
635 {
636 int i;
637 struct sem_undo *un;
638
639 sem_unlock(sma);
640
641 if(nsems > SEMMSL_FAST) {
642 sem_io = ipc_alloc(sizeof(ushort)*nsems);
643 if(sem_io == NULL)
644 return -ENOMEM;
645 }
646
647 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
648 err = -EFAULT;
649 goto out_free;
650 }
651
652 for (i = 0; i < nsems; i++) {
653 if (sem_io[i] > SEMVMX) {
654 err = -ERANGE;
655 goto out_free;
656 }
657 }
658 err = sem_revalidate(semid, sma, nsems, S_IWUGO);
659 if(err)
660 goto out_free;
661
662 for (i = 0; i < nsems; i++)
663 sma->sem_base[i].semval = sem_io[i];
664 for (un = sma->undo; un; un = un->id_next)
665 for (i = 0; i < nsems; i++)
666 un->semadj[i] = 0;
667 sma->sem_ctime = get_seconds();
668
669 update_queue(sma);
670 err = 0;
671 goto out_unlock;
672 }
673 case IPC_STAT:
674 {
675 struct semid64_ds tbuf;
676 memset(&tbuf,0,sizeof(tbuf));
677 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
678 tbuf.sem_otime = sma->sem_otime;
679 tbuf.sem_ctime = sma->sem_ctime;
680 tbuf.sem_nsems = sma->sem_nsems;
681 sem_unlock(sma);
682 if (copy_semid_to_user (arg.buf, &tbuf, version))
683 return -EFAULT;
684 return 0;
685 }
686
687 }
688 err = -EINVAL;
689 if(semnum < 0 || semnum >= nsems)
690 goto out_unlock;
691
692 curr = &sma->sem_base[semnum];
693
694 switch (cmd) {
695 case GETVAL:
696 err = curr->semval;
697 goto out_unlock;
698 case GETPID:
699 err = curr->sempid;
700 goto out_unlock;
701 case GETNCNT:
702 err = count_semncnt(sma,semnum);
703 goto out_unlock;
704 case GETZCNT:
705 err = count_semzcnt(sma,semnum);
706 goto out_unlock;
707 case SETVAL:
708 {
709 int val = arg.val;
710 struct sem_undo *un;
711 err = -ERANGE;
712 if (val > SEMVMX || val < 0)
713 goto out_unlock;
714
715 for (un = sma->undo; un; un = un->id_next)
716 un->semadj[semnum] = 0;
717 curr->semval = val;
718 curr->sempid = current->tgid;
719 sma->sem_ctime = get_seconds();
720
721 update_queue(sma);
722 err = 0;
723 goto out_unlock;
724 }
725 }
726out_unlock:
727 sem_unlock(sma);
728out_free:
729 if(sem_io != fast_sem_io)
730 ipc_free(sem_io, sizeof(ushort)*nsems);
731 return err;
732}
733
734struct sem_setbuf {
735 uid_t uid;
736 gid_t gid;
737 mode_t mode;
738};
739
740static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __user *buf, int version)
741{
742 switch(version) {
743 case IPC_64:
744 {
745 struct semid64_ds tbuf;
746
747 if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
748 return -EFAULT;
749
750 out->uid = tbuf.sem_perm.uid;
751 out->gid = tbuf.sem_perm.gid;
752 out->mode = tbuf.sem_perm.mode;
753
754 return 0;
755 }
756 case IPC_OLD:
757 {
758 struct semid_ds tbuf_old;
759
760 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
761 return -EFAULT;
762
763 out->uid = tbuf_old.sem_perm.uid;
764 out->gid = tbuf_old.sem_perm.gid;
765 out->mode = tbuf_old.sem_perm.mode;
766
767 return 0;
768 }
769 default:
770 return -EINVAL;
771 }
772}
773
774static int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
775{
776 struct sem_array *sma;
777 int err;
778 struct sem_setbuf setbuf;
779 struct kern_ipc_perm *ipcp;
780
781 if(cmd == IPC_SET) {
782 if(copy_semid_from_user (&setbuf, arg.buf, version))
783 return -EFAULT;
784 }
785 sma = sem_lock(semid);
786 if(sma==NULL)
787 return -EINVAL;
788
789 if (sem_checkid(sma,semid)) {
790 err=-EIDRM;
791 goto out_unlock;
792 }
793 ipcp = &sma->sem_perm;
794
795 if (current->euid != ipcp->cuid &&
796 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
797 err=-EPERM;
798 goto out_unlock;
799 }
800
801 err = security_sem_semctl(sma, cmd);
802 if (err)
803 goto out_unlock;
804
805 switch(cmd){
806 case IPC_RMID:
807 freeary(sma, semid);
808 err = 0;
809 break;
810 case IPC_SET:
811 ipcp->uid = setbuf.uid;
812 ipcp->gid = setbuf.gid;
813 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
814 | (setbuf.mode & S_IRWXUGO);
815 sma->sem_ctime = get_seconds();
816 sem_unlock(sma);
817 err = 0;
818 break;
819 default:
820 sem_unlock(sma);
821 err = -EINVAL;
822 break;
823 }
824 return err;
825
826out_unlock:
827 sem_unlock(sma);
828 return err;
829}
830
831asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
832{
833 int err = -EINVAL;
834 int version;
835
836 if (semid < 0)
837 return -EINVAL;
838
839 version = ipc_parse_version(&cmd);
840
841 switch(cmd) {
842 case IPC_INFO:
843 case SEM_INFO:
844 case SEM_STAT:
845 err = semctl_nolock(semid,semnum,cmd,version,arg);
846 return err;
847 case GETALL:
848 case GETVAL:
849 case GETPID:
850 case GETNCNT:
851 case GETZCNT:
852 case IPC_STAT:
853 case SETVAL:
854 case SETALL:
855 err = semctl_main(semid,semnum,cmd,version,arg);
856 return err;
857 case IPC_RMID:
858 case IPC_SET:
859 down(&sem_ids.sem);
860 err = semctl_down(semid,semnum,cmd,version,arg);
861 up(&sem_ids.sem);
862 return err;
863 default:
864 return -EINVAL;
865 }
866}
867
868static inline void lock_semundo(void)
869{
870 struct sem_undo_list *undo_list;
871
872 undo_list = current->sysvsem.undo_list;
873 if ((undo_list != NULL) && (atomic_read(&undo_list->refcnt) != 1))
874 spin_lock(&undo_list->lock);
875}
876
877
878
879
880
881
882
883
884
885
886
887
888static inline void unlock_semundo(void)
889{
890 struct sem_undo_list *undo_list;
891
892 undo_list = current->sysvsem.undo_list;
893 if ((undo_list != NULL) && (atomic_read(&undo_list->refcnt) != 1))
894 spin_unlock(&undo_list->lock);
895}
896
897
898
899
900
901
902
903
904
905
906
907
908
909static inline int get_undo_list(struct sem_undo_list **undo_listp)
910{
911 struct sem_undo_list *undo_list;
912 int size;
913
914 undo_list = current->sysvsem.undo_list;
915 if (!undo_list) {
916 size = sizeof(struct sem_undo_list);
917 undo_list = (struct sem_undo_list *) kmalloc(size, GFP_KERNEL);
918 if (undo_list == NULL)
919 return -ENOMEM;
920 memset(undo_list, 0, size);
921
922
923
924 atomic_set(&undo_list->refcnt, 1);
925 current->sysvsem.undo_list = undo_list;
926 }
927 *undo_listp = undo_list;
928 return 0;
929}
930
931static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
932{
933 struct sem_undo **last, *un;
934
935 last = &ulp->proc_list;
936 un = *last;
937 while(un != NULL) {
938 if(un->semid==semid)
939 break;
940 if(un->semid==-1) {
941 *last=un->proc_next;
942 kfree(un);
943 } else {
944 last=&un->proc_next;
945 }
946 un=*last;
947 }
948 return un;
949}
950
951static struct sem_undo *find_undo(int semid)
952{
953 struct sem_array *sma;
954 struct sem_undo_list *ulp;
955 struct sem_undo *un, *new;
956 int nsems;
957 int error;
958
959 error = get_undo_list(&ulp);
960 if (error)
961 return ERR_PTR(error);
962
963 lock_semundo();
964 un = lookup_undo(ulp, semid);
965 unlock_semundo();
966 if (likely(un!=NULL))
967 goto out;
968
969
970 sma = sem_lock(semid);
971 un = ERR_PTR(-EINVAL);
972 if(sma==NULL)
973 goto out;
974 un = ERR_PTR(-EIDRM);
975 if (sem_checkid(sma,semid)) {
976 sem_unlock(sma);
977 goto out;
978 }
979 nsems = sma->sem_nsems;
980 sem_unlock(sma);
981
982 new = (struct sem_undo *) kmalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
983 if (!new)
984 return ERR_PTR(-ENOMEM);
985 memset(new, 0, sizeof(struct sem_undo) + sizeof(short)*nsems);
986 new->semadj = (short *) &new[1];
987 new->semid = semid;
988
989 lock_semundo();
990 un = lookup_undo(ulp, semid);
991 if (un) {
992 unlock_semundo();
993 kfree(new);
994 goto out;
995 }
996 error = sem_revalidate(semid, sma, nsems, 0);
997 if (error) {
998 unlock_semundo();
999 kfree(new);
1000 un = ERR_PTR(error);
1001 goto out;
1002 }
1003 new->proc_next = ulp->proc_list;
1004 ulp->proc_list = new;
1005 new->id_next = sma->undo;
1006 sma->undo = new;
1007 sem_unlock(sma);
1008 un = new;
1009 unlock_semundo();
1010out:
1011 return un;
1012}
1013
1014asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
1015 unsigned nsops, const struct timespec __user *timeout)
1016{
1017 int error = -EINVAL;
1018 struct sem_array *sma;
1019 struct sembuf fast_sops[SEMOPM_FAST];
1020 struct sembuf* sops = fast_sops, *sop;
1021 struct sem_undo *un;
1022 int undos = 0, decrease = 0, alter = 0, max;
1023 struct sem_queue queue;
1024 unsigned long jiffies_left = 0;
1025
1026 if (nsops < 1 || semid < 0)
1027 return -EINVAL;
1028 if (nsops > sc_semopm)
1029 return -E2BIG;
1030 if(nsops > SEMOPM_FAST) {
1031 sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
1032 if(sops==NULL)
1033 return -ENOMEM;
1034 }
1035 if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
1036 error=-EFAULT;
1037 goto out_free;
1038 }
1039 if (timeout) {
1040 struct timespec _timeout;
1041 if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
1042 error = -EFAULT;
1043 goto out_free;
1044 }
1045 if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
1046 _timeout.tv_nsec >= 1000000000L) {
1047 error = -EINVAL;
1048 goto out_free;
1049 }
1050 jiffies_left = timespec_to_jiffies(&_timeout);
1051 }
1052 max = 0;
1053 for (sop = sops; sop < sops + nsops; sop++) {
1054 if (sop->sem_num >= max)
1055 max = sop->sem_num;
1056 if (sop->sem_flg & SEM_UNDO)
1057 undos++;
1058 if (sop->sem_op < 0)
1059 decrease = 1;
1060 if (sop->sem_op > 0)
1061 alter = 1;
1062 }
1063 alter |= decrease;
1064
1065retry_undos:
1066 if (undos) {
1067 un = find_undo(semid);
1068 if (IS_ERR(un)) {
1069 error = PTR_ERR(un);
1070 goto out_free;
1071 }
1072 } else
1073 un = NULL;
1074
1075 sma = sem_lock(semid);
1076 error=-EINVAL;
1077 if(sma==NULL)
1078 goto out_free;
1079 error = -EIDRM;
1080 if (sem_checkid(sma,semid))
1081 goto out_unlock_free;
1082
1083
1084
1085
1086
1087 if (un && un->semid == -1) {
1088 sem_unlock(sma);
1089 goto retry_undos;
1090 }
1091 error = -EFBIG;
1092 if (max >= sma->sem_nsems)
1093 goto out_unlock_free;
1094
1095 error = -EACCES;
1096 if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
1097 goto out_unlock_free;
1098
1099 error = security_sem_semop(sma, sops, nsops, alter);
1100 if (error)
1101 goto out_unlock_free;
1102
1103 error = try_atomic_semop (sma, sops, nsops, un, current->tgid);
1104 if (error <= 0)
1105 goto update;
1106
1107
1108
1109
1110
1111 queue.sma = sma;
1112 queue.sops = sops;
1113 queue.nsops = nsops;
1114 queue.undo = un;
1115 queue.pid = current->tgid;
1116 queue.id = semid;
1117 if (alter)
1118 append_to_queue(sma ,&queue);
1119 else
1120 prepend_to_queue(sma ,&queue);
1121
1122 queue.status = -EINTR;
1123 queue.sleeper = current;
1124 current->state = TASK_INTERRUPTIBLE;
1125 sem_unlock(sma);
1126
1127 if (timeout)
1128 jiffies_left = schedule_timeout(jiffies_left);
1129 else
1130 schedule();
1131
1132 error = queue.status;
1133 while(unlikely(error == IN_WAKEUP)) {
1134 cpu_relax();
1135 error = queue.status;
1136 }
1137
1138 if (error != -EINTR) {
1139
1140
1141 goto out_free;
1142 }
1143
1144 sma = sem_lock(semid);
1145 if(sma==NULL) {
1146 if(queue.prev != NULL)
1147 BUG();
1148 error = -EIDRM;
1149 goto out_free;
1150 }
1151
1152
1153
1154
1155 error = queue.status;
1156 if (error != -EINTR) {
1157 goto out_unlock_free;
1158 }
1159
1160
1161
1162
1163 if (timeout && jiffies_left == 0)
1164 error = -EAGAIN;
1165 remove_from_queue(sma,&queue);
1166 goto out_unlock_free;
1167
1168update:
1169 if (alter)
1170 update_queue (sma);
1171out_unlock_free:
1172 sem_unlock(sma);
1173out_free:
1174 if(sops != fast_sops)
1175 kfree(sops);
1176 return error;
1177}
1178
1179asmlinkage long sys_semop (int semid, struct sembuf __user *tsops, unsigned nsops)
1180{
1181 return sys_semtimedop(semid, tsops, nsops, NULL);
1182}
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
1193{
1194 struct sem_undo_list *undo_list;
1195 int error;
1196
1197 if (clone_flags & CLONE_SYSVSEM) {
1198 error = get_undo_list(&undo_list);
1199 if (error)
1200 return error;
1201 if (atomic_read(&undo_list->refcnt) == 1)
1202 spin_lock_init(&undo_list->lock);
1203 atomic_inc(&undo_list->refcnt);
1204 tsk->sysvsem.undo_list = undo_list;
1205 } else
1206 tsk->sysvsem.undo_list = NULL;
1207
1208 return 0;
1209}
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223void exit_sem(struct task_struct *tsk)
1224{
1225 struct sem_undo_list *undo_list;
1226 struct sem_undo *u, **up;
1227
1228 undo_list = tsk->sysvsem.undo_list;
1229 if (!undo_list)
1230 return;
1231
1232 if (!atomic_dec_and_test(&undo_list->refcnt))
1233 return;
1234
1235
1236
1237
1238 for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) {
1239 struct sem_array *sma;
1240 int nsems, i;
1241 struct sem_undo *un, **unp;
1242 int semid;
1243
1244 semid = u->semid;
1245
1246 if(semid == -1)
1247 continue;
1248 sma = sem_lock(semid);
1249 if (sma == NULL)
1250 continue;
1251
1252 if (u->semid == -1)
1253 goto next_entry;
1254
1255 BUG_ON(sem_checkid(sma,u->semid));
1256
1257
1258 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
1259 if (u == un)
1260 goto found;
1261 }
1262 printk ("exit_sem undo list error id=%d\n", u->semid);
1263 goto next_entry;
1264found:
1265 *unp = un->id_next;
1266
1267 nsems = sma->sem_nsems;
1268 for (i = 0; i < nsems; i++) {
1269 struct sem * sem = &sma->sem_base[i];
1270 if (u->semadj[i]) {
1271 sem->semval += u->semadj[i];
1272 if (sem->semval < 0)
1273 sem->semval = 0;
1274 sem->sempid = current->tgid;
1275 }
1276 }
1277 sma->sem_otime = get_seconds();
1278
1279 update_queue(sma);
1280next_entry:
1281 sem_unlock(sma);
1282 }
1283 kfree(undo_list);
1284}
1285
1286#ifdef CONFIG_PROC_FS
1287static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
1288{
1289 off_t pos = 0;
1290 off_t begin = 0;
1291 int i, len = 0;
1292
1293 len += sprintf(buffer, " key semid perms nsems uid gid cuid cgid otime ctime\n");
1294 down(&sem_ids.sem);
1295
1296 for(i = 0; i <= sem_ids.max_id; i++) {
1297 struct sem_array *sma;
1298 sma = sem_lock(i);
1299 if(sma) {
1300 len += sprintf(buffer + len, "%10d %10d %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
1301 sma->sem_perm.key,
1302 sem_buildid(i,sma->sem_perm.seq),
1303 sma->sem_perm.mode,
1304 sma->sem_nsems,
1305 sma->sem_perm.uid,
1306 sma->sem_perm.gid,
1307 sma->sem_perm.cuid,
1308 sma->sem_perm.cgid,
1309 sma->sem_otime,
1310 sma->sem_ctime);
1311 sem_unlock(sma);
1312
1313 pos += len;
1314 if(pos < offset) {
1315 len = 0;
1316 begin = pos;
1317 }
1318 if(pos > offset + length)
1319 goto done;
1320 }
1321 }
1322 *eof = 1;
1323done:
1324 up(&sem_ids.sem);
1325 *start = buffer + (offset - begin);
1326 len -= (offset - begin);
1327 if(len > length)
1328 len = length;
1329 if(len < 0)
1330 len = 0;
1331 return len;
1332}
1333#endif
1334