feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.dirRenameWindowsInner
fn dirRenameWindowsInner(
old_dir: Dir,
old_sub_path: []const u8,
new_dir: Dir,
new_sub_path: []const u8,
replace_if_exists: bool,
) Dir.RenamePreserveError!void
File
Code
fn dirRenameWindowsInner(
old_dir: Dir,
old_sub_path: []const u8,
new_dir: Dir,
new_sub_path: []const u8,
replace_if_exists: bool,
) Dir.RenamePreserveError!void {
const w = windows;
const old_path_w_buf = try sliceToPrefixedFileW(old_dir.handle, old_sub_path, .{});
const old_path_w = old_path_w_buf.span();
const new_path_w_buf = try sliceToPrefixedFileW(new_dir.handle, new_sub_path, .{});
const new_path_w = new_path_w_buf.span();
const src_fd = src_fd: {
if (OpenFile(old_path_w, .{
.dir = old_dir.handle,
.access_mask = .{
.GENERIC = .{ .WRITE = true },
.STANDARD = .{
.RIGHTS = .{ .DELETE = true },
.SYNCHRONIZE = true,
},
},
.creation = .OPEN,
.filter = .any,
.follow_symlinks = false,
})) |handle| {
break :src_fd handle;
} else |err| switch (err) {
error.WouldBlock => unreachable,
else => |e| return e,
}
};
defer w.CloseHandle(src_fd);
var rc: w.NTSTATUS = undefined;
// - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
// (INVALID_INFO_CLASS is returned if not supported)
// - Requires the NTFS filesystem
// (on filesystems like FAT32, INVALID_PARAMETER is returned)
// - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1
// - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
// (NOT_SUPPORTED is returned if a flag is unsupported)
//
// The strategy here is just to try using FileRenameInformationEx and fall back to
// FileRenameInformation if the return value lets us know that some aspect of it is not supported.
const need_fallback = need_fallback: {
var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
.Flags = .{
.REPLACE_IF_EXISTS = replace_if_exists,
.POSIX_SEMANTICS = true,
.IGNORE_READONLY_ATTRIBUTE = true,
},
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
.FileName = new_path_w,
});
var io_status_block: w.IO_STATUS_BLOCK = undefined;
const rename_info_buf = rename_info.toBuffer();
rc = w.ntdll.NtSetInformationFile(
src_fd,
&io_status_block,
rename_info_buf.ptr,
@intCast(rename_info_buf.len),
.RenameEx,
);
switch (rc) {
.SUCCESS => return,
.INVALID_PARAMETER,
.INVALID_INFO_CLASS,
.NOT_SUPPORTED,
=> break :need_fallback true,
else => break :need_fallback false,
}
};
if (need_fallback) {
var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
.Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
.RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
.FileName = new_path_w,
});
var io_status_block: w.IO_STATUS_BLOCK = undefined;
const rename_info_buf = rename_info.toBuffer();
rc = w.ntdll.NtSetInformationFile(
src_fd,
&io_status_block,
rename_info_buf.ptr,
@intCast(rename_info_buf.len),
.Rename,
);
}
switch (rc) {
.SUCCESS => {},
.INVALID_HANDLE => |err| return w.statusBug(err),
.INVALID_PARAMETER => |err| return w.statusBug(err),
.OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
.ACCESS_DENIED => return error.AccessDenied,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.NOT_SAME_DEVICE => return error.CrossDevice,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
.DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
.FILE_IS_A_DIRECTORY => return error.IsDir,
.NOT_A_DIRECTORY => return error.NotDir,
else => return w.unexpectedStatus(rc),
}
}