| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- /* Copyright (C) 1991-2026 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
- #include <hurd.h>
- #include <hurd/term.h>
- #include <hurd/fd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <limits.h>
- #include <lock-intern.h> /* For `struct mutex'. */
- #include "set-hooks.h"
- #include "hurdmalloc.h" /* XXX */
- struct mutex _hurd_dtable_lock = MUTEX_INITIALIZER; /* XXX ld bug; must init */
- struct hurd_fd **_hurd_dtable;
- int _hurd_dtablesize;
- DEFINE_HOOK (_hurd_fd_subinit, (void));
- /* Initialize the file descriptor table at startup. */
- static void attribute_used_retain
- init_dtable (void)
- {
- int i;
- __mutex_init (&_hurd_dtable_lock);
- /* The initial size of the descriptor table is that of the passed-in
- table. It will be expanded as necessary up to _hurd_dtable_rlimit. */
- _hurd_dtablesize = _hurd_init_dtablesize;
- /* Allocate the vector of pointers. */
- _hurd_dtable = malloc (_hurd_dtablesize * sizeof (*_hurd_dtable));
- if (_hurd_dtablesize != 0 && _hurd_dtable == NULL)
- __libc_fatal ("hurd: Can't allocate file descriptor table\n");
- /* Initialize the descriptor table. */
- for (i = 0; (unsigned int) i < _hurd_init_dtablesize; ++i)
- {
- if (_hurd_init_dtable[i] == MACH_PORT_NULL)
- /* An unused descriptor is marked by a null pointer. */
- _hurd_dtable[i] = NULL;
- else
- {
- int copy;
- /* Allocate a new file descriptor structure. */
- struct hurd_fd *new = malloc (sizeof (struct hurd_fd));
- if (new == NULL)
- __libc_fatal ("hurd: Can't allocate initial file descriptors\n");
- /* See if this file descriptor is the same as a previous one we have
- already installed. In this case, we can just copy over the same
- ctty port without making any more RPCs. We only check the the
- immediately preceding fd and fd 0 -- this should be enough to
- handle the common cases while not requiring quadratic
- complexity. */
- if (i > 0 && _hurd_init_dtable[i] == _hurd_init_dtable[i - 1])
- copy = i - 1;
- else if (i > 0 && _hurd_init_dtable[i] == _hurd_init_dtable[0])
- copy = 0;
- else
- copy = -1;
- if (copy < 0)
- {
- /* Initialize the port cells. */
- _hurd_port_init (&new->port, MACH_PORT_NULL);
- _hurd_port_init (&new->ctty, MACH_PORT_NULL);
- /* Install the port in the descriptor.
- This sets up all the ctty magic. */
- _hurd_port2fd (new, _hurd_init_dtable[i], 0);
- }
- else
- {
- /* Copy over ctty from the already set up file descriptor that
- contains the same port. We can access the contents of the
- cell without any locking since no one could have seen it
- yet. */
- mach_port_t ctty = _hurd_dtable[copy]->ctty.port;
- if (MACH_PORT_VALID (ctty))
- __mach_port_mod_refs (__mach_task_self (), ctty,
- MACH_PORT_RIGHT_SEND, +1);
- _hurd_port_init (&new->port, _hurd_init_dtable[i]);
- _hurd_port_init (&new->ctty, ctty);
- }
- _hurd_dtable[i] = new;
- }
- }
- /* Clear out the initial descriptor table.
- Everything must use _hurd_dtable now. */
- __vm_deallocate (__mach_task_self (),
- (vm_address_t) _hurd_init_dtable,
- _hurd_init_dtablesize * sizeof (_hurd_init_dtable[0]));
- _hurd_init_dtable = NULL;
- _hurd_init_dtablesize = 0;
- /* Initialize the remaining empty slots in the table. */
- for (; i < _hurd_dtablesize; ++i)
- _hurd_dtable[i] = NULL;
- /* Run things that want to run after the file descriptor table
- is initialized. */
- RUN_RELHOOK (_hurd_fd_subinit, ());
- }
- SET_RELHOOK (_hurd_subinit, init_dtable);
- /* XXX when the linker supports it, the following functions should all be
- elsewhere and just have text_set_elements here. */
- /* Called by `getdport' to do its work. */
- static file_t
- get_dtable_port (int fd)
- {
- struct hurd_fd *d = _hurd_fd_get (fd);
- file_t dport;
- if (!d)
- return __hurd_fail (EBADF), MACH_PORT_NULL;
- HURD_CRITICAL_BEGIN;
- dport = HURD_PORT_USE (&d->port,
- ({
- error_t err;
- mach_port_t outport;
- err = __mach_port_mod_refs (__mach_task_self (),
- port,
- MACH_PORT_RIGHT_SEND,
- 1);
- if (err)
- {
- errno = err;
- outport = MACH_PORT_NULL;
- }
- else
- outport = port;
- outport;
- }));
- HURD_CRITICAL_END;
- return dport;
- }
- file_t (*_hurd_getdport_fn) (int fd) = get_dtable_port;
- #include <hurd/signal.h>
- /* We are in the child fork; the dtable lock is still held.
- The parent has inserted send rights for all the normal io ports,
- but we must recover ctty-special ports for ourselves. */
- static error_t
- fork_child_dtable (void)
- {
- error_t err;
- int i;
- err = 0;
- for (i = 0; !err && i < _hurd_dtablesize; ++i)
- {
- struct hurd_fd *d = _hurd_dtable[i];
- if (d == NULL)
- continue;
- /* No other thread is using the send rights in the child task. */
- d->port.users = d->ctty.users = NULL;
- if (d->ctty.port != MACH_PORT_NULL)
- {
- /* There was a ctty-special port in the parent.
- We need to get one for ourselves too. */
- __mach_port_deallocate (__mach_task_self (), d->ctty.port);
- err = __term_open_ctty (d->port.port, _hurd_pid, _hurd_pgrp,
- &d->ctty.port);
- if (err)
- d->ctty.port = MACH_PORT_NULL;
- }
- /* XXX for each fd with a cntlmap, reauth and re-map_cntl. */
- }
- return err;
- (void) &fork_child_dtable; /* Avoid "defined but not used" warning. */
- }
- data_set_element (_hurd_fork_locks, _hurd_dtable_lock); /* XXX ld bug: bss */
- text_set_element (_hurd_fork_child_hook, fork_child_dtable);
- /* Called when our process group has changed. */
- static void
- ctty_new_pgrp (void)
- {
- int i;
- retry:
- HURD_CRITICAL_BEGIN;
- __mutex_lock (&_hurd_dtable_lock);
- if (__USEPORT (CTTYID, port == MACH_PORT_NULL))
- {
- /* We have no controlling terminal. If we haven't had one recently,
- but our pgrp is being pointlessly diddled anyway, then we will
- have nothing to do in the loop below because no fd will have a
- ctty port at all.
- More likely, a setsid call is responsible both for the change
- in pgrp and for clearing the cttyid port. In that case, setsid
- held the dtable lock while updating the dtable to clear all the
- ctty ports, and ergo must have finished doing so before we run here.
- So we can be sure, again, that the loop below has no work to do. */
- }
- else
- for (i = 0; i < _hurd_dtablesize; ++i)
- {
- struct hurd_fd *const d = _hurd_dtable[i];
- struct hurd_userlink ulink, ctty_ulink;
- io_t port, ctty;
- if (d == NULL)
- /* Nothing to do for an unused descriptor cell. */
- continue;
- port = _hurd_port_get (&d->port, &ulink);
- ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
- if (ctty != MACH_PORT_NULL)
- {
- /* This fd has a ctty-special port. We need a new one, to tell
- the io server of our different process group. */
- io_t new;
- error_t err;
- if ((err = __term_open_ctty (port, _hurd_pid, _hurd_pgrp, &new)))
- {
- if (err == EINTR)
- {
- /* Got a signal while inside an RPC of the critical section, retry again */
- __mutex_unlock (&_hurd_dtable_lock);
- HURD_CRITICAL_UNLOCK;
- goto retry;
- }
- new = MACH_PORT_NULL;
- }
- _hurd_port_set (&d->ctty, new);
- }
- _hurd_port_free (&d->port, &ulink, port);
- _hurd_port_free (&d->ctty, &ctty_ulink, ctty);
- }
- __mutex_unlock (&_hurd_dtable_lock);
- HURD_CRITICAL_END;
- (void) &ctty_new_pgrp; /* Avoid "defined but not used" warning. */
- }
- text_set_element (_hurd_pgrp_changed_hook, ctty_new_pgrp);
- /* Called to reauthenticate the dtable when the auth port changes. */
- static void
- reauth_dtable (void)
- {
- int i;
- retry:
- HURD_CRITICAL_BEGIN;
- __mutex_lock (&_hurd_dtable_lock);
- for (i = 0; i < _hurd_dtablesize; ++i)
- {
- struct hurd_fd *const d = _hurd_dtable[i];
- mach_port_t new, newctty, ref;
- error_t err = 0;
- if (d == NULL)
- /* Nothing to do for an unused descriptor cell. */
- continue;
- ref = __mach_reply_port ();
- /* Take the descriptor cell's lock. */
- __spin_lock (&d->port.lock);
- /* Reauthenticate the descriptor's port. */
- if (d->port.port != MACH_PORT_NULL
- && ! (err = __io_reauthenticate (d->port.port,
- ref, MACH_MSG_TYPE_MAKE_SEND))
- && ! (err = __USEPORT (AUTH, __auth_user_authenticate
- (port,
- ref, MACH_MSG_TYPE_MAKE_SEND,
- &new))))
- {
- /* Replace the port in the descriptor cell
- with the newly reauthenticated port. */
- if (d->ctty.port != MACH_PORT_NULL
- && ! (err = __io_reauthenticate (d->ctty.port,
- ref, MACH_MSG_TYPE_MAKE_SEND))
- && ! (err = __USEPORT (AUTH, __auth_user_authenticate
- (port,
- ref, MACH_MSG_TYPE_MAKE_SEND,
- &newctty))))
- _hurd_port_set (&d->ctty, newctty);
- _hurd_port_locked_set (&d->port, new);
- }
- else
- /* Lost. Leave this descriptor cell alone. */
- __spin_unlock (&d->port.lock);
- __mach_port_destroy (__mach_task_self (), ref);
- if (err == EINTR)
- {
- /* Got a signal while inside an RPC of the critical section,
- retry again */
- __mutex_unlock (&_hurd_dtable_lock);
- HURD_CRITICAL_UNLOCK;
- goto retry;
- }
- }
- __mutex_unlock (&_hurd_dtable_lock);
- HURD_CRITICAL_END;
- (void) &reauth_dtable; /* Avoid "defined but not used" warning. */
- }
- text_set_element (_hurd_reauth_hook, reauth_dtable);
|