Skip to content

fix std.fs.path.resolveWindows on UNC paths with mixed path separators - #25681

Merged
alexrp merged 1 commit into
ziglang:masterfrom
Techatrix:windows-resolve-unc
Oct 26, 2025
Merged

fix std.fs.path.resolveWindows on UNC paths with mixed path separators#25681
alexrp merged 1 commit into
ziglang:masterfrom
Techatrix:windows-resolve-unc

Conversation

@Techatrix

Copy link
Copy Markdown
Contributor

The std.fs.path.resolveWindows function does not appear to properly handle UNC paths that have mixed path separators.

Example:

conststd=@import("std");
constprint=std.debug.print;
constally=std.heap.page_allocator;
constresolve=std.fs.path.resolveWindows;
pubfnmain() !void {
// these are valid UNC paths when I tested them in my shellprint("{s}\n", .{tryresolve(ally, &.{ "//server/share", "..", "relative" })});
print("{s}\n", .{tryresolve(ally, &.{ "\\\\server\\share", "..", "relative" })});
print("{s}\n", .{tryresolve(ally, &.{ "//server\\share", "..", "relative" })});
print("{s}\n", .{tryresolve(ally, &.{ "\\\\server/share", "..", "relative" })});
// these aren't valid UNC paths when I tested them in my shellprint("{s}\n", .{tryresolve(ally, &.{ "/\\server\\share", "..", "relative" })});
print("{s}\n", .{tryresolve(ally, &.{ "\\/server/share", "..", "relative" })});
}

This would previously output the following:

\\server\share\relative
\\server\share\relative
server\relative
server\relative
server\relative
server\relative

With this patch it will output the following:

\\server\share\relative
\\server\share\relative
\\server\share\relative
\\server\share\relative
server\relative
server\relative

@squeek502squeek502 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

With regards to the \/ and /\ starting patterns, I think it'd be better to treat them as UNC paths, too. It does seem that not everything accepts them as UNC paths (e.g. putting them into the explorer location bar opened a web browser window for me), but for both /\server\share/../relative and \/server/share/../relative:

  • RtlDetermineDosPathNameType_U returns RtlPathTypeUncAbsolute
  • RtlGetFullPathName_U returns \\server\share\relative
  • RtlDosPathNameToNtPathName_U returns \??\UNC\server\share\relative
test code

Added to std/os/windows/test.zig

fnRtlGetFullPathName_U(path: [:0]constu16) !windows.PathSpace {
varpath_space: windows.PathSpace=undefined;
constpath_byte_len=windows.ntdll.RtlGetFullPathName_U(
path.ptr,
path_space.data.len*2,
&path_space.data,
null,
);
if (path_byte_len==0) {
// TODO: This may not be the right errorreturnerror.BadPathName;
} elseif (path_byte_len/2>path_space.data.len) {
returnerror.NameTooLong;
}
path_space.len=path_byte_len/2;
returnpath_space;
}
constRTL_PATH_TYPE=enum(c_int) {
Unknown,
UncAbsolute,
DriveAbsolute,
DriveRelative,
Rooted,
Relative,
LocalDevice,
RootLocalDevice,
};
pubextern"ntdll"fnRtlDetermineDosPathNameType_U(
Path: [*:0]constu16,
) callconv(.winapi) RTL_PATH_TYPE;
test"curiosity" {
constpath=std.unicode.wtf8ToWtf16LeStringLiteral("\\/server\\share/../relative");
constfull_path=tryRtlGetFullPathName_U(path);
constpath_type=RtlDetermineDosPathNameType_U(path);
constnt_path=tryRtlDosPathNameToNtPathName_U(path);
std.debug.print("full path: {f}\n", .{std.unicode.fmtUtf16Le(full_path.span())});
std.debug.print("path type: {}\n", .{path_type});
std.debug.print(" nt path: {f}\n", .{std.unicode.fmtUtf16Le(nt_path.span())});
}

outputs:

full path: \\server\share\relative
path type: .UncAbsolute
nt path: \??\UNC\server\share\relative

Additionally, at least the tree command recognizes the \/ variant (the /\ variant is mistaken for a command line flag so it's ineligible):

>tree "\/server\share\temp/stage4"
Folder PATH listing for volume share
Volume serial number is XXXX-XXXX
\\SERVER\SHARE\TEMP\STAGE4
└───bin

(note that this is a real command acting on a real path that I then redacted)

This will also bring path.resolveWindows in line with std.os.windows.getUnprefixedPathType and its callsites which accepts \/ and /\ as valid UNC starts.

@squeek502squeek502 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, I'm happy to merge this as-is and address os.windows.getUnprefixedPathType/fs.path.windowsParsePath discrepancies in a follow-up PR.

EDIT: Made an issue: #25702

@alexrp
alexrp merged commit bd1e960 into ziglang:masterOct 26, 2025
7 of 9 checks passed
@Techatrix
Techatrix deleted the windows-resolve-unc branch March 13, 2026 13:49
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Techatrix@squeek502@alexrp