1/* Copyright (C) 1995-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18#include <sys/msg.h>
19#include <ipc_priv.h>
20#include <sysdep.h>
21#include <shlib-compat.h>
22#include <errno.h>
23#include <linux/posix_types.h> /* For __kernel_mode_t. */
24
25/* POSIX states ipc_perm mode should have type of mode_t. */
26_Static_assert (sizeof ((struct msqid_ds){0}.msg_perm.mode)
27 == sizeof (mode_t),
28 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
29
30#if __IPC_TIME64 == 0
31typedef struct msqid_ds msgctl_arg_t;
32#else
33# include <struct_kernel_msqid64_ds.h>
34
35static void
36msqid64_to_kmsqid64 (const struct __msqid64_ds *msqid64,
37 struct kernel_msqid64_ds *kmsqid)
38{
39 kmsqid->msg_perm = msqid64->msg_perm;
40 kmsqid->msg_stime = msqid64->msg_stime;
41 kmsqid->msg_stime_high = msqid64->msg_stime >> 32;
42 kmsqid->msg_rtime = msqid64->msg_rtime;
43 kmsqid->msg_rtime_high = msqid64->msg_rtime >> 32;
44 kmsqid->msg_ctime = msqid64->msg_ctime;
45 kmsqid->msg_ctime_high = msqid64->msg_ctime >> 32;
46 kmsqid->msg_cbytes = msqid64->msg_cbytes;
47 kmsqid->msg_qnum = msqid64->msg_qnum;
48 kmsqid->msg_qbytes = msqid64->msg_qbytes;
49 kmsqid->msg_lspid = msqid64->msg_lspid;
50 kmsqid->msg_lrpid = msqid64->msg_lrpid;
51}
52
53static void
54kmsqid64_to_msqid64 (const struct kernel_msqid64_ds *kmsqid,
55 struct __msqid64_ds *msqid64)
56{
57 msqid64->msg_perm = kmsqid->msg_perm;
58 msqid64->msg_stime = kmsqid->msg_stime
59 | ((__time64_t) kmsqid->msg_stime_high << 32);
60 msqid64->msg_rtime = kmsqid->msg_rtime
61 | ((__time64_t) kmsqid->msg_rtime_high << 32);
62 msqid64->msg_ctime = kmsqid->msg_ctime
63 | ((__time64_t) kmsqid->msg_ctime_high << 32);
64 msqid64->msg_cbytes = kmsqid->msg_cbytes;
65 msqid64->msg_qnum = kmsqid->msg_qnum;
66 msqid64->msg_qbytes = kmsqid->msg_qbytes;
67 msqid64->msg_lspid = kmsqid->msg_lspid;
68 msqid64->msg_lrpid = kmsqid->msg_lrpid;
69}
70
71typedef struct kernel_msqid64_ds msgctl_arg_t;
72#endif
73
74static int
75msgctl_syscall (int msqid, int cmd, msgctl_arg_t *buf)
76{
77#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
78 return INLINE_SYSCALL_CALL (msgctl, msqid, cmd | __IPC_64, buf);
79#else
80 return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd | __IPC_64, 0,
81 buf);
82#endif
83}
84
85int
86__msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf)
87{
88#if IPC_CTL_NEED_TRANSLATION
89# if __IPC_TIME64
90 struct kernel_msqid64_ds ksemid, *arg = NULL;
91# else
92 msgctl_arg_t *arg;
93# endif
94
95 /* Some applications pass the __IPC_64 flag in cmd, to invoke
96 previously unsupported commands back when there was no EINVAL
97 error checking in glibc. Mask the flag for the switch statements
98 below. msgctl_syscall adds back the __IPC_64 flag for the actual
99 system call. */
100 cmd &= ~__IPC_64;
101
102 switch (cmd)
103 {
104 case IPC_RMID:
105 arg = NULL;
106 break;
107
108 case IPC_SET:
109 case IPC_STAT:
110 case MSG_STAT:
111 case MSG_STAT_ANY:
112# if __IPC_TIME64
113 if (buf != NULL)
114 {
115 msqid64_to_kmsqid64 (buf, &ksemid);
116 arg = &ksemid;
117 }
118# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
119 if (cmd == IPC_SET)
120 arg->msg_perm.mode *= 0x10000U;
121# endif
122# else
123 arg = buf;
124# endif
125 break;
126
127 case IPC_INFO:
128 case MSG_INFO:
129 /* This is a Linux extension where kernel returns a 'struct msginfo'
130 instead. */
131 arg = (__typeof__ (arg)) buf;
132 break;
133
134 default:
135 __set_errno (EINVAL);
136 return -1;
137 }
138
139 int ret = msgctl_syscall (msqid, cmd, arg);
140 if (ret < 0)
141 return ret;
142
143 switch (cmd)
144 {
145 case IPC_STAT:
146 case MSG_STAT:
147 case MSG_STAT_ANY:
148# ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
149 arg->msg_perm.mode >>= 16;
150# else
151 /* Old Linux kernel versions might not clear the mode padding. */
152 if (sizeof ((struct msqid_ds){0}.msg_perm.mode)
153 != sizeof (__kernel_mode_t))
154 arg->msg_perm.mode &= 0xFFFF;
155# endif
156
157# if __IPC_TIME64
158 kmsqid64_to_msqid64 (arg, buf);
159# endif
160 }
161
162 return ret;
163
164#else /* !IPC_CTL_NEED_TRANSLATION */
165 return msgctl_syscall (msqid, cmd, buf);
166#endif
167}
168#if __TIMESIZE != 64
169libc_hidden_def (__msgctl64)
170
171static void
172msqid_to_msqid64 (struct __msqid64_ds *mq64, const struct msqid_ds *mq)
173{
174 mq64->msg_perm = mq->msg_perm;
175 mq64->msg_stime = mq->msg_stime
176 | ((__time64_t) mq->__msg_stime_high << 32);
177 mq64->msg_rtime = mq->msg_rtime
178 | ((__time64_t) mq->__msg_rtime_high << 32);
179 mq64->msg_ctime = mq->msg_ctime
180 | ((__time64_t) mq->__msg_ctime_high << 32);
181 mq64->msg_cbytes = mq->msg_cbytes;
182 mq64->msg_qnum = mq->msg_qnum;
183 mq64->msg_qbytes = mq->msg_qbytes;
184 mq64->msg_lspid = mq->msg_lspid;
185 mq64->msg_lrpid = mq->msg_lrpid;
186}
187
188static void
189msqid64_to_msqid (struct msqid_ds *mq, const struct __msqid64_ds *mq64)
190{
191 mq->msg_perm = mq64->msg_perm;
192 mq->msg_stime = mq64->msg_stime;
193 mq->__msg_stime_high = 0;
194 mq->msg_rtime = mq64->msg_rtime;
195 mq->__msg_rtime_high = 0;
196 mq->msg_ctime = mq64->msg_ctime;
197 mq->__msg_ctime_high = 0;
198 mq->msg_cbytes = mq64->msg_cbytes;
199 mq->msg_qnum = mq64->msg_qnum;
200 mq->msg_qbytes = mq64->msg_qbytes;
201 mq->msg_lspid = mq64->msg_lspid;
202 mq->msg_lrpid = mq64->msg_lrpid;
203}
204
205int
206__msgctl (int msqid, int cmd, struct msqid_ds *buf)
207{
208 struct __msqid64_ds msqid64, *buf64 = NULL;
209 if (buf != NULL)
210 {
211 /* This is a Linux extension where kernel returns a 'struct msginfo'
212 instead. */
213 if (cmd == IPC_INFO || cmd == MSG_INFO)
214 buf64 = (struct __msqid64_ds *) buf;
215 else
216 {
217 msqid_to_msqid64 (&msqid64, buf);
218 buf64 = &msqid64;
219 }
220 }
221
222 int ret = __msgctl64 (msqid, cmd, buf64);
223 if (ret < 0)
224 return ret;
225
226 switch (cmd)
227 {
228 case IPC_STAT:
229 case MSG_STAT:
230 case MSG_STAT_ANY:
231 msqid64_to_msqid (buf, buf64);
232 }
233
234 return ret;
235}
236#endif
237
238#ifndef DEFAULT_VERSION
239# ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
240# define DEFAULT_VERSION GLIBC_2_2
241# else
242# define DEFAULT_VERSION GLIBC_2_31
243# endif
244#endif
245versioned_symbol (libc, __msgctl, msgctl, DEFAULT_VERSION);
246
247#if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
248 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
249int
250attribute_compat_text_section
251__msgctl_mode16 (int msqid, int cmd, struct msqid_ds *buf)
252{
253 return msgctl_syscall (msqid, cmd, (msgctl_arg_t *) buf);
254}
255compat_symbol (libc, __msgctl_mode16, msgctl, GLIBC_2_2);
256#endif
257
258#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
259struct __old_msqid_ds
260{
261 struct __old_ipc_perm msg_perm; /* structure describing operation permission */
262 struct msg *__msg_first; /* pointer to first message on queue */
263 struct msg *__msg_last; /* pointer to last message on queue */
264 __time_t msg_stime; /* time of last msgsnd command */
265 __time_t msg_rtime; /* time of last msgrcv command */
266 __time_t msg_ctime; /* time of last change */
267 struct wait_queue *__wwait; /* ??? */
268 struct wait_queue *__rwait; /* ??? */
269 unsigned short int __msg_cbytes; /* current number of bytes on queue */
270 unsigned short int msg_qnum; /* number of messages currently on queue */
271 unsigned short int msg_qbytes; /* max number of bytes allowed on queue */
272 __ipc_pid_t msg_lspid; /* pid of last msgsnd() */
273 __ipc_pid_t msg_lrpid; /* pid of last msgrcv() */
274};
275
276int
277attribute_compat_text_section
278__old_msgctl (int msqid, int cmd, struct __old_msqid_ds *buf)
279{
280#if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
281 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
282 /* For architecture that have wire-up msgctl but also have __IPC_64 to a
283 value different than default (0x0) it means the compat symbol used the
284 __NR_ipc syscall. */
285 return INLINE_SYSCALL_CALL (msgctl, msqid, cmd, buf);
286#else
287 return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd, 0, buf);
288#endif
289}
290compat_symbol (libc, __old_msgctl, msgctl, GLIBC_2_0);
291#endif
292

source code of glibc/sysdeps/unix/sysv/linux/msgctl.c