authorgravatar for rohlemF@gmail.comRohlem <rohlemF@gmail.com> 2020-12-17 16:37:40+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-11 17:48:18-07:00
log64c5f4979e99b35ef8a2a2c200c095b4d8917328
tree6a35a195a8e5f8dccf1ef7b2f00a1b00f49bf663
parent964bbcd0b1976ca55d3f845d4d1dcf496bc36a1b

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 files changed, 51 insertions(+), 68 deletions(-)

lib/std/os/windows.zig+51-68
......@@ -1001,19 +1001,13 @@ test "QueryObjectName" {
10011001 _ = try QueryObjectName(file.handle, out_buffer[0..result_path.len]);
10021002}
10031003
1004pub const GetFinalPathNameByHandleError = error {
1005 BadPathName,
1006 FileNotFound,
1007 NameTooLong,
1008 Unexpected,
1009 }
1010 || if((comptime builtin.os.tag != .windows) or (targetVersionIsAtLeast(WindowsVersion.win10_rs4) == true))
1011 error {}
1012 else
1013 error {
1014 AccessDenied,
1015 SystemResources,
1016 };
1004pub const GetFinalPathNameByHandleError = error{
1005 AccessDenied,
1006 BadPathName,
1007 FileNotFound,
1008 NameTooLong,
1009 Unexpected,
1010};
10171011
10181012/// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
10191013/// Defaults to DOS volume names.
......@@ -1036,75 +1030,64 @@ pub fn GetFinalPathNameByHandle(
10361030 fmt: GetFinalPathNameByHandleFormat,
10371031 out_buffer: []u16,
10381032) GetFinalPathNameByHandleError![]u16 {
1033 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;
1034 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
10391035
1040 var path_buffer: [@sizeOf(FILE_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined;
1041
1036 var file_name_u16: []const u16 = undefined;
1037 var volume_name_u16: []const u16 = undefined;
10421038 if ((comptime (targetVersionIsAtLeast(WindowsVersion.win10_rs4) != true)) //need explicit comptime, because error returns affect return type
1043 and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4)) {
1044 // TODO: directly replace/emulate QueryInformationFile of .FileNormalizedNameInformation
1045 // with ntdll instead of calling into kernel32
1046 // (probably using some less-powerful query and looping over path segments)
1047 const flags: DWORD = FILE_NAME_NORMALIZED | switch(fmt.volume_name) {
1048 .Dos => @as(DWORD, VOLUME_NAME_DOS),
1049 .Nt => @as(DWORD, VOLUME_NAME_NT),
1039 and !runtimeVersionIsAtLeast(WindowsVersion.win10_rs4))
1040 {
1041 const final_path = QueryObjectName(hFile, std.mem.bytesAsSlice(u16, path_buffer[0..])) catch |err| return switch (err) {
1042 error.InvalidHandle => error.FileNotFound, //close enough?
1043 else => |e| e,
10501044 };
1051 const wide_path_buffer = std.mem.bytesAsSlice(u16, path_buffer[0..]);
1052 const rc = kernel32.GetFinalPathNameByHandleW(hFile, wide_path_buffer.ptr, @intCast(u32, wide_path_buffer.len), flags);
1053 if (rc == 0) {
1054 switch (kernel32.GetLastError()) {
1055 .FILE_NOT_FOUND => return error.FileNotFound,
1056 .PATH_NOT_FOUND => return error.FileNotFound,
1057 .NOT_ENOUGH_MEMORY => return error.SystemResources,
1058 .FILENAME_EXCED_RANGE => return error.NameTooLong,
1059 .ACCESS_DENIED => return error.AccessDenied, //can happen in SMB sub-queries for parent path segments
1060 .INVALID_PARAMETER => unreachable,
1061 else => |err| return unexpectedError(err),
1045
1046 if (fmt.volume_name == .Nt) {
1047 if (out_buffer.len < final_path.len) {
1048 return error.NameTooLong;
10621049 }
1050 std.mem.copy(u16, out_buffer[0..], final_path[0..]);
1051 return final_path; //we can directly return the slice we received
10631052 }
10641053
1065 //in case of failure, rc == length of string INCLUDING null terminator,
1066 if (rc > wide_path_buffer.len) return error.NameTooLong;
1067 //in case of success, rc == length of string EXCLUDING null terminator
1068 const result_slice = switch(fmt.volume_name) {
1069 .Dos => blk: {
1070 const expected_prefix = [_]u16{'\\', '\\', '?', '\\'};
1071 if (!std.mem.eql(u16, expected_prefix[0..], wide_path_buffer[0..expected_prefix.len])) {
1072 return error.BadPathName;
1073 }
1074 break :blk wide_path_buffer[expected_prefix.len..rc:0];
1075 },
1076 //no prefix here
1077 .Nt => wide_path_buffer[0..rc:0],
1078 };
1079 if(result_slice.len > out_buffer.len) return error.NameTooLong;
1080 std.mem.copy(u16, out_buffer[0..], result_slice);
1081 return out_buffer[0..result_slice.len];
1082 }
1083
1084 // Get normalized path; doesn't include volume name though.
1085 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
1086
1087 // Get NT volume name.
1088 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
1089 try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
1054 //otherwise we need to parse the string for volume path for the .Dos logic below to work
1055 const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
1056 if (!std.mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) {
1057 //TODO find out if this can occur, and if we need to handle it differently
1058 //(i.e. how to determine the end of a volume name)
1059 return error.BadPathName;
1060 }
1061 const index = std.mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable;
1062 volume_name_u16 = final_path[0..index];
1063 file_name_u16 = final_path[index..];
10901064
1091 const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
1092 const file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0 .. file_name.FileNameLength / 2];
1065 //fallthrough for fmt.volume_name != .Nt
1066 } else {
1067 // Get normalized path; doesn't include volume name though.
1068 try QueryInformationFile(hFile, .FileNormalizedNameInformation, path_buffer[0..]);
1069 const file_name = @ptrCast(*const FILE_NAME_INFORMATION, &path_buffer[0]);
1070 file_name_u16 = @ptrCast([*]const u16, &file_name.FileName[0])[0..@divExact(file_name.FileNameLength, 2)];
10931071
1094 const volume_name = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
1072 // Get NT volume name.
1073 try QueryInformationFile(hFile, .FileVolumeNameInformation, volume_buffer[0..]);
1074 const volume_name_info = @ptrCast(*const FILE_NAME_INFORMATION, &volume_buffer[0]);
1075 volume_name_u16 = @ptrCast([*]const u16, &volume_name_info.FileName[0])[0..@divExact(volume_name_info.FileNameLength, 2)];
10951076
1096 switch (fmt.volume_name) {
1097 .Nt => {
1077 if (fmt.volume_name == .Nt) {
10981078 // Nothing to do, we simply copy the bytes to the user-provided buffer.
1099 const volume_name_u16 = @ptrCast([*]const u16, &volume_name.FileName[0])[0 .. volume_name.FileNameLength / 2];
1100
11011079 if (out_buffer.len < volume_name_u16.len + file_name_u16.len) return error.NameTooLong;
11021080
11031081 std.mem.copy(u16, out_buffer[0..], volume_name_u16);
11041082 std.mem.copy(u16, out_buffer[volume_name_u16.len..], file_name_u16);
11051083
11061084 return out_buffer[0 .. volume_name_u16.len + file_name_u16.len];
1107 },
1085 }
1086 //fallthrough for fmt.volume_name != .Nt
1087 }
1088
1089 switch (fmt.volume_name) {
1090 .Nt => unreachable, //handled above
11081091 .Dos => {
11091092 // Get DOS volume name. DOS volume names are actually symbolic link objects to the
11101093 // actual NT volume. For example:
......@@ -1138,8 +1121,8 @@ pub fn GetFinalPathNameByHandle(
11381121
11391122 var input_struct = @ptrCast(*MOUNTMGR_MOUNT_POINT, &input_buf[0]);
11401123 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);
1141 input_struct.DeviceNameLength = @intCast(USHORT, volume_name.FileNameLength);
1142 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, &volume_name.FileName[0]), volume_name.FileNameLength);
1124 input_struct.DeviceNameLength = @intCast(USHORT, volume_name_u16.len * 2);
1125 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..], @ptrCast([*]const u8, volume_name_u16.ptr), volume_name_u16.len * 2);
11431126
11441127 DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, input_buf[0..], output_buf[0..]) catch |err| switch (err) {
11451128 error.AccessDenied => unreachable,