Here's a sample test case showing the various transformations and testing validity. /* SPDX-License-Identifier: MIT */ /* * Description: test installing a direct descriptor into the regular * file table * */ #include <errno.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include "liburing.h" #include "helpers.h" static int no_fd_install; static void io_uring_prep_fixed_fd_install(struct io_uring_sqe *sqe, int fd) { memset(sqe, 0, sizeof(*sqe)); sqe->opcode = IORING_OP_FIXED_FD_INSTALL; sqe->fd = fd; } int main(int argc, char *argv[]) { struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; struct io_uring ring; int ret, fds[2]; char buf[32]; if (argc > 1) return T_EXIT_SKIP; ret = io_uring_queue_init(1, &ring, 0); if (ret) { fprintf(stderr, "ring setup failed: %d\n", ret); return T_EXIT_FAIL; } if (pipe(fds) < 0) { perror("pipe"); return T_EXIT_FAIL; } /* register read side */ ret = io_uring_register_files(&ring, &fds[0], 1); if (ret) { fprintf(stderr, "failed register files %d\n", ret); return T_EXIT_FAIL; } /* close normal descriptor */ close(fds[0]); /* normal read should fail */ ret = read(fds[0], buf, 1); if (ret != -1) { fprintf(stderr, "unexpected read ret %d\n", ret); return T_EXIT_FAIL; } if (errno != EBADF) { fprintf(stderr, "unexpected read failure %d\n", errno); return T_EXIT_FAIL; } /* verify we can read the data */ sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0); sqe->flags = IOSQE_FIXED_FILE; io_uring_submit(&ring); /* put some data in the pipe */ write(fds[1], "Hello", 5); ret = io_uring_wait_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); return T_EXIT_FAIL; } if (cqe->res != 5) { fprintf(stderr, "weird pipe read ret %d\n", cqe->res); return T_EXIT_FAIL; } io_uring_cqe_seen(&ring, cqe); /* fixed pipe read worked, now re-install as a regular fd */ sqe = io_uring_get_sqe(&ring); io_uring_prep_fixed_fd_install(sqe, 0); sqe->flags = IOSQE_FIXED_FILE; io_uring_submit(&ring); ret = io_uring_wait_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); return T_EXIT_FAIL; } if (cqe->res == -EINVAL) { no_fd_install = 1; return T_EXIT_SKIP; } if (cqe->res < 0) { fprintf(stderr, "failed install fd: %d\n", cqe->res); return T_EXIT_FAIL; } /* stash new pipe read side fd in old spot */ fds[0] = cqe->res; io_uring_cqe_seen(&ring, cqe); write(fds[1], "Hello", 5); /* normal pipe read should now work with new fd */ ret = read(fds[0], buf, sizeof(buf)); if (ret != 5) { fprintf(stderr, "unexpected read ret %d\n", ret); return T_EXIT_FAIL; } /* close fixed file */ sqe = io_uring_get_sqe(&ring); io_uring_prep_close_direct(sqe, 0); io_uring_submit(&ring); ret = io_uring_wait_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); return T_EXIT_FAIL; } if (cqe->res) { fprintf(stderr, "close fixed fd %d\n", cqe->res); return T_EXIT_FAIL; } io_uring_cqe_seen(&ring, cqe); write(fds[1], "Hello", 5); /* normal pipe read should still work with new fd */ ret = read(fds[0], buf, sizeof(buf)); if (ret != 5) { fprintf(stderr, "unexpected read ret %d\n", ret); return T_EXIT_FAIL; } /* fixed fd pipe read should now fail */ sqe = io_uring_get_sqe(&ring); io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0); sqe->flags = IOSQE_FIXED_FILE; io_uring_submit(&ring); /* put some data in the pipe */ write(fds[1], "Hello", 5); ret = io_uring_wait_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); return T_EXIT_FAIL; } if (cqe->res != -EBADF) { fprintf(stderr, "weird pipe read ret %d\n", cqe->res); return T_EXIT_FAIL; } io_uring_cqe_seen(&ring, cqe); return T_EXIT_PASS; } -- Jens Axboe