| ... | ... | @@ -747,6 +747,23 @@ pub const IO_Uring = struct { |
| 747 | 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 | 767 | /// Registers an array of file descriptors. |
| 751 | 768 | /// Every time a file descriptor is put in an SQE and submitted to the kernel, the kernel must |
| 752 | 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 | 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 | 1343 | test "structs/offsets/entries" { |
| 1307 | 1344 | if (builtin.os.tag != .linux) return error.SkipZigTest; |
| 1308 | 1345 | |
| ... | ... | @@ -2275,3 +2312,72 @@ test "shutdown" { |
| 2275 | 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 | } |