openat2.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: run various openat2(2) tests
  5. *
  6. */
  7. #include <errno.h>
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <fcntl.h>
  13. #include <sys/uio.h>
  14. #include "helpers.h"
  15. #include "liburing.h"
  16. static int test_openat2(struct io_uring *ring, const char *path, int dfd,
  17. bool direct, int fixed_index, int bad_how)
  18. {
  19. struct io_uring_cqe *cqe;
  20. struct io_uring_sqe *sqe;
  21. struct open_how __how, *how;
  22. int ret;
  23. if (bad_how)
  24. how = (struct open_how *) (uintptr_t) 0x1234;
  25. else
  26. how = &__how;
  27. sqe = io_uring_get_sqe(ring);
  28. if (!sqe) {
  29. fprintf(stderr, "get sqe failed\n");
  30. return -1;
  31. }
  32. if (!bad_how) {
  33. memset(how, 0, sizeof(*how));
  34. how->flags = O_RDWR;
  35. }
  36. if (!direct)
  37. io_uring_prep_openat2(sqe, dfd, path, how);
  38. else
  39. io_uring_prep_openat2_direct(sqe, dfd, path, how, fixed_index);
  40. ret = io_uring_submit(ring);
  41. if (ret <= 0) {
  42. fprintf(stderr, "sqe submit failed: %d\n", ret);
  43. return -1;
  44. }
  45. ret = io_uring_wait_cqe(ring, &cqe);
  46. if (ret < 0) {
  47. fprintf(stderr, "wait completion %d\n", ret);
  48. return -1;
  49. }
  50. ret = cqe->res;
  51. io_uring_cqe_seen(ring, cqe);
  52. if (direct && ret > 0) {
  53. close(ret);
  54. return -EINVAL;
  55. }
  56. return ret;
  57. }
  58. static int test_open_fixed(const char *path, int dfd)
  59. {
  60. struct io_uring_cqe *cqe;
  61. struct io_uring_sqe *sqe;
  62. struct io_uring ring;
  63. const char pattern = 0xac;
  64. char buffer[] = { 0, 0 };
  65. int i, ret, fd = -1;
  66. ret = io_uring_queue_init(8, &ring, 0);
  67. if (ret) {
  68. fprintf(stderr, "ring setup failed\n");
  69. return -1;
  70. }
  71. ret = io_uring_register_files(&ring, &fd, 1);
  72. if (ret) {
  73. if (ret == -EINVAL || ret == -EBADF)
  74. return 0;
  75. fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
  76. return -1;
  77. }
  78. ret = test_openat2(&ring, path, dfd, true, 0, 0);
  79. if (ret == -EINVAL) {
  80. printf("fixed open isn't supported\n");
  81. return 1;
  82. } else if (ret) {
  83. fprintf(stderr, "direct open failed %d\n", ret);
  84. return -1;
  85. }
  86. sqe = io_uring_get_sqe(&ring);
  87. io_uring_prep_write(sqe, 0, &pattern, 1, 0);
  88. sqe->user_data = 1;
  89. sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
  90. sqe = io_uring_get_sqe(&ring);
  91. io_uring_prep_read(sqe, 0, buffer, 1, 0);
  92. sqe->user_data = 2;
  93. sqe->flags |= IOSQE_FIXED_FILE;
  94. ret = io_uring_submit(&ring);
  95. if (ret != 2) {
  96. fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
  97. return -1;
  98. }
  99. for (i = 0; i < 2; i++) {
  100. ret = io_uring_wait_cqe(&ring, &cqe);
  101. if (ret < 0) {
  102. fprintf(stderr, "wait completion %d\n", ret);
  103. return -1;
  104. }
  105. if (cqe->res != 1) {
  106. fprintf(stderr, "unexpectetd ret %d\n", cqe->res);
  107. return -1;
  108. }
  109. io_uring_cqe_seen(&ring, cqe);
  110. }
  111. if (memcmp(&pattern, buffer, 1) != 0) {
  112. fprintf(stderr, "buf validation failed\n");
  113. return -1;
  114. }
  115. io_uring_queue_exit(&ring);
  116. return 0;
  117. }
  118. static int test_open_fixed_fail(const char *path, int dfd)
  119. {
  120. struct io_uring ring;
  121. int ret, fd = -1;
  122. ret = io_uring_queue_init(8, &ring, 0);
  123. if (ret) {
  124. fprintf(stderr, "ring setup failed\n");
  125. return -1;
  126. }
  127. ret = test_openat2(&ring, path, dfd, true, 0, 0);
  128. if (ret != -ENXIO) {
  129. fprintf(stderr, "install into not existing table, %i\n", ret);
  130. return 1;
  131. }
  132. ret = io_uring_register_files(&ring, &fd, 1);
  133. if (ret) {
  134. if (ret == -EINVAL || ret == -EBADF)
  135. return 0;
  136. fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
  137. return -1;
  138. }
  139. ret = test_openat2(&ring, path, dfd, true, 1, 0);
  140. if (ret != -EINVAL) {
  141. fprintf(stderr, "install out of bounds, %i\n", ret);
  142. return -1;
  143. }
  144. ret = test_openat2(&ring, path, dfd, true, (1u << 16), 0);
  145. if (ret != -EINVAL) {
  146. fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
  147. return -1;
  148. }
  149. ret = test_openat2(&ring, path, dfd, true, (1u << 16) + 1, 0);
  150. if (ret != -EINVAL) {
  151. fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
  152. return -1;
  153. }
  154. io_uring_queue_exit(&ring);
  155. return 0;
  156. }
  157. static int test_direct_reinstall(const char *path, int dfd)
  158. {
  159. struct io_uring_cqe *cqe;
  160. struct io_uring_sqe *sqe;
  161. char buf[1] = { 0xfa };
  162. struct io_uring ring;
  163. int ret, pipe_fds[2];
  164. ssize_t ret2;
  165. if (pipe2(pipe_fds, O_NONBLOCK)) {
  166. fprintf(stderr, "pipe() failed\n");
  167. return -1;
  168. }
  169. ret = io_uring_queue_init(8, &ring, 0);
  170. if (ret) {
  171. fprintf(stderr, "ring setup failed\n");
  172. return -1;
  173. }
  174. ret = io_uring_register_files(&ring, pipe_fds, 2);
  175. if (ret) {
  176. fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
  177. return -1;
  178. }
  179. /* reinstall into the second slot */
  180. ret = test_openat2(&ring, path, dfd, true, 1, 0);
  181. if (ret != 0) {
  182. fprintf(stderr, "reinstall failed, %i\n", ret);
  183. return -1;
  184. }
  185. /* verify it's reinstalled, first write into the slot... */
  186. sqe = io_uring_get_sqe(&ring);
  187. io_uring_prep_write(sqe, 1, buf, sizeof(buf), 0);
  188. sqe->flags |= IOSQE_FIXED_FILE;
  189. ret = io_uring_submit(&ring);
  190. if (ret != 1) {
  191. fprintf(stderr, "sqe submit failed: %d\n", ret);
  192. return -1;
  193. }
  194. ret = io_uring_wait_cqe(&ring, &cqe);
  195. if (ret < 0) {
  196. fprintf(stderr, "wait completion %d\n", ret);
  197. return ret;
  198. }
  199. ret = cqe->res;
  200. io_uring_cqe_seen(&ring, cqe);
  201. if (ret != 1) {
  202. fprintf(stderr, "invalid write %i\n", ret);
  203. return -1;
  204. }
  205. /* ... and make sure nothing has been written to the pipe */
  206. ret2 = read(pipe_fds[0], buf, 1);
  207. if (ret2 != 0 && !(ret2 < 0 && errno == EAGAIN)) {
  208. fprintf(stderr, "invalid pipe read, %d %d\n", errno, (int)ret2);
  209. return -1;
  210. }
  211. close(pipe_fds[0]);
  212. close(pipe_fds[1]);
  213. io_uring_queue_exit(&ring);
  214. return 0;
  215. }
  216. int main(int argc, char *argv[])
  217. {
  218. struct io_uring ring;
  219. const char *path, *path_rel;
  220. int ret, do_unlink;
  221. ret = io_uring_queue_init(8, &ring, 0);
  222. if (ret) {
  223. fprintf(stderr, "ring setup failed\n");
  224. return 1;
  225. }
  226. if (argc > 1) {
  227. path = "/tmp/.open.at2";
  228. path_rel = argv[1];
  229. do_unlink = 0;
  230. } else {
  231. path = "/tmp/.open.at2";
  232. path_rel = ".open.at2";
  233. do_unlink = 1;
  234. }
  235. t_create_file(path, 4096);
  236. if (do_unlink)
  237. t_create_file(path_rel, 4096);
  238. ret = test_openat2(&ring, path, -1, false, 0, 0);
  239. if (ret < 0) {
  240. if (ret == -EINVAL) {
  241. fprintf(stdout, "openat2 not supported, skipping\n");
  242. goto done;
  243. }
  244. if (ret == -EPERM || ret == -EACCES)
  245. return T_EXIT_SKIP;
  246. fprintf(stderr, "test_openat2 absolute failed: %d\n", ret);
  247. goto err;
  248. }
  249. ret = test_openat2(&ring, path_rel, AT_FDCWD, false, 0, 0);
  250. if (ret < 0) {
  251. if (ret == -EPERM || ret == -EACCES)
  252. return T_EXIT_SKIP;
  253. fprintf(stderr, "test_openat2 relative failed: %d\n", ret);
  254. goto err;
  255. }
  256. ret = test_open_fixed(path, -1);
  257. if (ret > 0)
  258. goto done;
  259. if (ret) {
  260. fprintf(stderr, "test_open_fixed failed\n");
  261. goto err;
  262. }
  263. ret = test_open_fixed_fail(path, -1);
  264. if (ret) {
  265. fprintf(stderr, "test_open_fixed_fail failed\n");
  266. goto err;
  267. }
  268. ret = test_direct_reinstall(path, -1);
  269. if (ret) {
  270. fprintf(stderr, "test_direct_reinstall failed\n");
  271. goto err;
  272. }
  273. ret = test_openat2(&ring, (const char *) (uintptr_t) 0x1234, AT_FDCWD, false, 0, 0);
  274. if (ret != -EFAULT) {
  275. fprintf(stderr, "test_openat2 bad address failed: %d\n", ret);
  276. goto err;
  277. }
  278. ret = test_openat2(&ring, path_rel, AT_FDCWD, false, 0, 1);
  279. if (ret != -EFAULT) {
  280. fprintf(stderr, "test_openat2 bad how failed: %d\n", ret);
  281. goto err;
  282. }
  283. done:
  284. unlink(path);
  285. if (do_unlink)
  286. unlink(path_rel);
  287. return 0;
  288. err:
  289. unlink(path);
  290. if (do_unlink)
  291. unlink(path_rel);
  292. return 1;
  293. }