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{...@@ -1122,8 +1122,7 @@ pub const RenamePreserveError = error{
1122 /// policy of the current thread or process disallows dataless directory materialization.1122 /// policy of the current thread or process disallows dataless directory materialization.
1123 AccessDenied,1123 AccessDenied,
1124 PathAlreadyExists,1124 PathAlreadyExists,
1125 /// Operating system or file system does not support atomic nonreplacing1125 /// Operating system or file system does not support atomic nonreplacing rename.
1126 /// rename.
1127 OperationUnsupported,1126 OperationUnsupported,
1128} || RenameError;1127} || RenameError;
11291128
...@@ -1131,6 +1130,12 @@ pub const RenamePreserveError = error{...@@ -1131,6 +1130,12 @@ pub const RenamePreserveError = error{
1131///1130///
1132/// If `new_sub_path` already exists, `error.PathAlreadyExists` will be returned.1131/// If `new_sub_path` already exists, `error.PathAlreadyExists` will be returned.
1133///1132///
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///
1134/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).1139/// * On Windows, both paths should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
1135/// * On WASI, both paths should be encoded as valid UTF-8.1140/// * On WASI, both paths should be encoded as valid UTF-8.
1136/// * On other platforms, both paths are an opaque sequence of bytes with no particular encoding.1141/// * 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(...@@ -7734,7 +7734,14 @@ fn dirRenamePreserve(
7734 const t: *Threaded = @ptrCast(@alignCast(userdata));7734 const t: *Threaded = @ptrCast(@alignCast(userdata));
7735 if (is_windows) return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, false);7735 if (is_windows) return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, false);
7736 if (is_darwin) return dirRenamePreserveDarwin(old_dir, old_sub_path, new_dir, new_sub_path);7736 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 }
7738 // Make a hard link then delete the original.7745 // Make a hard link then delete the original.
7739 try dirHardLink(t, old_dir, old_sub_path, new_dir, new_sub_path, .{ .follow_symlinks = false });7746 try dirHardLink(t, old_dir, old_sub_path, new_dir, new_sub_path, .{ .follow_symlinks = false });
7740 const prev = swapCancelProtection(t, .blocked);7747 const prev = swapCancelProtection(t, .blocked);
...@@ -8062,8 +8069,10 @@ fn dirRenamePreserveLinux(...@@ -8062,8 +8069,10 @@ fn dirRenamePreserveLinux(
8062 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),8069 .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
8063 .XDEV => return syscall.fail(error.CrossDevice),8070 .XDEV => return syscall.fail(error.CrossDevice),
8064 .ILSEQ => return syscall.fail(error.BadPathName),8071 .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),
8065 .FAULT => |err| return syscall.errnoBug(err),8075 .FAULT => |err| return syscall.errnoBug(err),
8066 .INVAL => return syscall.fail(error.OperationUnsupported),
8067 else => |err| return syscall.unexpectedErrno(err),8076 else => |err| return syscall.unexpectedErrno(err),
8068 };8077 };
8069}8078}