diff options
Diffstat (limited to 'src/misc')
-rw-r--r-- | src/misc/ioctl.c | 83 |
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); |