From 90f76c860fb41266fb977fcc41d0b81dc806bc57 Mon Sep 17 00:00:00 2001 From: Tom Read Cutting Date: Sun, 15 Oct 2023 20:08:18 +0100 Subject: [PATCH] Deal with NT paths in GetFinalPathNameByHandle When calling QueryObjectName, NT namespaced paths can be returned. This change appropriately strips the prefix to turn it into an absolute path. (The above behaviour was observed at least in Wine so far) Co-authored-by: Ryan Liptak --- lib/std/os/windows.zig | 43 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index d40fee8db241..8371a177a8d9 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1219,16 +1219,33 @@ pub fn GetFinalPathNameByHandle( }, .Dos => { // parse the string to separate volume path from file path - const expected_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\"); - - // TODO find out if a path can start with something besides `\Device\`, - // and if we need to handle it differently - // (i.e. how to determine the start and end of the volume name in that case) - if (!mem.eql(u16, expected_prefix, final_path[0..expected_prefix.len])) return error.Unexpected; + const device_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\"); + + // We aren't entirely sure of the structure of the path returned by + // QueryObjectName in all contexts/environments. + // This code is written to cover the various cases that have + // been encountered and solved appropriately. But note that there's + // no easy way to verify that they have all been tackled! + // (Unless you, the reader knows of one then please do action that!) + if (!mem.eql(u16, device_prefix, final_path[0..device_prefix.len])) { + // Wine seems to return NT namespaced paths from QueryObjectName + // (e.g. `\??\Z:\some\path\to\a\file.txt`), in which case we can just strip the + // prefix to turn it into an absolute paths + const namespace_prefix = getNamespacePrefix(u16, final_path); + if (namespace_prefix == .nt) { + const unprefixed_path = final_path[namespace_prefix..]; + // TODO: Handle other possible path types, only drive absolute has been observed so far + if (getUnprefixedPathType(u16, unprefixed_path) != .drive_absolute) { + return error.Unexpected; + } + return unprefixed_path; + } + return error.Unexpected; + } - const file_path_begin_index = mem.indexOfPos(u16, final_path, expected_prefix.len, &[_]u16{'\\'}) orelse unreachable; + const file_path_begin_index = mem.indexOfPos(u16, final_path, device_prefix.len, &[_]u16{'\\'}) orelse unreachable; const volume_name_u16 = final_path[0..file_path_begin_index]; - const device_name_u16 = volume_name_u16[expected_prefix.len..]; + const device_name_u16 = volume_name_u16[device_prefix.len..]; const file_name_u16 = final_path[file_path_begin_index..]; // MUP is Multiple UNC Provider, and indicates that the path is a UNC @@ -2343,6 +2360,16 @@ pub const NamespacePrefix = enum { fake_verbatim, /// `\??\` nt, + + pub fn prefixLen(namespace_prefix: NamespacePrefix) i32 { + switch (namespace_prefix) { + .none => 0, + .local_device => 4, + .verbatim => 4, + .fake_verbatim => 4, + .nt => 4, + } + } }; /// If `T` is `u16`, then `path` should be encoded as UTF-16LE.