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{
53645364 /// intercepts file system operations and makes them significantly slower
53655365 /// in addition to possibly failing with this error code.
53665366 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,
53675371} || UnexpectedError;
53685372
53695373/// Return the canonicalized absolute pathname.
......@@ -5371,6 +5375,7 @@ pub const RealPathError = error{
53715375/// extra `/` characters in `pathname`.
53725376/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
53735377/// See also `realpathZ` and `realpathW`.
5378/// Calling this function is usually a bug.
53745379pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
53755380 if (builtin.os.tag == .windows) {
53765381 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
53835388}
53845389
53855390/// Same as `realpath` except `pathname` is null-terminated.
5391/// Calling this function is usually a bug.
53865392pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
53875393 if (builtin.os.tag == .windows) {
53885394 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
54315437}
54325438
54335439/// Same as `realpath` except `pathname` is UTF16LE-encoded.
5440/// Calling this function is usually a bug.
54345441pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
54355442 const w = windows;
54365443
......@@ -5479,6 +5486,7 @@ pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
54795486/// This function is very host-specific and is not universally supported by all hosts.
54805487/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
54815488/// unsupported on WASI.
5489/// Calling this function is usually a bug.
54825490pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
54835491 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
54845492 @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,
178178 }
179179}
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
183189/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
184190/// It implements similar behavior to `DeviceIoControl` and is meant to serve
......@@ -234,6 +240,7 @@ pub fn DeviceIoControl(
234240 .ACCESS_DENIED => return error.AccessDenied,
235241 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
236242 .INVALID_PARAMETER => unreachable,
243 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
237244 else => return unexpectedStatus(rc),
238245 }
239246}
......@@ -606,6 +613,9 @@ pub const CreateSymbolicLinkError = error{
606613 NoDevice,
607614 NetworkNotFound,
608615 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,
609619 Unexpected,
610620};
611621
......@@ -688,12 +698,12 @@ pub fn CreateSymbolicLink(
688698 const target_is_absolute = std.fs.path.isAbsoluteWindowsWTF16(final_target_path);
689699 const symlink_data = SYMLINK_DATA{
690700 .ReparseTag = IO_REPARSE_TAG_SYMLINK,
691 .ReparseDataLength = @as(u16, @intCast(buf_len - header_len)),
701 .ReparseDataLength = @intCast(buf_len - header_len),
692702 .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),
695705 .PrintNameOffset = 0,
696 .PrintNameLength = @as(u16, @intCast(final_target_path.len * 2)),
706 .PrintNameLength = @intCast(final_target_path.len * 2),
697707 .Flags = if (!target_is_absolute) SYMLINK_FLAG_RELATIVE else 0,
698708 };
699709
......@@ -769,7 +779,8 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
769779
770780 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
771781 _ = 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,
773784 else => |e| return e,
774785 };
775786
......@@ -1084,6 +1095,9 @@ pub const GetFinalPathNameByHandleError = error{
10841095 BadPathName,
10851096 FileNotFound,
10861097 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,
10871101 Unexpected,
10881102};
10891103
......@@ -1174,16 +1188,16 @@ pub fn GetFinalPathNameByHandle(
11741188 };
11751189 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]);
11781192 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);
11801194 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
11811195
11821196 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,
11841198 else => |e| return e,
11851199 };
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
11881202 const mount_points = @as(
11891203 [*]const MOUNTMGR_MOUNT_POINT,
......@@ -2203,7 +2217,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
22032217 .unc_absolute => nt_prefix.len + 2,
22042218 else => nt_prefix.len,
22052219 };
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);
22072221 const path_to_get: [:0]const u16 = path_to_get: {
22082222 // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
22092223 // 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 {
22212235 // canonicalize it. We do this by getting the path of the `dir`
22222236 // and appending the relative path to it.
22232237 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 };
22252256 if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
22262257 return error.NameTooLong;
22272258 }
src/link.zig+1
......@@ -543,6 +543,7 @@ pub const File = struct {
543543 UnexpectedTable,
544544 UnexpectedValue,
545545 UnknownFeature,
546 UnrecognizedVolume,
546547 Unseekable,
547548 UnsupportedCpuArchitecture,
548549 UnsupportedVersion,