1 /* Copyright (C) 1995-2021 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.
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.
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.
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/>. */
23 #include <shlib-compat.h>
24 #include <bits/types/struct_semid64_ds.h> /* For __semid64_ds. */
25 #include <linux/posix_types.h> /* For __kernel_mode_t. */
27 /* The struct used to issue the syscall. For architectures that assume
28 64-bit time as default (!__ASSUME_TIME64_SYSCALLS) the syscall will
29 split the resulting 64-bit sem_{o,c}time in two fields (sem_{o,c}time
30 and __sem_{o,c}time_high). */
33 int val
; /* value for SETVAL */
34 struct semid_ds
*buf
; /* buffer for IPC_STAT & IPC_SET */
35 unsigned short int *array
; /* array for GETALL & SETALL */
36 struct seminfo
*__buf
; /* buffer for IPC_INFO */
40 # define semun64 semun
41 typedef union semun semctl_arg_t
;
43 # include <struct_kernel_semid64_ds.h>
48 struct kernel_semid64_ds
*buf
;
49 unsigned short int *array
;
50 struct seminfo
*__buf
;
54 # define semun64 semun
56 /* The struct used when __semctl64 is called. */
60 struct __semid64_ds
*buf
;
61 unsigned short int *array
;
62 struct seminfo
*__buf
;
67 semid64_to_ksemid64 (const struct __semid64_ds
*semid64
,
68 struct kernel_semid64_ds
*ksemid
)
70 ksemid
->sem_perm
= semid64
->sem_perm
;
71 ksemid
->sem_otime
= semid64
->sem_otime
;
72 ksemid
->sem_otime_high
= semid64
->sem_otime
>> 32;
73 ksemid
->sem_ctime
= semid64
->sem_ctime
;
74 ksemid
->sem_ctime_high
= semid64
->sem_ctime
>> 32;
75 ksemid
->sem_nsems
= semid64
->sem_nsems
;
79 ksemid64_to_semid64 (const struct kernel_semid64_ds
*ksemid
,
80 struct __semid64_ds
*semid64
)
82 semid64
->sem_perm
= ksemid
->sem_perm
;
83 semid64
->sem_otime
= ksemid
->sem_otime
84 | ((__time64_t
) ksemid
->sem_otime_high
<< 32);
85 semid64
->sem_ctime
= ksemid
->sem_ctime
86 | ((__time64_t
) ksemid
->sem_ctime_high
<< 32);
87 semid64
->sem_nsems
= ksemid
->sem_nsems
;
91 semun64_to_ksemun64 (int cmd
, union semun64 semun64
,
92 struct kernel_semid64_ds
*buf
)
94 union ksemun64 r
= { 0 };
102 r
.array
= semun64
.array
;
109 semid64_to_ksemid64 (semun64
.buf
, r
.buf
);
113 r
.__buf
= semun64
.__buf
;
119 typedef union ksemun64 semctl_arg_t
;
123 semctl_syscall (int semid
, int semnum
, int cmd
, semctl_arg_t arg
)
125 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
126 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
| __IPC_64
,
129 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
| __IPC_64
,
130 SEMCTL_ARG_ADDRESS (arg
));
134 /* POSIX states ipc_perm mode should have type of mode_t. */
135 _Static_assert (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
137 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
140 __semctl64 (int semid
, int semnum
, int cmd
, ...)
142 union semun64 arg64
= { 0 };
145 /* Get the argument only if required. */
148 case SETVAL
: /* arg.val */
149 case GETALL
: /* arg.array */
151 case IPC_STAT
: /* arg.buf */
155 case IPC_INFO
: /* arg.__buf */
158 arg64
= va_arg (ap
, union semun64
);
161 case IPC_RMID
: /* arg ignored. */
168 __set_errno (EINVAL
);
173 struct kernel_semid64_ds ksemid
;
174 union ksemun64 ksemun
= semun64_to_ksemun64 (cmd
, arg64
, &ksemid
);
175 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
177 ksemid
.sem_perm
.mode
*= 0x10000U
;
179 union ksemun64 arg
= ksemun
;
181 union semun arg
= arg64
;
184 int ret
= semctl_syscall (semid
, semnum
, cmd
, arg
);
193 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
194 arg
.buf
->sem_perm
.mode
>>= 16;
196 /* Old Linux kernel versions might not clear the mode padding. */
197 if (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
198 != sizeof (__kernel_mode_t
))
199 arg
.buf
->sem_perm
.mode
&= 0xFFFF;
203 ksemid64_to_semid64 (arg
.buf
, arg64
.buf
);
210 libc_hidden_def (__semctl64
)
213 /* The 64-bit time_t semid_ds version might have a different layout and
214 internal field alignment. */
217 semid_to_semid64 (struct __semid64_ds
*ds64
, const struct semid_ds
*ds
)
219 ds64
->sem_perm
= ds
->sem_perm
;
220 ds64
->sem_otime
= ds
->sem_otime
221 | ((__time64_t
) ds
->__sem_otime_high
<< 32);
222 ds64
->sem_ctime
= ds
->sem_ctime
223 | ((__time64_t
) ds
->__sem_ctime_high
<< 32);
224 ds64
->sem_nsems
= ds
->sem_nsems
;
228 semid64_to_semid (struct semid_ds
*ds
, const struct __semid64_ds
*ds64
)
230 ds
->sem_perm
= ds64
->sem_perm
;
231 ds
->sem_otime
= ds64
->sem_otime
;
232 ds
->__sem_otime_high
= 0;
233 ds
->sem_ctime
= ds64
->sem_ctime
;
234 ds
->__sem_ctime_high
= 0;
235 ds
->sem_nsems
= ds64
->sem_nsems
;
239 semun_to_semun64 (int cmd
, union semun semun
, struct __semid64_ds
*semid64
)
241 union semun64 r
= { 0 };
249 r
.array
= semun
.array
;
256 semid_to_semid64 (r
.buf
, semun
.buf
);
260 r
.__buf
= semun
.__buf
;
267 __semctl (int semid
, int semnum
, int cmd
, ...)
269 union semun arg
= { 0 };
273 /* Get the argument only if required. */
276 case SETVAL
: /* arg.val */
277 case GETALL
: /* arg.array */
279 case IPC_STAT
: /* arg.buf */
283 case IPC_INFO
: /* arg.__buf */
286 arg
= va_arg (ap
, union semun
);
289 /* __semctl64 handles non-supported commands. */
292 struct __semid64_ds semid64
;
293 union semun64 arg64
= semun_to_semun64 (cmd
, arg
, &semid64
);
295 int ret
= __semctl64 (semid
, semnum
, cmd
, arg64
);
304 semid64_to_semid (arg
.buf
, arg64
.buf
);
311 #ifndef DEFAULT_VERSION
312 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
313 # define DEFAULT_VERSION GLIBC_2_2
315 # define DEFAULT_VERSION GLIBC_2_31
318 versioned_symbol (libc
, __semctl
, semctl
, DEFAULT_VERSION
);
320 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
321 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
323 attribute_compat_text_section
324 __semctl_mode16 (int semid
, int semnum
, int cmd
, ...)
326 semctl_arg_t arg
= { 0 };
329 /* Get the argument only if required. */
332 case SETVAL
: /* arg.val */
333 case GETALL
: /* arg.array */
335 case IPC_STAT
: /* arg.buf */
339 case IPC_INFO
: /* arg.__buf */
342 arg
= va_arg (ap
, semctl_arg_t
);
347 return semctl_syscall (semid
, semnum
, cmd
, arg
);
349 compat_symbol (libc
, __semctl_mode16
, semctl
, GLIBC_2_2
);
352 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
353 /* Since semctl use a variadic argument for semid_ds there is not need to
354 define and tie the compatibility symbol to the old 'union semun'
357 attribute_compat_text_section
358 __old_semctl (int semid
, int semnum
, int cmd
, ...)
360 union semun arg
= { 0 };
363 /* Get the argument only if required. */
366 case SETVAL
: /* arg.val */
367 case GETALL
: /* arg.array */
369 case IPC_STAT
: /* arg.buf */
373 case IPC_INFO
: /* arg.__buf */
376 arg
= va_arg (ap
, union semun
);
381 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
382 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
383 /* For architectures that have wire-up semctl but also have __IPC_64 to a
384 value different than default (0x0) it means the compat symbol used the
386 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
, arg
.array
);
388 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
,
389 SEMCTL_ARG_ADDRESS (arg
));
392 compat_symbol (libc
, __old_semctl
, semctl
, GLIBC_2_0
);