main
cpp 435 lines 13 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5
6 // Exit macros
7 #define PathExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
8 #define PathExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PATHUTIL, x, s, __VA_ARGS__)
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__)
16 #define PathExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PATHUTIL, p, x, e, s, __VA_ARGS__)
17 #define PathExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PATHUTIL, p, x, s, __VA_ARGS__)
18 #define PathExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PATHUTIL, e, x, s, __VA_ARGS__)
19 #define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__)
20
21
22 typedef HRESULT(WINAPI* PFN_PATH_ALLOC_CANONICALIZE)(
23 __in LPCWSTR wzSource,
24 __in DWORD dwFlags,
25 __out_z LPWSTR* psczPathOut
26 );
27
28 static BOOL vfInitialized = FALSE;
29 static HMODULE vhPathApiSet_1_1_0 = NULL;
30 static PFN_PATH_ALLOC_CANONICALIZE vpfnPathAllocCanonicalize = NULL;
31 static BOOL vfForceFallback = FALSE;
32
33 // from PathCch.h
34 #ifndef PATHCCH_ALLOW_LONG_PATHS
35 #define PATHCCH_ALLOW_LONG_PATHS 0x01
36 #endif
37
38 static HRESULT Initialize()
39 {
40 HRESULT hr = S_OK;
41 DWORD er = ERROR_SUCCESS;
42
43 if (vfInitialized)
44 {
45 ExitFunction();
46 }
47
48 hr = LoadSystemApiSet(L"api-ms-win-core-path-l1-1-0.dll", &vhPathApiSet_1_1_0);
49 if (E_MODNOTFOUND == hr)
50 {
51 hr = E_NOTIMPL;
52 }
53 PathExitOnFailure(hr, "Failed to load api-ms-win-core-path-l1-1-0.dll");
54
55 vpfnPathAllocCanonicalize = reinterpret_cast<PFN_PATH_ALLOC_CANONICALIZE>(::GetProcAddress(vhPathApiSet_1_1_0, "PathAllocCanonicalize"));
56 if (!vpfnPathAllocCanonicalize)
57 {
58 er = ::GetLastError();
59 PathExitWithRootFailure(hr, ERROR_PROC_NOT_FOUND == er ? E_NOTIMPL : HRESULT_FROM_WIN32(er), "Failed to get address of PathAllocCanonicalize.");
60 }
61
62 vfInitialized = TRUE;
63
64 LExit:
65 return hr;
66 }
67
68
69 DAPI_(void) Path2FunctionAllowFallback()
70 {
71 vfForceFallback = FALSE;
72 }
73
74
75 DAPI_(void) Path2FunctionForceFallback()
76 {
77 vfForceFallback = TRUE;
78 }
79
80
81 DAPI_(HRESULT) PathCanonicalizePath(
82 __in_z LPCWSTR wzPath,
83 __deref_out_z LPWSTR* psczCanonicalized
84 )
85 {
86 HRESULT hr = S_OK;
87 int cch = MAX_PATH + 1;
88
89 hr = StrAlloc(psczCanonicalized, cch);
90 PathExitOnFailure(hr, "Failed to allocate string for the canonicalized path.");
91
92 if (::PathCanonicalizeW(*psczCanonicalized, wzPath))
93 {
94 hr = S_OK;
95 }
96 else
97 {
98 ExitFunctionWithLastError(hr);
99 }
100
101 LExit:
102 return hr;
103 }
104
105 DAPI_(HRESULT) PathAllocCanonicalizePath(
106 __in_z LPCWSTR wzPath,
107 __in DWORD dwFlags,
108 __deref_out_z LPWSTR* psczCanonicalized
109 )
110 {
111 HRESULT hr = S_OK;
112 LPWSTR sczCanonicalizedPath = NULL;
113
114 hr = Initialize();
115 if (E_NOTIMPL == hr || SUCCEEDED(hr) && !vpfnPathAllocCanonicalize)
116 {
117 ExitFunction1(hr = E_NOTIMPL);
118 }
119 PathExitOnFailure(hr, "Failed to initialize path2utl.");
120
121 hr = vpfnPathAllocCanonicalize(wzPath, dwFlags, &sczCanonicalizedPath);
122 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzPath);
123
124 hr = StrAllocString(psczCanonicalized, sczCanonicalizedPath, 0);
125 PathExitOnFailure(hr, "Failed to copy the canonicalized path.");
126
127 LExit:
128 if (sczCanonicalizedPath)
129 {
130 ::LocalFree(sczCanonicalizedPath);
131 }
132
133 return hr;
134 }
135
136 DAPI_(HRESULT) PathCanonicalizeForComparison(
137 __in_z LPCWSTR wzPath,
138 __in DWORD dwCanonicalizeFlags,
139 __deref_out_z LPWSTR* psczCanonicalized
140 )
141 {
142 HRESULT hr = S_OK;
143 LPWSTR sczNormalizedPath = NULL;
144 LPCWSTR wzNormalizedPath = NULL;
145 SIZE_T cchUncRootLength = 0;
146 BOOL fHasPrefix = FALSE;
147
148 hr = StrAllocString(&sczNormalizedPath, wzPath, 0);
149 PathExitOnFailure(hr, "Failed to allocate string for the normalized path.");
150
151 PathFixedNormalizeSlashes(sczNormalizedPath);
152
153 wzNormalizedPath = sczNormalizedPath;
154
155 if (PATH_CANONICALIZE_KEEP_UNC_ROOT & dwCanonicalizeFlags)
156 {
157 BOOL fUNC = FALSE;
158 LPCWSTR wzPastRoot = PathSkipPastRoot(sczNormalizedPath, NULL, NULL, &fUNC);
159 if (fUNC)
160 {
161 wzNormalizedPath = wzPastRoot;
162 cchUncRootLength = wzPastRoot - sczNormalizedPath;
163 }
164 }
165
166 if (*wzNormalizedPath)
167 {
168 if (!vfForceFallback)
169 {
170 hr = PathAllocCanonicalizePath(wzNormalizedPath, PATHCCH_ALLOW_LONG_PATHS, psczCanonicalized);
171 }
172 else
173 {
174 hr = E_NOTIMPL;
175 }
176
177 if (E_NOTIMPL == hr)
178 {
179 hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized);
180 }
181 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath);
182 }
183 else
184 {
185 Assert(cchUncRootLength);
186 ReleaseStr(*psczCanonicalized);
187 *psczCanonicalized = sczNormalizedPath;
188 sczNormalizedPath = NULL;
189 cchUncRootLength = 0;
190 }
191
192 if (cchUncRootLength)
193 {
194 hr = StrAllocPrefix(psczCanonicalized, sczNormalizedPath, cchUncRootLength);
195 PathExitOnFailure(hr, "Failed to prefix the UNC root to the canonicalized path.");
196 }
197
198 if (PATH_CANONICALIZE_BACKSLASH_TERMINATE & dwCanonicalizeFlags)
199 {
200 hr = PathBackslashTerminate(psczCanonicalized);
201 PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path.");
202 }
203
204 if (PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX & dwCanonicalizeFlags)
205 {
206 hr = PathPrefix(psczCanonicalized, 0, PATH_PREFIX_SHORT_PATHS);
207 PathExitOnFailure(hr, "Failed to ensure the extended path prefix on the canonicalized path.");
208 }
209
210 PathSkipPastRoot(*psczCanonicalized, &fHasPrefix, NULL, NULL);
211
212 if (fHasPrefix)
213 {
214 // Canonicalize prefix into \\?\.
215 (*psczCanonicalized)[0] = L'\\';
216 (*psczCanonicalized)[1] = L'\\';
217 (*psczCanonicalized)[2] = L'?';
218 (*psczCanonicalized)[3] = L'\\';
219 }
220
221 LExit:
222 ReleaseStr(sczNormalizedPath);
223
224 return hr;
225 }
226
227 DAPI_(HRESULT) PathConcatRelativeToBase(
228 __in LPCWSTR wzBase,
229 __in_opt LPCWSTR wzRelative,
230 __deref_out_z LPWSTR* psczCombined
231 )
232 {
233 HRESULT hr = S_OK;
234 LPWSTR sczCanonicalizedRelative = NULL;
235
236 if (!wzBase || !*wzBase)
237 {
238 PathExitWithRootFailure(hr, E_INVALIDARG, "wzBase is required.");
239 }
240
241 if (PathIsRooted(wzRelative))
242 {
243 PathExitWithRootFailure(hr, E_INVALIDARG, "wzRelative cannot be rooted.");
244 }
245
246 hr = StrAllocString(psczCombined, wzBase, 0);
247 PathExitOnFailure(hr, "Failed to copy base to output.");
248
249 if (wzRelative && *wzRelative)
250 {
251 hr = PathBackslashTerminate(psczCombined);
252 PathExitOnFailure(hr, "Failed to backslashify.");
253
254 hr = PathCanonicalizeForComparison(wzRelative, 0, &sczCanonicalizedRelative);
255 PathExitOnFailure(hr, "Failed to canonicalize wzRelative.");
256
257 hr = StrAllocConcat(psczCombined, sczCanonicalizedRelative, 0);
258 PathExitOnFailure(hr, "Failed to append relative to output.");
259 }
260
261 LExit:
262 ReleaseStr(sczCanonicalizedRelative);
263
264 return hr;
265 }
266
267 DAPI_(HRESULT) PathConcatRelativeToFullyQualifiedBase(
268 __in LPCWSTR wzBase,
269 __in_opt LPCWSTR wzRelative,
270 __deref_out_z LPWSTR* psczCombined
271 )
272 {
273 HRESULT hr = S_OK;
274
275 if (!PathIsFullyQualified(wzBase))
276 {
277 PathExitWithRootFailure(hr, E_INVALIDARG, "wzBase must be fully qualified: %ls.", wzBase);
278 }
279
280 hr = PathConcatRelativeToBase(wzBase, wzRelative, psczCombined);
281
282 LExit:
283 return hr;
284 }
285
286 DAPI_(HRESULT) PathCompareCanonicalized(
287 __in_z LPCWSTR wzPath1,
288 __in_z LPCWSTR wzPath2,
289 __out BOOL* pfEqual
290 )
291 {
292 HRESULT hr = S_OK;
293 LPWSTR sczCanonicalized1 = NULL;
294 LPWSTR sczCanonicalized2 = NULL;
295 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
296 int nResult = 0;
297
298 if (!wzPath1 || !wzPath2)
299 {
300 PathExitWithRootFailure(hr, E_INVALIDARG, "Both paths are required.");
301 }
302
303 hr = PathCanonicalizeForComparison(wzPath1, dwDefaultFlags, &sczCanonicalized1);
304 PathExitOnFailure(hr, "Failed to canonicalize wzPath1.");
305
306 hr = PathCanonicalizeForComparison(wzPath2, dwDefaultFlags, &sczCanonicalized2);
307 PathExitOnFailure(hr, "Failed to canonicalize wzPath2.");
308
309 nResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalized1, -1, sczCanonicalized2, -1);
310 PathExitOnNullWithLastError(nResult, hr, "Failed to compare canonicalized paths.");
311
312 *pfEqual = CSTR_EQUAL == nResult;
313
314 LExit:
315 ReleaseStr(sczCanonicalized1);
316 ReleaseStr(sczCanonicalized2);
317 return hr;
318 }
319
320 DAPI_(HRESULT) PathDirectoryContainsPath(
321 __in_z LPCWSTR wzDirectory,
322 __in_z LPCWSTR wzPath
323 )
324 {
325 HRESULT hr = S_OK;
326 LPWSTR sczCanonicalizedDirectory = NULL;
327 LPWSTR sczCanonicalizedPath = NULL;
328 DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_EXTENDED_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT;
329 size_t cchDirectory = 0;
330 size_t cchPath = 0;
331
332 if (!wzDirectory || !*wzDirectory)
333 {
334 PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory is required.");
335 }
336 if (!wzPath || !*wzPath)
337 {
338 PathExitWithRootFailure(hr, E_INVALIDARG, "wzPath is required.");
339 }
340
341 hr = PathCanonicalizeForComparison(wzDirectory, dwDefaultFlags | PATH_CANONICALIZE_BACKSLASH_TERMINATE, &sczCanonicalizedDirectory);
342 PathExitOnFailure(hr, "Failed to canonicalize the directory.");
343
344 hr = PathCanonicalizeForComparison(wzPath, dwDefaultFlags, &sczCanonicalizedPath);
345 PathExitOnFailure(hr, "Failed to canonicalize the path.");
346
347 if (!PathIsFullyQualified(sczCanonicalizedDirectory))
348 {
349 PathExitWithRootFailure(hr, E_INVALIDARG, "wzDirectory must be a fully qualified path.");
350 }
351 if (!sczCanonicalizedPath || !*sczCanonicalizedPath)
352 {
353 ExitFunction1(hr = S_FALSE);
354 }
355
356 hr = ::StringCchLengthW(sczCanonicalizedDirectory, STRSAFE_MAX_CCH, &cchDirectory);
357 PathExitOnFailure(hr, "Failed to get length of canonicalized directory.");
358
359 hr = ::StringCchLengthW(sczCanonicalizedPath, STRSAFE_MAX_CCH, &cchPath);
360 PathExitOnFailure(hr, "Failed to get length of canonicalized path.");
361
362 if (cchPath <= cchDirectory)
363 {
364 ExitFunction1(hr = S_FALSE);
365 }
366
367 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalizedDirectory, (DWORD)cchDirectory, sczCanonicalizedPath, (DWORD)cchDirectory))
368 {
369 ExitFunction1(hr = S_FALSE);
370 }
371
372 hr = S_OK;
373
374 LExit:
375 ReleaseStr(sczCanonicalizedPath);
376 ReleaseStr(sczCanonicalizedDirectory);
377 return hr;
378 }
379
380
381 DAPI_(HRESULT) PathSystemWindowsSubdirectory(
382 __in_z_opt LPCWSTR wzSubdirectory,
383 __out_z LPWSTR* psczFullPath
384 )
385 {
386 HRESULT hr = S_OK;
387 LPWSTR sczWindowsPath = NULL;
388 DWORD cchBuffer = MAX_PATH + 1;
389 DWORD cch = 0;
390
391 hr = StrAlloc(&sczWindowsPath, cchBuffer);
392 PathExitOnFailure(hr, "Failed to alloc Windows directory path.");
393
394 cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer);
395 PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with default size.");
396
397 cch += 1; // add 1 for null terminator.
398
399 if (cch > cchBuffer)
400 {
401 hr = StrAlloc(&sczWindowsPath, cch);
402 PathExitOnFailure(hr, "Failed to realloc Windows directory path.");
403
404 cchBuffer = cch;
405
406 cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer);
407 PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with returned size.");
408
409 cch += 1; // add 1 for null terminator.
410
411 if (cch > cchBuffer)
412 {
413 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get Windows directory path with returned size.");
414 }
415 }
416
417 if (wzSubdirectory)
418 {
419 hr = PathConcatRelativeToBase(sczWindowsPath, wzSubdirectory, psczFullPath);
420 PathExitOnFailure(hr, "Failed to concat subdirectory on Windows directory path.");
421 }
422 else
423 {
424 *psczFullPath = sczWindowsPath;
425 sczWindowsPath = NULL;
426 }
427
428 hr = PathBackslashTerminate(psczFullPath);
429 PathExitOnFailure(hr, "Failed to terminate Windows directory path with backslash.");
430
431 LExit:
432 ReleaseStr(sczWindowsPath);
433
434 return hr;
435 }