| ... | ... | @@ -957,29 +957,29 @@ pub fn QueryObjectName( |
| 957 | 957 | handle: HANDLE, |
| 958 | 958 | out_buffer: []u16, |
| 959 | 959 | ) ![]u16 { |
| 960 | | var full_buffer: [@sizeOf(OBJECT_NAME_INFORMATION) + PATH_MAX_WIDE * 2]u8 align(@alignOf(OBJECT_NAME_INFORMATION)) = undefined; |
| 961 | | var info = @ptrCast(*OBJECT_NAME_INFORMATION, &full_buffer); |
| 960 | const out_buffer_aligned = mem.alignInSlice(out_buffer, @alignOf(OBJECT_NAME_INFORMATION)) orelse return error.NameTooLong; |
| 961 | |
| 962 | const info = @ptrCast(*OBJECT_NAME_INFORMATION, out_buffer_aligned); |
| 962 | 963 | //buffer size is specified in bytes |
| 964 | const out_buffer_len = std.math.cast(ULONG, out_buffer_aligned.len * 2) catch |e| switch (e) { |
| 965 | error.Overflow => std.math.maxInt(ULONG), |
| 966 | }; |
| 963 | 967 | //last argument would return the length required for full_buffer, not exposed here |
| 964 | | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, &full_buffer, full_buffer.len, null); |
| 968 | const rc = ntdll.NtQueryObject(handle, .ObjectNameInformation, info, out_buffer_len, null); |
| 965 | 969 | switch (rc) { |
| 966 | 970 | .SUCCESS => { |
| 967 | 971 | // info.Name.Buffer from ObQueryNameString is documented to be null (and MaximumLength == 0) |
| 968 | 972 | // if the object was "unnamed", not sure if this can happen for file handles |
| 969 | 973 | if (info.Name.MaximumLength == 0) return error.Unexpected; |
| 970 | | //resulting string length is specified in bytes |
| 974 | // resulting string length is specified in bytes |
| 971 | 975 | const path_length_unterminated = @divExact(info.Name.Length, 2); |
| 972 | | if (out_buffer.len < path_length_unterminated) { |
| 973 | | return error.NameTooLong; |
| 974 | | } |
| 975 | | mem.copy(WCHAR, out_buffer[0..path_length_unterminated], info.Name.Buffer[0..path_length_unterminated]); |
| 976 | | return out_buffer[0..path_length_unterminated]; |
| 976 | return info.Name.Buffer[0..path_length_unterminated]; |
| 977 | 977 | }, |
| 978 | 978 | .ACCESS_DENIED => return error.AccessDenied, |
| 979 | 979 | .INVALID_HANDLE => return error.InvalidHandle, |
| 980 | | .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong, |
| 981 | | //name_buffer.len >= @sizeOf(OBJECT_NAME_INFORMATION) holds statically |
| 982 | | .INFO_LENGTH_MISMATCH => unreachable, |
| 980 | // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH), |
| 981 | // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL) |
| 982 | .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => return error.NameTooLong, |
| 983 | 983 | else => |e| return unexpectedStatus(e), |
| 984 | 984 | } |
| 985 | 985 | } |
| ... | ... | @@ -994,10 +994,11 @@ test "QueryObjectName" { |
| 994 | 994 | var out_buffer: [PATH_MAX_WIDE]u16 = undefined; |
| 995 | 995 | |
| 996 | 996 | var result_path = try QueryObjectName(handle, &out_buffer); |
| 997 | const required_len_in_u16 = result_path.len + @divExact(@ptrToInt(result_path.ptr) - @ptrToInt(&out_buffer), 2) + 1; |
| 997 | 998 | //insufficient size |
| 998 | | std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. result_path.len - 1])); |
| 999 | std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1])); |
| 999 | 1000 | //exactly-sufficient size |
| 1000 | | _ = try QueryObjectName(handle, out_buffer[0..result_path.len]); |
| 1001 | _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]); |
| 1001 | 1002 | } |
| 1002 | 1003 | |
| 1003 | 1004 | pub const GetFinalPathNameByHandleError = error{ |
| ... | ... | @@ -1029,8 +1030,7 @@ pub fn GetFinalPathNameByHandle( |
| 1029 | 1030 | fmt: GetFinalPathNameByHandleFormat, |
| 1030 | 1031 | out_buffer: []u16, |
| 1031 | 1032 | ) GetFinalPathNameByHandleError![]u16 { |
| 1032 | | var path_buffer: [math.max(@sizeOf(FILE_NAME_INFORMATION), @sizeOf(OBJECT_NAME_INFORMATION)) + PATH_MAX_WIDE * 2]u8 align(@alignOf(FILE_NAME_INFORMATION)) = undefined; |
| 1033 | | const final_path = QueryObjectName(hFile, mem.bytesAsSlice(u16, &path_buffer)) catch |err| switch (err) { |
| 1033 | const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) { |
| 1034 | 1034 | // we assume InvalidHandle is close enough to FileNotFound in semantics |
| 1035 | 1035 | // to not further complicate the error set |
| 1036 | 1036 | error.InvalidHandle => return error.FileNotFound, |
| ... | ... | @@ -1040,11 +1040,7 @@ pub fn GetFinalPathNameByHandle( |
| 1040 | 1040 | switch (fmt.volume_name) { |
| 1041 | 1041 | .Nt => { |
| 1042 | 1042 | // the returned path is already in .Nt format |
| 1043 | | if (out_buffer.len < final_path.len) { |
| 1044 | | return error.NameTooLong; |
| 1045 | | } |
| 1046 | | mem.copy(u16, out_buffer, final_path); |
| 1047 | | return out_buffer[0..final_path.len]; |
| 1043 | return final_path; |
| 1048 | 1044 | }, |
| 1049 | 1045 | .Dos => { |
| 1050 | 1046 | // parse the string to separate volume path from file path |
| ... | ... | @@ -1153,16 +1149,17 @@ test "GetFinalPathNameByHandle" { |
| 1153 | 1149 | var buffer: [PATH_MAX_WIDE]u16 = undefined; |
| 1154 | 1150 | |
| 1155 | 1151 | //check with sufficient size |
| 1156 | | const nt_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer)).len; |
| 1157 | | const dos_length = (try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer)).len; |
| 1152 | const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer); |
| 1153 | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer); |
| 1158 | 1154 | |
| 1155 | const required_len_in_u16 = nt_path.len + @divExact(@ptrToInt(nt_path.ptr) - @ptrToInt(&buffer), 2) + 1; |
| 1159 | 1156 | //check with insufficient size |
| 1160 | | std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. nt_length - 1])); |
| 1161 | | std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. dos_length - 1])); |
| 1157 | std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1])); |
| 1158 | std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1])); |
| 1162 | 1159 | |
| 1163 | 1160 | //check with exactly-sufficient size |
| 1164 | | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..nt_length]); |
| 1165 | | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..dos_length]); |
| 1161 | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]); |
| 1162 | _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]); |
| 1166 | 1163 | } |
| 1167 | 1164 | |
| 1168 | 1165 | pub const QueryInformationFileError = error{Unexpected}; |