aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/dirutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dirutil.cpp441
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*******************************************************************/
25extern "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
50LExit:
51 return fExists;
52}
53
54
55/*******************************************************************
56 DirCreateTempPath
57
58 *******************************************************************/
59extern "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
87LExit:
88 return hr;
89}
90
91
92/*******************************************************************
93 DirEnsureExists
94
95*******************************************************************/
96extern "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
156LExit:
157 return hr;
158}
159
160
161/*******************************************************************
162 DirEnsureDelete - removes an entire directory structure
163
164*******************************************************************/
165extern "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*******************************************************************/
186extern "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
347LExit:
348 ReleaseFileFindHandle(hFind);
349 ReleaseStr(sczDelete);
350
351 return hr;
352}
353
354
355/*******************************************************************
356DirDeleteEmptyDirectoriesToRoot - removes an empty directory and as many
357 of its parents as possible.
358
359 Returns: count of directories deleted.
360*******************************************************************/
361extern "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
379LExit:
380 ReleaseStr(sczPath);
381
382 return cDeletedDirs;
383}
384
385
386/*******************************************************************
387 DirGetCurrent - gets the current directory.
388
389*******************************************************************/
390extern "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
419LExit:
420 return hr;
421}
422
423
424/*******************************************************************
425 DirSetCurrent - sets the current directory.
426
427*******************************************************************/
428extern "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
439LExit:
440 return hr;
441}