Add PathCanonicalizeForComparison.
Sean Hall committed
May 26, 2022 at 17:33 UTC
dea25f58e6119ef1acc5c5cc2a7c98e52cdff519
8 files changed
+822
-116
src/burn/engine/variable.cpp
+1
-1
@@ -1969,7 +1969,7 @@ static HRESULT InitializeVariableSystemFolder(
1969
{
1970
HRESULT hr = S_OK;
1971
BOOL f64 = (BOOL)dwpData;
1972
- WCHAR wzSystemFolder[MAX_PATH] = { };
1972
+ WCHAR wzSystemFolder[MAX_PATH + 2] = { };
1973
1974
#if !defined(_WIN64)
1975
BOOL fIsWow64 = FALSE;
src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+48
-8
@@ -6,7 +6,17 @@
6
extern "C" {
7
#endif
8
9
-typedef enum PATH_EXPAND
9
+typedef enum _PATH_CANONICALIZE
10
+{
11
+ // Always prefix fully qualified paths with the long path prefix (\\?\).
12
+ PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX = 0x0001,
13
+ // Always terminate the path with \.
14
+ PATH_CANONICALIZE_BACKSLASH_TERMINATE = 0x0002,
15
+ // Don't collapse . or .. in the \\server\share portion of a UNC path.
16
+ PATH_CANONICALIZE_KEEP_UNC_ROOT = 0x0004,
17
+} PATH_CANONICALIZE;
18
+
19
+typedef enum _PATH_EXPAND
20
{
21
PATH_EXPAND_ENVIRONMENT = 0x0001,
22
PATH_EXPAND_FULLPATH = 0x0002,
@@ -29,7 +39,9 @@ DAPI_(LPCWSTR) PathExtension(
39
);
40
41
/*******************************************************************
32
- PathGetDirectory - extracts the directory from a path.
42
+ 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.
44
+ Returns S_FALSE if the path only contains a file name.
45
********************************************************************/
46
DAPI_(HRESULT) PathGetDirectory(
47
__in_z LPCWSTR wzPath,
@@ -38,6 +50,7 @@ DAPI_(HRESULT) PathGetDirectory(
50
51
/*******************************************************************
52
PathGetParentPath - extracts the parent directory from a full path.
53
+ *psczDirectory is NULL if the path only contains a file name.
54
********************************************************************/
55
DAPI_(HRESULT) PathGetParentPath(
56
__in_z LPCWSTR wzPath,
@@ -62,6 +75,21 @@ DAPI_(HRESULT) PathPrefix(
75
__inout LPWSTR *psczFullPath
76
);
77
78
+/*******************************************************************
79
+ PathFixedNormalizeSlashes - replaces all / with \ and
80
+ removes redundant consecutive slashes.
81
+********************************************************************/
82
+DAPI_(HRESULT) PathFixedNormalizeSlashes(
83
+ __inout_z LPWSTR wzPath
84
+ );
85
+
86
+/*******************************************************************
87
+ PathFixedReplaceForwardSlashes - replaces all / with \
88
+********************************************************************/
89
+DAPI_(void) PathFixedReplaceForwardSlashes(
90
+ __inout_z LPWSTR wzPath
91
+ );
92
+
93
/*******************************************************************
94
PathFixedBackslashTerminate - appends a \ if path does not have it
95
already, but fails if the buffer is
@@ -77,7 +105,7 @@ DAPI_(HRESULT) PathFixedBackslashTerminate(
105
already.
106
********************************************************************/
107
DAPI_(HRESULT) PathBackslashTerminate(
80
- __inout LPWSTR* psczPath
108
+ __inout_z LPWSTR* psczPath
109
);
110
111
/*******************************************************************
@@ -168,7 +196,7 @@ DAPI_(HRESULT) PathGetKnownFolder(
196
/*******************************************************************
197
PathIsFullyQualified - returns true if the path is fully qualified; false otherwise.
198
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.
199
+ For example, these are all fully qualified: C:\dir, C:/dir, \\server\share, \\?\C:\dir.
200
For example, these are not fully qualified: C:dir, C:, \dir, dir, dir\subdir.
201
*******************************************************************/
202
DAPI_(BOOL) PathIsFullyQualified(
@@ -179,7 +207,7 @@ DAPI_(BOOL) PathIsFullyQualified(
207
/*******************************************************************
208
PathIsRooted - returns true if the path is rooted; false otherwise.
209
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.
210
+ For example, these are all rooted: C:\dir, C:/dir, C:dir, C:, \dir, \\server\share, \\?\C:\dir.
211
For example, these are not rooted: dir, dir\subdir.
212
*******************************************************************/
213
DAPI_(BOOL) PathIsRooted(
@@ -240,7 +268,7 @@ DAPI_(HRESULT) PathGetHierarchyArray(
268
);
269
270
/*******************************************************************
243
- PathCanonicalizePath - wrapper around PathCanonicalizeW.
271
+ PathCanonicalizePath - wrapper around PathCanonicalizeW.
272
*******************************************************************/
273
DAPI_(HRESULT) PathCanonicalizePath(
274
__in_z LPCWSTR wzPath,
@@ -248,8 +276,20 @@ DAPI_(HRESULT) PathCanonicalizePath(
276
);
277
278
/*******************************************************************
251
-PathDirectoryContainsPath - checks if wzPath is located inside
252
- wzDirectory.
279
+ PathCanonicalizeForComparison - canonicalizes the path based on the given flags.
280
+ . and .. directories are collapsed.
281
+ All / are replaced with \.
282
+ All redundant consecutive slashes are replaced with a single \.
283
+*******************************************************************/
284
+DAPI_(HRESULT) PathCanonicalizeForComparison(
285
+ __in_z LPCWSTR wzPath,
286
+ __in DWORD dwCanonicalizeFlags,
287
+ __deref_out_z LPWSTR* psczCanonicalized
288
+ );
289
+
290
+/*******************************************************************
291
+ PathDirectoryContainsPath - checks if wzPath is located inside wzDirectory.
292
+ wzDirectory must be a fully qualified path.
293
*******************************************************************/
294
DAPI_(HRESULT) PathDirectoryContainsPath(
295
__in_z LPCWSTR wzDirectory,
src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+114
-35
@@ -9,6 +9,7 @@
9
#define PathExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
10
#define PathExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
11
#define PathExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
12
+#define PathExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_PATHUTIL, x, e, s, __VA_ARGS__)
13
#define PathExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
14
#define PathExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
15
#define PathExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
@@ -42,63 +43,141 @@ LExit:
43
return hr;
44
}
45
45
-DAPI_(HRESULT) PathDirectoryContainsPath(
46
- __in_z LPCWSTR wzDirectory,
47
- __in_z LPCWSTR wzPath
46
+DAPI_(HRESULT) PathCanonicalizeForComparison(
47
+ __in_z LPCWSTR wzPath,
48
+ __in DWORD dwCanonicalizeFlags,
49
+ __deref_out_z LPWSTR* psczCanonicalized
50
)
51
{
52
HRESULT hr = S_OK;
51
- LPWSTR sczPath = NULL;
52
- LPWSTR sczDirectory = NULL;
53
- LPWSTR sczOriginalPath = NULL;
54
- LPWSTR sczOriginalDirectory = NULL;
53
+ LPWSTR sczNormalizedPath = NULL;
54
+ LPCWSTR wzNormalizedPath = NULL;
55
+ SIZE_T cchUncRootLength = 0;
56
+ BOOL fHasPrefix = FALSE;
57
56
- hr = PathCanonicalizePath(wzPath, &sczOriginalPath);
57
- PathExitOnFailure(hr, "Failed to canonicalize the path.");
58
+ hr = StrAllocString(&sczNormalizedPath, wzPath, 0);
59
+ PathExitOnFailure(hr, "Failed to allocate string for the normalized path.");
60
59
- hr = PathCanonicalizePath(wzDirectory, &sczOriginalDirectory);
60
- PathExitOnFailure(hr, "Failed to canonicalize the directory.");
61
+ PathFixedNormalizeSlashes(sczNormalizedPath);
62
+
63
+ wzNormalizedPath = sczNormalizedPath;
64
62
- if (!sczOriginalPath || !*sczOriginalPath)
65
+ if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags)
66
{
64
- ExitFunction1(hr = S_FALSE);
67
+ if (L'\\' == sczNormalizedPath[0] && (L'\\' == sczNormalizedPath[1] || L'?' == sczNormalizedPath[1]) && L'?' == sczNormalizedPath[2] && L'\\' == sczNormalizedPath[3])
68
+ {
69
+ if (L'U' == sczNormalizedPath[4] && L'N' == sczNormalizedPath[5] && L'C' == sczNormalizedPath[6] && L'\\' == sczNormalizedPath[7])
70
+ {
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
+ }
93
+ }
94
+
95
+ if (*wzNormalizedPath)
96
+ {
97
+ hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized);
98
+ PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath);
99
}
66
- if (!sczOriginalDirectory || !*sczOriginalDirectory)
100
+ else
101
{
68
- ExitFunction1(hr = S_FALSE);
102
+ Assert(cchUncRootLength);
103
+ ReleaseStr(*psczCanonicalized);
104
+ *psczCanonicalized = sczNormalizedPath;
105
+ sczNormalizedPath = NULL;
106
+ cchUncRootLength = 0;
107
}
108
71
- sczPath = sczOriginalPath;
72
- sczDirectory = sczOriginalDirectory;
109
+ if (cchUncRootLength)
110
+ {
111
+ hr = StrAllocPrefix(psczCanonicalized, sczNormalizedPath, cchUncRootLength);
112
+ PathExitOnFailure(hr, "Failed to prefix the UNC root to the canonicalized path.");
113
+ }
114
74
- for (; *sczDirectory;)
115
+ if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags)
116
{
76
- if (!*sczPath)
77
- {
78
- ExitFunction1(hr = S_FALSE);
79
- }
117
+ hr = PathBackslashTerminate(psczCanonicalized);
118
+ PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path");
119
+ }
120
81
- if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczDirectory, 1, sczPath, 1))
82
- {
83
- ExitFunction1(hr = S_FALSE);
84
- }
121
+ if ((PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags) &&
122
+ PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix)
123
+ {
124
+ hr = PathPrefix(psczCanonicalized);
125
+ PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path");
126
+ }
127
+
128
+LExit:
129
+ ReleaseStr(sczNormalizedPath);
130
86
- ++sczDirectory;
87
- ++sczPath;
131
+ return hr;
132
+}
133
+
134
+DAPI_(HRESULT) PathDirectoryContainsPath(
135
+ __in_z LPCWSTR wzDirectory,
136
+ __in_z LPCWSTR wzPath
137
+ )
138
+{
139
+ HRESULT hr = S_OK;
140
+ LPWSTR sczCanonicalizedDirectory = NULL;
141
+ LPWSTR sczCanonicalizedPath = NULL;
142
+ DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
143
+ size_t cchDirectory = 0;
144
+
145
+ if (!wzDirectory || !*wzDirectory)
146
+ {
147
+ PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory is required.");
148
}
149
+ if (!wzPath || !*wzPath)
150
+ {
151
+ PathExitWithRootFailure(hr, E_INVALIDARG, "wzPath is required.");
152
+ }
153
+
154
+ hr = PathCanonicalizeForComparison(wzDirectory, dwDefaultFlags | PATH_CANONICALIZE_BACKSLASH_TERMINATE, &sczCanonicalizedDirectory);
155
+ PathExitOnFailure(hr, "Failed to canonicalize the directory.");
156
90
- --sczDirectory;
91
- if (('\\' == *sczDirectory && *sczPath) || '\\' == *sczPath)
157
+ hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath);
158
+ PathExitOnFailure(hr, "Failed to canonicalize the path.");
159
+
160
+ if (!PathIsFullyQualified(sczCanonicalizedDirectory, NULL))
161
{
93
- hr = S_OK;
162
+ PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path.");
163
}
95
- else
164
+ if (!sczCanonicalizedPath || !*sczCanonicalizedPath)
165
{
97
- hr = S_FALSE;
166
+ ExitFunction1(hr = S_FALSE);
167
}
168
169
+ hr = ::StringCchLengthW(sczCanonicalizedDirectory, STRSAFE_MAX_CCH, &cchDirectory);
170
+ PathExitOnFailure(hr, "Failed to get length of canonicalized directory.");
171
+
172
+ if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalizedDirectory, (DWORD)cchDirectory, sczCanonicalizedPath, (DWORD)cchDirectory))
173
+ {
174
+ ExitFunction1(hr = S_FALSE);
175
+ }
176
+
177
+ hr = sczCanonicalizedPath[cchDirectory] ? S_OK : S_FALSE;
178
+
179
LExit:
101
- ReleaseStr(sczOriginalPath);
102
- ReleaseStr(sczOriginalDirectory);
180
+ ReleaseStr(sczCanonicalizedPath);
181
+ ReleaseStr(sczCanonicalizedDirectory);
182
return hr;
183
}
src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+145
-26
@@ -20,6 +20,9 @@
20
21
#define PATH_GOOD_ENOUGH 64
22
23
+static BOOL IsPathSeparatorChar(
24
+ __in WCHAR wc
25
+ );
26
static BOOL IsValidDriveChar(
27
__in WCHAR wc
28
);
@@ -41,7 +44,7 @@ DAPI_(LPWSTR) PathFile(
44
// \ => Windows path
45
// / => unix and URL path
46
// : => relative path from mapped root
44
- if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
47
+ if (IsPathSeparatorChar(*wz) || (L':' == *wz && wz == wzPath + 1))
48
{
49
wzFile = wz + 1;
50
}
@@ -64,7 +67,7 @@ DAPI_(LPCWSTR) PathExtension(
67
LPCWSTR wzExtension = NULL;
68
for (LPCWSTR wz = wzPath; *wz; ++wz)
69
{
67
- if (L'\\' == *wz || L'/' == *wz || L':' == *wz)
70
+ if (IsPathSeparatorChar(*wz) || L':' == *wz)
71
{
72
wzExtension = NULL;
73
}
@@ -84,7 +87,8 @@ DAPI_(HRESULT) PathGetDirectory(
87
)
88
{
89
HRESULT hr = S_OK;
87
- size_t cchDirectory = SIZE_T_MAX;
90
+ LPCWSTR wzRemaining = NULL;
91
+ SIZE_T cchDirectory = 0;
92
93
for (LPCWSTR wz = wzPath; *wz; ++wz)
94
{
@@ -92,18 +96,20 @@ DAPI_(HRESULT) PathGetDirectory(
96
// \ => Windows path
97
// / => unix and URL path
98
// : => relative path from mapped root
95
- if (L'\\' == *wz || L'/' == *wz || (L':' == *wz && wz == wzPath + 1))
99
+ if (IsPathSeparatorChar(*wz) || (L':' == *wz && wz == wzPath + 1))
100
{
97
- cchDirectory = static_cast<size_t>(wz - wzPath) + 1;
101
+ wzRemaining = wz;
102
}
103
}
104
101
- if (SIZE_T_MAX == cchDirectory)
105
+ if (!wzRemaining)
106
{
107
// we were given just a file name, so there's no directory available
104
- return S_FALSE;
108
+ ExitFunction1(hr = S_FALSE);
109
}
110
111
+ cchDirectory = static_cast<SIZE_T>(wzRemaining - wzPath) + 1;
112
+
113
hr = StrAllocString(psczDirectory, wzPath, cchDirectory);
114
PathExitOnFailure(hr, "Failed to copy directory.");
115
@@ -122,7 +128,7 @@ DAPI_(HRESULT) PathGetParentPath(
128
129
for (LPCWSTR wz = wzPath; *wz; ++wz)
130
{
125
- if (wz[1] && (L'\\' == *wz || L'/' == *wz))
131
+ if (IsPathSeparatorChar(*wz) && wz[1])
132
{
133
wzParent = wz;
134
}
@@ -291,12 +297,13 @@ DAPI_(HRESULT) PathPrefix(
297
hr = StrAllocPrefix(psczFullPath, L"\\\\?\\", 4);
298
PathExitOnFailure(hr, "Failed to add prefix to file path.");
299
}
294
- else if (fFullyQualified && L'\\' == wzFullPath[1]) // UNC
300
+ else if (fFullyQualified && IsPathSeparatorChar(wzFullPath[1])) // UNC
301
{
302
hr = StrSize(*psczFullPath, &cbFullPath);
303
PathExitOnFailure(hr, "Failed to get size of full path.");
304
305
memmove_s(wzFullPath, cbFullPath, wzFullPath + 1, cbFullPath - sizeof(WCHAR));
306
+ wzFullPath[0] = L'\\';
307
308
hr = StrAllocPrefix(psczFullPath, L"\\\\?\\UNC", 7);
309
PathExitOnFailure(hr, "Failed to add prefix to UNC path.");
@@ -312,6 +319,90 @@ LExit:
319
}
320
321
322
+DAPI_(HRESULT) PathFixedNormalizeSlashes(
323
+ __inout_z LPWSTR wzPath
324
+ )
325
+{
326
+ HRESULT hr = S_OK;
327
+ size_t cchLength = 0;
328
+ BOOL fAllowDoubleSlash = FALSE;
329
+ SIZE_T iSource = 0;
330
+ SIZE_T jDestination = 0;
331
+
332
+ hr = ::StringCchLengthW(wzPath, STRSAFE_MAX_CCH, &cchLength);
333
+ PathExitOnFailure(hr, "Failed to get length of path.");
334
+
335
+ if (1 < cchLength && IsPathSeparatorChar(wzPath[0]))
336
+ {
337
+ if (IsPathSeparatorChar(wzPath[1]))
338
+ {
339
+ // \\?\\a\ is not equivalent to \\?\a\ and \\server\\a\ is not equivalent to \\server\a\.
340
+ fAllowDoubleSlash = TRUE;
341
+ wzPath[0] = '\\';
342
+ wzPath[1] = '\\';
343
+ iSource = 2;
344
+ jDestination = 2;
345
+ }
346
+ else if (2 < cchLength && L'?' == wzPath[1] && L'?' == wzPath[2])
347
+ {
348
+ // \??\\a\ is not equivalent to \??\a\.
349
+ fAllowDoubleSlash = TRUE;
350
+ wzPath[0] = '\\';
351
+ wzPath[1] = '?';
352
+ wzPath[2] = '?';
353
+ iSource = 3;
354
+ jDestination = 3;
355
+ }
356
+ }
357
+
358
+ for (; iSource < cchLength; ++iSource)
359
+ {
360
+ if (IsPathSeparatorChar(wzPath[iSource]))
361
+ {
362
+ if (fAllowDoubleSlash)
363
+ {
364
+ fAllowDoubleSlash = FALSE;
365
+ }
366
+ else if (IsPathSeparatorChar(wzPath[iSource + 1]))
367
+ {
368
+ // Skip consecutive slashes.
369
+ continue;
370
+ }
371
+
372
+ wzPath[jDestination] = '\\';
373
+ }
374
+ else
375
+ {
376
+ wzPath[jDestination] = wzPath[iSource];
377
+ }
378
+
379
+ ++jDestination;
380
+ }
381
+
382
+ for (; jDestination < cchLength; ++jDestination)
383
+ {
384
+ wzPath[jDestination] = '\0';
385
+ }
386
+
387
+LExit:
388
+ return hr;
389
+}
390
+
391
+
392
+DAPI_(void) PathFixedReplaceForwardSlashes(
393
+ __inout_z LPWSTR wzPath
394
+ )
395
+{
396
+ for (LPWSTR wz = wzPath; *wz; ++wz)
397
+ {
398
+ if (L'/' == *wz)
399
+ {
400
+ *wz = L'\\';
401
+ }
402
+ }
403
+}
404
+
405
+
406
DAPI_(HRESULT) PathFixedBackslashTerminate(
407
__inout_ecount_z(cchPath) LPWSTR wzPath,
408
__in SIZE_T cchPath
@@ -320,17 +411,28 @@ DAPI_(HRESULT) PathFixedBackslashTerminate(
411
HRESULT hr = S_OK;
412
size_t cchLength = 0;
413
414
+ if (!cchPath)
415
+ {
416
+ ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
417
+ }
418
+
419
hr = ::StringCchLengthW(wzPath, cchPath, &cchLength);
420
PathExitOnFailure(hr, "Failed to get length of path.");
421
326
- if (cchLength >= cchPath)
422
+ LPWSTR wzLast = wzPath + (cchLength - 1);
423
+ if (cchLength && L'/' == wzLast[0])
424
{
328
- hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
425
+ wzLast[0] = L'\\';
426
}
330
- else if (L'\\' != wzPath[cchLength - 1])
427
+ else if (!cchLength || L'\\' != wzLast[0])
428
{
332
- wzPath[cchLength] = L'\\';
333
- wzPath[cchLength + 1] = L'\0';
429
+ if (cchLength + 2 > cchPath)
430
+ {
431
+ ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
432
+ }
433
+
434
+ wzLast[1] = L'\\';
435
+ wzLast[2] = L'\0';
436
}
437
438
LExit:
@@ -339,10 +441,10 @@ LExit:
441
442
443
DAPI_(HRESULT) PathBackslashTerminate(
342
- __inout LPWSTR* psczPath
444
+ __inout_z LPWSTR* psczPath
445
)
446
{
345
- Assert(psczPath && *psczPath);
447
+ Assert(psczPath);
448
449
HRESULT hr = S_OK;
450
SIZE_T cchPath = 0;
@@ -354,7 +456,12 @@ DAPI_(HRESULT) PathBackslashTerminate(
456
hr = ::StringCchLengthW(*psczPath, cchPath, &cchLength);
457
PathExitOnFailure(hr, "Failed to get length of path.");
458
357
- if (L'\\' != (*psczPath)[cchLength - 1])
459
+ LPWSTR wzLast = *psczPath + (cchLength - 1);
460
+ if (cchLength && L'/' == wzLast[0])
461
+ {
462
+ wzLast[0] = L'\\';
463
+ }
464
+ else if (!cchLength || L'\\' != wzLast[0])
465
{
466
hr = StrAllocConcat(psczPath, L"\\", 1);
467
PathExitOnFailure(hr, "Failed to concat backslash onto string.");
@@ -833,13 +940,13 @@ DAPI_(BOOL) PathIsFullyQualified(
940
ExitFunction();
941
}
942
836
- if (L'\\' != wzPath[0])
943
+ if (!IsPathSeparatorChar(wzPath[0]))
944
{
945
// The only way to specify a fully qualified path that doesn't begin with a slash
946
// is the drive, colon, slash format (C:\).
947
if (IsValidDriveChar(wzPath[0]) &&
948
L':' == wzPath[1] &&
842
- L'\\' == wzPath[2])
949
+ IsPathSeparatorChar(wzPath[2]))
950
{
951
fFullyQualified = TRUE;
952
}
@@ -849,14 +956,14 @@ DAPI_(BOOL) PathIsFullyQualified(
956
957
// Non-drive fully qualified paths must start with \\ or \?.
958
// \??\ is an archaic form of \\?\.
852
- if (L'?' != wzPath[1] && L'\\' != wzPath[1])
959
+ if (L'?' != wzPath[1] && !IsPathSeparatorChar(wzPath[1]))
960
{
961
ExitFunction();
962
}
963
964
fFullyQualified = TRUE;
965
859
- if (L'?' == wzPath[2] && L'\\' == wzPath[3])
966
+ if (L'?' == wzPath[2] && IsPathSeparatorChar(wzPath[3]))
967
{
968
fHasLongPathPrefix = TRUE;
969
}
@@ -877,7 +984,7 @@ DAPI_(BOOL) PathIsRooted(
984
)
985
{
986
return wzPath &&
880
- (wzPath[0] == L'\\' ||
987
+ (IsPathSeparatorChar(wzPath[0]) ||
988
IsValidDriveChar(wzPath[0]) && wzPath[1] == L':');
989
}
990
@@ -1008,20 +1115,25 @@ DAPI_(HRESULT) PathGetHierarchyArray(
1115
1116
for (size_t i = 0; i < cchPath; ++i)
1117
{
1011
- if (wzPath[i] == L'\\')
1118
+ if (IsPathSeparatorChar(wzPath[i]))
1119
{
1120
++cArraySpacesNeeded;
1121
}
1122
}
1123
1017
- if (wzPath[cchPath - 1] != L'\\')
1124
+ if (!IsPathSeparatorChar(wzPath[cchPath - 1]))
1125
{
1126
++cArraySpacesNeeded;
1127
}
1128
1129
// 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.
1023
- if (wzPath[0] == L'\\' && wzPath[1] == L'\\')
1130
+ if (IsPathSeparatorChar(wzPath[0]) && IsPathSeparatorChar(wzPath[1]))
1131
{
1132
+ if (3 > cArraySpacesNeeded)
1133
+ {
1134
+ ExitFunction1(hr = E_INVALIDARG);
1135
+ }
1136
+
1137
cArraySpacesNeeded -= 3;
1138
}
1139
@@ -1042,7 +1154,7 @@ DAPI_(HRESULT) PathGetHierarchyArray(
1154
DWORD cchPathCopy = lstrlenW(sczPathCopy);
1155
1156
// 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
1045
- if (wzPath[cchPathCopy - 1] == L'\\')
1157
+ if (IsPathSeparatorChar(wzPath[cchPathCopy - 1]))
1158
{
1159
sczPathCopy[cchPathCopy - 1] = L'\0';
1160
}
@@ -1063,6 +1175,13 @@ LExit:
1175
return hr;
1176
}
1177
1178
+static BOOL IsPathSeparatorChar(
1179
+ __in WCHAR wc
1180
+ )
1181
+{
1182
+ return L'/' == wc || L'\\' == wc;
1183
+}
1184
+
1185
static BOOL IsValidDriveChar(
1186
__in WCHAR wc
1187
)
src/libs/dutil/test/DUtilUnitTest/DUtilUnitTest.vcxproj
+1
-1
@@ -40,7 +40,7 @@
40
41
<PropertyGroup>
42
<ProjectAdditionalIncludeDirectories>..\..\WixToolset.DUtil\inc</ProjectAdditionalIncludeDirectories>
43
- <ProjectAdditionalLinkLibraries>rpcrt4.lib;Mpr.lib;Ws2_32.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries>
43
+ <ProjectAdditionalLinkLibraries>rpcrt4.lib;Mpr.lib;Ws2_32.lib;shlwapi.lib;urlmon.lib;wininet.lib</ProjectAdditionalLinkLibraries>
44
</PropertyGroup>
45
46
<ItemGroup>
src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+511
-43
@@ -11,6 +11,290 @@ namespace DutilTests
11
public ref class PathUtil
12
{
13
public:
14
+ [Fact]
15
+ void PathBackslashFixedTerminateTest()
16
+ {
17
+ HRESULT hr = S_OK;
18
+ WCHAR wzEmpty[1] = { L'\0' };
19
+ WCHAR wzSingleLetter[1] = { L'a' };
20
+ WCHAR wzSingleBackslash[1] = { L'\\' };
21
+ WCHAR wzSingleForwardSlash[1] = { L'/' };
22
+ WCHAR wzSingleLetterNullTerminated[2] = { L'a', L'\0' };
23
+ WCHAR wzSingleBackslashNullTerminated[2] = { L'\\', L'\0' };
24
+ WCHAR wzSingleForwardSlashNullTerminated[2] = { L'/', L'\0' };
25
+ WCHAR wzExtraSpaceLetterNullTerminated[3] = { L'a', L'\0', L'\0' };
26
+ WCHAR wzExtraSpaceBackslashNullTerminated[3] = { L'\\', L'\0', L'\0' };
27
+ WCHAR wzExtraSpaceForwardSlashNullTerminated[3] = { L'/', L'\0', L'\0' };
28
+
29
+ hr = PathFixedBackslashTerminate(wzEmpty, 0);
30
+ NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: zero-length, {0}", wzEmpty);
31
+
32
+ hr = PathFixedBackslashTerminate(wzEmpty, countof(wzEmpty));
33
+ NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: '' (length 1), {0}", wzEmpty);
34
+
35
+ hr = PathFixedBackslashTerminate(wzSingleLetter, countof(wzSingleLetter));
36
+ NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: 'a' (length 1)");
37
+
38
+ hr = PathFixedBackslashTerminate(wzSingleBackslash, countof(wzSingleBackslash));
39
+ NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: '\\' (length 1)");
40
+
41
+ hr = PathFixedBackslashTerminate(wzSingleForwardSlash, countof(wzSingleForwardSlash));
42
+ NativeAssert::SpecificReturnCode(E_INVALIDARG, hr, "PathFixedBackslashTerminate: '/' (length 1)");
43
+
44
+ hr = PathFixedBackslashTerminate(wzSingleLetterNullTerminated, countof(wzSingleLetterNullTerminated));
45
+ NativeAssert::SpecificReturnCode(E_INSUFFICIENT_BUFFER, hr, "PathFixedBackslashTerminate: 'a' (length 2)");
46
+
47
+ hr = PathFixedBackslashTerminate(wzSingleBackslashNullTerminated, countof(wzSingleBackslashNullTerminated));
48
+ NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '\\' (length 2)");
49
+ NativeAssert::StringEqual(L"\\", wzSingleBackslashNullTerminated);
50
+
51
+ hr = PathFixedBackslashTerminate(wzSingleForwardSlashNullTerminated, countof(wzSingleForwardSlashNullTerminated));
52
+ NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '/' (length 2)");
53
+ NativeAssert::StringEqual(L"\\", wzSingleForwardSlashNullTerminated);
54
+
55
+ hr = PathFixedBackslashTerminate(wzExtraSpaceLetterNullTerminated, countof(wzExtraSpaceLetterNullTerminated));
56
+ NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: 'a' (length 3)");
57
+ NativeAssert::StringEqual(L"a\\", wzExtraSpaceLetterNullTerminated);
58
+
59
+ hr = PathFixedBackslashTerminate(wzExtraSpaceBackslashNullTerminated, countof(wzExtraSpaceBackslashNullTerminated));
60
+ NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '\\' (length 3)");
61
+ NativeAssert::StringEqual(L"\\", wzExtraSpaceBackslashNullTerminated);
62
+
63
+ hr = PathFixedBackslashTerminate(wzExtraSpaceForwardSlashNullTerminated, countof(wzExtraSpaceForwardSlashNullTerminated));
64
+ NativeAssert::Succeeded(hr, "PathFixedBackslashTerminate: '/' (length 3)");
65
+ NativeAssert::StringEqual(L"\\", wzExtraSpaceForwardSlashNullTerminated);
66
+ }
67
+
68
+ [Fact]
69
+ void PathBackslashTerminateTest()
70
+ {
71
+ HRESULT hr = S_OK;
72
+ LPWSTR sczPath = NULL;
73
+ LPCWSTR rgwzPaths[16] =
74
+ {
75
+ L"", L"\\",
76
+ L"a", L"a\\",
77
+ L"\\", L"\\",
78
+ L"a\\", L"a\\",
79
+ L"/", L"\\",
80
+ L"a/", L"a\\",
81
+ L"\\\\", L"\\\\",
82
+ L"//", L"/\\",
83
+ };
84
+
85
+ try
86
+ {
87
+ for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
88
+ {
89
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
90
+ NativeAssert::Succeeded(hr, "Failed to copy string");
91
+
92
+ hr = PathBackslashTerminate(&sczPath);
93
+ NativeAssert::Succeeded(hr, "PathBackslashTerminate: {0}", rgwzPaths[i]);
94
+ NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
95
+ }
96
+ }
97
+ finally
98
+ {
99
+ ReleaseStr(sczPath);
100
+ }
101
+ }
102
+
103
+ [Fact]
104
+ void PathCanonicalizeForComparisonTest()
105
+ {
106
+ HRESULT hr = S_OK;
107
+ LPWSTR sczCanonicalized = NULL;
108
+
109
+ try
110
+ {
111
+ hr = PathCanonicalizeForComparison(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
112
+ Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
113
+
114
+ hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
115
+ Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
116
+
117
+ hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
118
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
119
+ NativeAssert::StringEqual(L"\\\\server", sczCanonicalized);
120
+
121
+ hr = PathCanonicalizeForComparison(L"\\\\server", 0, &sczCanonicalized);
122
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
123
+ NativeAssert::StringEqual(L"\\\\server", sczCanonicalized);
124
+
125
+ hr = PathCanonicalizeForComparison(L"\\\\server\\", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
126
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
127
+ NativeAssert::StringEqual(L"\\\\server\\", sczCanonicalized);
128
+
129
+ hr = PathCanonicalizeForComparison(L"\\\\server\\share", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
130
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
131
+ NativeAssert::StringEqual(L"\\\\server\\share", sczCanonicalized);
132
+
133
+ hr = PathCanonicalizeForComparison(L"\\\\server\\share\\", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
134
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
135
+ NativeAssert::StringEqual(L"\\\\server\\share\\", sczCanonicalized);
136
+
137
+ hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
138
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
139
+ NativeAssert::StringEqual(L"\\\\.\\share\\otherdir\\unc.exe", sczCanonicalized);
140
+
141
+ hr = PathCanonicalizeForComparison(L"\\\\.\\share\\otherdir\\unc.exe", 0, &sczCanonicalized);
142
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
143
+ NativeAssert::StringEqual(L"\\\\share\\otherdir\\unc.exe", sczCanonicalized);
144
+
145
+ hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
146
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
147
+ NativeAssert::StringEqual(L"\\\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
148
+
149
+ hr = PathCanonicalizeForComparison(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", 0, &sczCanonicalized);
150
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
151
+ NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized);
152
+
153
+ hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
154
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
155
+ NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", sczCanonicalized);
156
+
157
+ hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", 0, &sczCanonicalized);
158
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
159
+ NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized);
160
+
161
+ hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
162
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
163
+ NativeAssert::StringEqual(L"C:\\otherdir\\pastroot.exe", sczCanonicalized);
164
+
165
+ hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
166
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
167
+ NativeAssert::StringEqual(L"C:\\otherdir\\pastroot.exe", sczCanonicalized);
168
+
169
+ hr = PathCanonicalizeForComparison(L"\\\\?\\C:dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", 0, &sczCanonicalized);
170
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
171
+ NativeAssert::StringEqual(L"\\otherdir\\pastroot.exe", sczCanonicalized);
172
+
173
+ hr = PathCanonicalizeForComparison(L"C:dir\\subdir\\..\\..\\..\\otherdir\\pastrelativeroot.exe", 0, &sczCanonicalized);
174
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
175
+ NativeAssert::StringEqual(L"\\otherdir\\pastrelativeroot.exe", sczCanonicalized);
176
+
177
+ hr = PathCanonicalizeForComparison(L"A:dir\\subdir\\..\\..\\otherdir\\relativeroot.exe", 0, &sczCanonicalized);
178
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
179
+ NativeAssert::StringEqual(L"\\otherdir\\relativeroot.exe", sczCanonicalized);
180
+
181
+ hr = PathCanonicalizeForComparison(L"C:dir\\subdir\\otherdir\\relativeroot.exe", 0, &sczCanonicalized);
182
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
183
+ NativeAssert::StringEqual(L"C:dir\\subdir\\otherdir\\relativeroot.exe", sczCanonicalized);
184
+
185
+ hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\otherdir\\backslashes.exe", 0, &sczCanonicalized);
186
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
187
+ NativeAssert::StringEqual(L"C:\\otherdir\\backslashes.exe", sczCanonicalized);
188
+
189
+ hr = PathCanonicalizeForComparison(L"C:\\dir\\subdir\\..\\..\\otherdir\\\\consecutivebackslashes.exe", 0, &sczCanonicalized);
190
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
191
+ NativeAssert::StringEqual(L"C:\\otherdir\\consecutivebackslashes.exe", sczCanonicalized);
192
+
193
+ hr = PathCanonicalizeForComparison(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", 0, &sczCanonicalized);
194
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
195
+ NativeAssert::StringEqual(L"C:\\otherdir\\forwardslashes.exe", sczCanonicalized);
196
+
197
+ hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\test\\..\\validlongpath.exe", 0, &sczCanonicalized);
198
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
199
+ NativeAssert::StringEqual(L"C:\\validlongpath.exe", sczCanonicalized);
200
+
201
+ hr = PathCanonicalizeForComparison(L"\\\\?\\test\\..\\invalidlongpath.exe", 0, &sczCanonicalized);
202
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
203
+ NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized);
204
+
205
+ hr = PathCanonicalizeForComparison(L"C:\\.\\invalid:pathchars?.exe", 0, &sczCanonicalized);
206
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
207
+ NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized);
208
+
209
+ hr = PathCanonicalizeForComparison(L"C:\\addprefix.exe", PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX, &sczCanonicalized);
210
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
211
+ NativeAssert::StringEqual(L"\\\\?\\C:\\addprefix.exe", sczCanonicalized);
212
+
213
+ hr = PathCanonicalizeForComparison(L"C:\\addbackslash.exe", PATH_CANONICALIZE_BACKSLASH_TERMINATE, &sczCanonicalized);
214
+ NativeAssert::Succeeded(hr, "Failed to canonicalize path");
215
+ NativeAssert::StringEqual(L"C:\\addbackslash.exe\\", sczCanonicalized);
216
+ }
217
+ finally
218
+ {
219
+ ReleaseStr(sczCanonicalized);
220
+ }
221
+ }
222
+
223
+ [Fact]
224
+ void PathDirectoryContainsPathTest()
225
+ {
226
+ HRESULT hr = S_OK;
227
+
228
+ hr = PathDirectoryContainsPath(L"", L"");
229
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
230
+
231
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"");
232
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
233
+
234
+ hr = PathDirectoryContainsPath(L"", L"C:\\Directory");
235
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
236
+
237
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\Directory");
238
+ Assert::Equal<HRESULT>(S_FALSE, hr);
239
+
240
+ hr = PathDirectoryContainsPath(L"C:\\Dir", L"C:\\Directory");
241
+ Assert::Equal<HRESULT>(S_FALSE, hr);
242
+
243
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\");
244
+ Assert::Equal<HRESULT>(S_FALSE, hr);
245
+
246
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\DirectoryPlus");
247
+ Assert::Equal<HRESULT>(S_FALSE, hr);
248
+
249
+ hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\DirectoryPlus");
250
+ Assert::Equal<HRESULT>(S_FALSE, hr);
251
+
252
+ hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory\\../Plus");
253
+ Assert::Equal<HRESULT>(S_FALSE, hr);
254
+
255
+ hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory/../Plus");
256
+ Assert::Equal<HRESULT>(S_FALSE, hr);
257
+
258
+ hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\server\\share\\DirectoryPlus");
259
+ Assert::Equal<HRESULT>(S_FALSE, hr);
260
+
261
+ hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\discarded\\..\\server\\share\\Directory\\Plus");
262
+ Assert::Equal<HRESULT>(S_FALSE, hr);
263
+
264
+ hr = PathDirectoryContainsPath(L"..\\..", L"..\\..\\plus");
265
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
266
+
267
+ hr = PathDirectoryContainsPath(L"..\\..", L"\\..\\..\\plus");
268
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
269
+
270
+ hr = PathDirectoryContainsPath(L"\\..\\..", L"\\..\\..\\plus");
271
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
272
+
273
+ hr = PathDirectoryContainsPath(L"C:..\\..", L"C:..\\..\\plus");
274
+ Assert::Equal<HRESULT>(E_INVALIDARG, hr);
275
+
276
+ hr = PathDirectoryContainsPath(L"\\\\server\\share\\Directory", L"\\\\server\\share\\Directory\\Plus");
277
+ Assert::Equal<HRESULT>(S_OK, hr);
278
+
279
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\directory\\plus");
280
+ Assert::Equal<HRESULT>(S_OK, hr);
281
+
282
+ hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory\\Plus");
283
+ Assert::Equal<HRESULT>(S_OK, hr);
284
+
285
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\.\\Directory\\Plus");
286
+ Assert::Equal<HRESULT>(S_OK, hr);
287
+
288
+ hr = PathDirectoryContainsPath(L"C:\\Directory", L"C:\\Directory/Plus");
289
+ Assert::Equal<HRESULT>(S_OK, hr);
290
+
291
+ hr = PathDirectoryContainsPath(L"C:\\Directory\\", L"C:\\Directory/Plus");
292
+ Assert::Equal<HRESULT>(S_OK, hr);
293
+
294
+ hr = PathDirectoryContainsPath(L"\\\\?\\C:\\Directory", L"C:\\Directory\\Plus");
295
+ Assert::Equal<HRESULT>(S_OK, hr);
296
+ }
297
+
298
[Fact]
299
void PathGetDirectoryTest()
300
{
@@ -103,6 +387,57 @@ namespace DutilTests
387
NativeAssert::StringEqual(L"Software\\Microsoft\\", rgsczPaths[1]);
388
NativeAssert::StringEqual(L"Software\\Microsoft\\Windows\\", rgsczPaths[2]);
389
ReleaseNullStrArray(rgsczPaths, cPaths);
390
+
391
+ hr = PathGetHierarchyArray(L"c:/foo/bar/bas/a.txt", &rgsczPaths, &cPaths);
392
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular file path");
393
+ Assert::Equal<DWORD>(5, cPaths);
394
+ NativeAssert::StringEqual(L"c:/", rgsczPaths[0]);
395
+ NativeAssert::StringEqual(L"c:/foo/", rgsczPaths[1]);
396
+ NativeAssert::StringEqual(L"c:/foo/bar/", rgsczPaths[2]);
397
+ NativeAssert::StringEqual(L"c:/foo/bar/bas/", rgsczPaths[3]);
398
+ NativeAssert::StringEqual(L"c:/foo/bar/bas/a.txt", rgsczPaths[4]);
399
+ ReleaseNullStrArray(rgsczPaths, cPaths);
400
+
401
+ hr = PathGetHierarchyArray(L"c:/foo/bar/bas/", &rgsczPaths, &cPaths);
402
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for regular directory path");
403
+ Assert::Equal<DWORD>(4, cPaths);
404
+ NativeAssert::StringEqual(L"c:/", rgsczPaths[0]);
405
+ NativeAssert::StringEqual(L"c:/foo/", rgsczPaths[1]);
406
+ NativeAssert::StringEqual(L"c:/foo/bar/", rgsczPaths[2]);
407
+ NativeAssert::StringEqual(L"c:/foo/bar/bas/", rgsczPaths[3]);
408
+ ReleaseNullStrArray(rgsczPaths, cPaths);
409
+
410
+ hr = PathGetHierarchyArray(L"//server/share/subdir/file.txt", &rgsczPaths, &cPaths);
411
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC file path");
412
+ Assert::Equal<DWORD>(3, cPaths);
413
+ NativeAssert::StringEqual(L"//server/share/", rgsczPaths[0]);
414
+ NativeAssert::StringEqual(L"//server/share/subdir/", rgsczPaths[1]);
415
+ NativeAssert::StringEqual(L"//server/share/subdir/file.txt", rgsczPaths[2]);
416
+ ReleaseNullStrArray(rgsczPaths, cPaths);
417
+
418
+ hr = PathGetHierarchyArray(L"//server/share/subdir/", &rgsczPaths, &cPaths);
419
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
420
+ Assert::Equal<DWORD>(2, cPaths);
421
+ NativeAssert::StringEqual(L"//server/share/", rgsczPaths[0]);
422
+ NativeAssert::StringEqual(L"//server/share/subdir/", rgsczPaths[1]);
423
+ ReleaseNullStrArray(rgsczPaths, cPaths);
424
+
425
+ hr = PathGetHierarchyArray(L"Software/Microsoft/Windows/ValueName", &rgsczPaths, &cPaths);
426
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
427
+ Assert::Equal<DWORD>(4, cPaths);
428
+ NativeAssert::StringEqual(L"Software/", rgsczPaths[0]);
429
+ NativeAssert::StringEqual(L"Software/Microsoft/", rgsczPaths[1]);
430
+ NativeAssert::StringEqual(L"Software/Microsoft/Windows/", rgsczPaths[2]);
431
+ NativeAssert::StringEqual(L"Software/Microsoft/Windows/ValueName", rgsczPaths[3]);
432
+ ReleaseNullStrArray(rgsczPaths, cPaths);
433
+
434
+ hr = PathGetHierarchyArray(L"Software/Microsoft/Windows/", &rgsczPaths, &cPaths);
435
+ NativeAssert::Succeeded(hr, "Failed to get parent directories array for UNC directory path");
436
+ Assert::Equal<DWORD>(3, cPaths);
437
+ NativeAssert::StringEqual(L"Software/", rgsczPaths[0]);
438
+ NativeAssert::StringEqual(L"Software/Microsoft/", rgsczPaths[1]);
439
+ NativeAssert::StringEqual(L"Software/Microsoft/Windows/", rgsczPaths[2]);
440
+ ReleaseNullStrArray(rgsczPaths, cPaths);
441
}
442
finally
443
{
@@ -110,12 +445,66 @@ namespace DutilTests
445
}
446
}
447
448
+ [Fact]
449
+ void PathNormalizeSlashesFixedTest()
450
+ {
451
+ HRESULT hr = S_OK;
452
+ LPWSTR sczPath = NULL;
453
+ LPCWSTR rgwzPaths[54] =
454
+ {
455
+ L"", L"",
456
+ L"\\", L"\\",
457
+ L"\\\\", L"\\\\",
458
+ L"\\\\\\", L"\\\\\\",
459
+ L"\\\\?\\UNC\\", L"\\\\?\\UNC\\",
460
+ L"C:\\\\foo2", L"C:\\foo2",
461
+ L"\\\\?\\C:\\\\foo2", L"\\\\?\\C:\\foo2",
462
+ L"\\\\a\\b\\", L"\\\\a\\b\\",
463
+ L"\\\\?\\UNC\\a\\b\\\\c\\", L"\\\\?\\UNC\\a\\b\\c\\",
464
+ L"\\\\?\\UNC\\a\\b\\\\", L"\\\\?\\UNC\\a\\b\\",
465
+ L"\\\\?\\UNC\\test\\unc\\path\\to\\\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
466
+ L"\\\\?\\C:\\\\foo\\\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
467
+ L"\\??\\C:\\\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt",
468
+ L"\\??\\\\C:\\\\foo\\bar.txt", L"\\??\\\\C:\\foo\\bar.txt",
469
+ L"/", L"\\",
470
+ L"//", L"\\\\",
471
+ L"///", L"\\\\\\",
472
+ L"//?/UNC/", L"\\\\?\\UNC\\",
473
+ L"C://foo2", L"C:\\foo2",
474
+ L"//?/C://foo2", L"\\\\?\\C:\\foo2",
475
+ L"//a/b/", L"\\\\a\\b\\",
476
+ L"//?/UNC/a/b//c/", L"\\\\?\\UNC\\a\\b\\c\\",
477
+ L"//?/UNC/a/b//", L"\\\\?\\UNC\\a\\b\\",
478
+ L"//?/UNC/test/unc/path/to//something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
479
+ L"//?/C://foo//bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
480
+ L"/??/C://foo/bar.txt", L"\\??\\C:\\foo\\bar.txt",
481
+ L"/??//C://foo/bar.txt", L"\\??\\\\C:\\foo\\bar.txt",
482
+ };
483
+
484
+ try
485
+ {
486
+ for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
487
+ {
488
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
489
+ NativeAssert::Succeeded(hr, "Failed to copy string");
490
+
491
+ hr = PathFixedNormalizeSlashes(sczPath);
492
+ NativeAssert::Succeeded(hr, "PathNormalizeSlashes: {0}", rgwzPaths[i]);
493
+ NativeAssert::StringEqual(rgwzPaths[i + 1], sczPath);
494
+ }
495
+ }
496
+ finally
497
+ {
498
+ ReleaseStr(sczPath);
499
+ }
500
+ }
501
+
502
[Fact]
503
void PathPrefixTest()
504
{
505
HRESULT hr = S_OK;
506
LPWSTR sczPath = NULL;
118
- LPCWSTR rgwzPaths[12] =
507
+ LPCWSTR rgwzPaths[24] =
508
{
509
L"\\\\", L"\\\\?\\UNC\\",
510
L"C:\\\\foo2", L"\\\\?\\C:\\\\foo2",
@@ -123,11 +512,17 @@ namespace DutilTests
512
L"\\\\?\\UNC\\test\\unc\\path\\to\\something", L"\\\\?\\UNC\\test\\unc\\path\\to\\something",
513
L"\\\\?\\C:\\foo\\bar.txt", L"\\\\?\\C:\\foo\\bar.txt",
514
L"\\??\\C:\\foo\\bar.txt", L"\\??\\C:\\foo\\bar.txt",
515
+ L"//", L"\\\\?\\UNC\\",
516
+ L"C://foo2", L"\\\\?\\C://foo2",
517
+ L"//a/b/", L"\\\\?\\UNC\\a/b/",
518
+ L"//?/UNC/test/unc/path/to/something", L"//?/UNC/test/unc/path/to/something",
519
+ L"//?/C:/foo/bar.txt", L"//?/C:/foo/bar.txt",
520
+ L"/??/C:/foo/bar.txt", L"/??/C:/foo/bar.txt",
521
};
522
523
try
524
{
130
- for (DWORD i = 0; i < countof(rgwzPaths) / 2; i += 2)
525
+ for (DWORD i = 0; i < countof(rgwzPaths); i += 2)
526
{
527
hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
528
NativeAssert::Succeeded(hr, "Failed to copy string");
@@ -148,16 +543,20 @@ namespace DutilTests
543
{
544
HRESULT hr = S_OK;
545
LPWSTR sczPath = NULL;
151
- LPCWSTR rgwzPaths[8] =
546
+ LPCWSTR rgwzPaths[12] =
547
{
548
L"\\",
549
+ L"/",
550
L"C:",
551
L"C:foo.txt",
552
L"",
553
L"\\?",
554
+ L"/?",
555
L"\\dir",
556
+ L"/dir",
557
L"dir",
558
L"dir\\subdir",
559
+ L"dir/subdir",
560
};
561
562
try
@@ -180,93 +579,162 @@ namespace DutilTests
579
[Fact]
580
void PathIsRootedAndFullyQualifiedTest()
581
{
582
+ HRESULT hr = S_OK;
583
+ LPWSTR sczPath = NULL;
584
LPCWSTR rgwzPaths[15] =
585
{
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",
586
+ L"//",
587
+ L"///",
588
+ L"C:/",
589
+ L"C://",
590
+ L"C:/foo1",
591
+ L"C://foo2",
592
+ L"//test/unc/path/to/something",
593
+ L"//a/b/c/d/e",
594
+ L"//a/b/",
595
+ L"//a/b",
596
+ L"//test/unc",
597
+ L"//Server",
598
+ L"//Server/Foo.txt",
599
+ L"//Server/Share/Foo.txt",
600
+ L"//Server/Share/Test/Foo.txt",
601
};
602
202
- for (DWORD i = 0; i < countof(rgwzPaths); ++i)
603
+ try
604
{
204
- ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE);
205
- ValidateRootedPath(rgwzPaths[i], TRUE);
605
+ for (DWORD i = 0; i < countof(rgwzPaths); ++i)
606
+ {
607
+ ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, FALSE);
608
+ ValidateRootedPath(rgwzPaths[i], TRUE);
609
+
610
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
611
+ NativeAssert::Succeeded(hr, "Failed to copy string");
612
+
613
+ PathFixedReplaceForwardSlashes(sczPath);
614
+ ValidateFullyQualifiedPath(sczPath, TRUE, FALSE);
615
+ ValidateRootedPath(sczPath, TRUE);
616
+ }
617
+ }
618
+ finally
619
+ {
620
+ ReleaseStr(sczPath);
621
}
622
}
623
624
[Fact]
625
void PathIsRootedAndFullyQualifiedWithPrefixTest()
626
{
627
+ HRESULT hr = S_OK;
628
+ LPWSTR sczPath = NULL;
629
LPCWSTR rgwzPaths[6] =
630
{
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",
631
+ L"//?/UNC/test/unc/path/to/something",
632
+ L"//?/UNC/test/unc",
633
+ L"//?/UNC/a/b1",
634
+ L"//?/UNC/a/b2/",
635
+ L"//?/C:/foo/bar.txt",
636
+ L"/??/C:/foo/bar.txt",
637
};
638
222
- for (DWORD i = 0; i < countof(rgwzPaths); ++i)
639
+ try
640
{
224
- ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE);
225
- ValidateRootedPath(rgwzPaths[i], TRUE);
641
+ for (DWORD i = 0; i < countof(rgwzPaths); ++i)
642
+ {
643
+ ValidateFullyQualifiedPath(rgwzPaths[i], TRUE, TRUE);
644
+ ValidateRootedPath(rgwzPaths[i], TRUE);
645
+
646
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
647
+ NativeAssert::Succeeded(hr, "Failed to copy string");
648
+
649
+ PathFixedReplaceForwardSlashes(sczPath);
650
+ ValidateFullyQualifiedPath(sczPath, TRUE, TRUE);
651
+ ValidateRootedPath(sczPath, TRUE);
652
+ }
653
+ }
654
+ finally
655
+ {
656
+ ReleaseStr(sczPath);
657
}
658
}
659
660
[Fact]
661
void PathIsRootedButNotFullyQualifiedTest()
662
{
663
+ HRESULT hr = S_OK;
664
+ LPWSTR sczPath = NULL;
665
LPCWSTR rgwzPaths[7] =
666
{
234
- L"\\",
667
+ L"/",
668
L"a:",
669
L"A:",
670
L"z:",
671
L"Z:",
672
L"C:foo.txt",
240
- L"\\dir",
673
+ L"/dir",
674
};
675
243
- for (DWORD i = 0; i < countof(rgwzPaths); ++i)
676
+ try
677
{
245
- ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
246
- ValidateRootedPath(rgwzPaths[i], TRUE);
678
+ for (DWORD i = 0; i < countof(rgwzPaths); ++i)
679
+ {
680
+ ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
681
+ ValidateRootedPath(rgwzPaths[i], TRUE);
682
+
683
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
684
+ NativeAssert::Succeeded(hr, "Failed to copy string");
685
+
686
+ PathFixedReplaceForwardSlashes(sczPath);
687
+ ValidateFullyQualifiedPath(sczPath, FALSE, FALSE);
688
+ ValidateRootedPath(sczPath, TRUE);
689
+ }
690
+ }
691
+ finally
692
+ {
693
+ ReleaseStr(sczPath);
694
}
695
}
696
697
[Fact]
698
void PathIsNotRootedAndNotFullyQualifiedTest()
699
{
700
+ HRESULT hr = S_OK;
701
+ LPWSTR sczPath = NULL;
702
LPCWSTR rgwzPaths[9] =
703
{
704
NULL,
705
L"",
706
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
707
+ L"dir/subdir",
708
+ L"@:/foo", // 064 = @ 065 = A
709
+ L"[://", // 091 = [ 090 = Z
710
+ L"`:/foo ", // 096 = ` 097 = a
711
+ L"{://", // 123 = { 122 = z
712
L"[:",
713
};
714
266
- for (DWORD i = 0; i < countof(rgwzPaths); ++i)
715
+ try
716
{
268
- ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
269
- ValidateRootedPath(rgwzPaths[i], FALSE);
717
+ for (DWORD i = 0; i < countof(rgwzPaths); ++i)
718
+ {
719
+ ValidateFullyQualifiedPath(rgwzPaths[i], FALSE, FALSE);
720
+ ValidateRootedPath(rgwzPaths[i], FALSE);
721
+
722
+ if (!rgwzPaths[i])
723
+ {
724
+ continue;
725
+ }
726
+
727
+ hr = StrAllocString(&sczPath, rgwzPaths[i], 0);
728
+ NativeAssert::Succeeded(hr, "Failed to copy string");
729
+
730
+ PathFixedReplaceForwardSlashes(sczPath);
731
+ ValidateFullyQualifiedPath(sczPath, FALSE, FALSE);
732
+ ValidateRootedPath(sczPath, FALSE);
733
+ }
734
+ }
735
+ finally
736
+ {
737
+ ReleaseStr(sczPath);
738
}
739
}
740
src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+1
-1
@@ -84,7 +84,7 @@ namespace WixToolsetTest.CoreIntegration
84
? field.AsNullableNumber()?.ToString()
85
: field?.AsString())
86
.ToList();
87
- Assert.Equal(@"c\d.exe", fields[(int)WixBundlePayloadSymbolFields.Name]);
87
+ Assert.Equal(@"c\d\e\f.exe", fields[(int)WixBundlePayloadSymbolFields.Name]);
88
}
89
}
90
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Payload/CanonicalizeName.wxs
+1
-1
@@ -1,7 +1,7 @@
1
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
<Fragment>
3
<PayloadGroup Id="CanonicalizeName">
4
- <Payload SourceFile="dir\file.ext" Name="a\..\c\.\d.exe" />
4
+ <Payload SourceFile="dir\file.ext" Name="a\..\c\.\d/e\\f.exe" />
5
</PayloadGroup>
6
</Fragment>
7
</Wix>