(root)/
glibc-2.38/
hurd/
dtable.c
       1  /* Copyright (C) 1991-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 <hurd.h>
      19  #include <hurd/term.h>
      20  #include <hurd/fd.h>
      21  #include <stdlib.h>
      22  #include <stdio.h>
      23  #include <fcntl.h>
      24  #include <limits.h>
      25  #include <lock-intern.h>	/* For `struct mutex'.  */
      26  #include "set-hooks.h"
      27  #include "hurdmalloc.h"		/* XXX */
      28  
      29  
      30  struct mutex _hurd_dtable_lock = MUTEX_INITIALIZER; /* XXX ld bug; must init */
      31  struct hurd_fd **_hurd_dtable;
      32  int _hurd_dtablesize;
      33  
      34  
      35  DEFINE_HOOK (_hurd_fd_subinit, (void));
      36  
      37  /* Initialize the file descriptor table at startup.  */
      38  
      39  static void attribute_used_retain
      40  init_dtable (void)
      41  {
      42    int i;
      43  
      44    __mutex_init (&_hurd_dtable_lock);
      45  
      46    /* The initial size of the descriptor table is that of the passed-in
      47       table.  It will be expanded as necessary up to _hurd_dtable_rlimit.  */
      48    _hurd_dtablesize = _hurd_init_dtablesize;
      49  
      50    /* Allocate the vector of pointers.  */
      51    _hurd_dtable = malloc (_hurd_dtablesize * sizeof (*_hurd_dtable));
      52    if (_hurd_dtablesize != 0 && _hurd_dtable == NULL)
      53      __libc_fatal ("hurd: Can't allocate file descriptor table\n");
      54  
      55    /* Initialize the descriptor table.  */
      56    for (i = 0; (unsigned int) i < _hurd_init_dtablesize; ++i)
      57      {
      58        if (_hurd_init_dtable[i] == MACH_PORT_NULL)
      59  	/* An unused descriptor is marked by a null pointer.  */
      60  	_hurd_dtable[i] = NULL;
      61        else
      62  	{
      63  	  int copy;
      64  	  /* Allocate a new file descriptor structure.  */
      65  	  struct hurd_fd *new = malloc (sizeof (struct hurd_fd));
      66  	  if (new == NULL)
      67  	    __libc_fatal ("hurd: Can't allocate initial file descriptors\n");
      68  
      69  	  /* See if this file descriptor is the same as a previous one we have
      70  	     already installed.  In this case, we can just copy over the same
      71  	     ctty port without making any more RPCs.  We only check the the
      72  	     immediately preceding fd and fd 0 -- this should be enough to
      73  	     handle the common cases while not requiring quadratic
      74  	     complexity.  */
      75  	  if (i > 0 && _hurd_init_dtable[i] == _hurd_init_dtable[i - 1])
      76  	    copy = i - 1;
      77  	  else if (i > 0 && _hurd_init_dtable[i] == _hurd_init_dtable[0])
      78  	    copy = 0;
      79  	  else
      80  	    copy = -1;
      81  
      82  	  if (copy < 0)
      83  	    {
      84  	      /* Initialize the port cells.  */
      85  	      _hurd_port_init (&new->port, MACH_PORT_NULL);
      86  	      _hurd_port_init (&new->ctty, MACH_PORT_NULL);
      87  
      88  	      /* Install the port in the descriptor.
      89  	         This sets up all the ctty magic.  */
      90  	      _hurd_port2fd (new, _hurd_init_dtable[i], 0);
      91  	    }
      92  	  else
      93  	    {
      94  	      /* Copy over ctty from the already set up file descriptor that
      95  	         contains the same port.  We can access the contents of the
      96  	         cell without any locking since no one could have seen it
      97  	         yet.  */
      98  	      mach_port_t ctty = _hurd_dtable[copy]->ctty.port;
      99  
     100  	      if (MACH_PORT_VALID (ctty))
     101  	        __mach_port_mod_refs (__mach_task_self (), ctty,
     102  	                              MACH_PORT_RIGHT_SEND, +1);
     103  
     104  	      _hurd_port_init (&new->port, _hurd_init_dtable[i]);
     105  	      _hurd_port_init (&new->ctty, ctty);
     106  	    }
     107  
     108  	  _hurd_dtable[i] = new;
     109  	}
     110      }
     111  
     112    /* Clear out the initial descriptor table.
     113       Everything must use _hurd_dtable now.  */
     114    __vm_deallocate (__mach_task_self (),
     115  		   (vm_address_t) _hurd_init_dtable,
     116  		   _hurd_init_dtablesize * sizeof (_hurd_init_dtable[0]));
     117    _hurd_init_dtable = NULL;
     118    _hurd_init_dtablesize = 0;
     119  
     120    /* Initialize the remaining empty slots in the table.  */
     121    for (; i < _hurd_dtablesize; ++i)
     122      _hurd_dtable[i] = NULL;
     123  
     124    /* Run things that want to run after the file descriptor table
     125       is initialized.  */
     126    RUN_RELHOOK (_hurd_fd_subinit, ());
     127  }
     128  
     129  SET_RELHOOK (_hurd_subinit, init_dtable);
     130  
     131  /* XXX when the linker supports it, the following functions should all be
     132     elsewhere and just have text_set_elements here.  */
     133  
     134  /* Called by `getdport' to do its work.  */
     135  
     136  static file_t
     137  get_dtable_port (int fd)
     138  {
     139    struct hurd_fd *d = _hurd_fd_get (fd);
     140    file_t dport;
     141  
     142    if (!d)
     143      return __hurd_fail (EBADF), MACH_PORT_NULL;
     144  
     145    HURD_CRITICAL_BEGIN;
     146  
     147    dport = HURD_PORT_USE (&d->port,
     148  			 ({
     149  			   error_t err;
     150  			   mach_port_t outport;
     151  			   err = __mach_port_mod_refs (__mach_task_self (),
     152  						       port,
     153  						       MACH_PORT_RIGHT_SEND,
     154  						       1);
     155  			   if (err)
     156  			     {
     157  			       errno = err;
     158  			       outport = MACH_PORT_NULL;
     159  			     }
     160  			   else
     161  			     outport = port;
     162  			   outport;
     163  			 }));
     164  
     165    HURD_CRITICAL_END;
     166  
     167    return dport;
     168  }
     169  
     170  file_t (*_hurd_getdport_fn) (int fd) = get_dtable_port;
     171  
     172  #include <hurd/signal.h>
     173  
     174  /* We are in the child fork; the dtable lock is still held.
     175     The parent has inserted send rights for all the normal io ports,
     176     but we must recover ctty-special ports for ourselves.  */
     177  static error_t
     178  fork_child_dtable (void)
     179  {
     180    error_t err;
     181    int i;
     182  
     183    err = 0;
     184  
     185    for (i = 0; !err && i < _hurd_dtablesize; ++i)
     186      {
     187        struct hurd_fd *d = _hurd_dtable[i];
     188        if (d == NULL)
     189  	continue;
     190  
     191        /* No other thread is using the send rights in the child task.  */
     192        d->port.users = d->ctty.users = NULL;
     193  
     194        if (d->ctty.port != MACH_PORT_NULL)
     195  	{
     196  	  /* There was a ctty-special port in the parent.
     197  	     We need to get one for ourselves too.  */
     198  	  __mach_port_deallocate (__mach_task_self (), d->ctty.port);
     199  	  err = __term_open_ctty (d->port.port, _hurd_pid, _hurd_pgrp,
     200  				  &d->ctty.port);
     201  	  if (err)
     202  	    d->ctty.port = MACH_PORT_NULL;
     203  	}
     204  
     205        /* XXX for each fd with a cntlmap, reauth and re-map_cntl.  */
     206      }
     207    return err;
     208  
     209    (void) &fork_child_dtable;	/* Avoid "defined but not used" warning.  */
     210  }
     211  
     212  data_set_element (_hurd_fork_locks, _hurd_dtable_lock);	/* XXX ld bug: bss */
     213  text_set_element (_hurd_fork_child_hook, fork_child_dtable);
     214  
     215  /* Called when our process group has changed.  */
     216  
     217  static void
     218  ctty_new_pgrp (void)
     219  {
     220    int i;
     221  
     222  retry:
     223    HURD_CRITICAL_BEGIN;
     224    __mutex_lock (&_hurd_dtable_lock);
     225  
     226    if (__USEPORT (CTTYID, port == MACH_PORT_NULL))
     227      {
     228        /* We have no controlling terminal.  If we haven't had one recently,
     229  	 but our pgrp is being pointlessly diddled anyway, then we will
     230  	 have nothing to do in the loop below because no fd will have a
     231  	 ctty port at all.
     232  
     233  	 More likely, a setsid call is responsible both for the change
     234  	 in pgrp and for clearing the cttyid port.  In that case, setsid
     235  	 held the dtable lock while updating the dtable to clear all the
     236  	 ctty ports, and ergo must have finished doing so before we run here.
     237  	 So we can be sure, again, that the loop below has no work to do.  */
     238      }
     239    else
     240      for (i = 0; i < _hurd_dtablesize; ++i)
     241        {
     242  	struct hurd_fd *const d = _hurd_dtable[i];
     243  	struct hurd_userlink ulink, ctty_ulink;
     244  	io_t port, ctty;
     245  
     246  	if (d == NULL)
     247  	  /* Nothing to do for an unused descriptor cell.  */
     248  	  continue;
     249  
     250  	port = _hurd_port_get (&d->port, &ulink);
     251  	ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
     252  
     253  	if (ctty != MACH_PORT_NULL)
     254  	  {
     255  	    /* This fd has a ctty-special port.  We need a new one, to tell
     256  	       the io server of our different process group.  */
     257  	    io_t new;
     258  	    error_t err;
     259  	    if ((err = __term_open_ctty (port, _hurd_pid, _hurd_pgrp, &new)))
     260  	      {
     261  		if (err == EINTR)
     262  		  {
     263  		    /* Got a signal while inside an RPC of the critical section, retry again */
     264  		    __mutex_unlock (&_hurd_dtable_lock);
     265  		    HURD_CRITICAL_UNLOCK;
     266  		    goto retry;
     267  		  }
     268  		new = MACH_PORT_NULL;
     269  	      }
     270  	    _hurd_port_set (&d->ctty, new);
     271  	  }
     272  
     273  	_hurd_port_free (&d->port, &ulink, port);
     274  	_hurd_port_free (&d->ctty, &ctty_ulink, ctty);
     275        }
     276  
     277    __mutex_unlock (&_hurd_dtable_lock);
     278    HURD_CRITICAL_END;
     279  
     280    (void) &ctty_new_pgrp;	/* Avoid "defined but not used" warning.  */
     281  }
     282  
     283  text_set_element (_hurd_pgrp_changed_hook, ctty_new_pgrp);
     284  
     285  /* Called to reauthenticate the dtable when the auth port changes.  */
     286  
     287  static void
     288  reauth_dtable (void)
     289  {
     290    int i;
     291  
     292    HURD_CRITICAL_BEGIN;
     293    __mutex_lock (&_hurd_dtable_lock);
     294  
     295    for (i = 0; i < _hurd_dtablesize; ++i)
     296      {
     297        struct hurd_fd *const d = _hurd_dtable[i];
     298        mach_port_t new, newctty, ref;
     299  
     300        if (d == NULL)
     301  	/* Nothing to do for an unused descriptor cell.  */
     302  	continue;
     303  
     304        ref = __mach_reply_port ();
     305  
     306        /* Take the descriptor cell's lock.  */
     307        __spin_lock (&d->port.lock);
     308  
     309        /* Reauthenticate the descriptor's port.  */
     310        if (d->port.port != MACH_PORT_NULL
     311  	  && ! __io_reauthenticate (d->port.port,
     312  				    ref, MACH_MSG_TYPE_MAKE_SEND)
     313  	  && ! __USEPORT (AUTH, __auth_user_authenticate
     314  			  (port,
     315  			   ref, MACH_MSG_TYPE_MAKE_SEND,
     316  			   &new)))
     317  	{
     318  	  /* Replace the port in the descriptor cell
     319  	     with the newly reauthenticated port.  */
     320  
     321  	  if (d->ctty.port != MACH_PORT_NULL
     322  	      && ! __io_reauthenticate (d->ctty.port,
     323  					ref, MACH_MSG_TYPE_MAKE_SEND)
     324  	      && ! __USEPORT (AUTH, __auth_user_authenticate
     325  			      (port,
     326  			       ref, MACH_MSG_TYPE_MAKE_SEND,
     327  			       &newctty)))
     328  	    _hurd_port_set (&d->ctty, newctty);
     329  
     330  	  _hurd_port_locked_set (&d->port, new);
     331  	}
     332        else
     333  	/* Lost.  Leave this descriptor cell alone.  */
     334  	__spin_unlock (&d->port.lock);
     335  
     336        __mach_port_destroy (__mach_task_self (), ref);
     337      }
     338  
     339    __mutex_unlock (&_hurd_dtable_lock);
     340    HURD_CRITICAL_END;
     341  
     342    (void) &reauth_dtable;	/* Avoid "defined but not used" warning.  */
     343  }
     344  
     345  text_set_element (_hurd_reauth_hook, reauth_dtable);