Skip to content

util/path: don't strip leading characters of UNC paths in sanitize_win_longpath - #53252

Open
kayaal34 wants to merge 1 commit into
spack:developfrom
kayaal34:fix-sanitize-win-longpath
Open

kayaal34 wants to merge 1 commit into
spack:developfrom
kayaal34:fix-sanitize-win-longpath

Conversation

@kayaal34

@kayaal34 kayaal34 commented Oct 23, 2026 •

Copy link
Copy Markdown

sanitize_win_longpath used path.lstrip("\\\\?\\"). str.lstrip takes a set of characters, not a prefix, so it removes every leading \ and ?:

input before after
\\?\C:\spack\opt C:\spack\opt C:\spack\opt
\\server\share\spack (UNC, no prefix) server\share\spack \\server\share\spack
\\?\UNC\server\share\spack UNC\server\share\spack \\server\share\spack
?dir\file dir\file ?dir\file

It is used by _windows_readlink, so on Windows a symlink pointing to a network share came back as a relative-looking server\share\... (or UNC\server\... when os.readlink returns the extended form). The function now checks for the prefix, and maps \\?\UNC\ back to \\, the same way CPython's ntpath does.

Added a parametrized test_sanitize_win_longpath in lib/spack/spack/test/util/path.py (pure string checks, so it runs on any platform); three of the cases fail without the change. ruff check and ruff format --check pass on both files.

Note: I found this with a static check (ruff PLE1310) and used Claude (an AI assistant) to help write the fix and test; I reviewed and ran them.

…n_longpath

str.lstrip removes any leading backslashes and question marks, not the
'\?\' prefix, so a UNC path such as \server\share lost its leading
backslashes. Compare the prefix instead, and map the '\?\UNC\' form back
to a regular UNC path.
@johnwparent
johnwparent self-requested a review October 23, 2026 16:07

@johnwparent johnwparent left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this! Looks pretty good and catches a bone-headed bug I added.
Just a couple of small things

Comment thread main.obj

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove this object file in general, but also from the history of this PR

Comment on lines +131 to +135
if path.startswith("\\\\?\\UNC\\"):
return "\\\\" + path[len("\\\\?\\UNC\\") :]
if path.startswith("\\\\?\\"):
return path[len("\\\\?\\") :]
return path

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is very close. I think the change we want to make though is to extract the long path prefix, and then make choices based on what follows it.

There are a few cases where we do want to return a path with the longpath prefix (because failing to do so would produce a broken path) like \\?\Volume{GUID}, we only want to strip the longpath if it precedes a path like a networked path or local absolute drive prefixed path.

UNC paths are also case insensitive.

Suggested change
if path.startswith("\\\\?\\UNC\\"):
return "\\\\" + path[len("\\\\?\\UNC\\") :]
if path.startswith("\\\\?\\"):
return path[len("\\\\?\\") :]
return path
prefix = "\\\\?\\"
if not path.startswith(prefix):
return path
rest = path[len(prefix) :]
if rest[:4].upper() == "UNC\\":
return "\\\\" + rest[4:]
if ntpath.splitdrive(rest)[0]:
return rest
return path

Note: this suggestion requires an ntpath import.

(r"\\server\share\spack", r"\\server\share\spack"),
(r"C:\spack\opt", r"C:\spack\opt"),
(r"?dir\file", r"?dir\file"),
("", ""),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's add:

  • drive relative paths,
  • standard relative paths,
  • \?\Volume{}\spack (GUID can be more or less whatever you want)

Let's also make this test Windows only pytest.mark.only_windows(...

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants