diff options
Diffstat (limited to 'src/libs')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 19 | ||||
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 102 | ||||
-rw-r--r-- | src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 199 |
3 files changed, 300 insertions, 20 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index 44d36568..602b4a80 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | |||
@@ -166,10 +166,23 @@ DAPI_(HRESULT) PathGetKnownFolder( | |||
166 | ); | 166 | ); |
167 | 167 | ||
168 | /******************************************************************* | 168 | /******************************************************************* |
169 | PathIsAbsolute - returns true if the path is absolute; false | 169 | PathIsFullyQualified - returns true if the path is fully qualified; false otherwise. |
170 | otherwise. | 170 | Note that some rooted paths like C:dir are not fully qualified. |
171 | For example, these are all fully qualified: C:\dir, \\server\share, \\?\C:\dir. | ||
172 | For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir. | ||
171 | *******************************************************************/ | 173 | *******************************************************************/ |
172 | DAPI_(BOOL) PathIsAbsolute( | 174 | DAPI_(BOOL) PathIsFullyQualified( |
175 | __in_z LPCWSTR wzPath, | ||
176 | __out_opt BOOL* pfHasLongPathPrefix | ||
177 | ); | ||
178 | |||
179 | /******************************************************************* | ||
180 | PathIsRooted - returns true if the path is rooted; false otherwise. | ||
181 | Note that some rooted paths like C:dir are not fully qualified. | ||
182 | For example, these are all rooted: C:\dir, C:dir, C:, \dir, \\server\share, \\?\C:\dir. | ||
183 | For example, these are not rooted: dir, dir\subdir. | ||
184 | *******************************************************************/ | ||
185 | DAPI_(BOOL) PathIsRooted( | ||
173 | __in_z LPCWSTR wzPath | 186 | __in_z LPCWSTR wzPath |
174 | ); | 187 | ); |
175 | 188 | ||
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index 9823779b..314eab85 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp | |||
@@ -20,6 +20,10 @@ | |||
20 | 20 | ||
21 | #define PATH_GOOD_ENOUGH 64 | 21 | #define PATH_GOOD_ENOUGH 64 |
22 | 22 | ||
23 | static BOOL IsValidDriveChar( | ||
24 | __in WCHAR wc | ||
25 | ); | ||
26 | |||
23 | 27 | ||
24 | DAPI_(LPWSTR) PathFile( | 28 | DAPI_(LPWSTR) PathFile( |
25 | __in_z LPCWSTR wzPath | 29 | __in_z LPCWSTR wzPath |
@@ -272,29 +276,30 @@ DAPI_(HRESULT) PathPrefix( | |||
272 | 276 | ||
273 | HRESULT hr = S_OK; | 277 | HRESULT hr = S_OK; |
274 | LPWSTR wzFullPath = *psczFullPath; | 278 | LPWSTR wzFullPath = *psczFullPath; |
279 | BOOL fFullyQualified = FALSE; | ||
280 | BOOL fHasPrefix = FALSE; | ||
275 | SIZE_T cbFullPath = 0; | 281 | SIZE_T cbFullPath = 0; |
276 | 282 | ||
277 | if (((L'a' <= wzFullPath[0] && L'z' >= wzFullPath[0]) || | 283 | fFullyQualified = PathIsFullyQualified(wzFullPath, &fHasPrefix); |
278 | (L'A' <= wzFullPath[0] && L'Z' >= wzFullPath[0])) && | 284 | if (fHasPrefix) |
279 | L':' == wzFullPath[1] && | 285 | { |
280 | L'\\' == wzFullPath[2]) // normal path | 286 | ExitFunction(); |
287 | } | ||
288 | |||
289 | if (fFullyQualified && L':' == wzFullPath[1]) // normal path | ||
281 | { | 290 | { |
282 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); | 291 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4); |
283 | PathExitOnFailure(hr, "Failed to add prefix to file path."); | 292 | PathExitOnFailure(hr, "Failed to add prefix to file path."); |
284 | } | 293 | } |
285 | else if (L'\\' == wzFullPath[0] && L'\\' == wzFullPath[1]) // UNC | 294 | else if (fFullyQualified && L'\\' == wzFullPath[1]) // UNC |
286 | { | 295 | { |
287 | // ensure that we're not already prefixed | 296 | hr = StrSize(*psczFullPath, &cbFullPath); |
288 | if (!(L'?' == wzFullPath[2] && L'\\' == wzFullPath[3])) | 297 | PathExitOnFailure(hr, "Failed to get size of full path."); |
289 | { | ||
290 | hr = StrSize(*psczFullPath, &cbFullPath); | ||
291 | PathExitOnFailure(hr, "Failed to get size of full path."); | ||
292 | 298 | ||
293 | memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); | 299 | memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR)); |
294 | 300 | ||
295 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); | 301 | hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7); |
296 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); | 302 | PathExitOnFailure(hr, "Failed to add prefix to UNC path."); |
297 | } | ||
298 | } | 303 | } |
299 | else | 304 | else |
300 | { | 305 | { |
@@ -814,11 +819,66 @@ LExit: | |||
814 | } | 819 | } |
815 | 820 | ||
816 | 821 | ||
817 | DAPI_(BOOL) PathIsAbsolute( | 822 | DAPI_(BOOL) PathIsFullyQualified( |
823 | __in_z LPCWSTR wzPath, | ||
824 | __out_opt BOOL* pfHasLongPathPrefix | ||
825 | ) | ||
826 | { | ||
827 | BOOL fFullyQualified = FALSE; | ||
828 | BOOL fHasLongPathPrefix = FALSE; | ||
829 | |||
830 | if (!wzPath || !wzPath[0] || !wzPath[1]) | ||
831 | { | ||
832 | // There is no way to specify a fully qualified path with one character (or less). | ||
833 | ExitFunction(); | ||
834 | } | ||
835 | |||
836 | if (L'\\' != wzPath[0]) | ||
837 | { | ||
838 | // The only way to specify a fully qualified path that doesn't begin with a slash | ||
839 | // is the drive, colon, slash format (C:\). | ||
840 | if (IsValidDriveChar(wzPath[0]) && | ||
841 | L':' == wzPath[1] && | ||
842 | L'\\' == wzPath[2]) | ||
843 | { | ||
844 | fFullyQualified = TRUE; | ||
845 | } | ||
846 | |||
847 | ExitFunction(); | ||
848 | } | ||
849 | |||
850 | // Non-drive fully qualified paths must start with \\ or \?. | ||
851 | // \??\ is an archaic form of \\?\. | ||
852 | if (L'?' != wzPath[1] && L'\\' != wzPath[1]) | ||
853 | { | ||
854 | ExitFunction(); | ||
855 | } | ||
856 | |||
857 | fFullyQualified = TRUE; | ||
858 | |||
859 | if (L'?' == wzPath[2] && L'\\' == wzPath[3]) | ||
860 | { | ||
861 | fHasLongPathPrefix = TRUE; | ||
862 | } | ||
863 | |||
864 | |||
865 | LExit: | ||
866 | if (pfHasLongPathPrefix) | ||
867 | { | ||
868 | *pfHasLongPathPrefix = fHasLongPathPrefix; | ||
869 | } | ||
870 | |||
871 | return fFullyQualified; | ||
872 | } | ||
873 | |||
874 | |||
875 | DAPI_(BOOL) PathIsRooted( | ||
818 | __in_z LPCWSTR wzPath | 876 | __in_z LPCWSTR wzPath |
819 | ) | 877 | ) |
820 | { | 878 | { |
821 | return wzPath && wzPath[0] && wzPath[1] && (wzPath[0] == L'\\') || (wzPath[1] == L':'); | 879 | return wzPath && |
880 | (wzPath[0] == L'\\' || | ||
881 | IsValidDriveChar(wzPath[0]) && wzPath[1] == L':'); | ||
822 | } | 882 | } |
823 | 883 | ||
824 | 884 | ||
@@ -847,7 +907,7 @@ DAPI_(HRESULT) PathConcatCch( | |||
847 | hr = StrAllocString(psczCombined, wzPath1, cchPath1); | 907 | hr = StrAllocString(psczCombined, wzPath1, cchPath1); |
848 | PathExitOnFailure(hr, "Failed to copy just path1 to output."); | 908 | PathExitOnFailure(hr, "Failed to copy just path1 to output."); |
849 | } | 909 | } |
850 | else if (!wzPath1 || !*wzPath1 || PathIsAbsolute(wzPath2)) | 910 | else if (!wzPath1 || !*wzPath1 || PathIsRooted(wzPath2)) |
851 | { | 911 | { |
852 | hr = StrAllocString(psczCombined, wzPath2, cchPath2); | 912 | hr = StrAllocString(psczCombined, wzPath2, cchPath2); |
853 | PathExitOnFailure(hr, "Failed to copy just path2 to output."); | 913 | PathExitOnFailure(hr, "Failed to copy just path2 to output."); |
@@ -1002,3 +1062,11 @@ LExit: | |||
1002 | 1062 | ||
1003 | return hr; | 1063 | return hr; |
1004 | } | 1064 | } |
1065 | |||
1066 | static BOOL IsValidDriveChar( | ||
1067 | __in WCHAR wc | ||
1068 | ) | ||
1069 | { | ||
1070 | return L'a' <= wc && L'z' >= wc || | ||
1071 | L'A' <= wc && L'Z' >= wc; | ||
1072 | } | ||
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp index 6a05a45c..65856514 100644 --- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | |||
@@ -109,5 +109,204 @@ namespace DutilTests | |||
109 | ReleaseStrArray(rgsczPaths, cPaths); | 109 | ReleaseStrArray(rgsczPaths, cPaths); |
110 | } | 110 | } |
111 | } | 111 | } |
112 | |||
113 | [Fact] | ||
114 | void PathPrefixTest() | ||
115 | { | ||
116 | HRESULT hr = S_OK; | ||
117 | LPWSTR sczPath = NULL; | ||
118 | LPCWSTR rgwzPaths[12] = | ||
119 | { | ||
120 | L"\\\\", L"\\\\?\\UNC\\", | ||
121 | L"C:\\\\foo2", L"\\\\?\\C:\\\\foo2", | ||
122 | L"\\\\a\\b\\", L"\\\\?\\UNC\\a\\b\\", | ||
123 | L"\\\\?\\UNC\\test\\unc\\path\\to\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something", | ||
124 | L"\\\\?\\C:\\foo\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt", | ||
125 | L"\\??\\C:\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt", | ||
126 | }; | ||
127 | |||
128 | try | ||
129 | { | ||
130 | for (DWORD i = 0; i < countof(rgwzPaths) / 2; i += 2) | ||
131 | { | ||
132 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | ||
133 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
134 | |||
135 | hr = PathPrefix(&sczPath); | ||
136 | NativeAssert::Succeeded(hr, "PathPrefix: {0}", rgwzPaths[i]); | ||
137 | NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath); | ||
138 | } | ||
139 | } | ||
140 | finally | ||
141 | { | ||
142 | ReleaseStr(sczPath); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | [Fact] | ||
147 | void PathPrefixFailureTest() | ||
148 | { | ||
149 | HRESULT hr = S_OK; | ||
150 | LPWSTR sczPath = NULL; | ||
151 | LPCWSTR rgwzPaths[8] = | ||
152 | { | ||
153 | L"\\", | ||
154 | L"C:", | ||
155 | L"C:foo.txt", | ||
156 | L"", | ||
157 | L"\\?", | ||
158 | L"\\dir", | ||
159 | L"dir", | ||
160 | L"dir\\subdir", | ||
161 | }; | ||
162 | |||
163 | try | ||
164 | { | ||
165 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
166 | { | ||
167 | hr = StrAllocString(&sczPath, rgwzPaths[i], 0); | ||
168 | NativeAssert::Succeeded(hr, "Failed to copy string"); | ||
169 | |||
170 | hr = PathPrefix(&sczPath); | ||
171 | NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathPrefix: {0}, {1}", rgwzPaths[i], sczPath); | ||
172 | } | ||
173 | } | ||
174 | finally | ||
175 | { | ||
176 | ReleaseStr(sczPath); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | [Fact] | ||
181 | void PathIsRootedAndFullyQualifiedTest() | ||
182 | { | ||
183 | LPCWSTR rgwzPaths[15] = | ||
184 | { | ||
185 | L"\\\\", | ||
186 | L"\\\\\\", | ||
187 | L"C:\\", | ||
188 | L"C:\\\\", | ||
189 | L"C:\\foo1", | ||
190 | L"C:\\\\foo2", | ||
191 | L"\\\\test\\unc\\path\\to\\something", | ||
192 | L"\\\\a\\b\\c\\d\\e", | ||
193 | L"\\\\a\\b\\", | ||
194 | L"\\\\a\\b", | ||
195 | L"\\\\test\\unc", | ||
196 | L"\\\\Server", | ||
197 | L"\\\\Server\\Foo.txt", | ||
198 | L"\\\\Server\\Share\\Foo.txt", | ||
199 | L"\\\\Server\\Share\\Test\\Foo.txt", | ||
200 | }; | ||
201 | |||
202 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
203 | { | ||
204 | ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE); | ||
205 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | [Fact] | ||
210 | void PathIsRootedAndFullyQualifiedWithPrefixTest() | ||
211 | { | ||
212 | LPCWSTR rgwzPaths[6] = | ||
213 | { | ||
214 | L"\\\\?\\UNC\\test\\unc\\path\\to\\something", | ||
215 | L"\\\\?\\UNC\\test\\unc", | ||
216 | L"\\\\?\\UNC\\a\\b1", | ||
217 | L"\\\\?\\UNC\\a\\b2\\", | ||
218 | L"\\\\?\\C:\\foo\\bar.txt", | ||
219 | L"\\??\\C:\\foo\\bar.txt", | ||
220 | }; | ||
221 | |||
222 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
223 | { | ||
224 | ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE); | ||
225 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | [Fact] | ||
230 | void PathIsRootedButNotFullyQualifiedTest() | ||
231 | { | ||
232 | LPCWSTR rgwzPaths[7] = | ||
233 | { | ||
234 | L"\\", | ||
235 | L"a:", | ||
236 | L"A:", | ||
237 | L"z:", | ||
238 | L"Z:", | ||
239 | L"C:foo.txt", | ||
240 | L"\\dir", | ||
241 | }; | ||
242 | |||
243 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
244 | { | ||
245 | ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); | ||
246 | ValidateRootedPath(rgwzPaths[i], TRUE); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | [Fact] | ||
251 | void PathIsNotRootedAndNotFullyQualifiedTest() | ||
252 | { | ||
253 | LPCWSTR rgwzPaths[9] = | ||
254 | { | ||
255 | NULL, | ||
256 | L"", | ||
257 | L"dir", | ||
258 | L"dir\\subdir", | ||
259 | L"@:\\foo", // 064 = @ 065 = A | ||
260 | L"[:\\\\", // 091 = [ 090 = Z | ||
261 | L"`:\\foo ", // 096 = ` 097 = a | ||
262 | L"{:\\\\", // 123 = { 122 = z | ||
263 | L"[:", | ||
264 | }; | ||
265 | |||
266 | for (DWORD i = 0; i < countof(rgwzPaths); ++i) | ||
267 | { | ||
268 | ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE); | ||
269 | ValidateRootedPath(rgwzPaths[i], FALSE); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | void ValidateFullyQualifiedPath(LPCWSTR wzPath, BOOL fExpected, BOOL fExpectedHasPrefix) | ||
274 | { | ||
275 | BOOL fHasLongPathPrefix = FALSE; | ||
276 | BOOL fRooted = PathIsFullyQualified(wzPath, &fHasLongPathPrefix); | ||
277 | String^ message = String::Format("IsFullyQualified: {0}", gcnew String(wzPath)); | ||
278 | if (fExpected) | ||
279 | { | ||
280 | Assert::True(fRooted, message); | ||
281 | } | ||
282 | else | ||
283 | { | ||
284 | Assert::False(fRooted, message); | ||
285 | } | ||
286 | |||
287 | message = String::Format("HasLongPathPrefix: {0}", gcnew String(wzPath)); | ||
288 | if (fExpectedHasPrefix) | ||
289 | { | ||
290 | Assert::True(fHasLongPathPrefix, message); | ||
291 | } | ||
292 | else | ||
293 | { | ||
294 | Assert::False(fHasLongPathPrefix, message); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | void ValidateRootedPath(LPCWSTR wzPath, BOOL fExpected) | ||
299 | { | ||
300 | BOOL fRooted = PathIsRooted(wzPath); | ||
301 | String^ message = String::Format("IsRooted: {0}", gcnew String(wzPath)); | ||
302 | if (fExpected) | ||
303 | { | ||
304 | Assert::True(fRooted, message); | ||
305 | } | ||
306 | else | ||
307 | { | ||
308 | Assert::False(fRooted, message); | ||
309 | } | ||
310 | } | ||
112 | }; | 311 | }; |
113 | } | 312 | } |