1 /* Copyright (C) 1991-2022 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
init_dtable(void)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 	  /* Allocate a new file descriptor structure.  */
64 	  struct hurd_fd *new = malloc (sizeof (struct hurd_fd));
65 	  if (new == NULL)
66 	    __libc_fatal ("hurd: Can't allocate initial file descriptors\n");
67 
68 	  /* Initialize the port cells.  */
69 	  _hurd_port_init (&new->port, MACH_PORT_NULL);
70 	  _hurd_port_init (&new->ctty, MACH_PORT_NULL);
71 
72 	  /* Install the port in the descriptor.
73 	     This sets up all the ctty magic.  */
74 	  _hurd_port2fd (new, _hurd_init_dtable[i], 0);
75 
76 	  _hurd_dtable[i] = new;
77 	}
78     }
79 
80   /* Clear out the initial descriptor table.
81      Everything must use _hurd_dtable now.  */
82   __vm_deallocate (__mach_task_self (),
83 		   (vm_address_t) _hurd_init_dtable,
84 		   _hurd_init_dtablesize * sizeof (_hurd_init_dtable[0]));
85   _hurd_init_dtable = NULL;
86   _hurd_init_dtablesize = 0;
87 
88   /* Initialize the remaining empty slots in the table.  */
89   for (; i < _hurd_dtablesize; ++i)
90     _hurd_dtable[i] = NULL;
91 
92   /* Run things that want to run after the file descriptor table
93      is initialized.  */
94   RUN_RELHOOK (_hurd_fd_subinit, ());
95 }
96 
97 SET_RELHOOK (_hurd_subinit, init_dtable);
98 
99 /* XXX when the linker supports it, the following functions should all be
100    elsewhere and just have text_set_elements here.  */
101 
102 /* Called by `getdport' to do its work.  */
103 
104 static file_t
get_dtable_port(int fd)105 get_dtable_port (int fd)
106 {
107   struct hurd_fd *d = _hurd_fd_get (fd);
108   file_t dport;
109 
110   if (!d)
111     return __hurd_fail (EBADF), MACH_PORT_NULL;
112 
113   HURD_CRITICAL_BEGIN;
114 
115   dport = HURD_PORT_USE (&d->port,
116 			 ({
117 			   error_t err;
118 			   mach_port_t outport;
119 			   err = __mach_port_mod_refs (__mach_task_self (),
120 						       port,
121 						       MACH_PORT_RIGHT_SEND,
122 						       1);
123 			   if (err)
124 			     {
125 			       errno = err;
126 			       outport = MACH_PORT_NULL;
127 			     }
128 			   else
129 			     outport = port;
130 			   outport;
131 			 }));
132 
133   HURD_CRITICAL_END;
134 
135   return dport;
136 }
137 
138 file_t (*_hurd_getdport_fn) (int fd) = get_dtable_port;
139 
140 #include <hurd/signal.h>
141 
142 /* We are in the child fork; the dtable lock is still held.
143    The parent has inserted send rights for all the normal io ports,
144    but we must recover ctty-special ports for ourselves.  */
145 static error_t
fork_child_dtable(void)146 fork_child_dtable (void)
147 {
148   error_t err;
149   int i;
150 
151   err = 0;
152 
153   for (i = 0; !err && i < _hurd_dtablesize; ++i)
154     {
155       struct hurd_fd *d = _hurd_dtable[i];
156       if (d == NULL)
157 	continue;
158 
159       /* No other thread is using the send rights in the child task.  */
160       d->port.users = d->ctty.users = NULL;
161 
162       if (d->ctty.port != MACH_PORT_NULL)
163 	{
164 	  /* There was a ctty-special port in the parent.
165 	     We need to get one for ourselves too.  */
166 	  __mach_port_deallocate (__mach_task_self (), d->ctty.port);
167 	  err = __term_open_ctty (d->port.port, _hurd_pid, _hurd_pgrp,
168 				  &d->ctty.port);
169 	  if (err)
170 	    d->ctty.port = MACH_PORT_NULL;
171 	}
172 
173       /* XXX for each fd with a cntlmap, reauth and re-map_cntl.  */
174     }
175   return err;
176 
177   (void) &fork_child_dtable;	/* Avoid "defined but not used" warning.  */
178 }
179 
180 data_set_element (_hurd_fork_locks, _hurd_dtable_lock);	/* XXX ld bug: bss */
181 text_set_element (_hurd_fork_child_hook, fork_child_dtable);
182 
183 /* Called when our process group has changed.  */
184 
185 static void
ctty_new_pgrp(void)186 ctty_new_pgrp (void)
187 {
188   int i;
189 
190 retry:
191   HURD_CRITICAL_BEGIN;
192   __mutex_lock (&_hurd_dtable_lock);
193 
194   if (__USEPORT (CTTYID, port == MACH_PORT_NULL))
195     {
196       /* We have no controlling terminal.  If we haven't had one recently,
197 	 but our pgrp is being pointlessly diddled anyway, then we will
198 	 have nothing to do in the loop below because no fd will have a
199 	 ctty port at all.
200 
201 	 More likely, a setsid call is responsible both for the change
202 	 in pgrp and for clearing the cttyid port.  In that case, setsid
203 	 held the dtable lock while updating the dtable to clear all the
204 	 ctty ports, and ergo must have finished doing so before we run here.
205 	 So we can be sure, again, that the loop below has no work to do.  */
206     }
207   else
208     for (i = 0; i < _hurd_dtablesize; ++i)
209       {
210 	struct hurd_fd *const d = _hurd_dtable[i];
211 	struct hurd_userlink ulink, ctty_ulink;
212 	io_t port, ctty;
213 
214 	if (d == NULL)
215 	  /* Nothing to do for an unused descriptor cell.  */
216 	  continue;
217 
218 	port = _hurd_port_get (&d->port, &ulink);
219 	ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
220 
221 	if (ctty != MACH_PORT_NULL)
222 	  {
223 	    /* This fd has a ctty-special port.  We need a new one, to tell
224 	       the io server of our different process group.  */
225 	    io_t new;
226 	    error_t err;
227 	    if ((err = __term_open_ctty (port, _hurd_pid, _hurd_pgrp, &new)))
228 	      {
229 		if (err == EINTR)
230 		  {
231 		    /* Got a signal while inside an RPC of the critical section, retry again */
232 		    __mutex_unlock (&_hurd_dtable_lock);
233 		    HURD_CRITICAL_UNLOCK;
234 		    goto retry;
235 		  }
236 		new = MACH_PORT_NULL;
237 	      }
238 	    _hurd_port_set (&d->ctty, new);
239 	  }
240 
241 	_hurd_port_free (&d->port, &ulink, port);
242 	_hurd_port_free (&d->ctty, &ctty_ulink, ctty);
243       }
244 
245   __mutex_unlock (&_hurd_dtable_lock);
246   HURD_CRITICAL_END;
247 
248   (void) &ctty_new_pgrp;	/* Avoid "defined but not used" warning.  */
249 }
250 
251 text_set_element (_hurd_pgrp_changed_hook, ctty_new_pgrp);
252 
253 /* Called to reauthenticate the dtable when the auth port changes.  */
254 
255 static void
reauth_dtable(void)256 reauth_dtable (void)
257 {
258   int i;
259 
260   HURD_CRITICAL_BEGIN;
261   __mutex_lock (&_hurd_dtable_lock);
262 
263   for (i = 0; i < _hurd_dtablesize; ++i)
264     {
265       struct hurd_fd *const d = _hurd_dtable[i];
266       mach_port_t new, newctty, ref;
267 
268       if (d == NULL)
269 	/* Nothing to do for an unused descriptor cell.  */
270 	continue;
271 
272       ref = __mach_reply_port ();
273 
274       /* Take the descriptor cell's lock.  */
275       __spin_lock (&d->port.lock);
276 
277       /* Reauthenticate the descriptor's port.  */
278       if (d->port.port != MACH_PORT_NULL
279 	  && ! __io_reauthenticate (d->port.port,
280 				    ref, MACH_MSG_TYPE_MAKE_SEND)
281 	  && ! __USEPORT (AUTH, __auth_user_authenticate
282 			  (port,
283 			   ref, MACH_MSG_TYPE_MAKE_SEND,
284 			   &new)))
285 	{
286 	  /* Replace the port in the descriptor cell
287 	     with the newly reauthenticated port.  */
288 
289 	  if (d->ctty.port != MACH_PORT_NULL
290 	      && ! __io_reauthenticate (d->ctty.port,
291 					ref, MACH_MSG_TYPE_MAKE_SEND)
292 	      && ! __USEPORT (AUTH, __auth_user_authenticate
293 			      (port,
294 			       ref, MACH_MSG_TYPE_MAKE_SEND,
295 			       &newctty)))
296 	    _hurd_port_set (&d->ctty, newctty);
297 
298 	  _hurd_port_locked_set (&d->port, new);
299 	}
300       else
301 	/* Lost.  Leave this descriptor cell alone.  */
302 	__spin_unlock (&d->port.lock);
303 
304       __mach_port_destroy (__mach_task_self (), ref);
305     }
306 
307   __mutex_unlock (&_hurd_dtable_lock);
308   HURD_CRITICAL_END;
309 
310   (void) &reauth_dtable;	/* Avoid "defined but not used" warning.  */
311 }
312 
313 text_set_element (_hurd_reauth_hook, reauth_dtable);
314