]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/mach/hurd/ioctl.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / sysdeps / mach / hurd / ioctl.c
CommitLineData
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. */
38int
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 328libc_hidden_def (__ioctl)
28f540f4 329weak_alias (__ioctl, ioctl)