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/>. */
22 #include <shlib-compat.h>
23 #include <linux/posix_types.h> /* For __kernel_mode_t. */
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). */
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 */
38 # define semun64 semun
39 typedef union semun semctl_arg_t
;
41 # include <struct_kernel_semid64_ds.h>
46 struct kernel_semid64_ds
*buf
;
47 unsigned short int *array
;
48 struct seminfo
*__buf
;
52 # define semun64 semun
54 /* The struct used when __semctl64 is called. */
58 struct __semid64_ds
*buf
;
59 unsigned short int *array
;
60 struct seminfo
*__buf
;
65 semid64_to_ksemid64 (const struct __semid64_ds
*semid64
,
66 struct kernel_semid64_ds
*ksemid
)
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
;
77 ksemid64_to_semid64 (const struct kernel_semid64_ds
*ksemid
,
78 struct __semid64_ds
*semid64
)
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
;
89 semun64_to_ksemun64 (int cmd
, union semun64 semun64
,
90 struct kernel_semid64_ds
*buf
)
92 union ksemun64 r
= { 0 };
100 r
.array
= semun64
.array
;
107 semid64_to_ksemid64 (semun64
.buf
, r
.buf
);
111 r
.__buf
= semun64
.__buf
;
117 typedef union ksemun64 semctl_arg_t
;
121 semctl_syscall (int semid
, int semnum
, int cmd
, semctl_arg_t arg
)
123 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
124 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
| __IPC_64
,
127 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
| __IPC_64
,
128 SEMCTL_ARG_ADDRESS (arg
));
132 /* POSIX states ipc_perm mode should have type of mode_t. */
133 _Static_assert (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
135 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
138 __semctl64 (int semid
, int semnum
, int cmd
, ...)
140 union semun64 arg64
= { 0 };
143 /* Some applications pass the __IPC_64 flag in cmd, to invoke
144 previously unsupported commands back when there was no EINVAL
145 error checking in glibc. Mask the flag for the switch statements
146 below. semctl_syscall adds back the __IPC_64 flag for the actual
150 /* Get the argument only if required. */
153 case SETVAL
: /* arg.val */
154 case GETALL
: /* arg.array */
156 case IPC_STAT
: /* arg.buf */
160 case IPC_INFO
: /* arg.__buf */
163 arg64
= va_arg (ap
, union semun64
);
166 case IPC_RMID
: /* arg ignored. */
173 __set_errno (EINVAL
);
178 struct kernel_semid64_ds ksemid
;
179 union ksemun64 ksemun
= semun64_to_ksemun64 (cmd
, arg64
, &ksemid
);
180 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
182 ksemid
.sem_perm
.mode
*= 0x10000U
;
184 union ksemun64 arg
= ksemun
;
186 union semun arg
= arg64
;
189 int ret
= semctl_syscall (semid
, semnum
, cmd
, arg
);
198 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
199 arg
.buf
->sem_perm
.mode
>>= 16;
201 /* Old Linux kernel versions might not clear the mode padding. */
202 if (sizeof ((struct semid_ds
){0}.sem_perm
.mode
)
203 != sizeof (__kernel_mode_t
))
204 arg
.buf
->sem_perm
.mode
&= 0xFFFF;
208 ksemid64_to_semid64 (arg
.buf
, arg64
.buf
);
215 libc_hidden_def (__semctl64
)
218 /* The 64-bit time_t semid_ds version might have a different layout and
219 internal field alignment. */
222 semid_to_semid64 (struct __semid64_ds
*ds64
, const struct semid_ds
*ds
)
224 ds64
->sem_perm
= ds
->sem_perm
;
225 ds64
->sem_otime
= ds
->sem_otime
226 | ((__time64_t
) ds
->__sem_otime_high
<< 32);
227 ds64
->sem_ctime
= ds
->sem_ctime
228 | ((__time64_t
) ds
->__sem_ctime_high
<< 32);
229 ds64
->sem_nsems
= ds
->sem_nsems
;
233 semid64_to_semid (struct semid_ds
*ds
, const struct __semid64_ds
*ds64
)
235 ds
->sem_perm
= ds64
->sem_perm
;
236 ds
->sem_otime
= ds64
->sem_otime
;
237 ds
->__sem_otime_high
= 0;
238 ds
->sem_ctime
= ds64
->sem_ctime
;
239 ds
->__sem_ctime_high
= 0;
240 ds
->sem_nsems
= ds64
->sem_nsems
;
244 semun_to_semun64 (int cmd
, union semun semun
, struct __semid64_ds
*semid64
)
246 union semun64 r
= { 0 };
254 r
.array
= semun
.array
;
261 semid_to_semid64 (r
.buf
, semun
.buf
);
265 r
.__buf
= semun
.__buf
;
272 __semctl (int semid
, int semnum
, int cmd
, ...)
274 union semun arg
= { 0 };
278 /* Get the argument only if required. */
281 case SETVAL
: /* arg.val */
282 case GETALL
: /* arg.array */
284 case IPC_STAT
: /* arg.buf */
288 case IPC_INFO
: /* arg.__buf */
291 arg
= va_arg (ap
, union semun
);
294 /* __semctl64 handles non-supported commands. */
297 struct __semid64_ds semid64
;
298 union semun64 arg64
= semun_to_semun64 (cmd
, arg
, &semid64
);
300 int ret
= __semctl64 (semid
, semnum
, cmd
, arg64
);
309 semid64_to_semid (arg
.buf
, arg64
.buf
);
316 #ifndef DEFAULT_VERSION
317 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
318 # define DEFAULT_VERSION GLIBC_2_2
320 # define DEFAULT_VERSION GLIBC_2_31
323 versioned_symbol (libc
, __semctl
, semctl
, DEFAULT_VERSION
);
325 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
326 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
328 attribute_compat_text_section
329 __semctl_mode16 (int semid
, int semnum
, int cmd
, ...)
331 semctl_arg_t arg
= { 0 };
334 /* Get the argument only if required. */
337 case SETVAL
: /* arg.val */
338 case GETALL
: /* arg.array */
340 case IPC_STAT
: /* arg.buf */
344 case IPC_INFO
: /* arg.__buf */
347 arg
= va_arg (ap
, semctl_arg_t
);
352 return semctl_syscall (semid
, semnum
, cmd
, arg
);
354 compat_symbol (libc
, __semctl_mode16
, semctl
, GLIBC_2_2
);
357 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
358 /* Since semctl use a variadic argument for semid_ds there is not need to
359 define and tie the compatibility symbol to the old 'union semun'
362 attribute_compat_text_section
363 __old_semctl (int semid
, int semnum
, int cmd
, ...)
365 union semun arg
= { 0 };
368 /* Get the argument only if required. */
371 case SETVAL
: /* arg.val */
372 case GETALL
: /* arg.array */
374 case IPC_STAT
: /* arg.buf */
378 case IPC_INFO
: /* arg.__buf */
381 arg
= va_arg (ap
, union semun
);
386 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
387 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
388 /* For architectures that have wire-up semctl but also have __IPC_64 to a
389 value different than default (0x0) it means the compat symbol used the
391 return INLINE_SYSCALL_CALL (semctl
, semid
, semnum
, cmd
, arg
.array
);
393 return INLINE_SYSCALL_CALL (ipc
, IPCOP_semctl
, semid
, semnum
, cmd
,
394 SEMCTL_ARG_ADDRESS (arg
));
397 compat_symbol (libc
, __old_semctl
, semctl
, GLIBC_2_0
);