authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-15 15:52:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-16 00:20:57-08:00
log0183b44bb10751c46bd520e673726a66c027b477
treeb3e6926ab5b21146ba1df9d4f37e70a92af7d131
parent2176a73d66ecdddf9e734504d77f0c1eb9f8e2e1

std.os.windows: add error.UnrecognizedVolume

Thanks to @matklad for finding this additional NTSTATUS possibility when calling GetFinalPathNameByHandle.

3 files changed, 52 insertions(+), 12 deletions(-)

lib/std/os.zig+8
...@@ -5364,6 +5364,10 @@ pub const RealPathError = error{...@@ -5364,6 +5364,10 @@ pub const RealPathError = error{
5364 /// intercepts file system operations and makes them significantly slower5364 /// intercepts file system operations and makes them significantly slower
5365 /// in addition to possibly failing with this error code.5365 /// in addition to possibly failing with this error code.
5366 AntivirusInterference,5366 AntivirusInterference,
5367
5368 /// On Windows, the volume does not contain a recognized file system. File
5369 /// system drivers might not be loaded, or the volume may be corrupt.
5370 UnrecognizedVolume,
5367} || UnexpectedError;5371} || UnexpectedError;
53685372
5369/// Return the canonicalized absolute pathname.5373/// Return the canonicalized absolute pathname.
...@@ -5371,6 +5375,7 @@ pub const RealPathError = error{...@@ -5371,6 +5375,7 @@ pub const RealPathError = error{
5371/// extra `/` characters in `pathname`.5375/// extra `/` characters in `pathname`.
5372/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.5376/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
5373/// See also `realpathZ` and `realpathW`.5377/// See also `realpathZ` and `realpathW`.
5378/// Calling this function is usually a bug.
5374pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {5379pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5375 if (builtin.os.tag == .windows) {5380 if (builtin.os.tag == .windows) {
5376 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);5381 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
...@@ -5383,6 +5388,7 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE...@@ -5383,6 +5388,7 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
5383}5388}
53845389
5385/// Same as `realpath` except `pathname` is null-terminated.5390/// Same as `realpath` except `pathname` is null-terminated.
5391/// Calling this function is usually a bug.
5386pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {5392pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5387 if (builtin.os.tag == .windows) {5393 if (builtin.os.tag == .windows) {
5388 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);5394 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
...@@ -5431,6 +5437,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP...@@ -5431,6 +5437,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
5431}5437}
54325438
5433/// Same as `realpath` except `pathname` is UTF16LE-encoded.5439/// Same as `realpath` except `pathname` is UTF16LE-encoded.
5440/// Calling this function is usually a bug.
5434pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {5441pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5435 const w = windows;5442 const w = windows;
54365443
...@@ -5479,6 +5486,7 @@ pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {...@@ -5479,6 +5486,7 @@ pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
5479/// This function is very host-specific and is not universally supported by all hosts.5486/// This function is very host-specific and is not universally supported by all hosts.
5480/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is5487/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
5481/// unsupported on WASI.5488/// unsupported on WASI.
5489/// Calling this function is usually a bug.
5482pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {5490pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5483 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {5491 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
5484 @compileError("querying for canonical path of a handle is unsupported on this host");5492 @compileError("querying for canonical path of a handle is unsupported on this host");
lib/std/os/windows.zig+43-12
...@@ -178,7 +178,13 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,...@@ -178,7 +178,13 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
178 }178 }
179}179}
180180
181pub const DeviceIoControlError = error{ AccessDenied, Unexpected };181pub 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};
182188
183/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.189/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
184/// It implements similar behavior to `DeviceIoControl` and is meant to serve190/// It implements similar behavior to `DeviceIoControl` and is meant to serve
...@@ -234,6 +240,7 @@ pub fn DeviceIoControl(...@@ -234,6 +240,7 @@ pub fn DeviceIoControl(
234 .ACCESS_DENIED => return error.AccessDenied,240 .ACCESS_DENIED => return error.AccessDenied,
235 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem241 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
236 .INVALID_PARAMETER => unreachable,242 .INVALID_PARAMETER => unreachable,
243 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
237 else => return unexpectedStatus(rc),244 else => return unexpectedStatus(rc),
238 }245 }
239}246}
...@@ -606,6 +613,9 @@ pub const CreateSymbolicLinkError = error{...@@ -606,6 +613,9 @@ pub const CreateSymbolicLinkError = error{
606 NoDevice,613 NoDevice,
607 NetworkNotFound,614 NetworkNotFound,
608 BadPathName,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 Unexpected,619 Unexpected,
610};620};
611621
...@@ -688,12 +698,12 @@ pub fn CreateSymbolicLink(...@@ -688,12 +698,12 @@ pub fn CreateSymbolicLink(
688 const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path);698 const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path);
689 const symlink_data = SYMLINK_DATA{699 const symlink_data = SYMLINK_DATA{
690 .ReparseTag = IO_REPARSE_TAG_SYMLINK,700 .ReparseTag = IO_REPARSE_TAG_SYMLINK,
691 .ReparseDataLength = @as(u16, @intCast(buf_len - header_len)),701 .ReparseDataLength = @intCast(buf_len - header_len),
692 .Reserved = 0,702 .Reserved = 0,
693 .SubstituteNameOffset = @as(u16, @intCast(final_target_path.len * 2)),703 .SubstituteNameOffset = @intCast(final_target_path.len * 2),
694 .SubstituteNameLength = @as(u16, @intCast(final_target_path.len * 2)),704 .SubstituteNameLength = @intCast(final_target_path.len * 2),
695 .PrintNameOffset = 0,705 .PrintNameOffset = 0,
696 .PrintNameLength = @as(u16, @intCast(final_target_path.len * 2)),706 .PrintNameLength = @intCast(final_target_path.len * 2),
697 .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,707 .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
698 };708 };
699709
...@@ -769,7 +779,8 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin...@@ -769,7 +779,8 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
769779
770 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;780 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
771 _ = DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {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 else => |e| return e,784 else => |e| return e,
774 };785 };
775786
...@@ -1084,6 +1095,9 @@ pub const GetFinalPathNameByHandleError = error{...@@ -1084,6 +1095,9 @@ pub const GetFinalPathNameByHandleError = error{
1084 BadPathName,1095 BadPathName,
1085 FileNotFound,1096 FileNotFound,
1086 NameTooLong,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 Unexpected,1101 Unexpected,
1088};1102};
10891103
...@@ -1174,16 +1188,16 @@ pub fn GetFinalPathNameByHandle(...@@ -1174,16 +1188,16 @@ pub fn GetFinalPathNameByHandle(
1174 };1188 };
1175 defer CloseHandle(mgmt_handle);1189 defer CloseHandle(mgmt_handle);
11761190
1177 var input_struct = @as(*MOUNTMGR_MOUNT_POINT, @ptrCast(&input_buf[0]));1191 var input_struct: *MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]);
1178 input_struct.DeviceNameOffset = @sizeOf(MOUNTMGR_MOUNT_POINT);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 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));1194 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
11811195
1182 DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_POINTS, &input_buf, &output_buf) catch |err| switch (err) {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 else => |e| return e,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]);
11871201
1188 const mount_points = @as(1202 const mount_points = @as(
1189 [*]const MOUNTMGR_MOUNT_POINT,1203 [*]const MOUNTMGR_MOUNT_POINT,
...@@ -2203,7 +2217,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {...@@ -2203,7 +2217,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
2203 .unc_absolute => nt_prefix.len + 2,2217 .unc_absolute => nt_prefix.len + 2,
2204 else => nt_prefix.len,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 const path_to_get: [:0]const u16 = path_to_get: {2221 const path_to_get: [:0]const u16 = path_to_get: {
2208 // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because2222 // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
2209 // RtlGetFullPathName_U will resolve relative paths against the CWD for us.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,7 +2235,24 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
2221 // canonicalize it. We do this by getting the path of the `dir`2235 // canonicalize it. We do this by getting the path of the `dir`
2222 // and appending the relative path to it.2236 // and appending the relative path to it.
2223 var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined;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 if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {2256 if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
2226 return error.NameTooLong;2257 return error.NameTooLong;
2227 }2258 }
src/link.zig+1
...@@ -543,6 +543,7 @@ pub const File = struct {...@@ -543,6 +543,7 @@ pub const File = struct {
543 UnexpectedTable,543 UnexpectedTable,
544 UnexpectedValue,544 UnexpectedValue,
545 UnknownFeature,545 UnknownFeature,
546 UnrecognizedVolume,
546 Unseekable,547 Unseekable,
547 UnsupportedCpuArchitecture,548 UnsupportedCpuArchitecture,
548 UnsupportedVersion,549 UnsupportedVersion,