aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-06-03 17:48:39 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-06-07 19:44:36 -0500
commit6b0f2d978504da82070523eb6adb0b59f9812e93 (patch)
tree6f0b258519a0f51bf589e4313206b3ffeaa32a41 /src/libs/dutil/WixToolset.DUtil
parentb652e93a460b4b822a01382e5992f96f1d805ffe (diff)
downloadwix-6b0f2d978504da82070523eb6adb0b59f9812e93.tar.gz
wix-6b0f2d978504da82070523eb6adb0b59f9812e93.tar.bz2
wix-6b0f2d978504da82070523eb6adb0b59f9812e93.zip
Add PathSkipPastRoot.
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dirutil.cpp20
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h55
-rw-r--r--src/libs/dutil/WixToolset.DUtil/logutil.cpp2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/path2utl.cpp53
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp296
5 files changed, 255 insertions, 171 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
index c106a467..94eab9e7 100644
--- a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
@@ -364,17 +364,27 @@ extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot(
364 __in DWORD /*dwFlags*/ 364 __in DWORD /*dwFlags*/
365 ) 365 )
366{ 366{
367 HRESULT hr = S_OK;
367 DWORD cDeletedDirs = 0; 368 DWORD cDeletedDirs = 0;
368 LPWSTR sczPath = NULL; 369 LPWSTR sczPath = NULL;
370 LPCWSTR wzPastRoot = NULL;
371 SIZE_T cchRoot = 0;
372
373 // Make sure the path is normalized and prefixed.
374 hr = PathExpand(&sczPath, wzPath, PATH_EXPAND_FULLPATH);
375 DirExitOnFailure(hr, "Failed to get full path for: %ls", wzPath);
376
377 wzPastRoot = PathSkipPastRoot(sczPath, NULL, NULL, NULL);
378 DirExitOnNull(wzPastRoot, hr, E_INVALIDARG, "Full path was not rooted: %ls", sczPath);
369 379
370 while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) 380 cchRoot = wzPastRoot - sczPath;
381
382 while (sczPath && sczPath[cchRoot] && ::RemoveDirectoryW(sczPath))
371 { 383 {
372 ++cDeletedDirs; 384 ++cDeletedDirs;
373 385
374 HRESULT hr = PathGetParentPath(wzPath, &sczPath); 386 hr = PathGetParentPath(sczPath, &sczPath, &cchRoot);
375 DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); 387 DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", sczPath);
376
377 wzPath = sczPath;
378 } 388 }
379 389
380LExit: 390LExit:
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
index e64c8ef3..727318f2 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -8,8 +8,8 @@ extern "C" {
8 8
9typedef enum _PATH_CANONICALIZE 9typedef enum _PATH_CANONICALIZE
10{ 10{
11 // Always prefix fully qualified paths with the long path prefix (\\?\). 11 // Always prefix fully qualified paths with the extended path prefix (\\?\).
12 PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX = 0x0001, 12 PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX = 0x0001,
13 // Always terminate the path with \. 13 // Always terminate the path with \.
14 PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002, 14 PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002,
15 // Don't collapse . or .. in the \\server\share portion of a UNC path. 15 // Don't collapse . or .. in the \\server\share portion of a UNC path.
@@ -22,6 +22,14 @@ typedef enum _PATH_EXPAND
22 PATH_EXPAND_FULLPATH = 0x0002, 22 PATH_EXPAND_FULLPATH = 0x0002,
23} PATH_EXPAND; 23} PATH_EXPAND;
24 24
25typedef enum _PATH_PREFIX
26{
27 // Add prefix even if the path is not longer than MAX_PATH.
28 PATH_PREFIX_SHORT_PATHS = 0x0001,
29 // Error with E_INVALIDARG if the path is not fully qualified.
30 PATH_PREFIX_EXPECT_FULLY_QUALIFIED = 0x0002,
31} PATH_PREFIX;
32
25 33
26/******************************************************************* 34/*******************************************************************
27 PathFile - returns a pointer to the file part of the path. 35 PathFile - returns a pointer to the file part of the path.
@@ -40,8 +48,9 @@ DAPI_(LPCWSTR) PathExtension(
40 48
41/******************************************************************* 49/*******************************************************************
42 PathGetDirectory - extracts the directory from a path including the directory separator. 50 PathGetDirectory - extracts the directory from a path including the directory separator.
43 This means calling the function again with the previous result returns the same result. 51 Calling the function again with the previous result returns the same result.
44 Returns S_FALSE if the path only contains a file name. 52 Returns S_FALSE if the path only contains a file name.
53 For example, C:\a\b -> C:\a\ -> C:\a\
45********************************************************************/ 54********************************************************************/
46DAPI_(HRESULT) PathGetDirectory( 55DAPI_(HRESULT) PathGetDirectory(
47 __in_z LPCWSTR wzPath, 56 __in_z LPCWSTR wzPath,
@@ -49,12 +58,18 @@ DAPI_(HRESULT) PathGetDirectory(
49 ); 58 );
50 59
51/******************************************************************* 60/*******************************************************************
52PathGetParentPath - extracts the parent directory from a full path. 61PathGetParentPath - extracts the parent directory from a path
53 *psczDirectory is NULL if the path only contains a file name. 62 ignoring a trailing slash so that when called repeatedly,
63 it eventually returns the root portion of the path.
64 *psczDirectory is NULL if the path only contains a file name or
65 the path only contains the root.
66 *pcchRoot is the length of the root part of the path.
67 For example, C:\a\b -> C:\a\ -> C:\ -> NULL
54********************************************************************/ 68********************************************************************/
55DAPI_(HRESULT) PathGetParentPath( 69DAPI_(HRESULT) PathGetParentPath(
56 __in_z LPCWSTR wzPath, 70 __in_z LPCWSTR wzPath,
57 __out_z LPWSTR *psczDirectory 71 __out_z LPWSTR *psczDirectory,
72 __out_opt SIZE_T* pcchRoot
58 ); 73 );
59 74
60/******************************************************************* 75/*******************************************************************
@@ -78,11 +93,14 @@ DAPI_(HRESULT) PathGetFullPathName(
78 ); 93 );
79 94
80/******************************************************************* 95/*******************************************************************
81 PathPrefix - prefixes a full path with \\?\ or \\?\UNC as 96 PathPrefix - prefixes a path with \\?\ or \\?\UNC if it doesn't
82 appropriate. 97 already have an extended prefix, is longer than MAX_PATH,
98 and is fully qualified.
83********************************************************************/ 99********************************************************************/
84DAPI_(HRESULT) PathPrefix( 100DAPI_(HRESULT) PathPrefix(
85 __inout LPWSTR *psczFullPath 101 __inout_z LPWSTR *psczFullPath,
102 __in SIZE_T cchFullPath,
103 __in DWORD dwPrefixFlags
86 ); 104 );
87 105
88/******************************************************************* 106/*******************************************************************
@@ -204,14 +222,27 @@ DAPI_(HRESULT) PathGetKnownFolder(
204 ); 222 );
205 223
206/******************************************************************* 224/*******************************************************************
225 PathSkipPastRoot - returns a pointer to the first character after
226 the root portion of the path or NULL if the path has no root.
227 For example, the pointer will point to the "a" in "after":
228 C:\after, C:after, \after, \\server\share\after,
229 \\?\C:\afterroot, \\?\UNC\server\share\after
230*******************************************************************/
231DAPI_(LPCWSTR) PathSkipPastRoot(
232 __in_z LPCWSTR wzPath,
233 __out_opt BOOL* pfHasExtendedPrefix,
234 __out_opt BOOL* pfFullyQualified,
235 __out_opt BOOL* pfUNC
236 );
237
238/*******************************************************************
207 PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. 239 PathIsFullyQualified - returns true if the path is fully qualified; false otherwise.
208 Note that some rooted paths like C:dir are not fully qualified. 240 Note that some rooted paths like C:dir are not fully qualified.
209 For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir. 241 For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir.
210 For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. 242 For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir.
211*******************************************************************/ 243*******************************************************************/
212DAPI_(BOOL) PathIsFullyQualified( 244DAPI_(BOOL) PathIsFullyQualified(
213 __in_z LPCWSTR wzPath, 245 __in_z LPCWSTR wzPath
214 __out_opt BOOL* pfHasLongPathPrefix
215 ); 246 );
216 247
217/******************************************************************* 248/*******************************************************************
diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
index 94c21374..88a90d8c 100644
--- a/src/libs/dutil/WixToolset.DUtil/logutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
@@ -133,7 +133,7 @@ extern "C" HRESULT DAPI LogOpen(
133 hr = PathConcat(wzDirectory, wzLog, &sczCombined); 133 hr = PathConcat(wzDirectory, wzLog, &sczCombined);
134 LoguExitOnFailure(hr, "Failed to combine the log path."); 134 LoguExitOnFailure(hr, "Failed to combine the log path.");
135 135
136 if (!PathIsFullyQualified(sczCombined, NULL)) 136 if (!PathIsFullyQualified(sczCombined))
137 { 137 {
138 hr = PathExpand(&LogUtil_sczLogPath, sczCombined, PATH_EXPAND_FULLPATH); 138 hr = PathExpand(&LogUtil_sczLogPath, sczCombined, PATH_EXPAND_FULLPATH);
139 LoguExitOnFailure(hr, "Failed to expand the log path."); 139 LoguExitOnFailure(hr, "Failed to expand the log path.");
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
index 1957a8c5..9e48f9d5 100644
--- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
@@ -64,31 +64,12 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
64 64
65 if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags) 65 if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags)
66 { 66 {
67 if (L'\\' == sczNormalizedPath[0] && (L'\\' == sczNormalizedPath[1] || L'?' == sczNormalizedPath[1]) && L'?' == sczNormalizedPath[2] && L'\\' == sczNormalizedPath[3]) 67 BOOL fUNC = FALSE;
68 LPCWSTR wzPastRoot = PathSkipPastRoot(sczNormalizedPath, NULL, NULL, &fUNC);
69 if (fUNC)
68 { 70 {
69 if (L'U' == sczNormalizedPath[4] && L'N' == sczNormalizedPath[5] && L'C' == sczNormalizedPath[6] && L'\\' == sczNormalizedPath[7]) 71 wzNormalizedPath = wzPastRoot;
70 { 72 cchUncRootLength = wzPastRoot - sczNormalizedPath;
71 cchUncRootLength = 8;
72 }
73 }
74 else if (L'\\' == sczNormalizedPath[0] && L'\\' == sczNormalizedPath[1])
75 {
76 cchUncRootLength = 2;
77 }
78
79 if (cchUncRootLength)
80 {
81 DWORD dwRemainingSlashes = 2;
82
83 for (wzNormalizedPath += cchUncRootLength; *wzNormalizedPath && dwRemainingSlashes; ++wzNormalizedPath)
84 {
85 ++cchUncRootLength;
86
87 if (L'\\' == *wzNormalizedPath)
88 {
89 --dwRemainingSlashes;
90 }
91 }
92 } 73 }
93 } 74 }
94 75
@@ -115,22 +96,24 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
115 if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags) 96 if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags)
116 { 97 {
117 hr = PathBackslashTerminate(psczCanonicalized); 98 hr = PathBackslashTerminate(psczCanonicalized);
118 PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path"); 99 PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path.");
119 } 100 }
120 101
121 if (PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix && 102 if (PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX & dwCanonicalizeFlags)
122 (PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags))
123 { 103 {
124 hr = PathPrefix(psczCanonicalized); 104 hr = PathPrefix(psczCanonicalized, 0, PATH_PREFIX_SHORT_PATHS);
125 PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path"); 105 PathExitOnFailure(hr, "Failed to ensure the extended path prefix on the canonicalized path.");
126
127 fHasPrefix = TRUE;
128 } 106 }
129 107
108 PathSkipPastRoot(*psczCanonicalized, &fHasPrefix, NULL, NULL);
109
130 if (fHasPrefix) 110 if (fHasPrefix)
131 { 111 {
132 // Canonicalize \??\ into \\?\. 112 // Canonicalize prefix into \\?\.
113 (*psczCanonicalized)[0] = L'\\';
133 (*psczCanonicalized)[1] = L'\\'; 114 (*psczCanonicalized)[1] = L'\\';
115 (*psczCanonicalized)[2] = L'?';
116 (*psczCanonicalized)[3] = L'\\';
134 } 117 }
135 118
136LExit: 119LExit:
@@ -188,7 +171,7 @@ DAPI_(HRESULT) PathCompareCanonicalized(
188 HRESULT hr = S_OK; 171 HRESULT hr = S_OK;
189 LPWSTR sczCanonicalized1 = NULL; 172 LPWSTR sczCanonicalized1 = NULL;
190 LPWSTR sczCanonicalized2 = NULL; 173 LPWSTR sczCanonicalized2 = NULL;
191 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; 174 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
192 int nResult = 0; 175 int nResult = 0;
193 176
194 if (!wzPath1 || !wzPath2) 177 if (!wzPath1 || !wzPath2)
@@ -221,7 +204,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath(
221 HRESULT hr = S_OK; 204 HRESULT hr = S_OK;
222 LPWSTR sczCanonicalizedDirectory = NULL; 205 LPWSTR sczCanonicalizedDirectory = NULL;
223 LPWSTR sczCanonicalizedPath = NULL; 206 LPWSTR sczCanonicalizedPath = NULL;
224 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; 207 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
225 size_t cchDirectory = 0; 208 size_t cchDirectory = 0;
226 209
227 if (!wzDirectory || !*wzDirectory) 210 if (!wzDirectory || !*wzDirectory)
@@ -239,7 +222,7 @@ DAPI_(HRESULT) PathDirectoryContainsPath(
239 hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath); 222 hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath);
240 PathExitOnFailure(hr, "Failed to canonicalize the path."); 223 PathExitOnFailure(hr, "Failed to canonicalize the path.");
241 224
242 if (!PathIsFullyQualified(sczCanonicalizedDirectory, NULL)) 225 if (!PathIsFullyQualified(sczCanonicalizedDirectory))
243 { 226 {
244 PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path."); 227 PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path.");
245 } 228 }
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
index abbf4d4b..1ac76626 100644
--- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -120,13 +120,34 @@ LExit:
120 120
121DAPI_(HRESULT) PathGetParentPath( 121DAPI_(HRESULT) PathGetParentPath(
122 __in_z LPCWSTR wzPath, 122 __in_z LPCWSTR wzPath,
123 __out_z LPWSTR *psczParent 123 __out_z LPWSTR* psczParent,
124 __out_opt SIZE_T* pcchRoot
124 ) 125 )
125{ 126{
126 HRESULT hr = S_OK; 127 HRESULT hr = S_OK;
128 LPCWSTR wzPastRoot = NULL;
127 LPCWSTR wzParent = NULL; 129 LPCWSTR wzParent = NULL;
130 LPCWSTR wz = NULL;
128 131
129 for (LPCWSTR wz = wzPath; *wz; ++wz) 132 wzPastRoot = PathSkipPastRoot(wzPath, NULL, NULL, NULL);
133
134 if (pcchRoot)
135 {
136 *pcchRoot = !wzPastRoot ? 0 : wzPastRoot - wzPath;
137 }
138
139 if (wzPastRoot && *wzPastRoot)
140 {
141 Assert(wzPastRoot > wzPath);
142 wz = wzPastRoot;
143 wzParent = wzPastRoot - 1;
144 }
145 else
146 {
147 wz = wzPath;
148 }
149
150 for (; *wz; ++wz)
130 { 151 {
131 if (IsPathSeparatorChar(*wz) && wz[1]) 152 if (IsPathSeparatorChar(*wz) && wz[1])
132 { 153 {
@@ -143,7 +164,7 @@ DAPI_(HRESULT) PathGetParentPath(
143 } 164 }
144 else 165 else
145 { 166 {
146 ReleaseNullStr(psczParent); 167 ReleaseNullStr(*psczParent);
147 } 168 }
148 169
149LExit: 170LExit:
@@ -164,9 +185,8 @@ DAPI_(HRESULT) PathExpand(
164 LPWSTR sczExpandedPath = NULL; 185 LPWSTR sczExpandedPath = NULL;
165 SIZE_T cchWritten = 0; 186 SIZE_T cchWritten = 0;
166 DWORD cchExpandedPath = 0; 187 DWORD cchExpandedPath = 0;
167 SIZE_T cbSize = 0;
168
169 LPWSTR sczFullPath = NULL; 188 LPWSTR sczFullPath = NULL;
189 DWORD dwPrefixFlags = 0;
170 190
171 // 191 //
172 // First, expand any environment variables. 192 // First, expand any environment variables.
@@ -201,20 +221,7 @@ DAPI_(HRESULT) PathExpand(
201 } 221 }
202 } 222 }
203 223
204 if (MAX_PATH < cch) 224 cchWritten = cch;
205 {
206 hr = PathPrefix(&sczExpandedPath); // ignore invald arg from path prefix because this may not be a complete path yet
207 if (E_INVALIDARG == hr)
208 {
209 hr = S_OK;
210 }
211 PathExitOnFailure(hr, "Failed to prefix long path after expanding environment variables.");
212
213 hr = StrMaxLength(sczExpandedPath, &cbSize);
214 PathExitOnFailure(hr, "Failed to get max length of expanded path.");
215
216 cchExpandedPath = (DWORD)min(DWORD_MAX, cbSize);
217 }
218 } 225 }
219 226
220 // 227 //
@@ -227,11 +234,7 @@ DAPI_(HRESULT) PathExpand(
227 hr = PathGetFullPathName(wzPath, &sczFullPath, NULL, &cchWritten); 234 hr = PathGetFullPathName(wzPath, &sczFullPath, NULL, &cchWritten);
228 PathExitOnFailure(hr, "Failed to get full path for string: %ls", wzPath); 235 PathExitOnFailure(hr, "Failed to get full path for string: %ls", wzPath);
229 236
230 if (MAX_PATH < cchWritten) 237 dwPrefixFlags |= PATH_PREFIX_EXPECT_FULLY_QUALIFIED;
231 {
232 hr = PathPrefix(&sczFullPath);
233 PathExitOnFailure(hr, "Failed to prefix long path after expanding.");
234 }
235 } 238 }
236 else 239 else
237 { 240 {
@@ -239,6 +242,12 @@ DAPI_(HRESULT) PathExpand(
239 sczExpandedPath = NULL; 242 sczExpandedPath = NULL;
240 } 243 }
241 244
245 if (dwResolveFlags)
246 {
247 hr = PathPrefix(&sczFullPath, cchWritten, dwPrefixFlags);
248 PathExitOnFailure(hr, "Failed to prefix path after expanding.");
249 }
250
242 hr = StrAllocString(psczFullPath, sczFullPath ? sczFullPath : wzRelativePath, 0); 251 hr = StrAllocString(psczFullPath, sczFullPath ? sczFullPath : wzRelativePath, 0);
243 PathExitOnFailure(hr, "Failed to copy relative path into full path."); 252 PathExitOnFailure(hr, "Failed to copy relative path into full path.");
244 253
@@ -319,29 +328,54 @@ LExit:
319 328
320 329
321DAPI_(HRESULT) PathPrefix( 330DAPI_(HRESULT) PathPrefix(
322 __inout LPWSTR *psczFullPath 331 __inout_z LPWSTR* psczFullPath,
332 __in SIZE_T cchFullPath,
333 __in DWORD dwPrefixFlags
323 ) 334 )
324{ 335{
325 Assert(psczFullPath && *psczFullPath); 336 Assert(psczFullPath);
326 337
327 HRESULT hr = S_OK; 338 HRESULT hr = S_OK;
328 LPWSTR wzFullPath = *psczFullPath; 339 LPWSTR wzFullPath = *psczFullPath;
329 BOOL fFullyQualified = FALSE; 340 BOOL fFullyQualified = FALSE;
330 BOOL fHasPrefix = FALSE; 341 BOOL fHasPrefix = FALSE;
342 BOOL fUNC = FALSE;
331 SIZE_T cbFullPath = 0; 343 SIZE_T cbFullPath = 0;
332 344
333 fFullyQualified = PathIsFullyQualified(wzFullPath, &fHasPrefix); 345 PathSkipPastRoot(wzFullPath, &fHasPrefix, &fFullyQualified, &fUNC);
346
334 if (fHasPrefix) 347 if (fHasPrefix)
335 { 348 {
336 ExitFunction(); 349 ExitFunction();
337 } 350 }
338 351
339 if (fFullyQualified && L':' == wzFullPath[1]) // normal path 352 // The prefix is only allowed on fully qualified paths.
353 if (!fFullyQualified)
340 { 354 {
341 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); 355 if (dwPrefixFlags & PATH_PREFIX_EXPECT_FULLY_QUALIFIED)
342 PathExitOnFailure(hr, "Failed to add prefix to file path."); 356 {
357 PathExitWithRootFailure(hr, E_INVALIDARG, "Expected fully qualified path provided to prefix: %ls.", wzFullPath);
358 }
359
360 ExitFunction();
361 }
362
363 if (!(dwPrefixFlags & PATH_PREFIX_SHORT_PATHS))
364 {
365 // The prefix is not necessary unless the path is longer than MAX_PATH.
366 if (!cchFullPath)
367 {
368 hr = ::StringCchLengthW(wzFullPath, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchFullPath));
369 PathExitOnFailure(hr, "Failed to get length of path to prefix.");
370 }
371
372 if (MAX_PATH >= cchFullPath)
373 {
374 ExitFunction();
375 }
343 } 376 }
344 else if (fFullyQualified && IsPathSeparatorChar(wzFullPath[1])) // UNC 377
378 if (fUNC)
345 { 379 {
346 hr = StrSize(*psczFullPath, &cbFullPath); 380 hr = StrSize(*psczFullPath, &cbFullPath);
347 PathExitOnFailure(hr, "Failed to get size of full path."); 381 PathExitOnFailure(hr, "Failed to get size of full path.");
@@ -352,10 +386,10 @@ DAPI_(HRESULT) PathPrefix(
352 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); 386 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
353 PathExitOnFailure(hr, "Failed to add prefix to UNC path."); 387 PathExitOnFailure(hr, "Failed to add prefix to UNC path.");
354 } 388 }
355 else 389 else // must be a normal path
356 { 390 {
357 hr = E_INVALIDARG; 391 hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
358 PathExitOnFailure(hr, "Invalid path provided to prefix: %ls.", wzFullPath); 392 PathExitOnFailure(hr, "Failed to add prefix to file path.");
359 } 393 }
360 394
361LExit: 395LExit:
@@ -970,55 +1004,114 @@ LExit:
970} 1004}
971 1005
972 1006
973DAPI_(BOOL) PathIsFullyQualified( 1007DAPI_(LPCWSTR) PathSkipPastRoot(
974 __in_z LPCWSTR wzPath, 1008 __in_z_opt LPCWSTR wzPath,
975 __out_opt BOOL* pfHasLongPathPrefix 1009 __out_opt BOOL* pfHasExtendedPrefix,
1010 __out_opt BOOL* pfFullyQualified,
1011 __out_opt BOOL* pfUNC
976 ) 1012 )
977{ 1013{
1014 LPCWSTR wzPastRoot = NULL;
1015 BOOL fHasPrefix = FALSE;
978 BOOL fFullyQualified = FALSE; 1016 BOOL fFullyQualified = FALSE;
979 BOOL fHasLongPathPrefix = FALSE; 1017 BOOL fUNC = FALSE;
1018 DWORD dwRootMissingSlashes = 0;
980 1019
981 if (!wzPath || !wzPath[0] || !wzPath[1]) 1020 if (!wzPath || !*wzPath)
982 { 1021 {
983 // There is no way to specify a fully qualified path with one character (or less).
984 ExitFunction(); 1022 ExitFunction();
985 } 1023 }
986 1024
987 if (!IsPathSeparatorChar(wzPath[0])) 1025 if (IsPathSeparatorChar(wzPath[0]))
988 { 1026 {
989 // The only way to specify a fully qualified path that doesn't begin with a slash 1027 if (IsPathSeparatorChar(wzPath[1]) && (L'?' == wzPath[2] || L'.' == wzPath[2]) && IsPathSeparatorChar(wzPath[3]) ||
990 // is the drive, colon, slash format (C:\). 1028 L'?' == wzPath[1] && L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3]))
991 if (IsValidDriveChar(wzPath[0]) &&
992 L':' == wzPath[1] &&
993 IsPathSeparatorChar(wzPath[2]))
994 { 1029 {
995 fFullyQualified = TRUE; 1030 fHasPrefix = TRUE;
996 }
997 1031
998 ExitFunction(); 1032 if (L'U' == wzPath[4] && L'N' == wzPath[5] && L'C' == wzPath[6] && IsPathSeparatorChar(wzPath[7]))
1033 {
1034 fUNC = TRUE;
1035 wzPastRoot = wzPath + 8;
1036 dwRootMissingSlashes = 2;
1037 }
1038 else
1039 {
1040 wzPastRoot = wzPath + 4;
1041 dwRootMissingSlashes = 1;
1042 }
1043 }
1044 else if (IsPathSeparatorChar(wzPath[1]))
1045 {
1046 fUNC = TRUE;
1047 wzPastRoot = wzPath + 2;
1048 dwRootMissingSlashes = 2;
1049 }
999 } 1050 }
1000 1051
1001 // Non-drive fully qualified paths must start with \\ or \?. 1052 if (dwRootMissingSlashes)
1002 // \??\ is an archaic form of \\?\.
1003 if (L'?' != wzPath[1] && !IsPathSeparatorChar(wzPath[1]))
1004 { 1053 {
1005 ExitFunction(); 1054 Assert(wzPastRoot);
1055 fFullyQualified = TRUE;
1056
1057 for (; *wzPastRoot && dwRootMissingSlashes; ++wzPastRoot)
1058 {
1059 if (IsPathSeparatorChar(*wzPastRoot))
1060 {
1061 --dwRootMissingSlashes;
1062 }
1063 }
1006 } 1064 }
1065 else
1066 {
1067 Assert(!wzPastRoot);
1007 1068
1008 fFullyQualified = TRUE; 1069 if (IsPathSeparatorChar(wzPath[0]))
1070 {
1071 wzPastRoot = wzPath + 1;
1072 }
1073 else if (IsValidDriveChar(wzPath[0]) && wzPath[1] == L':')
1074 {
1075 if (IsPathSeparatorChar(wzPath[2]))
1076 {
1077 fFullyQualified = TRUE;
1078 wzPastRoot = wzPath + 3;
1079 }
1080 else
1081 {
1082 wzPastRoot = wzPath + 2;
1083 }
1084 }
1085 }
1009 1086
1010 if (L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3])) 1087LExit:
1088 if (pfHasExtendedPrefix)
1011 { 1089 {
1012 fHasLongPathPrefix = TRUE; 1090 *pfHasExtendedPrefix = fHasPrefix;
1013 } 1091 }
1014 1092
1093 if (pfFullyQualified)
1094 {
1095 *pfFullyQualified = fFullyQualified;
1096 }
1015 1097
1016LExit: 1098 if (pfUNC)
1017 if (pfHasLongPathPrefix)
1018 { 1099 {
1019 *pfHasLongPathPrefix = fHasLongPathPrefix; 1100 *pfUNC = fUNC;
1020 } 1101 }
1021 1102
1103 return wzPastRoot;
1104}
1105
1106
1107DAPI_(BOOL) PathIsFullyQualified(
1108 __in_z LPCWSTR wzPath
1109 )
1110{
1111 BOOL fFullyQualified = FALSE;
1112
1113 PathSkipPastRoot(wzPath, NULL, &fFullyQualified, NULL);
1114
1022 return fFullyQualified; 1115 return fFullyQualified;
1023} 1116}
1024 1117
@@ -1027,9 +1120,7 @@ DAPI_(BOOL) PathIsRooted(
1027 __in_z LPCWSTR wzPath 1120 __in_z LPCWSTR wzPath
1028 ) 1121 )
1029{ 1122{
1030 return wzPath && 1123 return NULL != PathSkipPastRoot(wzPath, NULL, NULL, NULL);
1031 (IsPathSeparatorChar(wzPath[0]) ||
1032 IsValidDriveChar(wzPath[0]) && wzPath[1] == L':');
1033} 1124}
1034 1125
1035 1126
@@ -1118,78 +1209,47 @@ DAPI_(HRESULT) PathGetHierarchyArray(
1118 ) 1209 )
1119{ 1210{
1120 HRESULT hr = S_OK; 1211 HRESULT hr = S_OK;
1121 LPWSTR sczPathCopy = NULL; 1212 LPCWSTR wz = NULL;
1122 LPWSTR sczNewPathCopy = NULL; 1213 SIZE_T cch = 0;
1123 DWORD cArraySpacesNeeded = 0; 1214 *pcPathArray = 0;
1124 size_t cchPath = 0;
1125 1215
1126 hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_LENGTH, &cchPath); 1216 PathExitOnNull(wzPath, hr, E_INVALIDARG, "wzPath is required.");
1127 PathExitOnRootFailure(hr, "Failed to get string length of path: %ls", wzPath);
1128 1217
1129 if (!cchPath) 1218 wz = PathSkipPastRoot(wzPath, NULL, NULL, NULL);
1219 if (wz)
1130 { 1220 {
1131 ExitFunction1(hr = E_INVALIDARG); 1221 cch = wz - wzPath;
1132 }
1133 1222
1134 for (size_t i = 0; i < cchPath; ++i) 1223 hr = MemEnsureArraySize(reinterpret_cast<void**>(prgsczPathArray), 1, sizeof(LPWSTR), 5);
1135 { 1224 PathExitOnFailure(hr, "Failed to allocate array.");
1136 if (IsPathSeparatorChar(wzPath[i]))
1137 {
1138 ++cArraySpacesNeeded;
1139 }
1140 }
1141 1225
1142 if (!IsPathSeparatorChar(wzPath[cchPath - 1])) 1226 hr = StrAllocString(*prgsczPathArray, wzPath, cch);
1143 { 1227 PathExitOnFailure(hr, "Failed to copy root into array.");
1144 ++cArraySpacesNeeded;
1145 }
1146 1228
1147 // If it's a UNC path, cut off the first three paths, 2 because it starts with a double backslash, and another because the first ("\\servername\") isn't a path. 1229 *pcPathArray += 1;
1148 if (IsPathSeparatorChar(wzPath[0]) && IsPathSeparatorChar(wzPath[1])) 1230 }
1231 else
1149 { 1232 {
1150 if (3 > cArraySpacesNeeded) 1233 wz = wzPath;
1151 {
1152 ExitFunction1(hr = E_INVALIDARG);
1153 }
1154
1155 cArraySpacesNeeded -= 3;
1156 } 1234 }
1157 1235
1158 Assert(cArraySpacesNeeded >= 1); 1236 for (; *wz; ++wz)
1159
1160 hr = MemEnsureArraySize(reinterpret_cast<void **>(prgsczPathArray), cArraySpacesNeeded, sizeof(LPWSTR), 0);
1161 PathExitOnFailure(hr, "Failed to allocate array of size %u for parent directories", cArraySpacesNeeded);
1162 *pcPathArray = cArraySpacesNeeded;
1163
1164 hr = StrAllocString(&sczPathCopy, wzPath, 0);
1165 PathExitOnFailure(hr, "Failed to allocate copy of original path");
1166
1167 for (DWORD i = 0; i < cArraySpacesNeeded; ++i)
1168 { 1237 {
1169 hr = StrAllocString((*prgsczPathArray) + cArraySpacesNeeded - 1 - i, sczPathCopy, 0); 1238 ++cch;
1170 PathExitOnFailure(hr, "Failed to copy path");
1171 1239
1172 DWORD cchPathCopy = lstrlenW(sczPathCopy); 1240 if (IsPathSeparatorChar(*wz) || !wz[1])
1173
1174 // If it ends in a backslash, it's a directory path, so cut off everything the last backslash before we get the directory portion of the path
1175 if (IsPathSeparatorChar(wzPath[cchPathCopy - 1]))
1176 { 1241 {
1177 sczPathCopy[cchPathCopy - 1] = L'\0'; 1242 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<void**>(prgsczPathArray), *pcPathArray, 1, sizeof(LPWSTR), 5);
1178 } 1243 PathExitOnFailure(hr, "Failed to allocate array.");
1179
1180 hr = PathGetDirectory(sczPathCopy, &sczNewPathCopy);
1181 PathExitOnFailure(hr, "Failed to get directory portion of path");
1182 1244
1183 ReleaseStr(sczPathCopy); 1245 hr = StrAllocString(*prgsczPathArray + *pcPathArray, wzPath, cch);
1184 sczPathCopy = sczNewPathCopy; 1246 PathExitOnFailure(hr, "Failed to copy path into array.");
1185 sczNewPathCopy = NULL;
1186 }
1187 1247
1188 hr = S_OK; 1248 *pcPathArray += 1;
1249 }
1250 }
1189 1251
1190LExit: 1252LExit:
1191 ReleaseStr(sczPathCopy);
1192
1193 return hr; 1253 return hr;
1194} 1254}
1195 1255