@@ -98,23 +98,65 @@ constexpr bool IsWindowsDeviceRoot(const char c) noexcept {
9898 return (c >= ' a' && c <= ' z' ) || (c >= ' A' && c <= ' Z' );
9999}
100100
101- // Strip Windows extended-length path prefix (\\?\) only when it wraps a
102- // drive letter path (\\?\C:\...) or a UNC path (\\?\UNC\...).
103- // Device paths like \\?\PHYSICALDRIVE0 are left unchanged.
104- // See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
105- static void StripExtendedPathPrefix (std::string& path) {
106- if (path.size () >= 4 && path[0 ] == ' \\ ' && path[1 ] == ' \\ ' &&
107- path[2 ] == ' ?' && path[3 ] == ' \\ ' ) {
108- if (path.size () >= 6 && IsWindowsDeviceRoot (path[4 ]) && path[5 ] == ' :' ) {
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' &&
121+ ToLower (path[5 ]) == ' n' && ToLower (path[6 ]) == ' c' &&
122+ path[7 ] == ' \\ ' ) {
123+ size_t i = 8 ;
124+ const size_t server_start = i;
125+ while (i < path.size () && !IsPathSeparator (path[i])) {
126+ i++;
127+ }
128+ if (i == server_start || i == path.size ()) {
129+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
130+ }
131+
132+ while (i < path.size () && IsPathSeparator (path[i])) {
133+ i++;
134+ }
135+ const size_t share_start = i;
136+ while (i < path.size () && !IsPathSeparator (path[i])) {
137+ i++;
138+ }
139+ if (i == share_start) {
140+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
141+ }
142+
143+ return WindowsNamespacedPathType::kUNCPath ;
144+ }
145+
146+ return WindowsNamespacedPathType::kOtherNamespacedPath ;
147+ }
148+
149+ static void StripExtendedPathPrefixForPathResolve (std::string& path) {
150+ switch (ClassifyWindowsNamespacedPath (path)) {
151+ case WindowsNamespacedPathType::kDriveAbsolutePath :
109152 path = path.substr (4 );
110153 return ;
111- }
112- if (path.size () >= 8 && ToLower (path[4 ]) == ' u' &&
113- ToLower (path[5 ]) == ' n' && ToLower (path[6 ]) == ' c' &&
114- path[7 ] == ' \\ ' ) {
154+ case WindowsNamespacedPathType::kUNCPath :
115155 path = " \\\\ " + path.substr (8 );
116156 return ;
117- }
157+ case WindowsNamespacedPathType::kNotNamespaced :
158+ case WindowsNamespacedPathType::kOtherNamespacedPath :
159+ return ;
118160 }
119161}
120162
@@ -152,9 +194,7 @@ std::string PathResolve(Environment* env,
152194 }
153195 }
154196
155- // Strip extended-length path prefix (\\?\C:\... -> C:\...,
156- // \\?\UNC\... -> \\...) before processing.
157- StripExtendedPathPrefix (path);
197+ StripExtendedPathPrefixForPathResolve (path);
158198
159199 const size_t len = path.length ();
160200 int rootEnd = 0 ;
@@ -354,11 +394,16 @@ void ToNamespacedPath(Environment* env, BufferValue* path) {
354394// namespace-prefixed path.
355395void FromNamespacedPath (std::string* path) {
356396#ifdef _WIN32
357- if (path->starts_with (" \\\\ ?\\ UNC\\ " )) {
358- *path = path->substr (8 );
359- path->insert (0 , " \\\\ " );
360- } else if (path->starts_with (" \\\\ ?\\ " )) {
361- *path = path->substr (4 );
397+ switch (ClassifyWindowsNamespacedPath (*path)) {
398+ case WindowsNamespacedPathType::kUNCPath :
399+ *path = " \\\\ " + path->substr (8 );
400+ return ;
401+ case WindowsNamespacedPathType::kDriveAbsolutePath :
402+ *path = path->substr (4 );
403+ return ;
404+ case WindowsNamespacedPathType::kNotNamespaced :
405+ case WindowsNamespacedPathType::kOtherNamespacedPath :
406+ return ;
362407 }
363408#endif
364409}
0 commit comments