initial commit
[glibc.git] / .pc / git-updates.diff / sysdeps / unix / sysv / linux / semctl.c
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/sem.h>
19 #include <stdarg.h>
20 #include <ipc_priv.h>
21 #include <sysdep.h>
22 #include <shlib-compat.h>
23 #include <linux/posix_types.h> /* For __kernel_mode_t. */
24
25 /* The struct used to issue the syscall. For architectures that assume
26 64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
27 split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
28 and __sem_{o,c}time_high). */
29 union semun
30 {
31 int val; /* value for SETVAL */
32 struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
33 unsigned short int *array; /* array for GETALL & SETALL */
34 struct seminfo *__buf; /* buffer for IPC_INFO */
35 };
36
37 #if __IPC_TIME64 == 0
38 # define semun64 semun
39 typedef union semun semctl_arg_t;
40 #else
41 # include <struct_kernel_semid64_ds.h>
42
43 union ksemun64
44 {
45 int val;
46 struct kernel_semid64_ds *buf;
47 unsigned short int *array;
48 struct seminfo *__buf;
49 };
50
51 # if __TIMESIZE == 64
52 # define semun64 semun
53 # else
54 /* The struct used when __semctl64 is called. */
55 union semun64
56 {
57 int val;
58 struct __semid64_ds *buf;
59 unsigned short int *array;
60 struct seminfo *__buf;
61 };
62 # endif
63
64 static void
65 semid64_to_ksemid64 (const struct __semid64_ds *semid64,
66 struct kernel_semid64_ds *ksemid)
67 {
68 ksemid->sem_perm = semid64->sem_perm;
69 ksemid->sem_otime = semid64->sem_otime;
70 ksemid->sem_otime_high = semid64->sem_otime >> 32;
71 ksemid->sem_ctime = semid64->sem_ctime;
72 ksemid->sem_ctime_high = semid64->sem_ctime >> 32;
73 ksemid->sem_nsems = semid64->sem_nsems;
74 }
75
76 static void
77 ksemid64_to_semid64 (const struct kernel_semid64_ds *ksemid,
78 struct __semid64_ds *semid64)
79 {
80 semid64->sem_perm = ksemid->sem_perm;
81 semid64->sem_otime = ksemid->sem_otime
82 | ((__time64_t) ksemid->sem_otime_high << 32);
83 semid64->sem_ctime = ksemid->sem_ctime
84 | ((__time64_t) ksemid->sem_ctime_high << 32);
85 semid64->sem_nsems = ksemid->sem_nsems;
86 }
87
88 static union ksemun64
89 semun64_to_ksemun64 (int cmd, union semun64 semun64,
90 struct kernel_semid64_ds *buf)
91 {
92 union ksemun64 r = { 0 };
93 switch (cmd)
94 {
95 case SETVAL:
96 r.val = semun64.val;
97 break;
98 case GETALL:
99 case SETALL:
100 r.array = semun64.array;
101 break;
102 case SEM_STAT:
103 case SEM_STAT_ANY:
104 case IPC_STAT:
105 case IPC_SET:
106 r.buf = buf;
107 semid64_to_ksemid64 (semun64.buf, r.buf);
108 break;
109 case IPC_INFO:
110 case SEM_INFO:
111 r.__buf = semun64.__buf;
112 break;
113 }
114 return r;
115 }
116
117 typedef union ksemun64 semctl_arg_t;
118 #endif
119
120 static int
121 semctl_syscall (int semid, int semnum, int cmd, semctl_arg_t arg)
122 {
123 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
124 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
125 arg.array);
126 #else
127 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
128 SEMCTL_ARG_ADDRESS (arg));
129 #endif
130 }
131
132 /* POSIX states ipc_perm mode should have type of mode_t. */
133 _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
134 == sizeof (mode_t),
135 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
136
137 int
138 __semctl64 (int semid, int semnum, int cmd, ...)
139 {
140 union semun64 arg64 = { 0 };
141 va_list ap;
142
143 /* Get the argument only if required. */
144 switch (cmd)
145 {
146 case SETVAL: /* arg.val */
147 case GETALL: /* arg.array */
148 case SETALL:
149 case IPC_STAT: /* arg.buf */
150 case IPC_SET:
151 case SEM_STAT:
152 case SEM_STAT_ANY:
153 case IPC_INFO: /* arg.__buf */
154 case SEM_INFO:
155 va_start (ap, cmd);
156 arg64 = va_arg (ap, union semun64);
157 va_end (ap);
158 break;
159 case IPC_RMID: /* arg ignored. */
160 case GETNCNT:
161 case GETPID:
162 case GETVAL:
163 case GETZCNT:
164 break;
165 default:
166 __set_errno (EINVAL);
167 return -1;
168 }
169
170 #if __IPC_TIME64
171 struct kernel_semid64_ds ksemid;
172 union ksemun64 ksemun = semun64_to_ksemun64 (cmd, arg64, &ksemid);
173 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
174 if (cmd == IPC_SET)
175 ksemid.sem_perm.mode *= 0x10000U;
176 # endif
177 union ksemun64 arg = ksemun;
178 #else
179 union semun arg = arg64;
180 #endif
181
182 int ret = semctl_syscall (semid, semnum, cmd, arg);
183 if (ret < 0)
184 return ret;
185
186 switch (cmd)
187 {
188 case IPC_STAT:
189 case SEM_STAT:
190 case SEM_STAT_ANY:
191 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
192 arg.buf->sem_perm.mode >>= 16;
193 #else
194 /* Old Linux kernel versions might not clear the mode padding. */
195 if (sizeof ((struct semid_ds){0}.sem_perm.mode)
196 != sizeof (__kernel_mode_t))
197 arg.buf->sem_perm.mode &= 0xFFFF;
198 #endif
199
200 #if __IPC_TIME64
201 ksemid64_to_semid64 (arg.buf, arg64.buf);
202 #endif
203 }
204
205 return ret;
206 }
207 #if __TIMESIZE != 64
208 libc_hidden_def (__semctl64)
209
210
211 /* The 64-bit time_t semid_ds version might have a different layout and
212 internal field alignment. */
213
214 static void
215 semid_to_semid64 (struct __semid64_ds *ds64, const struct semid_ds *ds)
216 {
217 ds64->sem_perm = ds->sem_perm;
218 ds64->sem_otime = ds->sem_otime
219 | ((__time64_t) ds->__sem_otime_high << 32);
220 ds64->sem_ctime = ds->sem_ctime
221 | ((__time64_t) ds->__sem_ctime_high << 32);
222 ds64->sem_nsems = ds->sem_nsems;
223 }
224
225 static void
226 semid64_to_semid (struct semid_ds *ds, const struct __semid64_ds *ds64)
227 {
228 ds->sem_perm = ds64->sem_perm;
229 ds->sem_otime = ds64->sem_otime;
230 ds->__sem_otime_high = 0;
231 ds->sem_ctime = ds64->sem_ctime;
232 ds->__sem_ctime_high = 0;
233 ds->sem_nsems = ds64->sem_nsems;
234 }
235
236 static union semun64
237 semun_to_semun64 (int cmd, union semun semun, struct __semid64_ds *semid64)
238 {
239 union semun64 r = { 0 };
240 switch (cmd)
241 {
242 case SETVAL:
243 r.val = semun.val;
244 break;
245 case GETALL:
246 case SETALL:
247 r.array = semun.array;
248 break;
249 case SEM_STAT:
250 case SEM_STAT_ANY:
251 case IPC_STAT:
252 case IPC_SET:
253 r.buf = semid64;
254 semid_to_semid64 (r.buf, semun.buf);
255 break;
256 case IPC_INFO:
257 case SEM_INFO:
258 r.__buf = semun.__buf;
259 break;
260 }
261 return r;
262 }
263
264 int
265 __semctl (int semid, int semnum, int cmd, ...)
266 {
267 union semun arg = { 0 };
268
269 va_list ap;
270
271 /* Get the argument only if required. */
272 switch (cmd)
273 {
274 case SETVAL: /* arg.val */
275 case GETALL: /* arg.array */
276 case SETALL:
277 case IPC_STAT: /* arg.buf */
278 case IPC_SET:
279 case SEM_STAT:
280 case SEM_STAT_ANY:
281 case IPC_INFO: /* arg.__buf */
282 case SEM_INFO:
283 va_start (ap, cmd);
284 arg = va_arg (ap, union semun);
285 va_end (ap);
286 break;
287 /* __semctl64 handles non-supported commands. */
288 }
289
290 struct __semid64_ds semid64;
291 union semun64 arg64 = semun_to_semun64 (cmd, arg, &semid64);
292
293 int ret = __semctl64 (semid, semnum, cmd, arg64);
294 if (ret < 0)
295 return ret;
296
297 switch (cmd)
298 {
299 case IPC_STAT:
300 case SEM_STAT:
301 case SEM_STAT_ANY:
302 semid64_to_semid (arg.buf, arg64.buf);
303 }
304
305 return ret;
306 }
307 #endif
308
309 #ifndef DEFAULT_VERSION
310 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
311 # define DEFAULT_VERSION GLIBC_2_2
312 # else
313 # define DEFAULT_VERSION GLIBC_2_31
314 # endif
315 #endif
316 versioned_symbol (libc, __semctl, semctl, DEFAULT_VERSION);
317
318 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
319 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
320 int
321 attribute_compat_text_section
322 __semctl_mode16 (int semid, int semnum, int cmd, ...)
323 {
324 semctl_arg_t arg = { 0 };
325 va_list ap;
326
327 /* Get the argument only if required. */
328 switch (cmd)
329 {
330 case SETVAL: /* arg.val */
331 case GETALL: /* arg.array */
332 case SETALL:
333 case IPC_STAT: /* arg.buf */
334 case IPC_SET:
335 case SEM_STAT:
336 case SEM_STAT_ANY:
337 case IPC_INFO: /* arg.__buf */
338 case SEM_INFO:
339 va_start (ap, cmd);
340 arg = va_arg (ap, semctl_arg_t);
341 va_end (ap);
342 break;
343 }
344
345 return semctl_syscall (semid, semnum, cmd, arg);
346 }
347 compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
348 #endif
349
350 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
351 /* Since semctl use a variadic argument for semid_ds there is not need to
352 define and tie the compatibility symbol to the old 'union semun'
353 definition. */
354 int
355 attribute_compat_text_section
356 __old_semctl (int semid, int semnum, int cmd, ...)
357 {
358 union semun arg = { 0 };
359 va_list ap;
360
361 /* Get the argument only if required. */
362 switch (cmd)
363 {
364 case SETVAL: /* arg.val */
365 case GETALL: /* arg.array */
366 case SETALL:
367 case IPC_STAT: /* arg.buf */
368 case IPC_SET:
369 case SEM_STAT:
370 case SEM_STAT_ANY:
371 case IPC_INFO: /* arg.__buf */
372 case SEM_INFO:
373 va_start (ap, cmd);
374 arg = va_arg (ap, union semun);
375 va_end (ap);
376 break;
377 }
378
379 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
380 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
381 /* For architectures that have wire-up semctl but also have __IPC_64 to a
382 value different than default (0x0) it means the compat symbol used the
383 __NR_ipc syscall. */
384 return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd, arg.array);
385 # else
386 return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd,
387 SEMCTL_ARG_ADDRESS (arg));
388 # endif
389 }
390 compat_symbol (libc, __old_semctl, semctl, GLIBC_2_0);
391 #endif