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