diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2022-06-03 17:48:39 -0500 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2022-06-07 19:44:36 -0500 |
commit | 6b0f2d978504da82070523eb6adb0b59f9812e93 (patch) | |
tree | 6f0b258519a0f51bf589e4313206b3ffeaa32a41 /src/libs/dutil/WixToolset.DUtil | |
parent | b652e93a460b4b822a01382e5992f96f1d805ffe (diff) | |
download | wix-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.cpp | 20 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 55 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/logutil.cpp | 2 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 53 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 296 |
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 | ||
380 | LExit: | 390 | LExit: |
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 | ||
9 | typedef enum _PATH_CANONICALIZE | 9 | typedef 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 | ||
25 | typedef 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 | ********************************************************************/ |
46 | DAPI_(HRESULT) PathGetDirectory( | 55 | DAPI_(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 | /******************************************************************* |
52 | PathGetParentPath - extracts the parent directory from a full path. | 61 | PathGetParentPath - 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 | ********************************************************************/ |
55 | DAPI_(HRESULT) PathGetParentPath( | 69 | DAPI_(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 | ********************************************************************/ |
84 | DAPI_(HRESULT) PathPrefix( | 100 | DAPI_(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 | *******************************************************************/ | ||
231 | DAPI_(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 | *******************************************************************/ |
212 | DAPI_(BOOL) PathIsFullyQualified( | 244 | DAPI_(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 | ||
136 | LExit: | 119 | LExit: |
@@ -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 | ||
121 | DAPI_(HRESULT) PathGetParentPath( | 121 | DAPI_(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 | ||
149 | LExit: | 170 | LExit: |
@@ -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 | ||
321 | DAPI_(HRESULT) PathPrefix( | 330 | DAPI_(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 | ||
361 | LExit: | 395 | LExit: |
@@ -970,55 +1004,114 @@ LExit: | |||
970 | } | 1004 | } |
971 | 1005 | ||
972 | 1006 | ||
973 | DAPI_(BOOL) PathIsFullyQualified( | 1007 | DAPI_(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])) | 1087 | LExit: |
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 | ||
1016 | LExit: | 1098 | if (pfUNC) |
1017 | if (pfHasLongPathPrefix) | ||
1018 | { | 1099 | { |
1019 | *pfHasLongPathPrefix = fHasLongPathPrefix; | 1100 | *pfUNC = fUNC; |
1020 | } | 1101 | } |
1021 | 1102 | ||
1103 | return wzPastRoot; | ||
1104 | } | ||
1105 | |||
1106 | |||
1107 | DAPI_(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 | ||
1190 | LExit: | 1252 | LExit: |
1191 | ReleaseStr(sczPathCopy); | ||
1192 | |||
1193 | return hr; | 1253 | return hr; |
1194 | } | 1254 | } |
1195 | 1255 | ||