Skip to content

Commit 74963e8

Browse files
committed
std.os.windows.GetFinalPathNameByHandle: replace kernel32 by ntdll call
Removes the call to kernel32.GetFinalPathNameByHandleW in favor of NtQueryObject, which means we can reuse the other codepath's logic for DOS naming.
1 parent 6feccbd commit 74963e8

File tree

1 file changed

+51
-68
lines changed

1 file changed

+51
-68
lines changed

lib/std/os/windows.zig

Lines changed: 51 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,19 +1000,13 @@ test "QueryObjectName" {
10001000
_ = try QueryObjectName(file.handle, out_buffer[0..result_path.len]);
10011001
}
10021002

1003-
pub const GetFinalPathNameByHandleError = error {
1004-
BadPathName,
1005-
FileNotFound,
1006-
NameTooLong,
1007-
Unexpected,
1008-
}
1009-
|| if((comptime builtin.os.tag != .windows) or (targetVersionIsAtLeast(WindowsVersion.win10_rs4) == true))
1010-
error {}
1011-
else
1012-
error {
1013-
AccessDenied,
1014-
SystemResources,
1015-
};
1003+
pub const GetFinalPathNameByHandleError = error{
1004+
AccessDenied,
1005+
BadPathName,
1006+
FileNotFound,
1007+
NameTooLong,
1008+
Unexpected,
1009+
};
10161010

10171011
/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
10181012
/// Defaults to DOS volume names.
@@ -1035,75 +1029,64 @@ pub fn GetFinalPathNameByHandle(
10351029
fmt: GetFinalPathNameByHandleFormat,
10361030
out_buffer: []u16,
10371031
) GetFinalPathNameByHandleError![]u16 {
1032+
var path_buffer: [std.math.max(@sizeOf(FILE_NAME_INFORMATION), @sizeOf(OBJECT_NAME_INFORMATION)) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
1033+
var volume_buffer: [@sizeOf(FILE_NAME_INFORMATION) + MAX_PATH]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; // MAX_PATH bytes should be enough since it's Windows-defined name
10381034

1039-
var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
1040-
1035+
var file_name_u16: []const u16 = undefined;
1036+
var volume_name_u16: []const u16 = undefined;
10411037
if ((comptime (targetVersionIsAtLeast(WindowsVersion.win10_rs4) != true)) //need explicit comptime, because error returns affect return type
1042-
and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4)) {
1043-
// TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation
1044-
// with ntdll instead of calling into kernel32
1045-
// (probably using some less-powerful query and looping over path segments)
1046-
const flags: DWORD = FILE_NAME_NORMALIZED | switch(fmt.volume_name) {
1047-
.Dos => @as(DWORD, VOLUME_NAME_DOS),
1048-
.Nt => @as(DWORD, VOLUME_NAME_NT),
1038+
and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4))
1039+
{
1040+
const final_path = QueryObjectName(hFile, std.mem.bytesAsSlice(u16, path_buffer[0..])) catch |err| return switch (err) {
1041+
error.InvalidHandle => error.FileNotFound, //close enough?
1042+
else => |e| e,
10491043
};
1050-
const wide_path_buffer = std.mem.bytesAsSlice(u16, path_buffer[0..]);
1051-
const rc = kernel32.GetFinalPathNameByHandleW(hFile, wide_path_buffer.ptr, @intCast(u32, wide_path_buffer.len), flags);
1052-
if (rc == 0) {
1053-
switch (kernel32.GetLastError()) {
1054-
.FILE_NOT_FOUND => return error.FileNotFound,
1055-
.PATH_NOT_FOUND => return error.FileNotFound,
1056-
.NOT_ENOUGH_MEMORY => return error.SystemResources,
1057-
.FILENAME_EXCED_RANGE => return error.NameTooLong,
1058-
.ACCESS_DENIED => return error.AccessDenied, //can happen in SMB sub-queries for parent path segments
1059-
.INVALID_PARAMETER => unreachable,
1060-
else => |err| return unexpectedError(err),
1044+
1045+
if (fmt.volume_name == .Nt) {
1046+
if (out_buffer.len < final_path.len) {
1047+
return error.NameTooLong;
10611048
}
1049+
std.mem.copy(u16, out_buffer[0..], final_path[0..]);
1050+
return final_path; //we can directly return the slice we received
10621051
}
10631052

1064-
//in case of failure, rc == length of string INCLUDING null terminator,
1065-
if (rc > wide_path_buffer.len) return error.NameTooLong;
1066-
//in case of success, rc == length of string EXCLUDING null terminator
1067-
const result_slice = switch(fmt.volume_name) {
1068-
.Dos => blk: {
1069-
const expected_prefix = [_]u16{'\\', '\\', '?', '\\'};
1070-
if (!std.mem.eql(u16, expected_prefix[0..], wide_path_buffer[0..expected_prefix.len])) {
1071-
return error.BadPathName;
1072-
}
1073-
break :blk wide_path_buffer[expected_prefix.len..rc:0];
1074-
},
1075-
//no prefix here
1076-
.Nt => wide_path_buffer[0..rc:0],
1077-
};
1078-
if(result_slice.len > out_buffer.len) return error.NameTooLong;
1079-
std.mem.copy(u16, out_buffer[0..], result_slice);
1080-
return out_buffer[0..result_slice.len];
1081-
}
1082-
1083-
// Get normalized path; doesn't include volume name though.
1084-
try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
1085-
1086-
// Get NT volume name.
1087-
var volume_buffer: [@sizeOf(FILE_NAME_INFORMATION) + MAX_PATH]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; // MAX_PATH bytes should be enough since it's Windows-defined name
1088-
try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
1053+
//otherwise we need to parse the string for volume path for the .Dos logic below to work
1054+
const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
1055+
if (!std.mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) {
1056+
//TODO find out if this can occur, and if we need to handle it differently
1057+
//(i.e. how to determine the end of a volume name)
1058+
return error.BadPathName;
1059+
}
1060+
const index = std.mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable;
1061+
volume_name_u16 = final_path[0..index];
1062+
file_name_u16 = final_path[index..];
10891063

1090-
const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
1091-
const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0 .. file_name.FileNameLength / 2];
1064+
//fallthrough for fmt.volume_name != .Nt
1065+
} else {
1066+
// Get normalized path; doesn't include volume name though.
1067+
try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
1068+
const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
1069+
file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0..@divExact(file_name.FileNameLength, 2)];
10921070

1093-
const volume_name = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
1071+
// Get NT volume name.
1072+
try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
1073+
const volume_name_info = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
1074+
volume_name_u16 = @ptrCast([*]const u16, &volume_name_info.FileName[0])[0..@divExact(volume_name_info.FileNameLength, 2)];
10941075

1095-
switch (fmt.volume_name) {
1096-
.Nt => {
1076+
if (fmt.volume_name == .Nt) {
10971077
// Nothing to do, we simply copy the bytes to the user-provided buffer.
1098-
const volume_name_u16 = @ptrCast([*]const u16, &volume_name.FileName[0])[0 .. volume_name.FileNameLength / 2];
1099-
11001078
if (out_buffer.len < volume_name_u16.len + file_name_u16.len) return error.NameTooLong;
11011079

11021080
std.mem.copy(u16, out_buffer[0..], volume_name_u16);
11031081
std.mem.copy(u16, out_buffer[volume_name_u16.len..], file_name_u16);
11041082

11051083
return out_buffer[0 .. volume_name_u16.len + file_name_u16.len];
1106-
},
1084+
}
1085+
//fallthrough for fmt.volume_name != .Nt
1086+
}
1087+
1088+
switch (fmt.volume_name) {
1089+
.Nt => unreachable, //handled above
11071090
.Dos => {
11081091
// Get DOS volume name. DOS volume names are actually symbolic link objects to the
11091092
// actual NT volume. For example:
@@ -1137,8 +1120,8 @@ pub fn GetFinalPathNameByHandle(
11371120

11381121
var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
11391122
input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
1140-
input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength);
1141-
@memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength);
1123+
input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);
1124+
@memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, volume_name_u16.ptr), volume_name_u16.len * 2);
11421125

11431126
DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]) catch |err| switch (err) {
11441127
error.AccessDenied => unreachable,

0 commit comments

Comments
 (0)