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