| ... | ... | @@ -178,7 +178,13 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16, |
| 178 | 178 | } |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | | pub const DeviceIoControlError = error{ AccessDenied, Unexpected }; |
| 181 | pub const DeviceIoControlError = error{ |
| 182 | AccessDenied, |
| 183 | /// The volume does not contain a recognized file system. File system |
| 184 | /// drivers might not be loaded, or the volume may be corrupt. |
| 185 | UnrecognizedVolume, |
| 186 | Unexpected, |
| 187 | }; |
| 182 | 188 | |
| 183 | 189 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. |
| 184 | 190 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve |
| ... | ... | @@ -234,6 +240,7 @@ pub fn DeviceIoControl( |
| 234 | 240 | .ACCESS_DENIED => return error.AccessDenied, |
| 235 | 241 | .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem |
| 236 | 242 | .INVALID_PARAMETER => unreachable, |
| 243 | .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume, |
| 237 | 244 | else => return unexpectedStatus(rc), |
| 238 | 245 | } |
| 239 | 246 | } |
| ... | ... | @@ -606,6 +613,9 @@ pub const CreateSymbolicLinkError = error{ |
| 606 | 613 | NoDevice, |
| 607 | 614 | NetworkNotFound, |
| 608 | 615 | BadPathName, |
| 616 | /// The volume does not contain a recognized file system. File system |
| 617 | /// drivers might not be loaded, or the volume may be corrupt. |
| 618 | UnrecognizedVolume, |
| 609 | 619 | Unexpected, |
| 610 | 620 | }; |
| 611 | 621 | |
| ... | ... | @@ -688,12 +698,12 @@ pub fn CreateSymbolicLink( |
| 688 | 698 | const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path); |
| 689 | 699 | const symlink_data = SYMLINK_DATA{ |
| 690 | 700 | .ReparseTag = IO_REPARSE_TAG_SYMLINK, |
| 691 | | .ReparseDataLength = @as(u16, @intCast(buf_len - header_len)), |
| 701 | .ReparseDataLength = @intCast(buf_len - header_len), |
| 692 | 702 | .Reserved = 0, |
| 693 | | .SubstituteNameOffset = @as(u16, @intCast(final_target_path.len * 2)), |
| 694 | | .SubstituteNameLength = @as(u16, @intCast(final_target_path.len * 2)), |
| 703 | .SubstituteNameOffset = @intCast(final_target_path.len * 2), |
| 704 | .SubstituteNameLength = @intCast(final_target_path.len * 2), |
| 695 | 705 | .PrintNameOffset = 0, |
| 696 | | .PrintNameLength = @as(u16, @intCast(final_target_path.len * 2)), |
| 706 | .PrintNameLength = @intCast(final_target_path.len * 2), |
| 697 | 707 | .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0, |
| 698 | 708 | }; |
| 699 | 709 | |
| ... | ... | @@ -769,7 +779,8 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 769 | 779 | |
| 770 | 780 | var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined; |
| 771 | 781 | _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) { |
| 772 | | error.AccessDenied => unreachable, |
| 782 | error.AccessDenied => return error.Unexpected, |
| 783 | error.UnrecognizedVolume => return error.Unexpected, |
| 773 | 784 | else => |e| return e, |
| 774 | 785 | }; |
| 775 | 786 | |
| ... | ... | @@ -1084,6 +1095,9 @@ pub const GetFinalPathNameByHandleError = error{ |
| 1084 | 1095 | BadPathName, |
| 1085 | 1096 | FileNotFound, |
| 1086 | 1097 | NameTooLong, |
| 1098 | /// The volume does not contain a recognized file system. File system |
| 1099 | /// drivers might not be loaded, or the volume may be corrupt. |
| 1100 | UnrecognizedVolume, |
| 1087 | 1101 | Unexpected, |
| 1088 | 1102 | }; |
| 1089 | 1103 | |
| ... | ... | @@ -1174,16 +1188,16 @@ pub fn GetFinalPathNameByHandle( |
| 1174 | 1188 | }; |
| 1175 | 1189 | defer CloseHandle(mgmt_handle); |
| 1176 | 1190 | |
| 1177 | | var input_struct = @as(*MOUNTMGR_MOUNT_POINT, @ptrCast(&input_buf[0])); |
| 1191 | var input_struct: *MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]); |
| 1178 | 1192 | input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT); |
| 1179 | | input_struct.DeviceNameLength = @as(USHORT, @intCast(volume_name_u16.len * 2)); |
| 1193 | input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2); |
| 1180 | 1194 | @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr))); |
| 1181 | 1195 | |
| 1182 | 1196 | DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) { |
| 1183 | | error.AccessDenied => unreachable, |
| 1197 | error.AccessDenied => return error.Unexpected, |
| 1184 | 1198 | else => |e| return e, |
| 1185 | 1199 | }; |
| 1186 | | const mount_points_struct = @as(*const MOUNTMGR_MOUNT_POINTS, @ptrCast(&output_buf[0])); |
| 1200 | const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]); |
| 1187 | 1201 | |
| 1188 | 1202 | const mount_points = @as( |
| 1189 | 1203 | [*]const MOUNTMGR_MOUNT_POINT, |
| ... | ... | @@ -2203,7 +2217,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace { |
| 2203 | 2217 | .unc_absolute => nt_prefix.len + 2, |
| 2204 | 2218 | else => nt_prefix.len, |
| 2205 | 2219 | }; |
| 2206 | | const buf_len = @as(u32, @intCast(path_space.data.len - path_buf_offset)); |
| 2220 | const buf_len: u32 = @intCast(path_space.data.len - path_buf_offset); |
| 2207 | 2221 | const path_to_get: [:0]const u16 = path_to_get: { |
| 2208 | 2222 | // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because |
| 2209 | 2223 | // RtlGetFullPathName_U will resolve relative paths against the CWD for us. |
| ... | ... | @@ -2221,7 +2235,24 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace { |
| 2221 | 2235 | // canonicalize it. We do this by getting the path of the `dir` |
| 2222 | 2236 | // and appending the relative path to it. |
| 2223 | 2237 | var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined; |
| 2224 | | const dir_path = try GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf); |
| 2238 | const dir_path = GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf) catch |err| switch (err) { |
| 2239 | // This mapping is not correct; it is actually expected |
| 2240 | // that calling GetFinalPathNameByHandle might return |
| 2241 | // error.UnrecognizedVolume, and in fact has been observed |
| 2242 | // in the wild. The problem is that wToPrefixedFileW was |
| 2243 | // never intended to make *any* OS syscall APIs. It's only |
| 2244 | // supposed to convert a string to one that is eligible to |
| 2245 | // be used in the ntdll syscalls. |
| 2246 | // |
| 2247 | // To solve this, this function needs to no longer call |
| 2248 | // GetFinalPathNameByHandle under any conditions, or the |
| 2249 | // calling function needs to get reworked to not need to |
| 2250 | // call this function. |
| 2251 | // |
| 2252 | // This may involve making breaking API changes. |
| 2253 | error.UnrecognizedVolume => return error.Unexpected, |
| 2254 | else => |e| return e, |
| 2255 | }; |
| 2225 | 2256 | if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) { |
| 2226 | 2257 | return error.NameTooLong; |
| 2227 | 2258 | } |