Skip to content

Commit 5f7301b

Browse files
committed
rename: Add error.CircularLoop
Adds error.CircularLoop to rename functions. According to the man page for rename, "EINVAL The old pathname names an ancestor directory of the new pathname, or either pathname argument contains a final component that is dot or dot-dot." The later is always programmer error, so returning error.CircularLoop should always be correct. Based on empirical testing, SHARING_VIOLATION is returned by Windows to indicate a circular loop.
1 parent 4d79806 commit 5f7301b

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

lib/std/fs/test.zig

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,30 @@ test "Dir.rename file <-> dir" {
991991
}.impl);
992992
}
993993

994+
test "Dir.rename circular loop" {
995+
try testWithAllSupportedPathTypes(struct {
996+
fn impl(ctx: *TestContext) !void {
997+
const parent_path = try ctx.transformPath("parent");
998+
const subdir_path = try ctx.transformPath("parent/subdir");
999+
try ctx.dir.makeDir(parent_path);
1000+
try testing.expectError(error.CircularLoop, ctx.dir.rename(parent_path, subdir_path));
1001+
1002+
if (ctx.path_type == .relative) {
1003+
var parent = try ctx.dir.openDir("parent", .{});
1004+
defer parent.close();
1005+
try testing.expectError(error.CircularLoop, parent.rename("../parent", "subdir"));
1006+
}
1007+
1008+
if (ctx.path_type == .absolute) {
1009+
try testing.expectError(error.CircularLoop, std.fs.renameAbsolute(
1010+
parent_path,
1011+
subdir_path,
1012+
));
1013+
}
1014+
}
1015+
}.impl);
1016+
}
1017+
9941018
test "rename" {
9951019
var tmp_dir1 = tmpDir(.{});
9961020
defer tmp_dir1.cleanup();

lib/std/os/windows.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,12 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
11211121
}
11221122
}
11231123

1124-
pub const MoveFileError = error{ FileNotFound, AccessDenied, Unexpected };
1124+
pub const MoveFileError = error{
1125+
FileNotFound,
1126+
AccessDenied,
1127+
CircularLoop,
1128+
Unexpected,
1129+
};
11251130

11261131
pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) (MoveFileError || Wtf8ToPrefixedFileWError)!void {
11271132
const old_path_w = try sliceToPrefixedFileW(null, old_path);
@@ -1134,6 +1139,7 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
11341139
switch (GetLastError()) {
11351140
.FILE_NOT_FOUND => return error.FileNotFound,
11361141
.ACCESS_DENIED => return error.AccessDenied,
1142+
.SHARING_VIOLATION => return error.CircularLoop,
11371143
else => |err| return unexpectedError(err),
11381144
}
11391145
}

lib/std/posix.zig

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,6 +2599,7 @@ pub const RenameError = error{
25992599
FileBusy,
26002600
DiskQuota,
26012601
IsDir,
2602+
CircularLoop,
26022603
SymLinkLoop,
26032604
LinkQuotaExceeded,
26042605
NameTooLong,
@@ -2662,7 +2663,7 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi
26622663
.BUSY => return error.FileBusy,
26632664
.DQUOT => return error.DiskQuota,
26642665
.FAULT => unreachable,
2665-
.INVAL => unreachable,
2666+
.INVAL => return error.CircularLoop,
26662667
.ISDIR => return error.IsDir,
26672668
.LOOP => return error.SymLinkLoop,
26682669
.MLINK => return error.LinkQuotaExceeded,
@@ -2725,7 +2726,7 @@ fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void {
27252726
.BUSY => return error.FileBusy,
27262727
.DQUOT => return error.DiskQuota,
27272728
.FAULT => unreachable,
2728-
.INVAL => unreachable,
2729+
.INVAL => return error.CircularLoop,
27292730
.ISDIR => return error.IsDir,
27302731
.LOOP => return error.SymLinkLoop,
27312732
.MLINK => return error.LinkQuotaExceeded,
@@ -2777,7 +2778,7 @@ pub fn renameatZ(
27772778
.BUSY => return error.FileBusy,
27782779
.DQUOT => return error.DiskQuota,
27792780
.FAULT => unreachable,
2780-
.INVAL => unreachable,
2781+
.INVAL => return error.CircularLoop,
27812782
.ISDIR => return error.IsDir,
27822783
.LOOP => return error.SymLinkLoop,
27832784
.MLINK => return error.LinkQuotaExceeded,
@@ -2901,6 +2902,7 @@ pub fn renameatW(
29012902
.DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
29022903
.FILE_IS_A_DIRECTORY => return error.IsDir,
29032904
.NOT_A_DIRECTORY => return error.NotDir,
2905+
.SHARING_VIOLATION => return error.CircularLoop,
29042906
else => return windows.unexpectedStatus(rc),
29052907
}
29062908
}

src/fmt.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ const FmtError = error{
222222
NetNameDeleted,
223223
InvalidArgument,
224224
ProcessNotFound,
225+
CircularLoop,
225226
} || fs.File.OpenError;
226227

227228
fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {

0 commit comments

Comments
 (0)