| ... | ... | @@ -731,6 +731,101 @@ pub const IO_Uring = struct { |
| 731 | 731 | return sqe; |
| 732 | 732 | } |
| 733 | 733 | |
| 734 | /// Queues (but does not submit) an SQE to perform a `shutdown(2)`. |
| 735 | /// Returns a pointer to the SQE. |
| 736 | /// |
| 737 | /// The operation is identified by its `user_data`. |
| 738 | pub fn shutdown( |
| 739 | self: *IO_Uring, |
| 740 | user_data: u64, |
| 741 | sockfd: os.socket_t, |
| 742 | how: u32, |
| 743 | ) !*io_uring_sqe { |
| 744 | const sqe = try self.get_sqe(); |
| 745 | io_uring_prep_shutdown(sqe, sockfd, how); |
| 746 | sqe.user_data = user_data; |
| 747 | return sqe; |
| 748 | } |
| 749 | |
| 750 | /// Queues (but does not submit) an SQE to perform a `renameat2(2)`. |
| 751 | /// Returns a pointer to the SQE. |
| 752 | pub fn renameat( |
| 753 | self: *IO_Uring, |
| 754 | user_data: u64, |
| 755 | old_dir_fd: os.fd_t, |
| 756 | old_path: [*:0]const u8, |
| 757 | new_dir_fd: os.fd_t, |
| 758 | new_path: [*:0]const u8, |
| 759 | flags: u32, |
| 760 | ) !*io_uring_sqe { |
| 761 | const sqe = try self.get_sqe(); |
| 762 | io_uring_prep_renameat(sqe, old_dir_fd, old_path, new_dir_fd, new_path, flags); |
| 763 | sqe.user_data = user_data; |
| 764 | return sqe; |
| 765 | } |
| 766 | |
| 767 | /// Queues (but does not submit) an SQE to perform a `unlinkat(2)`. |
| 768 | /// Returns a pointer to the SQE. |
| 769 | pub fn unlinkat( |
| 770 | self: *IO_Uring, |
| 771 | user_data: u64, |
| 772 | dir_fd: os.fd_t, |
| 773 | path: [*:0]const u8, |
| 774 | flags: u32, |
| 775 | ) !*io_uring_sqe { |
| 776 | const sqe = try self.get_sqe(); |
| 777 | io_uring_prep_unlinkat(sqe, dir_fd, path, flags); |
| 778 | sqe.user_data = user_data; |
| 779 | return sqe; |
| 780 | } |
| 781 | |
| 782 | /// Queues (but does not submit) an SQE to perform a `mkdirat(2)`. |
| 783 | /// Returns a pointer to the SQE. |
| 784 | pub fn mkdirat( |
| 785 | self: *IO_Uring, |
| 786 | user_data: u64, |
| 787 | dir_fd: os.fd_t, |
| 788 | path: [*:0]const u8, |
| 789 | mode: os.mode_t, |
| 790 | ) !*io_uring_sqe { |
| 791 | const sqe = try self.get_sqe(); |
| 792 | io_uring_prep_mkdirat(sqe, dir_fd, path, mode); |
| 793 | sqe.user_data = user_data; |
| 794 | return sqe; |
| 795 | } |
| 796 | |
| 797 | /// Queues (but does not submit) an SQE to perform a `symlinkat(2)`. |
| 798 | /// Returns a pointer to the SQE. |
| 799 | pub fn symlinkat( |
| 800 | self: *IO_Uring, |
| 801 | user_data: u64, |
| 802 | target: [*:0]const u8, |
| 803 | new_dir_fd: os.fd_t, |
| 804 | link_path: [*:0]const u8, |
| 805 | ) !*io_uring_sqe { |
| 806 | const sqe = try self.get_sqe(); |
| 807 | io_uring_prep_symlinkat(sqe, target, new_dir_fd, link_path); |
| 808 | sqe.user_data = user_data; |
| 809 | return sqe; |
| 810 | } |
| 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 | |
| 734 | 829 | /// Registers an array of file descriptors. |
| 735 | 830 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 736 | 831 | /// retrieve a reference to the file, and once I/O has completed the file reference must be |
| ... | ... | @@ -798,7 +893,7 @@ pub const IO_Uring = struct { |
| 798 | 893 | } |
| 799 | 894 | |
| 800 | 895 | /// Registers the file descriptor for an eventfd that will be notified of completion events on |
| 801 | | /// an io_uring instance. Notifications are only posted for events that complete in an async manner. |
| 896 | /// an io_uring instance. Notifications are only posted for events that complete in an async manner. |
| 802 | 897 | /// This means that events that complete inline while being submitted do not trigger a notification event. |
| 803 | 898 | /// Only a single eventfd can be registered at any given point in time. |
| 804 | 899 | pub fn register_eventfd_async(self: *IO_Uring, fd: os.fd_t) !void { |
| ... | ... | @@ -1279,6 +1374,89 @@ pub fn io_uring_prep_cancel( |
| 1279 | 1374 | sqe.rw_flags = flags; |
| 1280 | 1375 | } |
| 1281 | 1376 | |
| 1377 | pub fn io_uring_prep_shutdown( |
| 1378 | sqe: *io_uring_sqe, |
| 1379 | sockfd: os.socket_t, |
| 1380 | how: u32, |
| 1381 | ) void { |
| 1382 | io_uring_prep_rw(.SHUTDOWN, sqe, sockfd, 0, how, 0); |
| 1383 | } |
| 1384 | |
| 1385 | pub fn io_uring_prep_renameat( |
| 1386 | sqe: *io_uring_sqe, |
| 1387 | old_dir_fd: os.fd_t, |
| 1388 | old_path: [*:0]const u8, |
| 1389 | new_dir_fd: os.fd_t, |
| 1390 | new_path: [*:0]const u8, |
| 1391 | flags: u32, |
| 1392 | ) void { |
| 1393 | io_uring_prep_rw( |
| 1394 | .RENAMEAT, |
| 1395 | sqe, |
| 1396 | old_dir_fd, |
| 1397 | @ptrToInt(old_path), |
| 1398 | 0, |
| 1399 | @ptrToInt(new_path), |
| 1400 | ); |
| 1401 | sqe.len = @bitCast(u32, new_dir_fd); |
| 1402 | sqe.rw_flags = flags; |
| 1403 | } |
| 1404 | |
| 1405 | pub fn io_uring_prep_unlinkat( |
| 1406 | sqe: *io_uring_sqe, |
| 1407 | dir_fd: os.fd_t, |
| 1408 | path: [*:0]const u8, |
| 1409 | flags: u32, |
| 1410 | ) void { |
| 1411 | io_uring_prep_rw(.UNLINKAT, sqe, dir_fd, @ptrToInt(path), 0, 0); |
| 1412 | sqe.rw_flags = flags; |
| 1413 | } |
| 1414 | |
| 1415 | pub fn io_uring_prep_mkdirat( |
| 1416 | sqe: *io_uring_sqe, |
| 1417 | dir_fd: os.fd_t, |
| 1418 | path: [*:0]const u8, |
| 1419 | mode: os.mode_t, |
| 1420 | ) void { |
| 1421 | io_uring_prep_rw(.MKDIRAT, sqe, dir_fd, @ptrToInt(path), mode, 0); |
| 1422 | } |
| 1423 | |
| 1424 | pub fn io_uring_prep_symlinkat( |
| 1425 | sqe: *io_uring_sqe, |
| 1426 | target: [*:0]const u8, |
| 1427 | new_dir_fd: os.fd_t, |
| 1428 | link_path: [*:0]const u8, |
| 1429 | ) void { |
| 1430 | io_uring_prep_rw( |
| 1431 | .SYMLINKAT, |
| 1432 | sqe, |
| 1433 | new_dir_fd, |
| 1434 | @ptrToInt(target), |
| 1435 | 0, |
| 1436 | @ptrToInt(link_path), |
| 1437 | ); |
| 1438 | } |
| 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 | |
| 1282 | 1460 | test "structs/offsets/entries" { |
| 1283 | 1461 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1284 | 1462 | |
| ... | ... | @@ -2191,3 +2369,334 @@ test "register_files_update" { |
| 2191 | 2369 | |
| 2192 | 2370 | try ring.unregister_files(); |
| 2193 | 2371 | } |
| 2372 | |
| 2373 | test "shutdown" { |
| 2374 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2375 | |
| 2376 | var ring = IO_Uring.init(16, 0) catch |err| switch (err) { |
| 2377 | error.SystemOutdated => return error.SkipZigTest, |
| 2378 | error.PermissionDenied => return error.SkipZigTest, |
| 2379 | else => return err, |
| 2380 | }; |
| 2381 | defer ring.deinit(); |
| 2382 | |
| 2383 | const address = try net.Address.parseIp4("127.0.0.1", 3131); |
| 2384 | |
| 2385 | // Socket bound, expect shutdown to work |
| 2386 | { |
| 2387 | const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
| 2388 | defer os.close(server); |
| 2389 | try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); |
| 2390 | try os.bind(server, &address.any, address.getOsSockLen()); |
| 2391 | try os.listen(server, 1); |
| 2392 | |
| 2393 | var shutdown_sqe = try ring.shutdown(0x445445445, server, os.linux.SHUT.RD); |
| 2394 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| 2395 | try testing.expectEqual(@as(i32, server), shutdown_sqe.fd); |
| 2396 | |
| 2397 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2398 | |
| 2399 | const cqe = try ring.copy_cqe(); |
| 2400 | switch (cqe.err()) { |
| 2401 | .SUCCESS => {}, |
| 2402 | // This kernel's io_uring does not yet implement shutdown (kernel version < 5.11) |
| 2403 | .INVAL => return error.SkipZigTest, |
| 2404 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2405 | } |
| 2406 | |
| 2407 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2408 | .user_data = 0x445445445, |
| 2409 | .res = 0, |
| 2410 | .flags = 0, |
| 2411 | }, cqe); |
| 2412 | } |
| 2413 | |
| 2414 | // Socket not bound, expect to fail with ENOTCONN |
| 2415 | { |
| 2416 | const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); |
| 2417 | defer os.close(server); |
| 2418 | |
| 2419 | var shutdown_sqe = ring.shutdown(0x445445445, server, os.linux.SHUT.RD) catch |err| switch (err) { |
| 2420 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2421 | }; |
| 2422 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| 2423 | try testing.expectEqual(@as(i32, server), shutdown_sqe.fd); |
| 2424 | |
| 2425 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2426 | |
| 2427 | const cqe = try ring.copy_cqe(); |
| 2428 | try testing.expectEqual(@as(u64, 0x445445445), cqe.user_data); |
| 2429 | try testing.expectEqual(os.linux.E.NOTCONN, cqe.err()); |
| 2430 | } |
| 2431 | } |
| 2432 | |
| 2433 | test "renameat" { |
| 2434 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2435 | |
| 2436 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| 2437 | error.SystemOutdated => return error.SkipZigTest, |
| 2438 | error.PermissionDenied => return error.SkipZigTest, |
| 2439 | else => return err, |
| 2440 | }; |
| 2441 | defer ring.deinit(); |
| 2442 | |
| 2443 | const old_path = "test_io_uring_renameat_old"; |
| 2444 | const new_path = "test_io_uring_renameat_new"; |
| 2445 | |
| 2446 | // Write old file with data |
| 2447 | |
| 2448 | const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 }); |
| 2449 | defer { |
| 2450 | old_file.close(); |
| 2451 | std.fs.cwd().deleteFile(new_path) catch {}; |
| 2452 | } |
| 2453 | try old_file.writeAll("hello"); |
| 2454 | |
| 2455 | // Submit renameat |
| 2456 | |
| 2457 | var sqe = try ring.renameat( |
| 2458 | 0x12121212, |
| 2459 | linux.AT.FDCWD, |
| 2460 | old_path, |
| 2461 | linux.AT.FDCWD, |
| 2462 | new_path, |
| 2463 | 0, |
| 2464 | ); |
| 2465 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); |
| 2466 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| 2467 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); |
| 2468 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2469 | |
| 2470 | const cqe = try ring.copy_cqe(); |
| 2471 | switch (cqe.err()) { |
| 2472 | .SUCCESS => {}, |
| 2473 | // This kernel's io_uring does not yet implement renameat (kernel version < 5.11) |
| 2474 | .INVAL => return error.SkipZigTest, |
| 2475 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2476 | } |
| 2477 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2478 | .user_data = 0x12121212, |
| 2479 | .res = 0, |
| 2480 | .flags = 0, |
| 2481 | }, cqe); |
| 2482 | |
| 2483 | // Validate that the old file doesn't exist anymore |
| 2484 | { |
| 2485 | _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) { |
| 2486 | error.FileNotFound => {}, |
| 2487 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2488 | }; |
| 2489 | } |
| 2490 | |
| 2491 | // Validate that the new file exists with the proper content |
| 2492 | { |
| 2493 | const new_file = try std.fs.cwd().openFile(new_path, .{}); |
| 2494 | defer new_file.close(); |
| 2495 | |
| 2496 | var new_file_data: [16]u8 = undefined; |
| 2497 | const read = try new_file.readAll(&new_file_data); |
| 2498 | try testing.expectEqualStrings("hello", new_file_data[0..read]); |
| 2499 | } |
| 2500 | } |
| 2501 | |
| 2502 | test "unlinkat" { |
| 2503 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2504 | |
| 2505 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| 2506 | error.SystemOutdated => return error.SkipZigTest, |
| 2507 | error.PermissionDenied => return error.SkipZigTest, |
| 2508 | else => return err, |
| 2509 | }; |
| 2510 | defer ring.deinit(); |
| 2511 | |
| 2512 | const path = "test_io_uring_unlinkat"; |
| 2513 | |
| 2514 | // Write old file with data |
| 2515 | |
| 2516 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2517 | defer file.close(); |
| 2518 | defer std.fs.cwd().deleteFile(path) catch {}; |
| 2519 | |
| 2520 | // Submit unlinkat |
| 2521 | |
| 2522 | var sqe = try ring.unlinkat( |
| 2523 | 0x12121212, |
| 2524 | linux.AT.FDCWD, |
| 2525 | path, |
| 2526 | 0, |
| 2527 | ); |
| 2528 | try testing.expectEqual(linux.IORING_OP.UNLINKAT, sqe.opcode); |
| 2529 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| 2530 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2531 | |
| 2532 | const cqe = try ring.copy_cqe(); |
| 2533 | switch (cqe.err()) { |
| 2534 | .SUCCESS => {}, |
| 2535 | // This kernel's io_uring does not yet implement unlinkat (kernel version < 5.11) |
| 2536 | .INVAL => return error.SkipZigTest, |
| 2537 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2538 | } |
| 2539 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2540 | .user_data = 0x12121212, |
| 2541 | .res = 0, |
| 2542 | .flags = 0, |
| 2543 | }, cqe); |
| 2544 | |
| 2545 | // Validate that the file doesn't exist anymore |
| 2546 | _ = std.fs.cwd().openFile(path, .{}) catch |err| switch (err) { |
| 2547 | error.FileNotFound => {}, |
| 2548 | else => std.debug.panic("unexpected error: {}", .{err}), |
| 2549 | }; |
| 2550 | } |
| 2551 | |
| 2552 | test "mkdirat" { |
| 2553 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2554 | |
| 2555 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| 2556 | error.SystemOutdated => return error.SkipZigTest, |
| 2557 | error.PermissionDenied => return error.SkipZigTest, |
| 2558 | else => return err, |
| 2559 | }; |
| 2560 | defer ring.deinit(); |
| 2561 | |
| 2562 | const path = "test_io_uring_mkdirat"; |
| 2563 | |
| 2564 | defer std.fs.cwd().deleteDir(path) catch {}; |
| 2565 | |
| 2566 | // Submit mkdirat |
| 2567 | |
| 2568 | var sqe = try ring.mkdirat( |
| 2569 | 0x12121212, |
| 2570 | linux.AT.FDCWD, |
| 2571 | path, |
| 2572 | 0o0755, |
| 2573 | ); |
| 2574 | try testing.expectEqual(linux.IORING_OP.MKDIRAT, sqe.opcode); |
| 2575 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| 2576 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2577 | |
| 2578 | const cqe = try ring.copy_cqe(); |
| 2579 | switch (cqe.err()) { |
| 2580 | .SUCCESS => {}, |
| 2581 | // This kernel's io_uring does not yet implement mkdirat (kernel version < 5.15) |
| 2582 | .INVAL => return error.SkipZigTest, |
| 2583 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2584 | } |
| 2585 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2586 | .user_data = 0x12121212, |
| 2587 | .res = 0, |
| 2588 | .flags = 0, |
| 2589 | }, cqe); |
| 2590 | |
| 2591 | // Validate that the directory exist |
| 2592 | _ = try std.fs.cwd().openDir(path, .{}); |
| 2593 | } |
| 2594 | |
| 2595 | test "symlinkat" { |
| 2596 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 2597 | |
| 2598 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| 2599 | error.SystemOutdated => return error.SkipZigTest, |
| 2600 | error.PermissionDenied => return error.SkipZigTest, |
| 2601 | else => return err, |
| 2602 | }; |
| 2603 | defer ring.deinit(); |
| 2604 | |
| 2605 | const path = "test_io_uring_symlinkat"; |
| 2606 | const link_path = "test_io_uring_symlinkat_link"; |
| 2607 | |
| 2608 | const file = try std.fs.cwd().createFile(path, .{ .truncate = true, .mode = 0o666 }); |
| 2609 | defer { |
| 2610 | file.close(); |
| 2611 | std.fs.cwd().deleteFile(path) catch {}; |
| 2612 | std.fs.cwd().deleteFile(link_path) catch {}; |
| 2613 | } |
| 2614 | |
| 2615 | // Submit symlinkat |
| 2616 | |
| 2617 | var sqe = try ring.symlinkat( |
| 2618 | 0x12121212, |
| 2619 | path, |
| 2620 | linux.AT.FDCWD, |
| 2621 | link_path, |
| 2622 | ); |
| 2623 | try testing.expectEqual(linux.IORING_OP.SYMLINKAT, sqe.opcode); |
| 2624 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| 2625 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2626 | |
| 2627 | const cqe = try ring.copy_cqe(); |
| 2628 | switch (cqe.err()) { |
| 2629 | .SUCCESS => {}, |
| 2630 | // This kernel's io_uring does not yet implement symlinkat (kernel version < 5.15) |
| 2631 | .INVAL => return error.SkipZigTest, |
| 2632 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2633 | } |
| 2634 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2635 | .user_data = 0x12121212, |
| 2636 | .res = 0, |
| 2637 | .flags = 0, |
| 2638 | }, cqe); |
| 2639 | |
| 2640 | // Validate that the symlink exist |
| 2641 | _ = try std.fs.cwd().openFile(link_path, .{}); |
| 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 | } |