(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
msgctl.c
       1  /* Copyright (C) 1995-2023 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/msg.h>
      19  #include <ipc_priv.h>
      20  #include <sysdep.h>
      21  #include <shlib-compat.h>
      22  #include <errno.h>
      23  #include <linux/posix_types.h>  /* For __kernel_mode_t.  */
      24  
      25  /* POSIX states ipc_perm mode should have type of mode_t.  */
      26  _Static_assert (sizeof ((struct msqid_ds){0}.msg_perm.mode)
      27  		== sizeof (mode_t),
      28  		"sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
      29  
      30  #if __IPC_TIME64 == 0
      31  typedef struct msqid_ds msgctl_arg_t;
      32  #else
      33  # include <struct_kernel_msqid64_ds.h>
      34  
      35  static void
      36  msqid64_to_kmsqid64 (const struct __msqid64_ds *msqid64,
      37  		     struct kernel_msqid64_ds *kmsqid)
      38  {
      39    kmsqid->msg_perm       = msqid64->msg_perm;
      40    kmsqid->msg_stime      = msqid64->msg_stime;
      41    kmsqid->msg_stime_high = msqid64->msg_stime >> 32;
      42    kmsqid->msg_rtime      = msqid64->msg_rtime;
      43    kmsqid->msg_rtime_high = msqid64->msg_rtime >> 32;
      44    kmsqid->msg_ctime      = msqid64->msg_ctime;
      45    kmsqid->msg_ctime_high = msqid64->msg_ctime >> 32;
      46    kmsqid->msg_cbytes     = msqid64->msg_cbytes;
      47    kmsqid->msg_qnum       = msqid64->msg_qnum;
      48    kmsqid->msg_qbytes     = msqid64->msg_qbytes;
      49    kmsqid->msg_lspid      = msqid64->msg_lspid;
      50    kmsqid->msg_lrpid      = msqid64->msg_lrpid;
      51  }
      52  
      53  static void
      54  kmsqid64_to_msqid64 (const struct kernel_msqid64_ds *kmsqid,
      55  		     struct __msqid64_ds *msqid64)
      56  {
      57    msqid64->msg_perm   = kmsqid->msg_perm;
      58    msqid64->msg_stime  = kmsqid->msg_stime
      59  		        | ((__time64_t) kmsqid->msg_stime_high << 32);
      60    msqid64->msg_rtime  = kmsqid->msg_rtime
      61  		        | ((__time64_t) kmsqid->msg_rtime_high << 32);
      62    msqid64->msg_ctime  = kmsqid->msg_ctime
      63  		        | ((__time64_t) kmsqid->msg_ctime_high << 32);
      64    msqid64->msg_cbytes = kmsqid->msg_cbytes;
      65    msqid64->msg_qnum   = kmsqid->msg_qnum;
      66    msqid64->msg_qbytes = kmsqid->msg_qbytes;
      67    msqid64->msg_lspid  = kmsqid->msg_lspid;
      68    msqid64->msg_lrpid  = kmsqid->msg_lrpid;
      69  }
      70  
      71  typedef struct kernel_msqid64_ds msgctl_arg_t;
      72  #endif
      73  
      74  static int
      75  msgctl_syscall (int msqid, int cmd, msgctl_arg_t *buf)
      76  {
      77  #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
      78    return INLINE_SYSCALL_CALL (msgctl, msqid, cmd | __IPC_64, buf);
      79  #else
      80    return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd | __IPC_64, 0,
      81  			      buf);
      82  #endif
      83  }
      84  
      85  int
      86  __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf)
      87  {
      88  #if IPC_CTL_NEED_TRANSLATION
      89  # if __IPC_TIME64
      90    struct kernel_msqid64_ds ksemid, *arg = NULL;
      91  # else
      92    msgctl_arg_t *arg;
      93  # endif
      94  
      95    /* Some applications pass the __IPC_64 flag in cmd, to invoke
      96       previously unsupported commands back when there was no EINVAL
      97       error checking in glibc.  Mask the flag for the switch statements
      98       below.  msgctl_syscall adds back the __IPC_64 flag for the actual
      99       system call.  */
     100    cmd &= ~__IPC_64;
     101  
     102    switch (cmd)
     103      {
     104      case IPC_RMID:
     105        arg = NULL;
     106        break;
     107  
     108      case IPC_SET:
     109      case IPC_STAT:
     110      case MSG_STAT:
     111      case MSG_STAT_ANY:
     112  # if __IPC_TIME64
     113        if (buf != NULL)
     114  	{
     115  	  msqid64_to_kmsqid64 (buf, &ksemid);
     116  	  arg = &ksemid;
     117  	}
     118  #  ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     119        if (cmd == IPC_SET)
     120  	arg->msg_perm.mode *= 0x10000U;
     121  #  endif
     122  # else
     123        arg = buf;
     124  # endif
     125        break;
     126  
     127      case IPC_INFO:
     128      case MSG_INFO:
     129        /* This is a Linux extension where kernel returns a 'struct msginfo'
     130  	 instead.  */
     131        arg = (__typeof__ (arg)) buf;
     132        break;
     133  
     134      default:
     135        __set_errno (EINVAL);
     136        return -1;
     137      }
     138  
     139    int ret = msgctl_syscall (msqid, cmd, arg);
     140    if (ret < 0)
     141      return ret;
     142  
     143    switch (cmd)
     144      {
     145      case IPC_STAT:
     146      case MSG_STAT:
     147      case MSG_STAT_ANY:
     148  # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
     149        arg->msg_perm.mode >>= 16;
     150  # else
     151        /* Old Linux kernel versions might not clear the mode padding.  */
     152        if (sizeof ((struct msqid_ds){0}.msg_perm.mode)
     153            != sizeof (__kernel_mode_t))
     154  	arg->msg_perm.mode &= 0xFFFF;
     155  # endif
     156  
     157  # if __IPC_TIME64
     158        kmsqid64_to_msqid64 (arg, buf);
     159  # endif
     160      }
     161  
     162    return ret;
     163  
     164  #else /* !IPC_CTL_NEED_TRANSLATION */
     165    return msgctl_syscall (msqid, cmd, buf);
     166  #endif
     167  }
     168  #if __TIMESIZE != 64
     169  libc_hidden_def (__msgctl64)
     170  
     171  static void
     172  msqid_to_msqid64 (struct __msqid64_ds *mq64, const struct msqid_ds *mq)
     173  {
     174    mq64->msg_perm   = mq->msg_perm;
     175    mq64->msg_stime  = mq->msg_stime
     176  		     | ((__time64_t) mq->__msg_stime_high << 32);
     177    mq64->msg_rtime  = mq->msg_rtime
     178  		     | ((__time64_t) mq->__msg_rtime_high << 32);
     179    mq64->msg_ctime  = mq->msg_ctime
     180  		     | ((__time64_t) mq->__msg_ctime_high << 32);
     181    mq64->msg_cbytes = mq->msg_cbytes;
     182    mq64->msg_qnum   = mq->msg_qnum;
     183    mq64->msg_qbytes = mq->msg_qbytes;
     184    mq64->msg_lspid  = mq->msg_lspid;
     185    mq64->msg_lrpid  = mq->msg_lrpid;
     186  }
     187  
     188  static void
     189  msqid64_to_msqid (struct msqid_ds *mq, const struct __msqid64_ds *mq64)
     190  {
     191    mq->msg_perm         = mq64->msg_perm;
     192    mq->msg_stime        = mq64->msg_stime;
     193    mq->__msg_stime_high = 0;
     194    mq->msg_rtime        = mq64->msg_rtime;
     195    mq->__msg_rtime_high = 0;
     196    mq->msg_ctime        = mq64->msg_ctime;
     197    mq->__msg_ctime_high = 0;
     198    mq->msg_cbytes       = mq64->msg_cbytes;
     199    mq->msg_qnum         = mq64->msg_qnum;
     200    mq->msg_qbytes       = mq64->msg_qbytes;
     201    mq->msg_lspid        = mq64->msg_lspid;
     202    mq->msg_lrpid        = mq64->msg_lrpid;
     203  }
     204  
     205  int
     206  __msgctl (int msqid, int cmd, struct msqid_ds *buf)
     207  {
     208    struct __msqid64_ds msqid64, *buf64 = NULL;
     209    if (buf != NULL)
     210      {
     211        /* This is a Linux extension where kernel returns a 'struct msginfo'
     212  	 instead.  */
     213        if (cmd == IPC_INFO || cmd == MSG_INFO)
     214  	buf64 = (struct __msqid64_ds *) buf;
     215        else
     216  	{
     217  	  msqid_to_msqid64 (&msqid64, buf);
     218  	  buf64 = &msqid64;
     219  	}
     220      }
     221  
     222    int ret = __msgctl64 (msqid, cmd, buf64);
     223    if (ret < 0)
     224      return ret;
     225  
     226    switch (cmd)
     227      {
     228      case IPC_STAT:
     229      case MSG_STAT:
     230      case MSG_STAT_ANY:
     231        msqid64_to_msqid (buf, buf64);
     232      }
     233  
     234    return ret;
     235  }
     236  #endif
     237  
     238  #ifndef DEFAULT_VERSION
     239  # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
     240  #  define DEFAULT_VERSION GLIBC_2_2
     241  # else
     242  #  define DEFAULT_VERSION GLIBC_2_31
     243  # endif
     244  #endif
     245  versioned_symbol (libc, __msgctl, msgctl, DEFAULT_VERSION);
     246  
     247  #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
     248      && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
     249  int
     250  attribute_compat_text_section
     251  __msgctl_mode16 (int msqid, int cmd, struct msqid_ds *buf)
     252  {
     253    return msgctl_syscall (msqid, cmd, (msgctl_arg_t *) buf);
     254  }
     255  compat_symbol (libc, __msgctl_mode16, msgctl, GLIBC_2_2);
     256  #endif
     257  
     258  #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
     259  struct __old_msqid_ds
     260  {
     261    struct __old_ipc_perm msg_perm;	/* structure describing operation permission */
     262    struct msg *__msg_first;		/* pointer to first message on queue */
     263    struct msg *__msg_last;		/* pointer to last message on queue */
     264    __time_t msg_stime;			/* time of last msgsnd command */
     265    __time_t msg_rtime;			/* time of last msgrcv command */
     266    __time_t msg_ctime;			/* time of last change */
     267    struct wait_queue *__wwait;		/* ??? */
     268    struct wait_queue *__rwait;		/* ??? */
     269    unsigned short int __msg_cbytes;	/* current number of bytes on queue */
     270    unsigned short int msg_qnum;		/* number of messages currently on queue */
     271    unsigned short int msg_qbytes;	/* max number of bytes allowed on queue */
     272    __ipc_pid_t msg_lspid;		/* pid of last msgsnd() */
     273    __ipc_pid_t msg_lrpid;		/* pid of last msgrcv() */
     274  };
     275  
     276  int
     277  attribute_compat_text_section
     278  __old_msgctl (int msqid, int cmd, struct __old_msqid_ds *buf)
     279  {
     280  #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
     281      && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
     282    /* For architecture that have wire-up msgctl but also have __IPC_64 to a
     283       value different than default (0x0) it means the compat symbol used the
     284       __NR_ipc syscall.  */
     285    return INLINE_SYSCALL_CALL (msgctl, msqid, cmd, buf);
     286  #else
     287    return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd, 0, buf);
     288  #endif
     289  }
     290  compat_symbol (libc, __old_msgctl, msgctl, GLIBC_2_0);
     291  #endif