ioctl.c 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #include <sys/ioctl.h>
  2. #include <stdarg.h>
  3. #include <errno.h>
  4. #include <time.h>
  5. #include <sys/time.h>
  6. #include <stddef.h>
  7. #include <string.h>
  8. #include "syscall.h"
  9. #define alignof(t) offsetof(struct { char c; t x; }, x)
  10. #define W 1
  11. #define R 2
  12. #define WR 3
  13. struct ioctl_compat_map {
  14. int new_req, old_req, old_size;
  15. char dir, force_align;
  16. int offsets[4];
  17. };
  18. static const struct ioctl_compat_map compat_map[] = {
  19. { SIOCGSTAMP, SIOCGSTAMP_OLD, 8, R, 0, { 0, -1, -1, -1 } },
  20. { SIOCGSTAMPNS, SIOCGSTAMPNS_OLD, 8, R, 0, { 0, -1, -1, -1 } },
  21. };
  22. static void convert_ioctl_struct(const struct ioctl_compat_map *map, char *old, char *new, int dir)
  23. {
  24. int new_offset = 0;
  25. int old_offset = 0;
  26. int old_size = map->old_size;
  27. if (!(dir & map->dir)) return;
  28. for (int i=0; i < sizeof map->offsets / sizeof *map->offsets; i++) {
  29. int ts_offset = map->offsets[i];
  30. if (ts_offset < 0) break;
  31. int len = ts_offset-old_offset;
  32. if (dir==W) memcpy(old+old_offset, new+new_offset, len);
  33. else memcpy(new+new_offset, old+old_offset, len);
  34. new_offset += len;
  35. old_offset += len;
  36. long long new_ts[2];
  37. long old_ts[2];
  38. int align = map->force_align ? sizeof(time_t) : alignof(time_t);
  39. new_offset += (align-1) & -new_offset;
  40. if (dir==W) {
  41. memcpy(new_ts, new+new_offset, sizeof new_ts);
  42. old_ts[0] = new_ts[0];
  43. old_ts[1] = new_ts[1];
  44. memcpy(old+old_offset, old_ts, sizeof old_ts);
  45. } else {
  46. memcpy(old_ts, old+old_offset, sizeof old_ts);
  47. new_ts[0] = old_ts[0];
  48. new_ts[1] = old_ts[1];
  49. memcpy(new+new_offset, new_ts, sizeof new_ts);
  50. }
  51. new_offset += sizeof new_ts;
  52. old_offset += sizeof old_ts;
  53. }
  54. if (dir==W) memcpy(old+old_offset, new+new_offset, old_size-old_offset);
  55. else memcpy(new+new_offset, old+old_offset, old_size-old_offset);
  56. }
  57. int ioctl(int fd, int req, ...)
  58. {
  59. void *arg;
  60. va_list ap;
  61. va_start(ap, req);
  62. arg = va_arg(ap, void *);
  63. va_end(ap);
  64. int r = __syscall(SYS_ioctl, fd, req, arg);
  65. if (r==-ENOTTY) {
  66. for (int i=0; i<sizeof compat_map/sizeof *compat_map; i++) {
  67. if (compat_map[i].new_req != req) continue;
  68. union {
  69. long long align;
  70. char buf[256];
  71. } u;
  72. convert_ioctl_struct(&compat_map[i], u.buf, arg, W);
  73. r = __syscall(SYS_ioctl, fd, compat_map[i].old_req, u.buf);
  74. if (r<0) break;
  75. convert_ioctl_struct(&compat_map[i], u.buf, arg, R);
  76. }
  77. }
  78. return __syscall_ret(r);
  79. }