authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-12 13:30:02-07:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-14 21:08:26+02:00
log4428d52ea145ed0f14af2e39569026c2a708e547
treee7a7dac17992fe0e8b906a27c4cd19486798a5d9
parent77aa4155ea62fa88895ddcc566ed99d0d7031c55
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

std.Io.Threaded: handle unsupported renamePreserveLinux

with link/unlink fallback. closes #31960

2 files changed, 18 insertions(+), 4 deletions(-)

lib/std/Io/Dir.zig+7-2
......@@ -1122,8 +1122,7 @@ pub const RenamePreserveError = error{
11221122 /// policy of the current thread or process disallows dataless directory materialization.
11231123 AccessDenied,
11241124 PathAlreadyExists,
1125 /// Operating system or file system does not support atomic nonreplacing
1126 /// rename.
1125 /// Operating system or file system does not support atomic nonreplacing rename.
11271126 OperationUnsupported,
11281127} || RenameError;
11291128
......@@ -1131,6 +1130,12 @@ pub const RenamePreserveError = error{
11311130///
11321131/// If `new_sub_path` already exists, `error.PathAlreadyExists` will be returned.
11331132///
1133/// If the operating system supports it, the entire operation will be done atomically. If unsupported, will
1134/// fall back to hard link followed by unlink, which is atomic with respect to `new_sub_path` but leaves a
1135/// window where the cleanup of `old_sub_path may fail. Although the operation is fairly portable, technically
1136/// this fallback, too, may be unsupported in some environments, in which case `error.OperationUnsupported` is
1137/// returned.
1138///
11341139/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
11351140/// * On WASI, both paths should be encoded as valid UTF-8.
11361141/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
lib/std/Io/Threaded.zig+11-2
......@@ -7734,7 +7734,14 @@ fn dirRenamePreserve(
77347734 const t: *Threaded = @ptrCast(@alignCast(userdata));
77357735 if (is_windows) return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, false);
77367736 if (is_darwin) return dirRenamePreserveDarwin(old_dir, old_sub_path, new_dir, new_sub_path);
7737 if (native_os == .linux) return dirRenamePreserveLinux(old_dir, old_sub_path, new_dir, new_sub_path);
7737 if (native_os == .linux) {
7738 if (dirRenamePreserveLinux(old_dir, old_sub_path, new_dir, new_sub_path)) |_| {
7739 return;
7740 } else |err| switch (err) {
7741 else => |e| return e,
7742 error.OperationUnsupported => {}, // Fall back to link/unlink below.
7743 }
7744 }
77387745 // Make a hard link then delete the original.
77397746 try dirHardLink(t, old_dir, old_sub_path, new_dir, new_sub_path, .{ .follow_symlinks = false });
77407747 const prev = swapCancelProtection(t, .blocked);
......@@ -8062,8 +8069,10 @@ fn dirRenamePreserveLinux(
80628069 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
80638070 .XDEV => return syscall.fail(error.CrossDevice),
80648071 .ILSEQ => return syscall.fail(error.BadPathName),
8072 // EINVAL when passing NOREPLACE=true observed on ext4, 7.0.0-38-generic #38~24.04.4-Ubuntu.
8073 // OPNOTSUPP handled defensively.
8074 .INVAL, .OPNOTSUPP => return syscall.fail(error.OperationUnsupported),
80658075 .FAULT => |err| return syscall.errnoBug(err),
8066 .INVAL => return syscall.fail(error.OperationUnsupported),
80678076 else => |err| return syscall.unexpectedErrno(err),
80688077 };
80698078}