1 | /* Copyright (C) 1991-2024 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 (message: "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 (message: "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 | retry: |
293 | HURD_CRITICAL_BEGIN; |
294 | __mutex_lock (&_hurd_dtable_lock); |
295 | |
296 | for (i = 0; i < _hurd_dtablesize; ++i) |
297 | { |
298 | struct hurd_fd *const d = _hurd_dtable[i]; |
299 | mach_port_t new, newctty, ref; |
300 | error_t err = 0; |
301 | |
302 | if (d == NULL) |
303 | /* Nothing to do for an unused descriptor cell. */ |
304 | continue; |
305 | |
306 | ref = __mach_reply_port (); |
307 | |
308 | /* Take the descriptor cell's lock. */ |
309 | __spin_lock (&d->port.lock); |
310 | |
311 | /* Reauthenticate the descriptor's port. */ |
312 | if (d->port.port != MACH_PORT_NULL |
313 | && ! (err = __io_reauthenticate (d->port.port, |
314 | ref, MACH_MSG_TYPE_MAKE_SEND)) |
315 | && ! (err = __USEPORT (AUTH, __auth_user_authenticate |
316 | (port, |
317 | ref, MACH_MSG_TYPE_MAKE_SEND, |
318 | &new)))) |
319 | { |
320 | /* Replace the port in the descriptor cell |
321 | with the newly reauthenticated port. */ |
322 | |
323 | if (d->ctty.port != MACH_PORT_NULL |
324 | && ! (err = __io_reauthenticate (d->ctty.port, |
325 | ref, MACH_MSG_TYPE_MAKE_SEND)) |
326 | && ! (err = __USEPORT (AUTH, __auth_user_authenticate |
327 | (port, |
328 | ref, MACH_MSG_TYPE_MAKE_SEND, |
329 | &newctty)))) |
330 | _hurd_port_set (&d->ctty, newctty); |
331 | |
332 | _hurd_port_locked_set (&d->port, new); |
333 | } |
334 | else |
335 | /* Lost. Leave this descriptor cell alone. */ |
336 | __spin_unlock (&d->port.lock); |
337 | |
338 | __mach_port_destroy (__mach_task_self (), ref); |
339 | |
340 | if (err == EINTR) |
341 | { |
342 | /* Got a signal while inside an RPC of the critical section, |
343 | retry again */ |
344 | __mutex_unlock (&_hurd_dtable_lock); |
345 | HURD_CRITICAL_UNLOCK; |
346 | goto retry; |
347 | } |
348 | } |
349 | |
350 | __mutex_unlock (&_hurd_dtable_lock); |
351 | HURD_CRITICAL_END; |
352 | |
353 | (void) &reauth_dtable; /* Avoid "defined but not used" warning. */ |
354 | } |
355 | |
356 | text_set_element (_hurd_reauth_hook, reauth_dtable); |
357 | |