summaryrefslogtreecommitdiff
path: root/src/misc
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2019-12-18 20:48:58 -0500
committerRich Felker <dalias@aerifal.cx>2019-12-18 23:17:28 -0500
commit221b1a1d0ae5de7ddc76a577f591f7ded090cc44 (patch)
treeaa7e91ce845442caa451ebe112d6ca3b181b4d66 /src/misc
parentb3290956508b808b245c3517013cbf2dcb1775ac (diff)
downloadmusl-221b1a1d0ae5de7ddc76a577f591f7ded090cc44.tar.gz
convert ioctl time64 fallbacks to table-driven framework
with the current set of supported ioctls, this conversion is hardly an improvement, but it sets the stage for being able to do alsa, v4l2, ppp, and other ioctls with timespec/timeval-derived types. without this capability, a lot of functionality users depend on would stop working with the time64 switchover.
Diffstat (limited to 'src/misc')
-rw-r--r--src/misc/ioctl.c83
1 files changed, 66 insertions, 17 deletions
diff --git a/src/misc/ioctl.c b/src/misc/ioctl.c
index 6f31d4bc..48613b3d 100644
--- a/src/misc/ioctl.c
+++ b/src/misc/ioctl.c
@@ -3,8 +3,63 @@
#include <errno.h>
#include <time.h>
#include <sys/time.h>
+#include <stddef.h>
+#include <string.h>
#include "syscall.h"
+#define alignof(t) offsetof(struct { char c; t x; }, x)
+
+#define W 1
+#define R 2
+#define WR 3
+
+struct ioctl_compat_map {
+ int new_req, old_req, old_size;
+ char dir, force_align;
+ int offsets[4];
+};
+
+static const struct ioctl_compat_map compat_map[] = {
+ { SIOCGSTAMP, SIOCGSTAMP_OLD, 8, R, 0, { 0, -1, -1, -1 } },
+ { SIOCGSTAMPNS, SIOCGSTAMPNS_OLD, 8, R, 0, { 0, -1, -1, -1 } },
+};
+
+static void convert_ioctl_struct(const struct ioctl_compat_map *map, char *old, char *new, int dir)
+{
+ int new_offset = 0;
+ int old_offset = 0;
+ int old_size = map->old_size;
+ if (!(dir & map->dir)) return;
+ for (int i=0; i < sizeof map->offsets / sizeof *map->offsets; i++) {
+ int ts_offset = map->offsets[i];
+ if (ts_offset < 0) break;
+ int len = ts_offset-old_offset;
+ if (dir==W) memcpy(old+old_offset, new+new_offset, len);
+ else memcpy(new+new_offset, old+old_offset, len);
+ new_offset += len;
+ old_offset += len;
+ long long new_ts[2];
+ long old_ts[2];
+ int align = map->force_align ? sizeof(time_t) : alignof(time_t);
+ new_offset += (align-1) & -new_offset;
+ if (dir==W) {
+ memcpy(new_ts, new+new_offset, sizeof new_ts);
+ old_ts[0] = new_ts[0];
+ old_ts[1] = new_ts[1];
+ memcpy(old+old_offset, old_ts, sizeof old_ts);
+ } else {
+ memcpy(old_ts, old+old_offset, sizeof old_ts);
+ new_ts[0] = old_ts[0];
+ new_ts[1] = old_ts[1];
+ memcpy(new+new_offset, new_ts, sizeof new_ts);
+ }
+ new_offset += sizeof new_ts;
+ old_offset += sizeof old_ts;
+ }
+ if (dir==W) memcpy(old+old_offset, new+new_offset, old_size-old_offset);
+ else memcpy(new+new_offset, old+old_offset, old_size-old_offset);
+}
+
int ioctl(int fd, int req, ...)
{
void *arg;
@@ -13,23 +68,17 @@ int ioctl(int fd, int req, ...)
arg = va_arg(ap, void *);
va_end(ap);
int r = __syscall(SYS_ioctl, fd, req, arg);
- if (r==-ENOTTY) switch (req) {
- case SIOCGSTAMP:
- case SIOCGSTAMPNS:
- if (SIOCGSTAMP==SIOCGSTAMP_OLD) break;
- if (req==SIOCGSTAMP) req=SIOCGSTAMP_OLD;
- if (req==SIOCGSTAMPNS) req=SIOCGSTAMPNS_OLD;
- long t32[2];
- r = __syscall(SYS_ioctl, fd, req, t32);
- if (r<0) break;
- if (req==SIOCGSTAMP_OLD) {
- struct timeval *tv = arg;
- tv->tv_sec = t32[0];
- tv->tv_usec = t32[1];
- } else {
- struct timespec *ts = arg;
- ts->tv_sec = t32[0];
- ts->tv_nsec = t32[1];
+ if (r==-ENOTTY) {
+ for (int i=0; i<sizeof compat_map/sizeof *compat_map; i++) {
+ if (compat_map[i].new_req != req) continue;
+ union {
+ long long align;
+ char buf[256];
+ } u;
+ convert_ioctl_struct(&compat_map[i], u.buf, arg, W);
+ r = __syscall(SYS_ioctl, fd, compat_map[i].old_req, u.buf);
+ if (r<0) break;
+ convert_ioctl_struct(&compat_map[i], u.buf, arg, R);
}
}
return __syscall_ret(r);