Skip to content

Commit f4620d4

Browse files
committed
std.os.windows: add error.UnrecognizedVolume
Thanks to @matklad for finding this additional NTSTATUS possibility when calling GetFinalPathNameByHandle.
1 parent 2176a73 commit f4620d4

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

lib/std/os.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5364,13 +5364,18 @@ 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.
53705374
/// Expands all symbolic links and resolves references to `.`, `..`, and
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.
53745379
pub 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.
53865392
pub 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.
54345441
pub 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.
54825490
pub 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

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,13 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
178178
}
179179
}
180180

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+
};
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ pub const File = struct {
543543
UnexpectedTable,
544544
UnexpectedValue,
545545
UnknownFeature,
546+
UnrecognizedVolume,
546547
Unseekable,
547548
UnsupportedCpuArchitecture,
548549
UnsupportedVersion,

0 commit comments

Comments
 (0)