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