1 | /* Copyright (C) 1992-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 <errno.h> |
19 | #include <libc-pointer-arith.h> |
20 | #include <sys/ioctl.h> |
21 | #include <hurd.h> |
22 | #include <hurd/fd.h> |
23 | #include <hurd/signal.h> |
24 | #include <stdarg.h> |
25 | #include <mach/notify.h> |
26 | #include <assert.h> |
27 | #include <string.h> |
28 | #include <stdint.h> |
29 | #include <hurd/ioctl.h> |
30 | #include <mach/mig_support.h> |
31 | #include <mach_rpc.h> |
32 | #include <sysdep-cancel.h> |
33 | |
34 | #include <hurd/ioctls.defs> |
35 | |
36 | #define msg_align(x) ALIGN_UP (x, __alignof__ (uintptr_t)) |
37 | #define typesize(type) (1 << (type)) |
38 | |
39 | /* Perform the I/O control operation specified by REQUEST on FD. |
40 | The actual type and use of ARG and the return value depend on REQUEST. */ |
41 | int |
42 | __ioctl (int fd, unsigned long int request, ...) |
43 | { |
44 | #ifdef MACH_MSG_TYPE_BIT |
45 | /* Map individual type fields to Mach IPC types. */ |
46 | static const int mach_types[] = |
47 | { MACH_MSG_TYPE_CHAR, MACH_MSG_TYPE_INTEGER_16, MACH_MSG_TYPE_INTEGER_32, |
48 | MACH_MSG_TYPE_INTEGER_64 }; |
49 | #define io2mach_type(count, type) \ |
50 | ((mach_msg_type_t) { \ |
51 | .msgt_name = mach_types[type], \ |
52 | .msgt_size = typesize(type) * 8, \ |
53 | .msgt_number = count, \ |
54 | .msgt_inline = TRUE, \ |
55 | .msgt_longform = FALSE, \ |
56 | .msgt_deallocate = FALSE, \ |
57 | .msgt_unused = 0 \ |
58 | }) |
59 | #endif |
60 | |
61 | /* Extract the type information encoded in the request. */ |
62 | unsigned int type = _IOC_TYPE (request); |
63 | |
64 | /* Message buffer. */ |
65 | struct |
66 | { |
67 | #ifdef MACH_MSG_TYPE_BIT |
68 | union |
69 | { |
70 | mig_reply_header_t header; |
71 | struct |
72 | { |
73 | mach_msg_header_t Head; |
74 | mach_msg_type_t RetCodeType; |
75 | kern_return_t RetCode; |
76 | } header_typecheck; |
77 | }; |
78 | char data[3 * sizeof (mach_msg_type_t) |
79 | + msg_align (_IOT_COUNT0 (type) * typesize (_IOT_TYPE0 (type))) |
80 | + msg_align (_IOT_COUNT1 (type) * typesize (_IOT_TYPE1 (type))) |
81 | + msg_align (_IOT_COUNT2 (type) * typesize (_IOT_TYPE2 (type)))]; |
82 | #else /* Untyped Mach IPC format. */ |
83 | mig_reply_error_t ; |
84 | char data[_IOT_COUNT0 (type) * typesize (_IOT_TYPE0 (type)) |
85 | + _IOT_COUNT1 (type) * typesize (_IOT_TYPE1 (type)) |
86 | + _IOT_COUNT2 (type) * typesize (_IOT_TYPE2 (type))]; |
87 | mach_msg_trailer_t trailer; |
88 | #endif |
89 | } msg; |
90 | mach_msg_header_t *const m = &msg.header.Head; |
91 | mach_msg_id_t msgid; |
92 | unsigned int reply_size; |
93 | #ifdef MACH_MSG_TYPE_BIT |
94 | mach_msg_type_t *t; |
95 | #else |
96 | void *p; |
97 | #endif |
98 | |
99 | void *arg = NULL; |
100 | |
101 | error_t err; |
102 | |
103 | /* Send the RPC already packed up in MSG to IOPORT |
104 | and decode the return value. */ |
105 | error_t send_rpc (io_t ioport) |
106 | { |
107 | error_t err; |
108 | #ifdef MACH_MSG_TYPE_BIT |
109 | mach_msg_type_t *t = &msg.header.RetCodeType; |
110 | #else |
111 | void *p = &msg.header.RetCode; |
112 | #endif |
113 | |
114 | /* Marshal the request arguments into the message buffer. |
115 | We must redo this work each time we retry the RPC after a SIGTTOU, |
116 | because the reply message containing the EBACKGROUND error code |
117 | clobbers the same message buffer also used for the request. */ |
118 | |
119 | if (_IOC_INOUT (request) & IOC_IN) |
120 | { |
121 | /* We don't want to advance ARG since it will be used to copy out |
122 | too if IOC_OUT is also set. */ |
123 | void *argptr = arg; |
124 | int zero = 0; |
125 | |
126 | if (request == TIOCFLUSH && !argptr) |
127 | argptr = &zero; |
128 | |
129 | /* Pack an argument into the message buffer. */ |
130 | void in (unsigned int count, enum __ioctl_datum type) |
131 | { |
132 | if (count > 0) |
133 | { |
134 | const size_t len = count * typesize ((unsigned int) type); |
135 | #ifdef MACH_MSG_TYPE_BIT |
136 | void *p = &t[1]; |
137 | *t = io2mach_type (count, type); |
138 | p = __mempcpy (p, argptr, len); |
139 | p = (void *) msg_align ((uintptr_t) p); |
140 | t = p; |
141 | #else |
142 | p = __mempcpy (p, argptr, len); |
143 | #endif |
144 | argptr += len; |
145 | } |
146 | } |
147 | |
148 | /* Pack the argument data. */ |
149 | in (_IOT_COUNT0 (type), _IOT_TYPE0 (type)); |
150 | in (_IOT_COUNT1 (type), _IOT_TYPE1 (type)); |
151 | in (_IOT_COUNT2 (type), _IOT_TYPE2 (type)); |
152 | } |
153 | else if (_IOC_INOUT (request) == IOC_VOID && _IOT_COUNT0 (type) != 0) |
154 | { |
155 | /* The RPC takes a single integer_t argument. |
156 | Rather than pointing to the value, ARG is the value itself. */ |
157 | #ifdef MACH_MSG_TYPE_BIT |
158 | *t++ = io2mach_type (1, _IOTS (integer_t)); |
159 | *(integer_t *) t = (integer_t) (intptr_t) arg; |
160 | t = (void *) msg_align ((uintptr_t) t + sizeof (integer_t)); |
161 | #else |
162 | *(integer_t *) p = (integer_t) (intptr_t) arg; |
163 | p = (void *) p + sizeof (integer_t); |
164 | #endif |
165 | } |
166 | |
167 | memset (m, 0, sizeof *m); /* Clear unused fields. */ |
168 | m->msgh_size = ( |
169 | #ifdef MACH_MSG_TYPE_BIT |
170 | (char *) t |
171 | #else |
172 | (char *) p |
173 | #endif |
174 | - (char *) &msg); |
175 | m->msgh_remote_port = ioport; |
176 | m->msgh_local_port = __mig_get_reply_port (); |
177 | m->msgh_id = msgid; |
178 | m->msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, |
179 | MACH_MSG_TYPE_MAKE_SEND_ONCE); |
180 | err = _hurd_intr_rpc_mach_msg (m, MACH_SEND_MSG|MACH_RCV_MSG, |
181 | m->msgh_size, sizeof (msg), |
182 | m->msgh_local_port, |
183 | MACH_MSG_TIMEOUT_NONE, |
184 | MACH_PORT_NULL); |
185 | switch (err) |
186 | { |
187 | case MACH_MSG_SUCCESS: |
188 | break; |
189 | case MACH_SEND_INVALID_REPLY: |
190 | case MACH_RCV_INVALID_NAME: |
191 | __mig_dealloc_reply_port (m->msgh_local_port); |
192 | /* Fall through. */ |
193 | default: |
194 | return err; |
195 | } |
196 | |
197 | if ((m->msgh_bits & MACH_MSGH_BITS_COMPLEX)) |
198 | { |
199 | /* Allow no ports or VM. */ |
200 | __mach_msg_destroy (m); |
201 | /* Want to return a different error below for a different msgid. */ |
202 | if (m->msgh_id == msgid + 100) |
203 | return MIG_TYPE_ERROR; |
204 | } |
205 | |
206 | if (m->msgh_id != msgid + 100) |
207 | return (m->msgh_id == MACH_NOTIFY_SEND_ONCE |
208 | ? MIG_SERVER_DIED : MIG_REPLY_MISMATCH); |
209 | |
210 | if (m->msgh_size != reply_size |
211 | && m->msgh_size != sizeof msg.header) |
212 | return MIG_TYPE_ERROR; |
213 | |
214 | #ifdef MACH_MSG_TYPE_BIT |
215 | mach_msg_type_t ipctype = io2mach_type(1, _IOTS (msg.header.RetCode)); |
216 | if (BAD_TYPECHECK (&msg.header_typecheck.RetCodeType, &ipctype)) |
217 | return MIG_TYPE_ERROR; |
218 | #endif |
219 | return msg.header.RetCode; |
220 | } |
221 | |
222 | if (_IOT_COUNT0 (type) != 0) |
223 | { |
224 | /* Data need either be sent, received, or even both. */ |
225 | va_list ap; |
226 | |
227 | va_start (ap, request); |
228 | arg = va_arg (ap, void *); |
229 | va_end (ap); |
230 | } |
231 | |
232 | { |
233 | /* Check for a registered handler for REQUEST. */ |
234 | ioctl_handler_t handler = _hurd_lookup_ioctl_handler (request); |
235 | if (handler) |
236 | { |
237 | /* This handler groks REQUEST. Se lo puntamonos. */ |
238 | int save = errno; |
239 | int result = (*handler) (fd, request, arg); |
240 | if (result != -1 || errno != ENOTTY) |
241 | return result; |
242 | |
243 | /* The handler doesn't really grok this one. |
244 | Try the normal RPC translation. */ |
245 | errno = save; |
246 | } |
247 | } |
248 | |
249 | /* Compute the Mach message ID for the RPC from the group and command |
250 | parts of the ioctl request. */ |
251 | msgid = IOC_MSGID (request); |
252 | |
253 | /* Compute the expected size of the reply. There is a standard header |
254 | consisting of the message header and the reply code. Then, for out |
255 | and in/out ioctls, there come the data with their type headers. */ |
256 | reply_size = sizeof msg.header; |
257 | |
258 | if (_IOC_INOUT (request) & IOC_OUT) |
259 | { |
260 | inline void figure_reply (unsigned int count, enum __ioctl_datum type) |
261 | { |
262 | if (count > 0) |
263 | { |
264 | #ifdef MACH_MSG_TYPE_BIT |
265 | /* Add the size of the type and data. */ |
266 | reply_size += sizeof (mach_msg_type_t) + typesize (type) * count; |
267 | /* Align it to word size. */ |
268 | reply_size = msg_align (reply_size); |
269 | #else |
270 | reply_size += typesize (type) * count; |
271 | #endif |
272 | } |
273 | } |
274 | figure_reply (_IOT_COUNT0 (type), _IOT_TYPE0 (type)); |
275 | figure_reply (_IOT_COUNT1 (type), _IOT_TYPE1 (type)); |
276 | figure_reply (_IOT_COUNT2 (type), _IOT_TYPE2 (type)); |
277 | } |
278 | |
279 | /* Marshal the arguments into the request message and make the RPC. |
280 | This wrapper function handles EBACKGROUND returns, turning them |
281 | into either SIGTTOU or EIO. */ |
282 | if (request == TIOCDRAIN) |
283 | { |
284 | /* This is a cancellation point. */ |
285 | int cancel_oldtype = LIBC_CANCEL_ASYNC(); |
286 | err = HURD_DPORT_USE_CANCEL (fd, _hurd_ctty_output (port, ctty, send_rpc)); |
287 | LIBC_CANCEL_RESET (cancel_oldtype); |
288 | } |
289 | else |
290 | err = HURD_DPORT_USE (fd, _hurd_ctty_output (port, ctty, send_rpc)); |
291 | |
292 | #ifdef MACH_MSG_TYPE_BIT |
293 | t = (mach_msg_type_t *) msg.data; |
294 | #else |
295 | p = (void *) msg.data; |
296 | #endif |
297 | switch (err) |
298 | { |
299 | /* Unpack the message buffer into the argument location. */ |
300 | int out (unsigned int count, unsigned int type, |
301 | void *store, void **update) |
302 | { |
303 | if (count > 0) |
304 | { |
305 | const size_t len = count * typesize (type); |
306 | #ifdef MACH_MSG_TYPE_BIT |
307 | const mach_msg_type_t ipctype = io2mach_type(count, type); |
308 | if (BAD_TYPECHECK (t, &ipctype)) |
309 | return 1; |
310 | ++t; |
311 | memcpy (store, t, len); |
312 | if (update != NULL) |
313 | *update += len; |
314 | t = (mach_msg_type_t *) msg_align ((uintptr_t) t + len); |
315 | #else |
316 | memcpy (store, p, len); |
317 | p += len; |
318 | if (update != NULL) |
319 | *update += len; |
320 | #endif |
321 | } |
322 | return 0; |
323 | } |
324 | |
325 | case 0: |
326 | if (m->msgh_size != reply_size |
327 | || ((_IOC_INOUT (request) & IOC_OUT) |
328 | && (out (_IOT_COUNT0 (type), _IOT_TYPE0 (type), arg, &arg) |
329 | || out (_IOT_COUNT1 (type), _IOT_TYPE1 (type), arg, &arg) |
330 | || out (_IOT_COUNT2 (type), _IOT_TYPE2 (type), arg, &arg)))) |
331 | return __hurd_fail (MIG_TYPE_ERROR); |
332 | return 0; |
333 | |
334 | case MIG_BAD_ID: |
335 | case EOPNOTSUPP: |
336 | /* The server didn't understand the RPC. */ |
337 | err = ENOTTY; |
338 | /* Fall through. */ |
339 | default: |
340 | return __hurd_fail (err); |
341 | } |
342 | } |
343 | |
344 | libc_hidden_def (__ioctl) |
345 | weak_alias (__ioctl, ioctl) |
346 | |