xref: /minix/minix/lib/libc/sys/ioctl.c (revision fb9c64b2)
1 #include <sys/cdefs.h>
2 #include "namespace.h"
3 #include <lib.h>
4 #include <stdarg.h>
5 
6 #include <sys/ioctl.h>
7 #include <minix/i2c.h>
8 #include <string.h>
9 #include <sys/ioccom.h>
10 #include <stdarg.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <minix/if.h>
14 #include <minix/bpf.h>
15 #include <assert.h>
16 
17 static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
18     i2c_ioctl_exec_t *in);
19 static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
20     minix_i2c_ioctl_exec_t *in);
21 
22 static void rewrite_i2c_netbsd_to_minix(minix_i2c_ioctl_exec_t *out,
23 	i2c_ioctl_exec_t *in)
24 {
25   memset(out, '\0', sizeof(minix_i2c_ioctl_exec_t));
26 
27   out->iie_op = in->iie_op;
28   out->iie_addr = in->iie_addr;
29   out->iie_cmdlen = I2C_EXEC_MAX_CMDLEN < in->iie_cmdlen ?
30   	I2C_EXEC_MAX_CMDLEN : in->iie_cmdlen;
31   out->iie_buflen = I2C_EXEC_MAX_BUFLEN < in->iie_buflen ?
32   	I2C_EXEC_MAX_BUFLEN : in->iie_buflen;
33 
34   if (in->iie_cmdlen > 0 && in->iie_cmd != NULL) {
35 	memcpy(out->iie_cmd, in->iie_cmd, in->iie_cmdlen);
36   }
37 
38   if (in->iie_buflen > 0 && in->iie_buf != NULL) {
39 	memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
40   }
41 }
42 
43 static void rewrite_i2c_minix_to_netbsd(i2c_ioctl_exec_t *out,
44 	minix_i2c_ioctl_exec_t *in)
45 {
46   /* the only field that changes is iie_buf, everything else is the same */
47   if (in->iie_buflen > 0 ) {
48 	memcpy(out->iie_buf, in->iie_buf, in->iie_buflen);
49   }
50 }
51 
52 /*
53  * Convert a network interface related IOCTL with pointers to a flat format
54  * suitable for MINIX3.  Return a pointer to the new data on success, or zero
55  * (with errno set) on failure.  The original request code is given in
56  * 'request' and must be replaced by the new request code to be used.
57  */
58 static vir_bytes
59 ioctl_convert_if_to_minix(void * data, unsigned long * request)
60 {
61 	struct minix_ifmediareq *mifm;
62 	struct ifmediareq *ifm;
63 	struct minix_if_clonereq *mifcr;
64 	struct if_clonereq *ifcr;
65 
66 	switch (*request) {
67 	case SIOCGIFMEDIA:
68 		ifm = (struct ifmediareq *)data;
69 
70 		mifm = (struct minix_ifmediareq *)malloc(sizeof(*mifm));
71 		if (mifm != NULL) {
72 			/*
73 			 * The count may exceed MINIX_IF_MAXMEDIA, and should
74 			 * be truncated as needed by the IF implementation.
75 			 */
76 			memcpy(&mifm->mifm_ifm, ifm, sizeof(*ifm));
77 
78 			*request = MINIX_SIOCGIFMEDIA;
79 		} else
80 			errno = ENOMEM;
81 
82 		return (vir_bytes)mifm;
83 
84 	case SIOCIFGCLONERS:
85 		ifcr = (struct if_clonereq *)data;
86 
87 		mifcr = (struct minix_if_clonereq *)malloc(sizeof(*mifcr));
88 		if (mifcr != NULL) {
89 			/*
90 			 * The count may exceed MINIX_IF_MAXCLONERS, and should
91 			 * be truncated as needed by the IF implementation.
92 			 */
93 			memcpy(&mifcr->mifcr_ifcr, ifcr, sizeof(*ifcr));
94 
95 			*request = MINIX_SIOCIFGCLONERS;
96 		} else
97 			errno = ENOMEM;
98 
99 		return (vir_bytes)mifcr;
100 
101 	default:
102 		assert(0);
103 
104 		errno = ENOTTY;
105 		return 0;
106 	}
107 }
108 
109 /*
110  * Convert a the result of a network interface related IOCTL with pointers from
111  * the flat format used to make the call to MINIX3.  Called on success only.
112  * The given request code is that of the (NetBSD-type) original.
113  */
114 static void
115 ioctl_convert_if_from_minix(vir_bytes addr, void * data, unsigned long request)
116 {
117 	struct minix_ifmediareq *mifm;
118 	struct ifmediareq *ifm;
119 	struct minix_if_clonereq *mifcr;
120 	struct if_clonereq *ifcr;
121 	int count;
122 
123 	switch (request) {
124 	case SIOCGIFMEDIA:
125 		mifm = (struct minix_ifmediareq *)addr;
126 		ifm = (struct ifmediareq *)data;
127 
128 		memcpy(ifm, &mifm->mifm_ifm, sizeof(*ifm));
129 
130 		if (ifm->ifm_ulist != NULL && ifm->ifm_count > 0)
131 			memcpy(ifm->ifm_ulist, mifm->mifm_list,
132 			    ifm->ifm_count * sizeof(ifm->ifm_ulist[0]));
133 
134 		break;
135 
136 	case SIOCIFGCLONERS:
137 		mifcr = (struct minix_if_clonereq *)addr;
138 		ifcr = (struct if_clonereq *)data;
139 
140 		memcpy(ifcr, &mifcr->mifcr_ifcr, sizeof(*ifcr));
141 
142 		count = (ifcr->ifcr_count < ifcr->ifcr_total) ?
143 		    ifcr->ifcr_count : ifcr->ifcr_total;
144 		if (ifcr->ifcr_buffer != NULL && count > 0)
145 			memcpy(ifcr->ifcr_buffer, mifcr->mifcr_buffer,
146 			    count * IFNAMSIZ);
147 
148 		break;
149 
150 	default:
151 		assert(0);
152 	}
153 }
154 
155 /*
156  * Convert a BPF (Berkeley Packet Filter) related IOCTL with pointers to a flat
157  * format suitable for MINIX3.  Return a pointer to the new data on success, or
158  * zero (with errno set) on failure.  The original request code is given in
159  * 'request' and must be replaced by the new request code to be used.
160  */
161 static vir_bytes
162 ioctl_convert_bpf_to_minix(void * data, unsigned long * request)
163 {
164 	struct minix_bpf_program *mbf;
165 	struct bpf_program *bf;
166 	struct minix_bpf_dltlist *mbfl;
167 	struct bpf_dltlist *bfl;
168 
169 	switch (*request) {
170 	case BIOCSETF:
171 		bf = (struct bpf_program *)data;
172 
173 		if (bf->bf_len > __arraycount(mbf->mbf_insns)) {
174 			errno = EINVAL;
175 			return 0;
176 		}
177 
178 		mbf = (struct minix_bpf_program *)malloc(sizeof(*mbf));
179 		if (mbf != NULL) {
180 			mbf->mbf_len = bf->bf_len;
181 			memcpy(mbf->mbf_insns, bf->bf_insns,
182 			    bf->bf_len * sizeof(mbf->mbf_insns[0]));
183 
184 			*request = MINIX_BIOCSETF;
185 		} else
186 			errno = ENOMEM;
187 
188 		return (vir_bytes)mbf;
189 
190 	case BIOCGDLTLIST:
191 		bfl = (struct bpf_dltlist *)data;
192 
193 		mbfl = (struct minix_bpf_dltlist *)malloc(sizeof(*mbfl));
194 		if (mbfl != NULL) {
195 			/*
196 			 * The length may exceed MINIX_BPF_MAXDLT, and should
197 			 * be truncated as needed by the BPF implementation.
198 			 */
199 			memcpy(&mbfl->mbfl_dltlist, bfl, sizeof(*bfl));
200 
201 			*request = MINIX_BIOCGDLTLIST;
202 		} else
203 			errno = ENOMEM;
204 
205 		return (vir_bytes)mbfl;
206 
207 	default:
208 		assert(0);
209 
210 		errno = ENOTTY;
211 		return 0;
212 	}
213 }
214 
215 /*
216  * Convert a the result of BPF (Berkeley Packet Filter) related IOCTL with
217  * pointers from the flat format used to make the call to MINIX3.  Called on
218  * success only.  The given request code is that of the (NetBSD-type) original.
219  */
220 static void
221 ioctl_convert_bpf_from_minix(vir_bytes addr, void * data,
222 	unsigned long request)
223 {
224 	struct minix_bpf_dltlist *mbfl;
225 	struct bpf_dltlist *bfl;
226 
227 	switch (request) {
228 	case BIOCGDLTLIST:
229 		mbfl = (struct minix_bpf_dltlist *)addr;
230 		bfl = (struct bpf_dltlist *)data;
231 
232 		memcpy(bfl, &mbfl->mbfl_dltlist, sizeof(*bfl));
233 
234 		if (bfl->bfl_list != NULL && bfl->bfl_len > 0)
235 			memcpy(bfl->bfl_list, mbfl->mbfl_list,
236 			    bfl->bfl_len * sizeof(bfl->bfl_list[0]));
237 
238 		break;
239 
240 	default:
241 		assert(0);
242 	}
243 }
244 
245 /*
246  * Library implementation of FIOCLEX and FIONCLEX.
247  */
248 static int
249 ioctl_to_setfd(int fd, int mask, int val)
250 {
251 	int fl;
252 
253 	if ((fl = fcntl(fd, F_GETFD)) == -1)
254 		return -1;
255 
256 	fl = (fl & ~mask) | val;
257 
258 	return fcntl(fd, F_SETFD, fl);
259 }
260 
261 /*
262  * Library implementation of FIONBIO and FIOASYNC.
263  */
264 static int
265 ioctl_to_setfl(int fd, void * data, int sfl)
266 {
267 	int arg, fl;
268 
269 	arg = *(int *)data;
270 
271 	if ((fl = fcntl(fd, F_GETFL)) == -1)
272 		return -1;
273 
274 	if (arg)
275 		fl |= sfl;
276 	else
277 		fl &= ~sfl;
278 
279 	return fcntl(fd, F_SETFL, fl & ~O_ACCMODE);
280 }
281 
282 /*
283  * Library implementation of various deprecated IOCTLs.  These particular IOCTL
284  * calls change how the file descriptors behave, and have nothing to do with
285  * the actual open file.  They should therefore be handled by VFS rather than
286  * individual device drivers.  We rewrite them to use fcntl(2) instead here.
287  */
288 static int
289 ioctl_to_fcntl(int fd, unsigned long request, void * data)
290 {
291 	switch (request) {
292 	case FIOCLEX:
293 		return ioctl_to_setfd(fd, FD_CLOEXEC, FD_CLOEXEC);
294 	case FIONCLEX:
295 		return ioctl_to_setfd(fd, FD_CLOEXEC, 0);
296 	case FIONBIO:
297 		return ioctl_to_setfl(fd, data, O_NONBLOCK);
298 	case FIOASYNC:
299 		return ioctl_to_setfl(fd, data, O_ASYNC);
300 	case FIOSETOWN: /* XXX TODO */
301 	case FIOGETOWN: /* XXX TODO */
302 	default:
303 		errno = ENOTTY;
304 		return -1;
305 	}
306 }
307 
308 int     ioctl(int fd, unsigned long request, ...)
309 {
310   minix_i2c_ioctl_exec_t i2c;
311   int r, request_save;
312   message m;
313   vir_bytes addr;
314   void *data;
315   va_list ap;
316 
317   va_start(ap, request);
318   data = va_arg(ap, void *);
319   va_end(ap);
320 
321   /*
322    * To support compatibility with interfaces on other systems, certain
323    * requests are re-written to flat structures (i.e. without pointers).
324    */
325   request_save = request;
326 
327   switch (request) {
328 	case FIOCLEX:
329 	case FIONCLEX:
330 	case FIONBIO:
331 	case FIOASYNC:
332 	case FIOSETOWN:
333 	case FIOGETOWN:
334 		return ioctl_to_fcntl(fd, request, data);
335 
336 	case I2C_IOCTL_EXEC:
337 		rewrite_i2c_netbsd_to_minix(&i2c, data);
338 		addr = (vir_bytes) &i2c;
339 		request = MINIX_I2C_IOCTL_EXEC;
340 		break;
341 
342 	case SIOCGIFMEDIA:
343 	case SIOCIFGCLONERS:
344 		if ((addr = ioctl_convert_if_to_minix(data, &request)) == 0)
345 			return -1;	/* errno has already been set */
346 		break;
347 
348 	case BIOCSETF:
349 	case BIOCGDLTLIST:
350 		if ((addr = ioctl_convert_bpf_to_minix(data, &request)) == 0)
351 			return -1;	/* errno has already been set */
352 		break;
353 
354 	default:
355 		/* Keep original as-is */
356 		addr = (vir_bytes)data;
357 		break;
358   }
359 
360   memset(&m, 0, sizeof(m));
361   m.m_lc_vfs_ioctl.fd = fd;
362   m.m_lc_vfs_ioctl.req = request;
363   m.m_lc_vfs_ioctl.arg = addr;
364 
365   r = _syscall(VFS_PROC_NR, VFS_IOCTL, &m);
366 
367   /*
368    * Translate back to original form.  Do this on failure as well, as
369    * temporarily allocated resources may have to be freed up again.
370    */
371   switch (request_save) {
372 	case I2C_IOCTL_EXEC:
373 		rewrite_i2c_minix_to_netbsd(data, &i2c);
374 		break;
375 
376 	case SIOCGIFMEDIA:
377 	case SIOCIFGCLONERS:
378 		if (r == 0)
379 			ioctl_convert_if_from_minix(addr, data, request_save);
380 		free((void *)addr);
381 		break;
382 
383 	case BIOCGDLTLIST:
384 		if (r == 0)
385 			ioctl_convert_bpf_from_minix(addr, data, request_save);
386 		/* FALLTHROUGH */
387 	case BIOCSETF:
388 		free((void *)addr);
389 		break;
390 
391 	default:
392 		/* Nothing to do */
393 		break;
394   }
395 
396   return r;
397 }
398