| ... | @@ -747,6 +747,23 @@ pub const IO_Uring = struct { | ... | @@ -747,6 +747,23 @@ pub const IO_Uring = struct { |
| 747 | return sqe; | 747 | return sqe; |
| 748 | } | 748 | } |
| 749 | | 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 | |
| 750 | /// Registers an array of file descriptors. | 767 | /// Registers an array of file descriptors. |
| 751 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must | 768 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 752 | /// retrieve a reference to the file, and once I/O has completed the file reference must be | 769 | /// retrieve a reference to the file, and once I/O has completed the file reference must be |
| ... | @@ -1303,6 +1320,26 @@ pub fn io_uring_prep_shutdown( | ... | @@ -1303,6 +1320,26 @@ pub fn io_uring_prep_shutdown( |
| 1303 | io_uring_prep_rw(.SHUTDOWN, sqe, sockfd, 0, how, 0); | 1320 | io_uring_prep_rw(.SHUTDOWN, sqe, sockfd, 0, how, 0); |
| 1304 | } | 1321 | } |
| 1305 | | 1322 | |
| | 1323 | pub fn io_uring_prep_renameat( |
| | 1324 | sqe: *io_uring_sqe, |
| | 1325 | old_dir_fd: os.fd_t, |
| | 1326 | old_path: [*:0]const u8, |
| | 1327 | new_dir_fd: os.fd_t, |
| | 1328 | new_path: [*:0]const u8, |
| | 1329 | flags: u32, |
| | 1330 | ) void { |
| | 1331 | io_uring_prep_rw( |
| | 1332 | .RENAMEAT, |
| | 1333 | sqe, |
| | 1334 | old_dir_fd, |
| | 1335 | @ptrToInt(old_path), |
| | 1336 | 0, |
| | 1337 | @ptrToInt(new_path), |
| | 1338 | ); |
| | 1339 | sqe.len = @bitCast(u32, new_dir_fd); |
| | 1340 | sqe.rw_flags = flags; |
| | 1341 | } |
| | 1342 | |
| 1306 | test "structs/offsets/entries" { | 1343 | test "structs/offsets/entries" { |
| 1307 | if (builtin.os.tag != .linux) return error.SkipZigTest; | 1344 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1308 | | 1345 | |
| ... | @@ -2275,3 +2312,72 @@ test "shutdown" { | ... | @@ -2275,3 +2312,72 @@ test "shutdown" { |
| 2275 | try testing.expectEqual(os.linux.E.NOTCONN, cqe.err()); | 2312 | try testing.expectEqual(os.linux.E.NOTCONN, cqe.err()); |
| 2276 | } | 2313 | } |
| 2277 | } | 2314 | } |
| | 2315 | |
| | 2316 | test "renameat" { |
| | 2317 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| | 2318 | |
| | 2319 | var ring = IO_Uring.init(1, 0) catch |err| switch (err) { |
| | 2320 | error.SystemOutdated => return error.SkipZigTest, |
| | 2321 | error.PermissionDenied => return error.SkipZigTest, |
| | 2322 | else => return err, |
| | 2323 | }; |
| | 2324 | defer ring.deinit(); |
| | 2325 | |
| | 2326 | const old_path = "test_io_uring_renameat_old"; |
| | 2327 | const new_path = "test_io_uring_renameat_new"; |
| | 2328 | |
| | 2329 | // Write old file with data |
| | 2330 | |
| | 2331 | const old_file = try std.fs.cwd().createFile(old_path, .{ .truncate = true, .mode = 0o666 }); |
| | 2332 | defer { |
| | 2333 | old_file.close(); |
| | 2334 | std.fs.cwd().deleteFile(new_path) catch {}; |
| | 2335 | } |
| | 2336 | try old_file.writeAll("hello"); |
| | 2337 | |
| | 2338 | // Submit renameat |
| | 2339 | |
| | 2340 | var sqe = try ring.renameat( |
| | 2341 | 0x12121212, |
| | 2342 | linux.AT.FDCWD, |
| | 2343 | old_path, |
| | 2344 | linux.AT.FDCWD, |
| | 2345 | new_path, |
| | 2346 | 0, |
| | 2347 | ); |
| | 2348 | try testing.expectEqual(linux.IORING_OP.RENAMEAT, sqe.opcode); |
| | 2349 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), sqe.fd); |
| | 2350 | try testing.expectEqual(@as(i32, linux.AT.FDCWD), @bitCast(i32, sqe.len)); |
| | 2351 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| | 2352 | |
| | 2353 | const cqe = try ring.copy_cqe(); |
| | 2354 | switch (cqe.err()) { |
| | 2355 | .SUCCESS => {}, |
| | 2356 | // This kernel's io_uring does not yet implement renameat (kernel version < 5.11) |
| | 2357 | .INVAL => return error.SkipZigTest, |
| | 2358 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| | 2359 | } |
| | 2360 | try testing.expectEqual(linux.io_uring_cqe{ |
| | 2361 | .user_data = 0x12121212, |
| | 2362 | .res = 0, |
| | 2363 | .flags = 0, |
| | 2364 | }, cqe); |
| | 2365 | |
| | 2366 | // Validate that the old file doesn't exist anymore |
| | 2367 | { |
| | 2368 | _ = std.fs.cwd().openFile(old_path, .{}) catch |err| switch (err) { |
| | 2369 | error.FileNotFound => {}, |
| | 2370 | else => std.debug.panic("unexpected error: {}", .{err}), |
| | 2371 | }; |
| | 2372 | } |
| | 2373 | |
| | 2374 | // Validate that the new file exists with the proper content |
| | 2375 | { |
| | 2376 | const new_file = try std.fs.cwd().openFile(new_path, .{}); |
| | 2377 | defer new_file.close(); |
| | 2378 | |
| | 2379 | var new_file_data: [16]u8 = undefined; |
| | 2380 | const read = try new_file.readAll(&new_file_data); |
| | 2381 | try testing.expectEqualStrings("hello", new_file_data[0..read]); |
| | 2382 | } |
| | 2383 | } |