aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux/semctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/semctl.c')
-rw-r--r--sysdeps/unix/sysv/linux/semctl.c84
1 files changed, 77 insertions, 7 deletions
diff --git a/sysdeps/unix/sysv/linux/semctl.c b/sysdeps/unix/sysv/linux/semctl.c
index fbe4b4f11f..e7f48e4093 100644
--- a/sysdeps/unix/sysv/linux/semctl.c
+++ b/sysdeps/unix/sysv/linux/semctl.c
@@ -33,12 +33,33 @@ union semun
};
#ifndef DEFAULT_VERSION
-# define DEFAULT_VERSION GLIBC_2_2
+# ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
+# define DEFAULT_VERSION GLIBC_2_2
+# else
+# define DEFAULT_VERSION GLIBC_2_31
+# endif
#endif
+static int
+semctl_syscall (int semid, int semnum, int cmd, union semun arg)
+{
+#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
+ return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
+ arg.array);
+#else
+ return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
+ SEMCTL_ARG_ADDRESS (arg));
+#endif
+}
+
int
__new_semctl (int semid, int semnum, int cmd, ...)
{
+ /* POSIX states ipc_perm mode should have type of mode_t. */
+ _Static_assert (sizeof ((struct semid_ds){0}.sem_perm.mode)
+ == sizeof (mode_t),
+ "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
+
union semun arg = { 0 };
va_list ap;
@@ -59,16 +80,65 @@ __new_semctl (int semid, int semnum, int cmd, ...)
break;
}
-#ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
- return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
- arg.array);
-#else
- return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
- SEMCTL_ARG_ADDRESS (arg));
+#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
+ struct semid_ds tmpds;
+ if (cmd == IPC_SET)
+ {
+ tmpds = *arg.buf;
+ tmpds.sem_perm.mode *= 0x10000U;
+ arg.buf = &tmpds;
+ }
+#endif
+
+ int ret = semctl_syscall (semid, semnum, cmd, arg);
+
+#ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
+ if (ret >= 0)
+ {
+ switch (cmd)
+ {
+ case IPC_STAT:
+ case SEM_STAT:
+ case SEM_STAT_ANY:
+ arg.buf->sem_perm.mode >>= 16;
+ }
+ }
#endif
+
+ return ret;
}
versioned_symbol (libc, __new_semctl, semctl, DEFAULT_VERSION);
+#if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
+ && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
+int
+attribute_compat_text_section
+__semctl_mode16 (int semid, int semnum, int cmd, ...)
+{
+ union semun arg = { 0 };
+ va_list ap;
+
+ /* Get the argument only if required. */
+ switch (cmd)
+ {
+ case SETVAL: /* arg.val */
+ case GETALL: /* arg.array */
+ case SETALL:
+ case IPC_STAT: /* arg.buf */
+ case IPC_SET:
+ case SEM_STAT:
+ case IPC_INFO: /* arg.__buf */
+ case SEM_INFO:
+ va_start (ap, cmd);
+ arg = va_arg (ap, union semun);
+ va_end (ap);
+ break;
+ }
+
+ return semctl_syscall (semid, semnum, cmd, arg);
+}
+compat_symbol (libc, __semctl_mode16, semctl, GLIBC_2_2);
+#endif
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
/* Since semctl use a variadic argument for semid_ds there is not need to