| ... | ... | @@ -809,6 +809,23 @@ pub const IO_Uring = struct { |
| 809 | 809 | return sqe; |
| 810 | 810 | } |
| 811 | 811 | |
| 812 | /// Queues (but does not submit) an SQE to perform a `linkat(2)`. |
| 813 | /// Returns a pointer to the SQE. |
| 814 | pub fn linkat( |
| 815 | self: *IO_Uring, |
| 816 | user_data: u64, |
| 817 | old_dir_fd: os.fd_t, |
| 818 | old_path: [*:0]const u8, |
| 819 | new_dir_fd: os.fd_t, |
| 820 | new_path: [*:0]const u8, |
| 821 | flags: u32, |
| 822 | ) !*io_uring_sqe { |
| 823 | const sqe = try self.get_sqe(); |
| 824 | io_uring_prep_linkat(sqe, old_dir_fd, old_path, new_dir_fd, new_path, flags); |
| 825 | sqe.user_data = user_data; |
| 826 | return sqe; |
| 827 | } |
| 828 | |
| 812 | 829 | /// Registers an array of file descriptors. |
| 813 | 830 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 814 | 831 | /// retrieve a reference to the file, and once I/O has completed the file reference must be |
| ... | ... | @@ -1420,6 +1437,26 @@ pub fn io_uring_prep_symlinkat( |
| 1420 | 1437 | ); |
| 1421 | 1438 | } |
| 1422 | 1439 | |
| 1440 | pub fn io_uring_prep_linkat( |
| 1441 | sqe: *io_uring_sqe, |
| 1442 | old_dir_fd: os.fd_t, |
| 1443 | old_path: [*:0]const u8, |
| 1444 | new_dir_fd: os.fd_t, |
| 1445 | new_path: [*:0]const u8, |
| 1446 | flags: u32, |
| 1447 | ) void { |
| 1448 | io_uring_prep_rw( |
| 1449 | .LINKAT, |
| 1450 | sqe, |
| 1451 | old_dir_fd, |
| 1452 | @ptrToInt(old_path), |
| 1453 | 0, |
| 1454 | @ptrToInt(new_path), |
| 1455 | ); |
| 1456 | sqe.len = @bitCast(u32, new_dir_fd); |
| 1457 | sqe.rw_flags = flags; |
| 1458 | } |
| 1459 | |
| 1423 | 1460 | test "structs/offsets/entries" { |
| 1424 | 1461 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1425 | 1462 | |
| ... | ... | @@ -2603,3 +2640,63 @@ test "symlinkat" { |
| 2603 | 2640 | // Validate that the symlink exist |
| 2604 | 2641 | _ = try std.fs.cwd().openFile(link_path, .{}); |
| 2605 | 2642 | } |
| 2643 | |
| 2644 | test "linkat" { |
| 2645 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2646 | |
| 2647 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| 2648 | error.SystemOutdated => return error.SkipZigTest, |
| 2649 | error.PermissionDenied => return error.SkipZigTest, |
| 2650 | else => return err, |
| 2651 | }; |
| 2652 | defer ring.deinit(); |
| 2653 | |
| 2654 | const first_path = "test_io_uring_linkat_first"; |
| 2655 | const second_path = "test_io_uring_linkat_second"; |
| 2656 | |
| 2657 | // Write file with data |
| 2658 | |
| 2659 | const first_file = try std.fs.cwd().createFile(first_path, .{ .truncate = true, .mode = 0o666 }); |
| 2660 | defer { |
| 2661 | first_file.close(); |
| 2662 | std.fs.cwd().deleteFile(first_path) catch {}; |
| 2663 | std.fs.cwd().deleteFile(second_path) catch {}; |
| 2664 | } |
| 2665 | try first_file.writeAll("hello"); |
| 2666 | |
| 2667 | // Submit linkat |
| 2668 | |
| 2669 | var sqe = try ring.linkat( |
| 2670 | 0x12121212, |
| 2671 | linux.AT.FDCWD, |
| 2672 | first_path, |
| 2673 | linux.AT.FDCWD, |
| 2674 | second_path, |
| 2675 | 0, |
| 2676 | ); |
| 2677 | try testing.expectEqual(linux.IORING_OP.LINKAT, sqe.opcode); |
| 2678 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| 2679 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); |
| 2680 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2681 | |
| 2682 | const cqe = try ring.copy_cqe(); |
| 2683 | switch (cqe.err()) { |
| 2684 | .SUCCESS => {}, |
| 2685 | // This kernel's io_uring does not yet implement linkat (kernel version < 5.15) |
| 2686 | .INVAL => return error.SkipZigTest, |
| 2687 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2688 | } |
| 2689 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2690 | .user_data = 0x12121212, |
| 2691 | .res = 0, |
| 2692 | .flags = 0, |
| 2693 | }, cqe); |
| 2694 | |
| 2695 | // Validate the second file |
| 2696 | const second_file = try std.fs.cwd().openFile(second_path, .{}); |
| 2697 | defer second_file.close(); |
| 2698 | |
| 2699 | var second_file_data: [16]u8 = undefined; |
| 2700 | const read = try second_file.readAll(&second_file_data); |
| 2701 | try testing.expectEqualStrings("hello", second_file_data[0..read]); |
| 2702 | } |