(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
ioctl.c
       1  /* Copyright (C) 1992-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 <errno.h>
      19  #include <libc-pointer-arith.h>
      20  #include <sys/ioctl.h>
      21  #include <hurd.h>
      22  #include <hurd/fd.h>
      23  #include <hurd/signal.h>
      24  #include <stdarg.h>
      25  #include <mach/notify.h>
      26  #include <assert.h>
      27  #include <string.h>
      28  #include <stdint.h>
      29  #include <hurd/ioctl.h>
      30  #include <mach/mig_support.h>
      31  #include <mach_rpc.h>
      32  #include <sysdep-cancel.h>
      33  
      34  #include <hurd/ioctls.defs>
      35  
      36  #define msg_align(x) ALIGN_UP (x, __alignof__ (uintptr_t))
      37  #define typesize(type)	(1 << (type))
      38  
      39  /* Perform the I/O control operation specified by REQUEST on FD.
      40     The actual type and use of ARG and the return value depend on REQUEST.  */
      41  int
      42  __ioctl (int fd, unsigned long int request, ...)
      43  {
      44  #ifdef MACH_MSG_TYPE_BIT
      45    /* Map individual type fields to Mach IPC types.  */
      46    static const int mach_types[] =
      47      { MACH_MSG_TYPE_CHAR, MACH_MSG_TYPE_INTEGER_16, MACH_MSG_TYPE_INTEGER_32,
      48        MACH_MSG_TYPE_INTEGER_64 };
      49  #define io2mach_type(count, type)   \
      50    ((mach_msg_type_t) {		    \
      51     .msgt_name = mach_types[type],   \
      52     .msgt_size = typesize(type) * 8, \
      53     .msgt_number = count,	    \
      54     .msgt_inline = TRUE,		    \
      55     .msgt_longform = FALSE,	    \
      56     .msgt_deallocate = FALSE,	    \
      57     .msgt_unused = 0		    \
      58     })
      59  #endif
      60  
      61    /* Extract the type information encoded in the request.  */
      62    unsigned int type = _IOC_TYPE (request);
      63  
      64    /* Message buffer.  */
      65    struct
      66    {
      67  #ifdef MACH_MSG_TYPE_BIT
      68      union
      69      {
      70        mig_reply_header_t header;
      71        struct
      72        {
      73  	mach_msg_header_t	Head;
      74  	mach_msg_type_t		RetCodeType;
      75  	kern_return_t		RetCode;
      76        } header_typecheck;
      77      };
      78      char data[3 * sizeof (mach_msg_type_t)
      79  	      + msg_align (_IOT_COUNT0 (type) * typesize (_IOT_TYPE0 (type)))
      80  	      + msg_align (_IOT_COUNT1 (type) * typesize (_IOT_TYPE1 (type)))
      81  	      + msg_align (_IOT_COUNT2 (type) * typesize (_IOT_TYPE2 (type)))];
      82  #else  /* Untyped Mach IPC format.  */
      83      mig_reply_error_t header;
      84      char data[_IOT_COUNT0 (type) * typesize (_IOT_TYPE0 (type))
      85  	      + _IOT_COUNT1 (type) * typesize (_IOT_TYPE1 (type))
      86  	      + _IOT_COUNT2 (type) * typesize (_IOT_TYPE2 (type))];
      87      mach_msg_trailer_t trailer;
      88  #endif
      89    } msg;
      90    mach_msg_header_t *const m = &msg.header.Head;
      91    mach_msg_id_t msgid;
      92    unsigned int reply_size;
      93  #ifdef MACH_MSG_TYPE_BIT
      94    mach_msg_type_t *t;
      95  #else
      96    void *p;
      97  #endif
      98  
      99    void *arg = NULL;
     100  
     101    error_t err;
     102  
     103    /* Send the RPC already packed up in MSG to IOPORT
     104       and decode the return value.  */
     105    error_t send_rpc (io_t ioport)
     106      {
     107        error_t err;
     108  #ifdef MACH_MSG_TYPE_BIT
     109        mach_msg_type_t *t = &msg.header.RetCodeType;
     110  #else
     111        void *p = &msg.header.RetCode;
     112  #endif
     113  
     114        /* Marshal the request arguments into the message buffer.
     115  	 We must redo this work each time we retry the RPC after a SIGTTOU,
     116  	 because the reply message containing the EBACKGROUND error code
     117  	 clobbers the same message buffer also used for the request.  */
     118  
     119        if (_IOC_INOUT (request) & IOC_IN)
     120  	{
     121  	  /* We don't want to advance ARG since it will be used to copy out
     122  	     too if IOC_OUT is also set.  */
     123  	  void *argptr = arg;
     124  	  int zero = 0;
     125  
     126  	  if (request == TIOCFLUSH && !argptr)
     127  	    argptr = &zero;
     128  
     129  	  /* Pack an argument into the message buffer.  */
     130  	  void in (unsigned int count, enum __ioctl_datum type)
     131  	    {
     132  	      if (count > 0)
     133  		{
     134  		  const size_t len = count * typesize ((unsigned int) type);
     135  #ifdef MACH_MSG_TYPE_BIT
     136  		  void *p = &t[1];
     137  		  *t = io2mach_type (count, type);
     138  		  p = __mempcpy (p, argptr, len);
     139  		  p = (void *) msg_align ((uintptr_t) p);
     140  		  t = p;
     141  #else
     142  		  p = __mempcpy (p, argptr, len);
     143  #endif
     144  		  argptr += len;
     145  		}
     146  	    }
     147  
     148  	  /* Pack the argument data.  */
     149  	  in (_IOT_COUNT0 (type), _IOT_TYPE0 (type));
     150  	  in (_IOT_COUNT1 (type), _IOT_TYPE1 (type));
     151  	  in (_IOT_COUNT2 (type), _IOT_TYPE2 (type));
     152  	}
     153        else if (_IOC_INOUT (request) == IOC_VOID && _IOT_COUNT0 (type) != 0)
     154  	{
     155  	  /* The RPC takes a single integer_t argument.
     156  	     Rather than pointing to the value, ARG is the value itself.  */
     157  #ifdef MACH_MSG_TYPE_BIT
     158  	  *t++ = io2mach_type (1, _IOTS (integer_t));
     159  	  *(integer_t *) t = (integer_t) (intptr_t) arg;
     160  	  t = (void *) msg_align ((uintptr_t) t + sizeof (integer_t));
     161  #else
     162  	  *(integer_t *) p = (integer_t) (intptr_t) arg;
     163  	  p = (void *) p + sizeof (integer_t);
     164  #endif
     165  	}
     166  
     167        memset (m, 0, sizeof *m);	/* Clear unused fields.  */
     168        m->msgh_size = (
     169  #ifdef MACH_MSG_TYPE_BIT
     170  		      (char *) t
     171  #else
     172  		      (char *) p
     173  #endif
     174  		      - (char *) &msg);
     175        m->msgh_remote_port = ioport;
     176        m->msgh_local_port = __mig_get_reply_port ();
     177        m->msgh_id = msgid;
     178        m->msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
     179  				     MACH_MSG_TYPE_MAKE_SEND_ONCE);
     180        err = _hurd_intr_rpc_mach_msg (m, MACH_SEND_MSG|MACH_RCV_MSG,
     181  				     m->msgh_size, sizeof (msg),
     182  				     m->msgh_local_port,
     183  				     MACH_MSG_TIMEOUT_NONE,
     184  				     MACH_PORT_NULL);
     185        switch (err)
     186  	{
     187  	case MACH_MSG_SUCCESS:
     188  	  break;
     189  	case MACH_SEND_INVALID_REPLY:
     190  	case MACH_RCV_INVALID_NAME:
     191  	  __mig_dealloc_reply_port (m->msgh_local_port);
     192  	  /* Fall through.  */
     193  	default:
     194  	  return err;
     195  	}
     196  
     197        if ((m->msgh_bits & MACH_MSGH_BITS_COMPLEX))
     198  	{
     199  	  /* Allow no ports or VM.  */
     200  	  __mach_msg_destroy (m);
     201  	  /* Want to return a different error below for a different msgid.  */
     202  	  if (m->msgh_id == msgid + 100)
     203  	    return MIG_TYPE_ERROR;
     204  	}
     205  
     206        if (m->msgh_id != msgid + 100)
     207  	return (m->msgh_id == MACH_NOTIFY_SEND_ONCE
     208  		? MIG_SERVER_DIED : MIG_REPLY_MISMATCH);
     209  
     210        if (m->msgh_size != reply_size
     211  	  && m->msgh_size != sizeof msg.header)
     212  	return MIG_TYPE_ERROR;
     213  
     214  #ifdef MACH_MSG_TYPE_BIT
     215        mach_msg_type_t ipctype = io2mach_type(1, _IOTS (msg.header.RetCode));
     216        if (BAD_TYPECHECK (&msg.header_typecheck.RetCodeType, &ipctype))
     217  	return MIG_TYPE_ERROR;
     218  #endif
     219        return msg.header.RetCode;
     220      }
     221  
     222    if (_IOT_COUNT0 (type) != 0)
     223      {
     224        /* Data need either be sent, received, or even both.  */
     225        va_list ap;
     226  
     227        va_start (ap, request);
     228        arg = va_arg (ap, void *);
     229        va_end (ap);
     230      }
     231  
     232    {
     233      /* Check for a registered handler for REQUEST.  */
     234      ioctl_handler_t handler = _hurd_lookup_ioctl_handler (request);
     235      if (handler)
     236        {
     237  	/* This handler groks REQUEST.  Se lo puntamonos.  */
     238  	int save = errno;
     239  	int result = (*handler) (fd, request, arg);
     240  	if (result != -1 || errno != ENOTTY)
     241  	  return result;
     242  
     243  	/* The handler doesn't really grok this one.
     244  	   Try the normal RPC translation.  */
     245  	errno = save;
     246        }
     247    }
     248  
     249    /* Compute the Mach message ID for the RPC from the group and command
     250       parts of the ioctl request.  */
     251    msgid = IOC_MSGID (request);
     252  
     253    /* Compute the expected size of the reply.  There is a standard header
     254       consisting of the message header and the reply code.  Then, for out
     255       and in/out ioctls, there come the data with their type headers.  */
     256    reply_size = sizeof msg.header;
     257  
     258    if (_IOC_INOUT (request) & IOC_OUT)
     259      {
     260        inline void figure_reply (unsigned int count, enum __ioctl_datum type)
     261  	{
     262  	  if (count > 0)
     263  	    {
     264  #ifdef MACH_MSG_TYPE_BIT
     265  	      /* Add the size of the type and data.  */
     266  	      reply_size += sizeof (mach_msg_type_t) + typesize (type) * count;
     267  	      /* Align it to word size.  */
     268  	      reply_size = msg_align (reply_size);
     269  #else
     270  	      reply_size += typesize (type) * count;
     271  #endif
     272  	    }
     273  	}
     274        figure_reply (_IOT_COUNT0 (type), _IOT_TYPE0 (type));
     275        figure_reply (_IOT_COUNT1 (type), _IOT_TYPE1 (type));
     276        figure_reply (_IOT_COUNT2 (type), _IOT_TYPE2 (type));
     277      }
     278  
     279    /* Marshal the arguments into the request message and make the RPC.
     280       This wrapper function handles EBACKGROUND returns, turning them
     281       into either SIGTTOU or EIO.  */
     282    if (request == TIOCDRAIN)
     283      {
     284        /* This is a cancellation point.  */
     285        int cancel_oldtype = LIBC_CANCEL_ASYNC();
     286        err = HURD_DPORT_USE_CANCEL (fd, _hurd_ctty_output (port, ctty, send_rpc));
     287        LIBC_CANCEL_RESET (cancel_oldtype);
     288      }
     289    else
     290      err = HURD_DPORT_USE (fd, _hurd_ctty_output (port, ctty, send_rpc));
     291  
     292  #ifdef MACH_MSG_TYPE_BIT
     293    t = (mach_msg_type_t *) msg.data;
     294  #else
     295    p = (void *) msg.data;
     296  #endif
     297    switch (err)
     298      {
     299        /* Unpack the message buffer into the argument location.  */
     300        int out (unsigned int count, unsigned int type,
     301  	       void *store, void **update)
     302  	{
     303  	  if (count > 0)
     304  	    {
     305  	      const size_t len = count * typesize (type);
     306  #ifdef MACH_MSG_TYPE_BIT
     307  	      const mach_msg_type_t ipctype = io2mach_type(count, type);
     308  	      if (BAD_TYPECHECK (t, &ipctype))
     309  		return 1;
     310  	      ++t;
     311  	      memcpy (store, t, len);
     312  	      if (update != NULL)
     313  		*update += len;
     314  	      t = (mach_msg_type_t *) msg_align ((uintptr_t) t + len);
     315  #else
     316  	      memcpy (store, p, len);
     317  	      p += len;
     318  	      if (update != NULL)
     319  		*update += len;
     320  #endif
     321  	    }
     322  	  return 0;
     323  	}
     324  
     325      case 0:
     326        if (m->msgh_size != reply_size
     327  	  || ((_IOC_INOUT (request) & IOC_OUT)
     328  	      && (out (_IOT_COUNT0 (type), _IOT_TYPE0 (type), arg, &arg)
     329  		  || out (_IOT_COUNT1 (type), _IOT_TYPE1 (type), arg, &arg)
     330  		  || out (_IOT_COUNT2 (type), _IOT_TYPE2 (type), arg, &arg))))
     331  	return __hurd_fail (MIG_TYPE_ERROR);
     332        return 0;
     333  
     334      case MIG_BAD_ID:
     335      case EOPNOTSUPP:
     336        /* The server didn't understand the RPC.  */
     337        err = ENOTTY;
     338        /* Fall through.  */
     339      default:
     340        return __hurd_fail (err);
     341      }
     342  }
     343  
     344  libc_hidden_def (__ioctl)
     345  weak_alias (__ioctl, ioctl)