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