| /* SPDX-License-Identifier: MIT */ | 
 | /* | 
 |  * Description: run various statx(2) tests | 
 |  * | 
 |  */ | 
 | #include <errno.h> | 
 | #include <stdio.h> | 
 | #include <unistd.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <fcntl.h> | 
 | #include <sys/types.h> | 
 | #include <sys/syscall.h> | 
 | #include <linux/stat.h> | 
 |  | 
 | #include "liburing.h" | 
 |  | 
 | #ifdef __NR_statx | 
 | static int do_statx(int dfd, const char *path, int flags, unsigned mask, | 
 | 		    struct statx *statxbuf) | 
 | { | 
 | 	return syscall(__NR_statx, dfd, path, flags, mask, statxbuf); | 
 | } | 
 | #else | 
 | static int do_statx(int dfd, const char *path, int flags, unsigned mask, | 
 | 		    struct statx *statxbuf) | 
 | { | 
 | 	errno = ENOSYS; | 
 | 	return -1; | 
 | } | 
 | #endif | 
 |  | 
 | static int create_file(const char *file, size_t size) | 
 | { | 
 | 	ssize_t ret; | 
 | 	char *buf; | 
 | 	int fd; | 
 |  | 
 | 	buf = malloc(size); | 
 | 	memset(buf, 0xaa, size); | 
 |  | 
 | 	fd = open(file, O_WRONLY | O_CREAT, 0644); | 
 | 	if (fd < 0) { | 
 | 		perror("open file"); | 
 | 		return 1; | 
 | 	} | 
 | 	ret = write(fd, buf, size); | 
 | 	close(fd); | 
 | 	return ret != size; | 
 | } | 
 |  | 
 | static int statx_syscall_supported(void) | 
 | { | 
 | 	return errno == ENOSYS ? 0 : -1; | 
 | } | 
 |  | 
 | static int test_statx(struct io_uring *ring, const char *path) | 
 | { | 
 | 	struct io_uring_cqe *cqe; | 
 | 	struct io_uring_sqe *sqe; | 
 | 	struct statx x1, x2; | 
 | 	int ret; | 
 |  | 
 | 	sqe = io_uring_get_sqe(ring); | 
 | 	if (!sqe) { | 
 | 		fprintf(stderr, "get sqe failed\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	io_uring_prep_statx(sqe, -1, path, 0, STATX_ALL, &x1); | 
 |  | 
 | 	ret = io_uring_submit(ring); | 
 | 	if (ret <= 0) { | 
 | 		fprintf(stderr, "sqe submit failed: %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	ret = io_uring_wait_cqe(ring, &cqe); | 
 | 	if (ret < 0) { | 
 | 		fprintf(stderr, "wait completion %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 | 	ret = cqe->res; | 
 | 	io_uring_cqe_seen(ring, cqe); | 
 | 	if (ret) | 
 | 		return ret; | 
 | 	ret = do_statx(-1, path, 0, STATX_ALL, &x2); | 
 | 	if (ret < 0) | 
 | 		return statx_syscall_supported(); | 
 | 	if (memcmp(&x1, &x2, sizeof(x1))) { | 
 | 		fprintf(stderr, "Miscompare between io_uring and statx\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	return 0; | 
 | err: | 
 | 	return -1; | 
 | } | 
 |  | 
 | static int test_statx_fd(struct io_uring *ring, const char *path) | 
 | { | 
 | 	struct io_uring_cqe *cqe; | 
 | 	struct io_uring_sqe *sqe; | 
 | 	struct statx x1, x2; | 
 | 	int ret, fd; | 
 |  | 
 | 	fd = open(path, O_RDONLY); | 
 | 	if (fd < 0) { | 
 | 		perror("open"); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	memset(&x1, 0, sizeof(x1)); | 
 |  | 
 | 	sqe = io_uring_get_sqe(ring); | 
 | 	if (!sqe) { | 
 | 		fprintf(stderr, "get sqe failed\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	io_uring_prep_statx(sqe, fd, "", AT_EMPTY_PATH, STATX_ALL, &x1); | 
 |  | 
 | 	ret = io_uring_submit(ring); | 
 | 	if (ret <= 0) { | 
 | 		fprintf(stderr, "sqe submit failed: %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	ret = io_uring_wait_cqe(ring, &cqe); | 
 | 	if (ret < 0) { | 
 | 		fprintf(stderr, "wait completion %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 | 	ret = cqe->res; | 
 | 	io_uring_cqe_seen(ring, cqe); | 
 | 	if (ret) | 
 | 		return ret; | 
 | 	memset(&x2, 0, sizeof(x2)); | 
 | 	ret = do_statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &x2); | 
 | 	if (ret < 0) | 
 | 		return statx_syscall_supported(); | 
 | 	if (memcmp(&x1, &x2, sizeof(x1))) { | 
 | 		fprintf(stderr, "Miscompare between io_uring and statx\n"); | 
 | 		goto err; | 
 | 	} | 
 | 	return 0; | 
 | err: | 
 | 	return -1; | 
 | } | 
 |  | 
 | int main(int argc, char *argv[]) | 
 | { | 
 | 	struct io_uring ring; | 
 | 	const char *fname; | 
 | 	int ret; | 
 |  | 
 | 	ret = io_uring_queue_init(8, &ring, 0); | 
 | 	if (ret) { | 
 | 		fprintf(stderr, "ring setup failed\n"); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	if (argc > 1) { | 
 | 		fname = argv[1]; | 
 | 	} else { | 
 | 		fname = "/tmp/.statx"; | 
 | 		if (create_file(fname, 4096)) { | 
 | 			fprintf(stderr, "file create failed\n"); | 
 | 			return 1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	ret = test_statx(&ring, fname); | 
 | 	if (ret) { | 
 | 		if (ret == -EINVAL) { | 
 | 			fprintf(stdout, "statx not supported, skipping\n"); | 
 | 			goto done; | 
 | 		} | 
 | 		fprintf(stderr, "test_statx failed: %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	ret = test_statx_fd(&ring, fname); | 
 | 	if (ret) { | 
 | 		fprintf(stderr, "test_statx_fd failed: %d\n", ret); | 
 | 		goto err; | 
 | 	} | 
 | done: | 
 | 	if (fname != argv[1]) | 
 | 		unlink(fname); | 
 | 	return 0; | 
 | err: | 
 | 	if (fname != argv[1]) | 
 | 		unlink(fname); | 
 | 	return 1; | 
 | } |