@@ -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
}
@@ -688,20 +695,24 @@ pub fn CreateSymbolicLink(
688
695
const target_is_absolute = std .fs .path .isAbsoluteWindowsWTF16 (final_target_path );
689
696
const symlink_data = SYMLINK_DATA {
690
697
.ReparseTag = IO_REPARSE_TAG_SYMLINK ,
691
- .ReparseDataLength = @as ( u16 , @ intCast (buf_len - header_len ) ),
698
+ .ReparseDataLength = @intCast (buf_len - header_len ),
692
699
.Reserved = 0 ,
693
- .SubstituteNameOffset = @as ( u16 , @ intCast (final_target_path .len * 2 ) ),
694
- .SubstituteNameLength = @as ( u16 , @ intCast (final_target_path .len * 2 ) ),
700
+ .SubstituteNameOffset = @intCast (final_target_path .len * 2 ),
701
+ .SubstituteNameLength = @intCast (final_target_path .len * 2 ),
695
702
.PrintNameOffset = 0 ,
696
- .PrintNameLength = @as ( u16 , @ intCast (final_target_path .len * 2 ) ),
703
+ .PrintNameLength = @intCast (final_target_path .len * 2 ),
697
704
.Flags = if (! target_is_absolute ) SYMLINK_FLAG_RELATIVE else 0 ,
698
705
};
699
706
700
707
@memcpy (buffer [0.. @sizeOf (SYMLINK_DATA )], std .mem .asBytes (& symlink_data ));
701
708
@memcpy (buffer [@sizeOf (SYMLINK_DATA ).. ][0 .. final_target_path .len * 2 ], @as ([* ]const u8 , @ptrCast (final_target_path )));
702
709
const paths_start = @sizeOf (SYMLINK_DATA ) + final_target_path .len * 2 ;
703
710
@memcpy (buffer [paths_start .. ][0 .. final_target_path .len * 2 ], @as ([* ]const u8 , @ptrCast (final_target_path )));
704
- _ = try DeviceIoControl (symlink_handle , FSCTL_SET_REPARSE_POINT , buffer [0.. buf_len ], null );
711
+ _ = DeviceIoControl (symlink_handle , FSCTL_SET_REPARSE_POINT , buffer [0.. buf_len ], null ) catch | err | switch (err ) {
712
+ error .AccessDenied = > return error .Unexpected ,
713
+ error .UnrecognizedVolume = > return error .Unexpected ,
714
+ error .Unexpected = > return error .Unexpected ,
715
+ };
705
716
}
706
717
707
718
pub const ReadLinkError = error {
@@ -769,7 +780,8 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
769
780
770
781
var reparse_buf : [MAXIMUM_REPARSE_DATA_BUFFER_SIZE ]u8 align (@alignOf (REPARSE_DATA_BUFFER )) = undefined ;
771
782
_ = DeviceIoControl (result_handle , FSCTL_GET_REPARSE_POINT , null , reparse_buf [0.. ]) catch | err | switch (err ) {
772
- error .AccessDenied = > unreachable ,
783
+ error .AccessDenied = > return error .Unexpected ,
784
+ error .UnrecognizedVolume = > return error .Unexpected ,
773
785
else = > | e | return e ,
774
786
};
775
787
@@ -1084,6 +1096,9 @@ pub const GetFinalPathNameByHandleError = error{
1084
1096
BadPathName ,
1085
1097
FileNotFound ,
1086
1098
NameTooLong ,
1099
+ /// The volume does not contain a recognized file system. File system
1100
+ /// drivers might not be loaded, or the volume may be corrupt.
1101
+ UnrecognizedVolume ,
1087
1102
Unexpected ,
1088
1103
};
1089
1104
@@ -1174,13 +1189,13 @@ pub fn GetFinalPathNameByHandle(
1174
1189
};
1175
1190
defer CloseHandle (mgmt_handle );
1176
1191
1177
- var input_struct = @as ( * MOUNTMGR_MOUNT_POINT , @ptrCast (& input_buf [0 ]) );
1192
+ var input_struct : * MOUNTMGR_MOUNT_POINT = @ptrCast (& input_buf [0 ]);
1178
1193
input_struct .DeviceNameOffset = @sizeOf (MOUNTMGR_MOUNT_POINT );
1179
1194
input_struct .DeviceNameLength = @as (USHORT , @intCast (volume_name_u16 .len * 2 ));
1180
1195
@memcpy (input_buf [@sizeOf (MOUNTMGR_MOUNT_POINT ).. ][0 .. volume_name_u16 .len * 2 ], @as ([* ]const u8 , @ptrCast (volume_name_u16 .ptr )));
1181
1196
1182
1197
DeviceIoControl (mgmt_handle , IOCTL_MOUNTMGR_QUERY_POINTS , & input_buf , & output_buf ) catch | err | switch (err ) {
1183
- error .AccessDenied = > unreachable ,
1198
+ error .AccessDenied = > return error . Unexpected ,
1184
1199
else = > | e | return e ,
1185
1200
};
1186
1201
const mount_points_struct = @as (* const MOUNTMGR_MOUNT_POINTS , @ptrCast (& output_buf [0 ]));
@@ -2203,7 +2218,7 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
2203
2218
.unc_absolute = > nt_prefix .len + 2 ,
2204
2219
else = > nt_prefix .len ,
2205
2220
};
2206
- const buf_len = @as ( u32 , @ intCast (path_space .data .len - path_buf_offset ) );
2221
+ const buf_len : u32 = @intCast (path_space .data .len - path_buf_offset );
2207
2222
const path_to_get : [:0 ]const u16 = path_to_get : {
2208
2223
// If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
2209
2224
// RtlGetFullPathName_U will resolve relative paths against the CWD for us.
@@ -2221,7 +2236,24 @@ pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
2221
2236
// canonicalize it. We do this by getting the path of the `dir`
2222
2237
// and appending the relative path to it.
2223
2238
var dir_path_buf : [PATH_MAX_WIDE :0 ]u16 = undefined ;
2224
- const dir_path = try GetFinalPathNameByHandle (dir .? , .{}, & dir_path_buf );
2239
+ const dir_path = GetFinalPathNameByHandle (dir .? , .{}, & dir_path_buf ) catch | err | switch (err ) {
2240
+ // This mapping is not correct; it is actually expected
2241
+ // that calling GetFinalPathNameByHandle might return
2242
+ // error.UnrecognizedVolume, and in fact has been observed
2243
+ // in the wild. The problem is that wToPrefixedFileW was
2244
+ // never intended to make *any* OS syscall APIs. It's only
2245
+ // supposed to convert a string to one that is eligible to
2246
+ // be used in the ntdll syscalls.
2247
+ //
2248
+ // To solve this, this function needs to no longer call
2249
+ // GetFinalPathNameByHandle under any conditions, or the
2250
+ // calling function needs to get reworked to not need to
2251
+ // call this function.
2252
+ //
2253
+ // This may involve making breaking API changes.
2254
+ error .UnrecognizedVolume = > return error .Unexpected ,
2255
+ else = > | e | return e ,
2256
+ };
2225
2257
if (dir_path .len + 1 + path .len > PATH_MAX_WIDE ) {
2226
2258
return error .NameTooLong ;
2227
2259
}
0 commit comments