diff --git a/include/uv.h b/include/uv.h index 3b6b7224..b5744a8d 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1398,7 +1398,8 @@ typedef enum { UV_FS_CLOSEDIR, UV_FS_STATFS, UV_FS_MKSTEMP, - UV_FS_LUTIME + UV_FS_LUTIME, + UV_FS_OPENAT } uv_fs_type; struct uv_dir_s { @@ -1439,6 +1440,13 @@ UV_EXTERN int uv_fs_open(uv_loop_t* loop, int flags, int mode, uv_fs_cb cb); +UV_EXTERN int uv_fs_openat(uv_loop_t* loop, + uv_fs_t* req, + uv_os_fd_t file, + const char* path, + int flags, + int mode, + uv_fs_cb cb); UV_EXTERN int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, diff --git a/src/unix/fs.c b/src/unix/fs.c index c4eadd63..40b57465 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -504,6 +504,34 @@ static ssize_t uv__pwritev(int fd, return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/0); } +static ssize_t uv__fs_openat(uv_fs_t* req) { +#ifdef O_CLOEXEC + return openat(req->file, req->path, req->flags | O_CLOEXEC, req->mode); +#else /* O_CLOEXEC */ + int r; + + if (req->cb != NULL) + uv_rwlock_rdlock(&req->loop->cloexec_lock); + + r = openat(req->file, req->path, req->flags, req->mode); + + /* In case of failure `uv__cloexec` will leave error in `errno`, + * so it is enough to just set `r` to `-1`. + */ + if (r >= 0 && uv__cloexec(r, 1) != 0) { + r = uv__close(r); + if (r != 0) + abort(); + r = -1; + } + + if (req->cb != NULL) + uv_rwlock_rdunlock(&req->loop->cloexec_lock); + + return r; +#endif /* O_CLOEXEC */ +} + static ssize_t uv__fs_read(uv_fs_t* req) { const struct iovec* bufs; @@ -1718,6 +1746,7 @@ static void uv__fs_work(struct uv__work* w) { X(MKDTEMP, uv__fs_mkdtemp(req)); X(MKSTEMP, uv__fs_mkstemp(req)); X(OPEN, uv__fs_open(req)); + X(OPENAT, uv__fs_openat(req)); X(READ, uv__fs_read(req)); X(SCANDIR, uv__fs_scandir(req)); X(OPENDIR, uv__fs_opendir(req)); @@ -2013,6 +2042,24 @@ int uv_fs_open(uv_loop_t* loop, POST; } +int uv_fs_openat(uv_loop_t* loop, + uv_fs_t* req, + uv_os_fd_t file, + const char* path, + int flags, + int mode, + uv_fs_cb cb) { + INIT(OPENAT); + PATH; + req->file = file; + req->flags = flags; + req->mode = mode; + if (cb != NULL) + if (uv__iou_fs_openat(loop, req)) + return 0; + POST; +} + int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, diff --git a/src/unix/internal.h b/src/unix/internal.h index 3ad37052..b0617d57 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -338,6 +338,7 @@ int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop, int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req); int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req); int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_openat(uv_loop_t* loop, uv_fs_t* req); int uv__iou_fs_read_or_write(uv_loop_t* loop, uv_fs_t* req, int is_read); @@ -354,6 +355,7 @@ int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req); #define uv__iou_fs_link(loop, req) 0 #define uv__iou_fs_mkdir(loop, req) 0 #define uv__iou_fs_open(loop, req) 0 +#define uv__iou_fs_openat(loop, req) 0 #define uv__iou_fs_read_or_write(loop, req, is_read) 0 #define uv__iou_fs_rename(loop, req) 0 #define uv__iou_fs_statx(loop, req, is_fstat, is_lstat) 0 diff --git a/src/unix/linux.c b/src/unix/linux.c index 8fdcb12c..46bea209 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -948,6 +948,28 @@ int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req) { } +int uv__iou_fs_openat(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = req->file; + sqe->len = req->mode; + sqe->opcode = UV__IORING_OP_OPENAT; + sqe->open_flags = req->flags | O_CLOEXEC; + + uv__iou_submit(iou); + + return 1; +} + + int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req) { struct uv__io_uring_sqe* sqe; struct uv__iou* iou; diff --git a/test/test-fs.c b/test/test-fs.c index fe78117b..c2828aa7 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -3006,6 +3006,60 @@ TEST_IMPL(fs_scandir_early_exit) { } +TEST_IMPL(fs_openat) { + uv_fs_t req; + int r; + uv_os_fd_t fd; + uv_os_fd_t dirfd; + + /* Setup. */ + unlink("test/fixtures/test_dir/test_file"); + rmdir("test/fixtures/test_dir"); + + loop = uv_default_loop(); + + r = uv_fs_mkdir(NULL, &req, "test/fixtures/test_dir", 0755, NULL); + ASSERT_OK(r); + + r = uv_fs_open(NULL, + &req, + "test/fixtures/test_dir", + UV_FS_O_RDONLY | UV_FS_O_DIRECTORY, + 0, + NULL); + ASSERT_GE(r, 0); + uv_fs_req_cleanup(&req); + + dirfd = (uv_os_fd_t) req.result; + + r = uv_fs_openat(NULL, + &req, + dirfd, + "test_file", + UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + uv_fs_req_cleanup(&req); + + fd = (uv_os_fd_t) req.result; + + r = uv_fs_close(NULL, &req, dirfd, NULL); + ASSERT_OK(r); + uv_fs_req_cleanup(&req); + r = uv_fs_close(NULL, &req, fd, NULL); + ASSERT_OK(r); + uv_fs_req_cleanup(&req); + + /* Cleanup */ + unlink("test/fixtures/test_dir/test_file"); + rmdir("test/fixtures/test_dir"); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + TEST_IMPL(fs_open_dir) { const char* path; uv_fs_t req; diff --git a/test/test-list.h b/test/test-list.h index ad4593d8..394c805b 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -420,6 +420,7 @@ TEST_DECLARE (fs_scandir_empty_dir) TEST_DECLARE (fs_scandir_non_existent_dir) TEST_DECLARE (fs_scandir_file) TEST_DECLARE (fs_scandir_early_exit) +TEST_DECLARE (fs_openat) TEST_DECLARE (fs_open_dir) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) @@ -1127,6 +1128,7 @@ TASK_LIST_START TEST_ENTRY (fs_scandir_non_existent_dir) TEST_ENTRY (fs_scandir_file) TEST_ENTRY (fs_scandir_early_exit) + TEST_ENTRY (fs_openat) TEST_ENTRY (fs_open_dir) TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file)