linux_ipc.c revision 194910
1/*-
2 * Copyright (c) 1994-1995 S�ren Schmidt
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer
10 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/compat/linux/linux_ipc.c 194910 2009-06-24 21:10:52Z jhb $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/syscallsubr.h>
35#include <sys/sysproto.h>
36#include <sys/proc.h>
37#include <sys/limits.h>
38#include <sys/msg.h>
39#include <sys/sem.h>
40#include <sys/shm.h>
41
42#include "opt_compat.h"
43
44#ifdef COMPAT_LINUX32
45#include <machine/../linux32/linux.h>
46#include <machine/../linux32/linux32_proto.h>
47#include <machine/../linux32/linux32_ipc64.h>
48#else
49#include <machine/../linux/linux.h>
50#include <machine/../linux/linux_proto.h>
51#include <machine/../linux/linux_ipc64.h>
52#endif
53#include <compat/linux/linux_ipc.h>
54#include <compat/linux/linux_util.h>
55
56struct l_seminfo {
57	l_int semmap;
58	l_int semmni;
59	l_int semmns;
60	l_int semmnu;
61	l_int semmsl;
62	l_int semopm;
63	l_int semume;
64	l_int semusz;
65	l_int semvmx;
66	l_int semaem;
67};
68
69struct l_shminfo {
70	l_int shmmax;
71	l_int shmmin;
72	l_int shmmni;
73	l_int shmseg;
74	l_int shmall;
75};
76
77struct l_shm_info {
78	l_int used_ids;
79	l_ulong shm_tot;  /* total allocated shm */
80	l_ulong shm_rss;  /* total resident shm */
81	l_ulong shm_swp;  /* total swapped shm */
82	l_ulong swap_attempts;
83	l_ulong swap_successes;
84};
85
86struct l_msginfo {
87	l_int msgpool;
88	l_int msgmap;
89	l_int msgmax;
90	l_int msgmnb;
91	l_int msgmni;
92	l_int msgssz;
93	l_int msgtql;
94	l_ushort msgseg;
95};
96
97static void
98bsd_to_linux_shminfo( struct shminfo *bpp, struct l_shminfo *lpp)
99{
100	lpp->shmmax = bpp->shmmax;
101	lpp->shmmin = bpp->shmmin;
102	lpp->shmmni = bpp->shmmni;
103	lpp->shmseg = bpp->shmseg;
104	lpp->shmall = bpp->shmall;
105}
106
107static void
108bsd_to_linux_shm_info( struct shm_info *bpp, struct l_shm_info *lpp)
109{
110	lpp->used_ids = bpp->used_ids ;
111	lpp->shm_tot = bpp->shm_tot ;
112	lpp->shm_rss = bpp->shm_rss ;
113	lpp->shm_swp = bpp->shm_swp ;
114	lpp->swap_attempts = bpp->swap_attempts ;
115	lpp->swap_successes = bpp->swap_successes ;
116}
117
118struct l_ipc_perm {
119	l_key_t		key;
120	l_uid16_t	uid;
121	l_gid16_t	gid;
122	l_uid16_t	cuid;
123	l_gid16_t	cgid;
124	l_ushort	mode;
125	l_ushort	seq;
126};
127
128static void
129linux_to_bsd_ipc_perm(struct l_ipc_perm *lpp, struct ipc_perm *bpp)
130{
131    bpp->key = lpp->key;
132    bpp->uid = lpp->uid;
133    bpp->gid = lpp->gid;
134    bpp->cuid = lpp->cuid;
135    bpp->cgid = lpp->cgid;
136    bpp->mode = lpp->mode;
137    bpp->seq = lpp->seq;
138}
139
140
141static void
142bsd_to_linux_ipc_perm(struct ipc_perm *bpp, struct l_ipc_perm *lpp)
143{
144    lpp->key = bpp->key;
145    lpp->uid = bpp->uid;
146    lpp->gid = bpp->gid;
147    lpp->cuid = bpp->cuid;
148    lpp->cgid = bpp->cgid;
149    lpp->mode = bpp->mode;
150    lpp->seq = bpp->seq;
151}
152
153struct l_msqid_ds {
154	struct l_ipc_perm	msg_perm;
155	l_uintptr_t		msg_first;	/* first message on queue,unused */
156	l_uintptr_t		msg_last;	/* last message in queue,unused */
157	l_time_t		msg_stime;	/* last msgsnd time */
158	l_time_t		msg_rtime;	/* last msgrcv time */
159	l_time_t		msg_ctime;	/* last change time */
160	l_ulong			msg_lcbytes;	/* Reuse junk fields for 32 bit */
161	l_ulong			msg_lqbytes;	/* ditto */
162	l_ushort		msg_cbytes;	/* current number of bytes on queue */
163	l_ushort		msg_qnum;	/* number of messages in queue */
164	l_ushort		msg_qbytes;	/* max number of bytes on queue */
165	l_pid_t			msg_lspid;	/* pid of last msgsnd */
166	l_pid_t			msg_lrpid;	/* last receive pid */
167}
168#if defined(__amd64__) && defined(COMPAT_LINUX32)
169__packed
170#endif
171;
172
173struct l_semid_ds {
174	struct l_ipc_perm	sem_perm;
175	l_time_t		sem_otime;
176	l_time_t		sem_ctime;
177	l_uintptr_t		sem_base;
178	l_uintptr_t		sem_pending;
179	l_uintptr_t		sem_pending_last;
180	l_uintptr_t		undo;
181	l_ushort		sem_nsems;
182}
183#if defined(__amd64__) && defined(COMPAT_LINUX32)
184__packed
185#endif
186;
187
188struct l_shmid_ds {
189	struct l_ipc_perm	shm_perm;
190	l_int			shm_segsz;
191	l_time_t		shm_atime;
192	l_time_t		shm_dtime;
193	l_time_t		shm_ctime;
194	l_ushort		shm_cpid;
195	l_ushort		shm_lpid;
196	l_short			shm_nattch;
197	l_ushort		private1;
198	l_uintptr_t		private2;
199	l_uintptr_t		private3;
200};
201
202static void
203linux_to_bsd_semid_ds(struct l_semid_ds *lsp, struct semid_ds *bsp)
204{
205    linux_to_bsd_ipc_perm(&lsp->sem_perm, &bsp->sem_perm);
206    bsp->sem_otime = lsp->sem_otime;
207    bsp->sem_ctime = lsp->sem_ctime;
208    bsp->sem_nsems = lsp->sem_nsems;
209    bsp->sem_base = PTRIN(lsp->sem_base);
210}
211
212static void
213bsd_to_linux_semid_ds(struct semid_ds *bsp, struct l_semid_ds *lsp)
214{
215	bsd_to_linux_ipc_perm(&bsp->sem_perm, &lsp->sem_perm);
216	lsp->sem_otime = bsp->sem_otime;
217	lsp->sem_ctime = bsp->sem_ctime;
218	lsp->sem_nsems = bsp->sem_nsems;
219	lsp->sem_base = PTROUT(bsp->sem_base);
220}
221
222static void
223linux_to_bsd_shmid_ds(struct l_shmid_ds *lsp, struct shmid_ds *bsp)
224{
225    linux_to_bsd_ipc_perm(&lsp->shm_perm, &bsp->shm_perm);
226    bsp->shm_segsz = lsp->shm_segsz;
227    bsp->shm_lpid = lsp->shm_lpid;
228    bsp->shm_cpid = lsp->shm_cpid;
229    bsp->shm_nattch = lsp->shm_nattch;
230    bsp->shm_atime = lsp->shm_atime;
231    bsp->shm_dtime = lsp->shm_dtime;
232    bsp->shm_ctime = lsp->shm_ctime;
233}
234
235static void
236bsd_to_linux_shmid_ds(struct shmid_ds *bsp, struct l_shmid_ds *lsp)
237{
238    bsd_to_linux_ipc_perm(&bsp->shm_perm, &lsp->shm_perm);
239    if (bsp->shm_segsz > INT_MAX)
240	    lsp->shm_segsz = INT_MAX;
241    else
242	    lsp->shm_segsz = bsp->shm_segsz;
243    lsp->shm_lpid = bsp->shm_lpid;
244    lsp->shm_cpid = bsp->shm_cpid;
245    if (bsp->shm_nattch > SHRT_MAX)
246	    lsp->shm_nattch = SHRT_MAX;
247    else
248	    lsp->shm_nattch = bsp->shm_nattch;
249    lsp->shm_atime = bsp->shm_atime;
250    lsp->shm_dtime = bsp->shm_dtime;
251    lsp->shm_ctime = bsp->shm_ctime;
252    lsp->private3 = 0;
253}
254
255static void
256linux_to_bsd_msqid_ds(struct l_msqid_ds *lsp, struct msqid_ds *bsp)
257{
258    linux_to_bsd_ipc_perm(&lsp->msg_perm, &bsp->msg_perm);
259    bsp->msg_cbytes = lsp->msg_cbytes;
260    bsp->msg_qnum = lsp->msg_qnum;
261    bsp->msg_qbytes = lsp->msg_qbytes;
262    bsp->msg_lspid = lsp->msg_lspid;
263    bsp->msg_lrpid = lsp->msg_lrpid;
264    bsp->msg_stime = lsp->msg_stime;
265    bsp->msg_rtime = lsp->msg_rtime;
266    bsp->msg_ctime = lsp->msg_ctime;
267}
268
269static void
270bsd_to_linux_msqid_ds(struct msqid_ds *bsp, struct l_msqid_ds *lsp)
271{
272    bsd_to_linux_ipc_perm(&bsp->msg_perm, &lsp->msg_perm);
273    lsp->msg_cbytes = bsp->msg_cbytes;
274    lsp->msg_qnum = bsp->msg_qnum;
275    lsp->msg_qbytes = bsp->msg_qbytes;
276    lsp->msg_lspid = bsp->msg_lspid;
277    lsp->msg_lrpid = bsp->msg_lrpid;
278    lsp->msg_stime = bsp->msg_stime;
279    lsp->msg_rtime = bsp->msg_rtime;
280    lsp->msg_ctime = bsp->msg_ctime;
281}
282
283static void
284linux_ipc_perm_to_ipc64_perm(struct l_ipc_perm *in, struct l_ipc64_perm *out)
285{
286
287	/* XXX: do we really need to do something here? */
288	out->key = in->key;
289	out->uid = in->uid;
290	out->gid = in->gid;
291	out->cuid = in->cuid;
292	out->cgid = in->cgid;
293	out->mode = in->mode;
294	out->seq = in->seq;
295}
296
297static int
298linux_msqid_pullup(l_int ver, struct l_msqid_ds *linux_msqid, caddr_t uaddr)
299{
300	struct l_msqid64_ds linux_msqid64;
301	int error;
302
303	if (ver == LINUX_IPC_64) {
304		error = copyin(uaddr, &linux_msqid64, sizeof(linux_msqid64));
305		if (error != 0)
306			return (error);
307
308		bzero(linux_msqid, sizeof(*linux_msqid));
309
310		linux_msqid->msg_perm.uid = linux_msqid64.msg_perm.uid;
311		linux_msqid->msg_perm.gid = linux_msqid64.msg_perm.gid;
312		linux_msqid->msg_perm.mode = linux_msqid64.msg_perm.mode;
313
314		if (linux_msqid64.msg_qbytes > USHRT_MAX)
315			linux_msqid->msg_lqbytes = linux_msqid64.msg_qbytes;
316		else
317			linux_msqid->msg_qbytes = linux_msqid64.msg_qbytes;
318	} else {
319		error = copyin(uaddr, linux_msqid, sizeof(*linux_msqid));
320	}
321	return (error);
322}
323
324static int
325linux_msqid_pushdown(l_int ver, struct l_msqid_ds *linux_msqid, caddr_t uaddr)
326{
327	struct l_msqid64_ds linux_msqid64;
328
329	if (ver == LINUX_IPC_64) {
330		bzero(&linux_msqid64, sizeof(linux_msqid64));
331
332		linux_ipc_perm_to_ipc64_perm(&linux_msqid->msg_perm,
333		    &linux_msqid64.msg_perm);
334
335		linux_msqid64.msg_stime = linux_msqid->msg_stime;
336		linux_msqid64.msg_rtime = linux_msqid->msg_rtime;
337		linux_msqid64.msg_ctime = linux_msqid->msg_ctime;
338
339		if (linux_msqid->msg_cbytes == 0)
340			linux_msqid64.msg_cbytes = linux_msqid->msg_lcbytes;
341		else
342			linux_msqid64.msg_cbytes = linux_msqid->msg_cbytes;
343
344		linux_msqid64.msg_qnum = linux_msqid->msg_qnum;
345
346		if (linux_msqid->msg_qbytes == 0)
347			linux_msqid64.msg_qbytes = linux_msqid->msg_lqbytes;
348		else
349			linux_msqid64.msg_qbytes = linux_msqid->msg_qbytes;
350
351		linux_msqid64.msg_lspid = linux_msqid->msg_lspid;
352		linux_msqid64.msg_lrpid = linux_msqid->msg_lrpid;
353
354		return (copyout(&linux_msqid64, uaddr, sizeof(linux_msqid64)));
355	} else {
356		return (copyout(linux_msqid, uaddr, sizeof(*linux_msqid)));
357	}
358}
359
360static int
361linux_semid_pullup(l_int ver, struct l_semid_ds *linux_semid, caddr_t uaddr)
362{
363	struct l_semid64_ds linux_semid64;
364	int error;
365
366	if (ver == LINUX_IPC_64) {
367		error = copyin(uaddr, &linux_semid64, sizeof(linux_semid64));
368		if (error != 0)
369			return (error);
370
371		bzero(linux_semid, sizeof(*linux_semid));
372
373		linux_semid->sem_perm.uid = linux_semid64.sem_perm.uid;
374		linux_semid->sem_perm.gid = linux_semid64.sem_perm.gid;
375		linux_semid->sem_perm.mode = linux_semid64.sem_perm.mode;
376	} else {
377		error = copyin(uaddr, linux_semid, sizeof(*linux_semid));
378	}
379	return (error);
380}
381
382static int
383linux_semid_pushdown(l_int ver, struct l_semid_ds *linux_semid, caddr_t uaddr)
384{
385	struct l_semid64_ds linux_semid64;
386
387	if (ver == LINUX_IPC_64) {
388		bzero(&linux_semid64, sizeof(linux_semid64));
389
390		linux_ipc_perm_to_ipc64_perm(&linux_semid->sem_perm,
391		    &linux_semid64.sem_perm);
392
393		linux_semid64.sem_otime = linux_semid->sem_otime;
394		linux_semid64.sem_ctime = linux_semid->sem_ctime;
395		linux_semid64.sem_nsems = linux_semid->sem_nsems;
396
397		return (copyout(&linux_semid64, uaddr, sizeof(linux_semid64)));
398	} else {
399		return (copyout(linux_semid, uaddr, sizeof(*linux_semid)));
400	}
401}
402
403static int
404linux_shmid_pullup(l_int ver, struct l_shmid_ds *linux_shmid, caddr_t uaddr)
405{
406	struct l_shmid64_ds linux_shmid64;
407	int error;
408
409	if (ver == LINUX_IPC_64) {
410		error = copyin(uaddr, &linux_shmid64, sizeof(linux_shmid64));
411		if (error != 0)
412			return (error);
413
414		bzero(linux_shmid, sizeof(*linux_shmid));
415
416		linux_shmid->shm_perm.uid = linux_shmid64.shm_perm.uid;
417		linux_shmid->shm_perm.gid = linux_shmid64.shm_perm.gid;
418		linux_shmid->shm_perm.mode = linux_shmid64.shm_perm.mode;
419	} else {
420		error = copyin(uaddr, linux_shmid, sizeof(*linux_shmid));
421	}
422	return (error);
423}
424
425static int
426linux_shmid_pushdown(l_int ver, struct l_shmid_ds *linux_shmid, caddr_t uaddr)
427{
428	struct l_shmid64_ds linux_shmid64;
429
430	/*
431	 * XXX: This is backwards and loses information in shm_nattch
432	 * and shm_segsz.  We should probably either expose the BSD
433	 * shmid structure directly and convert it to either the
434	 * non-64 or 64 variant directly or the code should always
435	 * convert to the 64 variant and then truncate values into the
436	 * non-64 variant if needed since the 64 variant has more
437	 * precision.
438	 */
439	if (ver == LINUX_IPC_64) {
440		bzero(&linux_shmid64, sizeof(linux_shmid64));
441
442		linux_ipc_perm_to_ipc64_perm(&linux_shmid->shm_perm,
443		    &linux_shmid64.shm_perm);
444
445		linux_shmid64.shm_segsz = linux_shmid->shm_segsz;
446		linux_shmid64.shm_atime = linux_shmid->shm_atime;
447		linux_shmid64.shm_dtime = linux_shmid->shm_dtime;
448		linux_shmid64.shm_ctime = linux_shmid->shm_ctime;
449		linux_shmid64.shm_cpid = linux_shmid->shm_cpid;
450		linux_shmid64.shm_lpid = linux_shmid->shm_lpid;
451		linux_shmid64.shm_nattch = linux_shmid->shm_nattch;
452
453		return (copyout(&linux_shmid64, uaddr, sizeof(linux_shmid64)));
454	} else {
455		return (copyout(linux_shmid, uaddr, sizeof(*linux_shmid)));
456	}
457}
458
459static int
460linux_shminfo_pushdown(l_int ver, struct l_shminfo *linux_shminfo,
461    caddr_t uaddr)
462{
463	struct l_shminfo64 linux_shminfo64;
464
465	if (ver == LINUX_IPC_64) {
466		bzero(&linux_shminfo64, sizeof(linux_shminfo64));
467
468		linux_shminfo64.shmmax = linux_shminfo->shmmax;
469		linux_shminfo64.shmmin = linux_shminfo->shmmin;
470		linux_shminfo64.shmmni = linux_shminfo->shmmni;
471		linux_shminfo64.shmseg = linux_shminfo->shmseg;
472		linux_shminfo64.shmall = linux_shminfo->shmall;
473
474		return (copyout(&linux_shminfo64, uaddr,
475		    sizeof(linux_shminfo64)));
476	} else {
477		return (copyout(linux_shminfo, uaddr, sizeof(*linux_shminfo)));
478	}
479}
480
481int
482linux_semop(struct thread *td, struct linux_semop_args *args)
483{
484	struct semop_args /* {
485	int	semid;
486	struct	sembuf *sops;
487	int		nsops;
488	} */ bsd_args;
489
490	bsd_args.semid = args->semid;
491	bsd_args.sops = PTRIN(args->tsops);
492	bsd_args.nsops = args->nsops;
493	return semop(td, &bsd_args);
494}
495
496int
497linux_semget(struct thread *td, struct linux_semget_args *args)
498{
499	struct semget_args /* {
500	key_t	key;
501	int		nsems;
502	int		semflg;
503	} */ bsd_args;
504
505	if (args->nsems < 0)
506		return (EINVAL);
507	bsd_args.key = args->key;
508	bsd_args.nsems = args->nsems;
509	bsd_args.semflg = args->semflg;
510	return semget(td, &bsd_args);
511}
512
513int
514linux_semctl(struct thread *td, struct linux_semctl_args *args)
515{
516	struct l_semid_ds linux_semid;
517	struct l_seminfo linux_seminfo;
518	struct semid_ds semid;
519	union semun semun;
520	register_t rval;
521	int cmd, error;
522
523	switch (args->cmd & ~LINUX_IPC_64) {
524	case LINUX_IPC_RMID:
525		cmd = IPC_RMID;
526		break;
527	case LINUX_GETNCNT:
528		cmd = GETNCNT;
529		break;
530	case LINUX_GETPID:
531		cmd = GETPID;
532		break;
533	case LINUX_GETVAL:
534		cmd = GETVAL;
535		break;
536	case LINUX_GETZCNT:
537		cmd = GETZCNT;
538		break;
539	case LINUX_SETVAL:
540		cmd = SETVAL;
541		semun.val = args->arg.val;
542		break;
543	case LINUX_IPC_SET:
544		cmd = IPC_SET;
545		error = linux_semid_pullup(args->cmd & LINUX_IPC_64,
546		    &linux_semid, PTRIN(args->arg.buf));
547		if (error)
548			return (error);
549		linux_to_bsd_semid_ds(&linux_semid, &semid);
550		semun.buf = &semid;
551		return (kern_semctl(td, args->semid, args->semnum, cmd, &semun,
552		    td->td_retval));
553	case LINUX_IPC_STAT:
554	case LINUX_SEM_STAT:
555		if ((args->cmd & ~LINUX_IPC_64) == LINUX_IPC_STAT)
556			cmd = IPC_STAT;
557		else
558			cmd = SEM_STAT;
559		semun.buf = &semid;
560		error = kern_semctl(td, args->semid, args->semnum, cmd, &semun,
561		    &rval);
562		if (error)
563			return (error);
564		bsd_to_linux_semid_ds(&semid, &linux_semid);
565		error = linux_semid_pushdown(args->cmd & LINUX_IPC_64,
566		    &linux_semid, PTRIN(args->arg.buf));
567		if (error == 0)
568			td->td_retval[0] = (cmd == SEM_STAT) ? rval : 0;
569		return (error);
570	case LINUX_IPC_INFO:
571	case LINUX_SEM_INFO:
572		bcopy(&seminfo, &linux_seminfo, sizeof(linux_seminfo) );
573/* XXX BSD equivalent?
574#define used_semids 10
575#define used_sems 10
576		linux_seminfo.semusz = used_semids;
577		linux_seminfo.semaem = used_sems;
578*/
579		error = copyout(&linux_seminfo,
580		    PTRIN(args->arg.buf), sizeof(linux_seminfo));
581		if (error)
582			return error;
583		td->td_retval[0] = seminfo.semmni;
584		return 0;			/* No need for __semctl call */
585	case LINUX_GETALL:
586		cmd = GETALL;
587		semun.val = args->arg.val;
588		break;
589	case LINUX_SETALL:
590		cmd = SETALL;
591		semun.val = args->arg.val;
592		break;
593	default:
594		linux_msg(td, "ipc type %d is not implemented",
595		  args->cmd & ~LINUX_IPC_64);
596		return EINVAL;
597	}
598	return (kern_semctl(td, args->semid, args->semnum, cmd, &semun,
599	    td->td_retval));
600}
601
602int
603linux_msgsnd(struct thread *td, struct linux_msgsnd_args *args)
604{
605	const void *msgp;
606	long mtype;
607	l_long lmtype;
608	int error;
609
610	if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax)
611		return (EINVAL);
612	msgp = PTRIN(args->msgp);
613	if ((error = copyin(msgp, &lmtype, sizeof(lmtype))) != 0)
614		return (error);
615	mtype = (long)lmtype;
616	return (kern_msgsnd(td, args->msqid,
617	    (const char *)msgp + sizeof(lmtype),
618	    args->msgsz, args->msgflg, mtype));
619}
620
621int
622linux_msgrcv(struct thread *td, struct linux_msgrcv_args *args)
623{
624	void *msgp;
625	long mtype;
626	l_long lmtype;
627	int error;
628
629	if ((l_long)args->msgsz < 0 || args->msgsz > (l_long)msginfo.msgmax)
630		return (EINVAL);
631	msgp = PTRIN(args->msgp);
632	if ((error = kern_msgrcv(td, args->msqid,
633	    (char *)msgp + sizeof(lmtype), args->msgsz,
634	    args->msgtyp, args->msgflg, &mtype)) != 0)
635		return (error);
636	lmtype = (l_long)mtype;
637	return (copyout(&lmtype, msgp, sizeof(lmtype)));
638}
639
640int
641linux_msgget(struct thread *td, struct linux_msgget_args *args)
642{
643    struct msgget_args /* {
644	key_t	key;
645	int	msgflg;
646    } */ bsd_args;
647
648    bsd_args.key = args->key;
649    bsd_args.msgflg = args->msgflg;
650    return msgget(td, &bsd_args);
651}
652
653int
654linux_msgctl(struct thread *td, struct linux_msgctl_args *args)
655{
656    int error, bsd_cmd;
657    struct l_msqid_ds linux_msqid;
658    struct msqid_ds bsd_msqid;
659
660    bsd_cmd = args->cmd & ~LINUX_IPC_64;
661    switch (bsd_cmd) {
662    case LINUX_IPC_INFO:
663    case LINUX_MSG_INFO: {
664	struct l_msginfo linux_msginfo;
665
666	/*
667	 * XXX MSG_INFO uses the same data structure but returns different
668	 * dynamic counters in msgpool, msgmap, and msgtql fields.
669	 */
670	linux_msginfo.msgpool = (long)msginfo.msgmni *
671	    (long)msginfo.msgmnb / 1024L;	/* XXX MSG_INFO. */
672	linux_msginfo.msgmap = msginfo.msgmnb;	/* XXX MSG_INFO. */
673	linux_msginfo.msgmax = msginfo.msgmax;
674	linux_msginfo.msgmnb = msginfo.msgmnb;
675	linux_msginfo.msgmni = msginfo.msgmni;
676	linux_msginfo.msgssz = msginfo.msgssz;
677	linux_msginfo.msgtql = msginfo.msgtql;	/* XXX MSG_INFO. */
678	linux_msginfo.msgseg = msginfo.msgseg;
679	error = copyout(&linux_msginfo, PTRIN(args->buf),
680	    sizeof(linux_msginfo));
681	if (error == 0)
682	    td->td_retval[0] = msginfo.msgmni;	/* XXX */
683
684	return (error);
685    }
686
687/*
688 * TODO: implement this
689 * case LINUX_MSG_STAT:
690 */
691    case LINUX_IPC_STAT:
692	/* NOTHING */
693	break;
694
695    case LINUX_IPC_SET:
696	error = linux_msqid_pullup(args->cmd & LINUX_IPC_64,
697	    &linux_msqid, PTRIN(args->buf));
698	if (error)
699	    return (error);
700	linux_to_bsd_msqid_ds(&linux_msqid, &bsd_msqid);
701	break;
702
703    case LINUX_IPC_RMID:
704	/* NOTHING */
705	break;
706
707    default:
708	return (EINVAL);
709	break;
710    }
711
712    error = kern_msgctl(td, args->msqid, bsd_cmd, &bsd_msqid);
713    if (error != 0)
714	if (bsd_cmd != LINUX_IPC_RMID || error != EINVAL)
715	    return (error);
716
717    if (bsd_cmd == LINUX_IPC_STAT) {
718	bsd_to_linux_msqid_ds(&bsd_msqid, &linux_msqid);
719	return (linux_msqid_pushdown(args->cmd & LINUX_IPC_64,
720	  &linux_msqid, PTRIN(args->buf)));
721    }
722
723    return (0);
724}
725
726int
727linux_shmat(struct thread *td, struct linux_shmat_args *args)
728{
729    struct shmat_args /* {
730	int shmid;
731	void *shmaddr;
732	int shmflg;
733    } */ bsd_args;
734    int error;
735#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
736    l_uintptr_t addr;
737#endif
738
739    bsd_args.shmid = args->shmid;
740    bsd_args.shmaddr = PTRIN(args->shmaddr);
741    bsd_args.shmflg = args->shmflg;
742    if ((error = shmat(td, &bsd_args)))
743	return error;
744#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
745    addr = td->td_retval[0];
746    if ((error = copyout(&addr, PTRIN(args->raddr), sizeof(addr))))
747	return error;
748    td->td_retval[0] = 0;
749#endif
750    return 0;
751}
752
753int
754linux_shmdt(struct thread *td, struct linux_shmdt_args *args)
755{
756    struct shmdt_args /* {
757	void *shmaddr;
758    } */ bsd_args;
759
760    bsd_args.shmaddr = PTRIN(args->shmaddr);
761    return shmdt(td, &bsd_args);
762}
763
764int
765linux_shmget(struct thread *td, struct linux_shmget_args *args)
766{
767    struct shmget_args /* {
768	key_t key;
769	int size;
770	int shmflg;
771    } */ bsd_args;
772
773    bsd_args.key = args->key;
774    bsd_args.size = args->size;
775    bsd_args.shmflg = args->shmflg;
776    return shmget(td, &bsd_args);
777}
778
779int
780linux_shmctl(struct thread *td, struct linux_shmctl_args *args)
781{
782    struct l_shmid_ds linux_shmid;
783	struct l_shminfo linux_shminfo;
784	struct l_shm_info linux_shm_info;
785	struct shmid_ds bsd_shmid;
786    int error;
787
788    switch (args->cmd & ~LINUX_IPC_64) {
789
790	case LINUX_IPC_INFO: {
791	    struct shminfo bsd_shminfo;
792
793	    /* Perform shmctl wanting removed segments lookup */
794	    error = kern_shmctl(td, args->shmid, IPC_INFO,
795	        (void *)&bsd_shminfo, NULL);
796	    if (error)
797		return error;
798
799	    bsd_to_linux_shminfo(&bsd_shminfo, &linux_shminfo);
800
801	    return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64,
802	       &linux_shminfo, PTRIN(args->buf)));
803	}
804
805	case LINUX_SHM_INFO: {
806	    struct shm_info bsd_shm_info;
807
808	    /* Perform shmctl wanting removed segments lookup */
809	    error = kern_shmctl(td, args->shmid, SHM_INFO,
810	        (void *)&bsd_shm_info, NULL);
811	    if (error)
812		return error;
813
814	    bsd_to_linux_shm_info(&bsd_shm_info, &linux_shm_info);
815
816	    return copyout(&linux_shm_info, PTRIN(args->buf),
817	        sizeof(struct l_shm_info));
818	}
819
820	case LINUX_IPC_STAT:
821	    /* Perform shmctl wanting removed segments lookup */
822	    error = kern_shmctl(td, args->shmid, IPC_STAT,
823	        (void *)&bsd_shmid, NULL);
824	    if (error)
825		return error;
826
827	    bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid);
828
829	    return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64,
830	  &linux_shmid, PTRIN(args->buf)));
831
832    case LINUX_SHM_STAT:
833	/* Perform shmctl wanting removed segments lookup */
834	error = kern_shmctl(td, args->shmid, IPC_STAT,
835	    (void *)&bsd_shmid, NULL);
836	if (error)
837		return error;
838
839	bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid);
840
841	return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64,
842	   &linux_shmid, PTRIN(args->buf)));
843
844    case LINUX_IPC_SET:
845	error = linux_shmid_pullup(args->cmd & LINUX_IPC_64,
846	  &linux_shmid, PTRIN(args->buf));
847	if (error)
848    		return error;
849
850	linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid);
851
852	/* Perform shmctl wanting removed segments lookup */
853	return kern_shmctl(td, args->shmid, IPC_SET,
854	    (void *)&bsd_shmid, NULL);
855
856    case LINUX_IPC_RMID: {
857	void *buf;
858
859	if (args->buf == 0)
860    		buf = NULL;
861	else {
862    		error = linux_shmid_pullup(args->cmd & LINUX_IPC_64,
863		    &linux_shmid, PTRIN(args->buf));
864		if (error)
865			return error;
866		linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid);
867		buf = (void *)&bsd_shmid;
868	}
869	return kern_shmctl(td, args->shmid, IPC_RMID, buf, NULL);
870    }
871
872    case LINUX_SHM_LOCK:
873    case LINUX_SHM_UNLOCK:
874    default:
875	linux_msg(td, "ipc typ=%d not implemented", args->cmd & ~LINUX_IPC_64);
876	return EINVAL;
877    }
878}
879
880MODULE_DEPEND(linux, sysvmsg, 1, 1, 1);
881MODULE_DEPEND(linux, sysvsem, 1, 1, 1);
882MODULE_DEPEND(linux, sysvshm, 1, 1, 1);
883