| ... | ... | @@ -1368,6 +1368,61 @@ pub fn GetFinalPathNameByHandle( |
| 1368 | 1368 | return error.BadPathName; |
| 1369 | 1369 | } |
| 1370 | 1370 | |
| 1371 | return out_buffer[0..total_len]; |
| 1372 | } else if (mountmgrIsVolumeName(symlink)) { |
| 1373 | // If the symlink is a volume GUID like \??\Volume{383da0b0-717f-41b6-8c36-00500992b58d}, |
| 1374 | // then it is a volume mounted as a path rather than a drive letter. We need to |
| 1375 | // query the mount manager again to get the DOS path for the volume. |
| 1376 | |
| 1377 | // 49 is the maximum length accepted by mountmgrIsVolumeName |
| 1378 | const vol_input_size = @sizeOf(MOUNTMGR_TARGET_NAME) + (49 * 2); |
| 1379 | var vol_input_buf: [vol_input_size]u8 align(@alignOf(MOUNTMGR_TARGET_NAME)) = [_]u8{0} ** vol_input_size; |
| 1380 | // Note: If the path exceeds MAX_PATH, the Disk Management GUI doesn't accept the full path, |
| 1381 | // and instead if must be specified using a shortened form (e.g. C:\FOO~1\BAR~1\<...>). |
| 1382 | // However, just to be sure we can handle any path length, we use PATH_MAX_WIDE here. |
| 1383 | const min_output_size = @sizeOf(MOUNTMGR_VOLUME_PATHS) + (PATH_MAX_WIDE * 2); |
| 1384 | var vol_output_buf: [min_output_size]u8 align(@alignOf(MOUNTMGR_VOLUME_PATHS)) = undefined; |
| 1385 | |
| 1386 | var vol_input_struct: *MOUNTMGR_TARGET_NAME = @ptrCast(&vol_input_buf[0]); |
| 1387 | vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2); |
| 1388 | @memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink); |
| 1389 | |
| 1390 | DeviceIoControl(mgmt_handle, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH, &vol_input_buf, &vol_output_buf) catch |err| switch (err) { |
| 1391 | error.AccessDenied => return error.Unexpected, |
| 1392 | else => |e| return e, |
| 1393 | }; |
| 1394 | const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]); |
| 1395 | const volume_path = std.mem.sliceTo(@as( |
| 1396 | [*]const u16, |
| 1397 | &volume_paths_struct.MultiSz, |
| 1398 | )[0 .. volume_paths_struct.MultiSzLength / 2], 0); |
| 1399 | |
| 1400 | if (out_buffer.len < volume_path.len + file_name_u16.len) return error.NameTooLong; |
| 1401 | |
| 1402 | // `out_buffer` currently contains the memory of `file_name_u16`, so it can overlap with where |
| 1403 | // we want to place the filename before returning. Here are the possible overlapping cases: |
| 1404 | // |
| 1405 | // out_buffer: [filename] |
| 1406 | // dest: [___(a)___] [___(b)___] |
| 1407 | // |
| 1408 | // In the case of (a), we need to copy forwards, and in the case of (b) we need |
| 1409 | // to copy backwards. We also need to do this before copying the volume path because |
| 1410 | // it could overwrite the file_name_u16 memory. |
| 1411 | const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len]; |
| 1412 | const file_name_byte_offset = @intFromPtr(file_name_u16.ptr) - @intFromPtr(out_buffer.ptr); |
| 1413 | const file_name_index = file_name_byte_offset / @sizeOf(u16); |
| 1414 | if (volume_path.len > file_name_index) |
| 1415 | mem.copyBackwards(u16, file_name_dest, file_name_u16) |
| 1416 | else |
| 1417 | mem.copyForwards(u16, file_name_dest, file_name_u16); |
| 1418 | @memcpy(out_buffer[0..volume_path.len], volume_path); |
| 1419 | const total_len = volume_path.len + file_name_u16.len; |
| 1420 | |
| 1421 | // Validate that DOS does not contain any spurious nul bytes. |
| 1422 | if (mem.indexOfScalar(u16, out_buffer[0..total_len], 0)) |_| { |
| 1423 | return error.BadPathName; |
| 1424 | } |
| 1425 | |
| 1371 | 1426 | return out_buffer[0..total_len]; |
| 1372 | 1427 | } |
| 1373 | 1428 | } |
| ... | ... | @@ -1379,6 +1434,32 @@ pub fn GetFinalPathNameByHandle( |
| 1379 | 1434 | } |
| 1380 | 1435 | } |
| 1381 | 1436 | |
| 1437 | /// Equivalent to the MOUNTMGR_IS_VOLUME_NAME macro in mountmgr.h |
| 1438 | fn mountmgrIsVolumeName(name: []const u16) bool { |
| 1439 | return (name.len == 48 or (name.len == 49 and name[48] == mem.nativeToLittle(u16, '\\'))) and |
| 1440 | name[0] == mem.nativeToLittle(u16, '\\') and |
| 1441 | (name[1] == mem.nativeToLittle(u16, '?') or name[1] == mem.nativeToLittle(u16, '\\')) and |
| 1442 | name[2] == mem.nativeToLittle(u16, '?') and |
| 1443 | name[3] == mem.nativeToLittle(u16, '\\') and |
| 1444 | mem.startsWith(u16, name[4..], std.unicode.utf8ToUtf16LeStringLiteral("Volume{")) and |
| 1445 | name[19] == mem.nativeToLittle(u16, '-') and |
| 1446 | name[24] == mem.nativeToLittle(u16, '-') and |
| 1447 | name[29] == mem.nativeToLittle(u16, '-') and |
| 1448 | name[34] == mem.nativeToLittle(u16, '-') and |
| 1449 | name[47] == mem.nativeToLittle(u16, '}'); |
| 1450 | } |
| 1451 | |
| 1452 | test mountmgrIsVolumeName { |
| 1453 | const L = std.unicode.utf8ToUtf16LeStringLiteral; |
| 1454 | try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}"))); |
| 1455 | try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}"))); |
| 1456 | try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\"))); |
| 1457 | try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\"))); |
| 1458 | try std.testing.expect(!mountmgrIsVolumeName(L("\\\\.\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}"))); |
| 1459 | try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\foo"))); |
| 1460 | try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58}"))); |
| 1461 | } |
| 1462 | |
| 1382 | 1463 | test GetFinalPathNameByHandle { |
| 1383 | 1464 | if (builtin.os.tag != .windows) |
| 1384 | 1465 | return; |
| ... | ... | @@ -4845,6 +4926,8 @@ pub const SYMLINK_FLAG_RELATIVE: ULONG = 0x1; |
| 4845 | 4926 | pub const SYMBOLIC_LINK_FLAG_DIRECTORY: DWORD = 0x1; |
| 4846 | 4927 | pub const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE: DWORD = 0x2; |
| 4847 | 4928 | |
| 4929 | pub const MOUNTMGRCONTROLTYPE = 0x0000006D; |
| 4930 | |
| 4848 | 4931 | pub const MOUNTMGR_MOUNT_POINT = extern struct { |
| 4849 | 4932 | SymbolicLinkNameOffset: ULONG, |
| 4850 | 4933 | SymbolicLinkNameLength: USHORT, |
| ... | ... | @@ -4861,7 +4944,17 @@ pub const MOUNTMGR_MOUNT_POINTS = extern struct { |
| 4861 | 4944 | NumberOfMountPoints: ULONG, |
| 4862 | 4945 | MountPoints: [1]MOUNTMGR_MOUNT_POINT, |
| 4863 | 4946 | }; |
| 4864 | | pub const IOCTL_MOUNTMGR_QUERY_POINTS: ULONG = 0x6d0008; |
| 4947 | pub const IOCTL_MOUNTMGR_QUERY_POINTS = CTL_CODE(MOUNTMGRCONTROLTYPE, 2, .METHOD_BUFFERED, FILE_ANY_ACCESS); |
| 4948 | |
| 4949 | pub const MOUNTMGR_TARGET_NAME = extern struct { |
| 4950 | DeviceNameLength: USHORT, |
| 4951 | DeviceName: [1]WCHAR, |
| 4952 | }; |
| 4953 | pub const MOUNTMGR_VOLUME_PATHS = extern struct { |
| 4954 | MultiSzLength: ULONG, |
| 4955 | MultiSz: [1]WCHAR, |
| 4956 | }; |
| 4957 | pub const IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH = CTL_CODE(MOUNTMGRCONTROLTYPE, 12, .METHOD_BUFFERED, FILE_ANY_ACCESS); |
| 4865 | 4958 | |
| 4866 | 4959 | pub const OBJECT_INFORMATION_CLASS = enum(c_int) { |
| 4867 | 4960 | ObjectBasicInformation = 0, |