diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/dirutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/dirutil.cpp | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp new file mode 100644 index 00000000..ae2c5e1c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp | |||
@@ -0,0 +1,441 @@ | |||
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 DirExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
8 | #define DirExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
9 | #define DirExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
10 | #define DirExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
11 | #define DirExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
12 | #define DirExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DIRUTIL, x, s, __VA_ARGS__) | ||
13 | #define DirExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) | ||
14 | #define DirExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) | ||
15 | #define DirExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DIRUTIL, p, x, e, s, __VA_ARGS__) | ||
16 | #define DirExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DIRUTIL, p, x, s, __VA_ARGS__) | ||
17 | #define DirExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DIRUTIL, e, x, s, __VA_ARGS__) | ||
18 | #define DirExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DIRUTIL, g, x, s, __VA_ARGS__) | ||
19 | |||
20 | |||
21 | /******************************************************************* | ||
22 | DirExists | ||
23 | |||
24 | *******************************************************************/ | ||
25 | extern "C" BOOL DAPI DirExists( | ||
26 | __in_z LPCWSTR wzPath, | ||
27 | __out_opt DWORD *pdwAttributes | ||
28 | ) | ||
29 | { | ||
30 | Assert(wzPath); | ||
31 | |||
32 | BOOL fExists = FALSE; | ||
33 | |||
34 | DWORD dwAttributes = ::GetFileAttributesW(wzPath); | ||
35 | if (0xFFFFFFFF == dwAttributes) // TODO: figure out why "INVALID_FILE_ATTRIBUTES" can't be used here | ||
36 | { | ||
37 | ExitFunction(); | ||
38 | } | ||
39 | |||
40 | if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) | ||
41 | { | ||
42 | if (pdwAttributes) | ||
43 | { | ||
44 | *pdwAttributes = dwAttributes; | ||
45 | } | ||
46 | |||
47 | fExists = TRUE; | ||
48 | } | ||
49 | |||
50 | LExit: | ||
51 | return fExists; | ||
52 | } | ||
53 | |||
54 | |||
55 | /******************************************************************* | ||
56 | DirCreateTempPath | ||
57 | |||
58 | *******************************************************************/ | ||
59 | extern "C" HRESULT DAPI DirCreateTempPath( | ||
60 | __in_z LPCWSTR wzPrefix, | ||
61 | __out_ecount_z(cchPath) LPWSTR wzPath, | ||
62 | __in DWORD cchPath | ||
63 | ) | ||
64 | { | ||
65 | Assert(wzPrefix); | ||
66 | Assert(wzPath); | ||
67 | |||
68 | HRESULT hr = S_OK; | ||
69 | |||
70 | WCHAR wzDir[MAX_PATH]; | ||
71 | WCHAR wzFile[MAX_PATH]; | ||
72 | DWORD cch = 0; | ||
73 | |||
74 | cch = ::GetTempPathW(countof(wzDir), wzDir); | ||
75 | if (!cch || cch >= countof(wzDir)) | ||
76 | { | ||
77 | DirExitWithLastError(hr, "Failed to GetTempPath."); | ||
78 | } | ||
79 | |||
80 | if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile)) | ||
81 | { | ||
82 | DirExitWithLastError(hr, "Failed to GetTempFileName."); | ||
83 | } | ||
84 | |||
85 | hr = ::StringCchCopyW(wzPath, cchPath, wzFile); | ||
86 | |||
87 | LExit: | ||
88 | return hr; | ||
89 | } | ||
90 | |||
91 | |||
92 | /******************************************************************* | ||
93 | DirEnsureExists | ||
94 | |||
95 | *******************************************************************/ | ||
96 | extern "C" HRESULT DAPI DirEnsureExists( | ||
97 | __in_z LPCWSTR wzPath, | ||
98 | __in_opt LPSECURITY_ATTRIBUTES psa | ||
99 | ) | ||
100 | { | ||
101 | HRESULT hr = S_OK; | ||
102 | UINT er; | ||
103 | |||
104 | // try to create this directory | ||
105 | if (!::CreateDirectoryW(wzPath, psa)) | ||
106 | { | ||
107 | // if the directory already exists, bail | ||
108 | er = ::GetLastError(); | ||
109 | if (ERROR_ALREADY_EXISTS == er) | ||
110 | { | ||
111 | ExitFunction1(hr = S_OK); | ||
112 | } | ||
113 | else if (ERROR_PATH_NOT_FOUND != er && DirExists(wzPath, NULL)) // if the directory happens to exist (don't check if CreateDirectory said it doesn't), declare success. | ||
114 | { | ||
115 | ExitFunction1(hr = S_OK); | ||
116 | } | ||
117 | |||
118 | // get the parent path and try to create it | ||
119 | LPWSTR pwzLastSlash = NULL; | ||
120 | for (LPWSTR pwz = const_cast<LPWSTR>(wzPath); *pwz; ++pwz) | ||
121 | { | ||
122 | if (*pwz == L'\\') | ||
123 | { | ||
124 | pwzLastSlash = pwz; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | // if there is no parent directory fail | ||
129 | DirExitOnNullDebugTrace(pwzLastSlash, hr, HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), "cannot find parent path"); | ||
130 | |||
131 | *pwzLastSlash = L'\0'; // null terminate the parent path | ||
132 | hr = DirEnsureExists(wzPath, psa); // recurse! | ||
133 | *pwzLastSlash = L'\\'; // put the slash back | ||
134 | DirExitOnFailureDebugTrace(hr, "failed to create path: %ls", wzPath); | ||
135 | |||
136 | // try to create the directory now that all parents are created | ||
137 | if (!::CreateDirectoryW(wzPath, psa)) | ||
138 | { | ||
139 | // if the directory already exists for some reason no error | ||
140 | er = ::GetLastError(); | ||
141 | if (ERROR_ALREADY_EXISTS == er) | ||
142 | { | ||
143 | hr = S_FALSE; | ||
144 | } | ||
145 | else | ||
146 | { | ||
147 | hr = HRESULT_FROM_WIN32(er); | ||
148 | } | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | hr = S_OK; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | LExit: | ||
157 | return hr; | ||
158 | } | ||
159 | |||
160 | |||
161 | /******************************************************************* | ||
162 | DirEnsureDelete - removes an entire directory structure | ||
163 | |||
164 | *******************************************************************/ | ||
165 | extern "C" HRESULT DAPI DirEnsureDelete( | ||
166 | __in_z LPCWSTR wzPath, | ||
167 | __in BOOL fDeleteFiles, | ||
168 | __in BOOL fRecurse | ||
169 | ) | ||
170 | { | ||
171 | HRESULT hr = S_OK; | ||
172 | DWORD dwDeleteFlags = 0; | ||
173 | |||
174 | dwDeleteFlags |= fDeleteFiles ? DIR_DELETE_FILES : 0; | ||
175 | dwDeleteFlags |= fRecurse ? DIR_DELETE_RECURSE : 0; | ||
176 | |||
177 | hr = DirEnsureDeleteEx(wzPath, dwDeleteFlags); | ||
178 | return hr; | ||
179 | } | ||
180 | |||
181 | |||
182 | /******************************************************************* | ||
183 | DirEnsureDeleteEx - removes an entire directory structure | ||
184 | |||
185 | *******************************************************************/ | ||
186 | extern "C" HRESULT DAPI DirEnsureDeleteEx( | ||
187 | __in_z LPCWSTR wzPath, | ||
188 | __in DWORD dwFlags | ||
189 | ) | ||
190 | { | ||
191 | Assert(wzPath && *wzPath); | ||
192 | |||
193 | HRESULT hr = S_OK; | ||
194 | DWORD er; | ||
195 | |||
196 | DWORD dwAttrib; | ||
197 | HANDLE hFind = INVALID_HANDLE_VALUE; | ||
198 | LPWSTR sczDelete = NULL; | ||
199 | WIN32_FIND_DATAW wfd; | ||
200 | |||
201 | BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); | ||
202 | BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); | ||
203 | BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); | ||
204 | WCHAR wzTempDirectory[MAX_PATH] = { }; | ||
205 | WCHAR wzTempPath[MAX_PATH] = { }; | ||
206 | |||
207 | if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) | ||
208 | { | ||
209 | er = ::GetLastError(); | ||
210 | if (ERROR_FILE_NOT_FOUND == er) // change "file not found" to "path not found" since we were looking for a directory. | ||
211 | { | ||
212 | er = ERROR_PATH_NOT_FOUND; | ||
213 | } | ||
214 | hr = HRESULT_FROM_WIN32(er); | ||
215 | DirExitOnRootFailure(hr, "Failed to get attributes for path: %ls", wzPath); | ||
216 | } | ||
217 | |||
218 | if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) | ||
219 | { | ||
220 | if (dwAttrib & FILE_ATTRIBUTE_READONLY) | ||
221 | { | ||
222 | if (!::SetFileAttributesW(wzPath, FILE_ATTRIBUTE_NORMAL)) | ||
223 | { | ||
224 | DirExitWithLastError(hr, "Failed to remove read-only attribute from path: %ls", wzPath); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | // If we're deleting files and/or child directories loop through the contents of the directory. | ||
229 | if (fDeleteFiles || fRecurse) | ||
230 | { | ||
231 | if (fScheduleDelete) | ||
232 | { | ||
233 | if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) | ||
234 | { | ||
235 | DirExitWithLastError(hr, "Failed to get temp directory."); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | // Delete everything in this directory. | ||
240 | hr = PathConcat(wzPath, L"*.*", &sczDelete); | ||
241 | DirExitOnFailure(hr, "Failed to concat wild cards to string: %ls", wzPath); | ||
242 | |||
243 | hFind = ::FindFirstFileW(sczDelete, &wfd); | ||
244 | if (INVALID_HANDLE_VALUE == hFind) | ||
245 | { | ||
246 | DirExitWithLastError(hr, "failed to get first file in directory: %ls", wzPath); | ||
247 | } | ||
248 | |||
249 | do | ||
250 | { | ||
251 | // Skip the dot directories. | ||
252 | if (L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2]))) | ||
253 | { | ||
254 | continue; | ||
255 | } | ||
256 | |||
257 | // For extra safety and to silence OACR. | ||
258 | wfd.cFileName[MAX_PATH - 1] = L'\0'; | ||
259 | |||
260 | hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); | ||
261 | DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); | ||
262 | |||
263 | if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) | ||
264 | { | ||
265 | hr = PathBackslashTerminate(&sczDelete); | ||
266 | DirExitOnFailure(hr, "Failed to ensure path is backslash terminated: %ls", sczDelete); | ||
267 | |||
268 | hr = DirEnsureDeleteEx(sczDelete, dwFlags); // recursive call | ||
269 | if (FAILED(hr)) | ||
270 | { | ||
271 | // if we failed to delete a subdirectory, keep trying to finish any remaining files | ||
272 | ExitTraceSource(DUTIL_SOURCE_DIRUTIL, hr, "Failed to delete subdirectory; continuing: %ls", sczDelete); | ||
273 | hr = S_OK; | ||
274 | } | ||
275 | } | ||
276 | else if (fDeleteFiles) // this is a file, just delete it | ||
277 | { | ||
278 | if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY || wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN || wfd.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) | ||
279 | { | ||
280 | if (!::SetFileAttributesW(sczDelete, FILE_ATTRIBUTE_NORMAL)) | ||
281 | { | ||
282 | DirExitWithLastError(hr, "Failed to remove attributes from file: %ls", sczDelete); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | if (!::DeleteFileW(sczDelete)) | ||
287 | { | ||
288 | if (fScheduleDelete) | ||
289 | { | ||
290 | if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) | ||
291 | { | ||
292 | DirExitWithLastError(hr, "Failed to get temp file to move to."); | ||
293 | } | ||
294 | |||
295 | // Try to move the file to the temp directory then schedule for delete, | ||
296 | // otherwise just schedule for delete. | ||
297 | if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) | ||
298 | { | ||
299 | ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); | ||
300 | } | ||
301 | else | ||
302 | { | ||
303 | ::MoveFileExW(sczDelete, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); | ||
304 | } | ||
305 | } | ||
306 | else | ||
307 | { | ||
308 | DirExitWithLastError(hr, "Failed to delete file: %ls", sczDelete); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | } while (::FindNextFileW(hFind, &wfd)); | ||
313 | |||
314 | er = ::GetLastError(); | ||
315 | if (ERROR_NO_MORE_FILES == er) | ||
316 | { | ||
317 | hr = S_OK; | ||
318 | } | ||
319 | else | ||
320 | { | ||
321 | DirExitWithLastError(hr, "Failed while looping through files in directory: %ls", wzPath); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | if (!::RemoveDirectoryW(wzPath)) | ||
326 | { | ||
327 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
328 | if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == hr && fScheduleDelete) | ||
329 | { | ||
330 | if (::MoveFileExW(wzPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)) | ||
331 | { | ||
332 | hr = S_OK; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | DirExitOnRootFailure(hr, "Failed to remove directory: %ls", wzPath); | ||
337 | } | ||
338 | } | ||
339 | else | ||
340 | { | ||
341 | hr = E_UNEXPECTED; | ||
342 | DirExitOnFailure(hr, "Directory delete cannot delete file: %ls", wzPath); | ||
343 | } | ||
344 | |||
345 | Assert(S_OK == hr); | ||
346 | |||
347 | LExit: | ||
348 | ReleaseFileFindHandle(hFind); | ||
349 | ReleaseStr(sczDelete); | ||
350 | |||
351 | return hr; | ||
352 | } | ||
353 | |||
354 | |||
355 | /******************************************************************* | ||
356 | DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many | ||
357 | of its parents as possible. | ||
358 | |||
359 | Returns: count of directories deleted. | ||
360 | *******************************************************************/ | ||
361 | extern "C" DWORD DAPI DirDeleteEmptyDirectoriesToRoot( | ||
362 | __in_z LPCWSTR wzPath, | ||
363 | __in DWORD /*dwFlags*/ | ||
364 | ) | ||
365 | { | ||
366 | DWORD cDeletedDirs = 0; | ||
367 | LPWSTR sczPath = NULL; | ||
368 | |||
369 | while (wzPath && *wzPath && ::RemoveDirectoryW(wzPath)) | ||
370 | { | ||
371 | ++cDeletedDirs; | ||
372 | |||
373 | HRESULT hr = PathGetParentPath(wzPath, &sczPath); | ||
374 | DirExitOnFailure(hr, "Failed to get parent directory for path: %ls", wzPath); | ||
375 | |||
376 | wzPath = sczPath; | ||
377 | } | ||
378 | |||
379 | LExit: | ||
380 | ReleaseStr(sczPath); | ||
381 | |||
382 | return cDeletedDirs; | ||
383 | } | ||
384 | |||
385 | |||
386 | /******************************************************************* | ||
387 | DirGetCurrent - gets the current directory. | ||
388 | |||
389 | *******************************************************************/ | ||
390 | extern "C" HRESULT DAPI DirGetCurrent( | ||
391 | __deref_out_z LPWSTR* psczCurrentDirectory | ||
392 | ) | ||
393 | { | ||
394 | HRESULT hr = S_OK; | ||
395 | SIZE_T cch = 0; | ||
396 | |||
397 | if (psczCurrentDirectory && *psczCurrentDirectory) | ||
398 | { | ||
399 | hr = StrMaxLength(*psczCurrentDirectory, &cch); | ||
400 | DirExitOnFailure(hr, "Failed to determine size of current directory."); | ||
401 | } | ||
402 | |||
403 | DWORD cchRequired = ::GetCurrentDirectoryW((DWORD)min(DWORD_MAX, cch), 0 == cch ? NULL : *psczCurrentDirectory); | ||
404 | if (0 == cchRequired) | ||
405 | { | ||
406 | DirExitWithLastError(hr, "Failed to get current directory."); | ||
407 | } | ||
408 | else if (cch < cchRequired) | ||
409 | { | ||
410 | hr = StrAlloc(psczCurrentDirectory, cchRequired); | ||
411 | DirExitOnFailure(hr, "Failed to allocate string for current directory."); | ||
412 | |||
413 | if (!::GetCurrentDirectoryW(cchRequired, *psczCurrentDirectory)) | ||
414 | { | ||
415 | DirExitWithLastError(hr, "Failed to get current directory using allocated string."); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | LExit: | ||
420 | return hr; | ||
421 | } | ||
422 | |||
423 | |||
424 | /******************************************************************* | ||
425 | DirSetCurrent - sets the current directory. | ||
426 | |||
427 | *******************************************************************/ | ||
428 | extern "C" HRESULT DAPI DirSetCurrent( | ||
429 | __in_z LPCWSTR wzDirectory | ||
430 | ) | ||
431 | { | ||
432 | HRESULT hr = S_OK; | ||
433 | |||
434 | if (!::SetCurrentDirectoryW(wzDirectory)) | ||
435 | { | ||
436 | DirExitWithLastError(hr, "Failed to set current directory to: %ls", wzDirectory); | ||
437 | } | ||
438 | |||
439 | LExit: | ||
440 | return hr; | ||
441 | } | ||