@@ -98,6 +98,75 @@ constexpr bool IsWindowsDeviceRoot(const char c) noexcept {
9898 return (c >= ' a' && c <= ' z' ) || (c >= ' A' && c <= ' Z' );
9999}
100100
101+ enum class WindowsNamespacedPathType {
102+ kNotNamespaced ,
103+ kDriveAbsolutePath ,
104+ kUNCPath ,
105+ kOtherNamespacedPath ,
106+ };
107+
108+ static WindowsNamespacedPathType ClassifyWindowsNamespacedPath (
109+ std::string_view path) {
110+ if (!(path.size () >= 4 && path[0 ] == ' \\ ' && path[1 ] == ' \\ ' &&
111+ path[2 ] == ' ?' && path[3 ] == ' \\ ' )) {
112+ return WindowsNamespacedPathType::kNotNamespaced ;
113+ }
114+
115+ if (path.size () >= 7 && IsWindowsDeviceRoot (path[4 ]) && path[5 ] == ' :' &&
116+ IsPathSeparator (path[6 ])) {
117+ return WindowsNamespacedPathType::kDriveAbsolutePath ;
118+ }
119+
120+ if (path.size () >= 8 && ToLower (path[4 ]) == ' u' && ToLower (path[5 ]) == ' n' &&
121+ ToLower (path[6 ]) == ' c' && path[7 ] == ' \\ ' ) {
122+ size_t i = 8 ;
123+ const size_t server_start = i;
124+ while (i < path.size () && !IsPathSeparator (path[i])) {
125+ i++;
126+ }
127+ if (i == server_start || i == path.size ()) {
128+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
129+ }
130+
131+ while (i < path.size () && IsPathSeparator (path[i])) {
132+ i++;
133+ }
134+ const size_t share_start = i;
135+ while (i < path.size () && !IsPathSeparator (path[i])) {
136+ i++;
137+ }
138+ if (i == share_start) {
139+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
140+ }
141+
142+ return WindowsNamespacedPathType::kUNCPath ;
143+ }
144+
145+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
146+ }
147+
148+ static void StripExtendedPathPrefixForPathResolve (std::string& path) {
149+ // PathResolve() is an internal filesystem identity resolver, not the public
150+ // path.win32.resolve() API. For valid namespaced filesystem paths, the
151+ // namespace prefix does not identify a different file or directory:
152+ // \\?\C:\foo -> C:\foo
153+ // \\?\UNC\server\share\foo -> \\server\share\foo
154+ // Keep all other namespaced forms intact. For example,
155+ // \\?\PHYSICALDRIVE0 is not a drive-absolute filesystem path, and
156+ // \\?\C:foo is drive-relative rather than drive-absolute.
157+ switch (ClassifyWindowsNamespacedPath (path)) {
158+ case WindowsNamespacedPathType::kDriveAbsolutePath :
159+ path = path.substr (4 );
160+ return ;
161+ case WindowsNamespacedPathType::kUNCPath :
162+ path = " \\\\ " + path.substr (8 );
163+ return ;
164+ case WindowsNamespacedPathType::kNotNamespaced :
165+ case WindowsNamespacedPathType::kOtherNamespacedPath :
166+ return ;
167+ }
168+ }
169+
101170std::string PathResolve (Environment* env,
102171 const std::vector<std::string_view>& paths) {
103172 std::string resolvedDevice = " " ;
@@ -132,6 +201,8 @@ std::string PathResolve(Environment* env,
132201 }
133202 }
134203
204+ StripExtendedPathPrefixForPathResolve (path);
205+
135206 const size_t len = path.length ();
136207 int rootEnd = 0 ;
137208 std::string device = " " ;
@@ -330,11 +401,16 @@ void ToNamespacedPath(Environment* env, BufferValue* path) {
330401// namespace-prefixed path.
331402void FromNamespacedPath (std::string* path) {
332403#ifdef _WIN32
333- if (path->starts_with (" \\\\ ?\\ UNC\\ " )) {
334- *path = path->substr (8 );
335- path->insert (0 , " \\\\ " );
336- } else if (path->starts_with (" \\\\ ?\\ " )) {
337- *path = path->substr (4 );
404+ switch (ClassifyWindowsNamespacedPath (*path)) {
405+ case WindowsNamespacedPathType::kUNCPath :
406+ *path = " \\\\ " + path->substr (8 );
407+ return ;
408+ case WindowsNamespacedPathType::kDriveAbsolutePath :
409+ *path = path->substr (4 );
410+ return ;
411+ case WindowsNamespacedPathType::kNotNamespaced :
412+ case WindowsNamespacedPathType::kOtherNamespacedPath :
413+ return ;
338414 }
339415#endif
340416}
0 commit comments