mips: FIx clone3 implementation (BZ 31325)
[glibc.git] / sysdeps / unix / sysv / linux / msgctl.c
blob3120cae0e218806afb1705595c8f3e2569c083ef
1 /* Copyright (C) 1995-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
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.
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.
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/>. */
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. */
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)");
30 #if __IPC_TIME64 == 0
31 typedef struct msqid_ds msgctl_arg_t;
32 #else
33 # include <struct_kernel_msqid64_ds.h>
35 static void
36 msqid64_to_kmsqid64 (const struct __msqid64_ds *msqid64,
37 struct kernel_msqid64_ds *kmsqid)
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;
53 static void
54 kmsqid64_to_msqid64 (const struct kernel_msqid64_ds *kmsqid,
55 struct __msqid64_ds *msqid64)
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;
71 typedef struct kernel_msqid64_ds msgctl_arg_t;
72 #endif
74 static int
75 msgctl_syscall (int msqid, int cmd, msgctl_arg_t *buf)
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
85 int
86 __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf)
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
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;
102 switch (cmd)
104 case IPC_RMID:
105 arg = NULL;
106 break;
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)
115 msqid64_to_kmsqid64 (buf, &ksemid);
116 arg = &ksemid;
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;
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;
134 default:
135 __set_errno (EINVAL);
136 return -1;
139 int ret = msgctl_syscall (msqid, cmd, arg);
140 if (ret < 0)
141 return ret;
143 switch (cmd)
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
157 # if __IPC_TIME64
158 kmsqid64_to_msqid64 (arg, buf);
159 # endif
162 return ret;
164 #else /* !IPC_CTL_NEED_TRANSLATION */
165 return msgctl_syscall (msqid, cmd, buf);
166 #endif
168 #if __TIMESIZE != 64
169 libc_hidden_def (__msgctl64)
171 static void
172 msqid_to_msqid64 (struct __msqid64_ds *mq64, const struct msqid_ds *mq)
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;
188 static void
189 msqid64_to_msqid (struct msqid_ds *mq, const struct __msqid64_ds *mq64)
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;
206 __msgctl (int msqid, int cmd, struct msqid_ds *buf)
208 struct __msqid64_ds msqid64, *buf64 = NULL;
209 if (buf != NULL)
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
217 msqid_to_msqid64 (&msqid64, buf);
218 buf64 = &msqid64;
222 int ret = __msgctl64 (msqid, cmd, buf64);
223 if (ret < 0)
224 return ret;
226 switch (cmd)
228 case IPC_STAT:
229 case MSG_STAT:
230 case MSG_STAT_ANY:
231 msqid64_to_msqid (buf, buf64);
234 return ret;
236 #endif
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
245 versioned_symbol (libc, __msgctl, msgctl, DEFAULT_VERSION);
247 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
248 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
250 attribute_compat_text_section
251 __msgctl_mode16 (int msqid, int cmd, struct msqid_ds *buf)
253 return msgctl_syscall (msqid, cmd, (msgctl_arg_t *) buf);
255 compat_symbol (libc, __msgctl_mode16, msgctl, GLIBC_2_2);
256 #endif
258 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
259 struct __old_msqid_ds
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() */
277 attribute_compat_text_section
278 __old_msgctl (int msqid, int cmd, struct __old_msqid_ds *buf)
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
290 compat_symbol (libc, __old_msgctl, msgctl, GLIBC_2_0);
291 #endif