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 |
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 | /* Allocate a new file descriptor structure. */ |
64 | struct hurd_fd *new = malloc (sizeof (struct hurd_fd)); |
65 | if (new == NULL) |
66 | __libc_fatal (message: "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 |
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 |
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 |
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 |
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 | |