diff options
Diffstat (limited to 'src/dutil/fileutil.cpp')
-rw-r--r-- | src/dutil/fileutil.cpp | 1860 |
1 files changed, 1860 insertions, 0 deletions
diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp new file mode 100644 index 00000000..8666da65 --- /dev/null +++ b/src/dutil/fileutil.cpp | |||
@@ -0,0 +1,1860 @@ | |||
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 | // constants | ||
6 | |||
7 | const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; | ||
8 | const BYTE UTF16BOM[] = {0xFF, 0xFE}; | ||
9 | |||
10 | const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; | ||
11 | const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; | ||
12 | |||
13 | /******************************************************************* | ||
14 | FileFromPath - returns a pointer to the file part of the path | ||
15 | |||
16 | ********************************************************************/ | ||
17 | extern "C" LPWSTR DAPI FileFromPath( | ||
18 | __in LPCWSTR wzPath | ||
19 | ) | ||
20 | { | ||
21 | if (!wzPath) | ||
22 | return NULL; | ||
23 | |||
24 | LPWSTR wzFile = const_cast<LPWSTR>(wzPath); | ||
25 | for (LPWSTR wz = wzFile; *wz; ++wz) | ||
26 | { | ||
27 | // valid delineators | ||
28 | // \ => Windows path | ||
29 | // / => unix and URL path | ||
30 | // : => relative path from mapped root | ||
31 | if (L'\\' == *wz || L'/' == *wz || L':' == *wz) | ||
32 | wzFile = wz + 1; | ||
33 | } | ||
34 | |||
35 | return wzFile; | ||
36 | } | ||
37 | |||
38 | |||
39 | /******************************************************************* | ||
40 | FileResolvePath - gets the full path to a file resolving environment | ||
41 | variables along the way. | ||
42 | |||
43 | ********************************************************************/ | ||
44 | extern "C" HRESULT DAPI FileResolvePath( | ||
45 | __in LPCWSTR wzRelativePath, | ||
46 | __out LPWSTR *ppwzFullPath | ||
47 | ) | ||
48 | { | ||
49 | Assert(wzRelativePath && *wzRelativePath); | ||
50 | |||
51 | HRESULT hr = S_OK; | ||
52 | DWORD cch = 0; | ||
53 | LPWSTR pwzExpandedPath = NULL; | ||
54 | DWORD cchExpandedPath = 0; | ||
55 | |||
56 | LPWSTR pwzFullPath = NULL; | ||
57 | DWORD cchFullPath = 0; | ||
58 | |||
59 | LPWSTR wzFileName = NULL; | ||
60 | |||
61 | // | ||
62 | // First, expand any environment variables. | ||
63 | // | ||
64 | cchExpandedPath = MAX_PATH; | ||
65 | hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); | ||
66 | ExitOnFailure(hr, "Failed to allocate space for expanded path."); | ||
67 | |||
68 | cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); | ||
69 | if (0 == cch) | ||
70 | { | ||
71 | ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); | ||
72 | } | ||
73 | else if (cchExpandedPath < cch) | ||
74 | { | ||
75 | cchExpandedPath = cch; | ||
76 | hr = StrAlloc(&pwzExpandedPath, cchExpandedPath); | ||
77 | ExitOnFailure(hr, "Failed to re-allocate more space for expanded path."); | ||
78 | |||
79 | cch = ::ExpandEnvironmentStringsW(wzRelativePath, pwzExpandedPath, cchExpandedPath); | ||
80 | if (0 == cch) | ||
81 | { | ||
82 | ExitWithLastError(hr, "Failed to expand environment variables in string: %ls", wzRelativePath); | ||
83 | } | ||
84 | else if (cchExpandedPath < cch) | ||
85 | { | ||
86 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
87 | ExitOnRootFailure(hr, "Failed to allocate buffer for expanded path."); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | // | ||
92 | // Second, get the full path. | ||
93 | // | ||
94 | cchFullPath = MAX_PATH; | ||
95 | hr = StrAlloc(&pwzFullPath, cchFullPath); | ||
96 | ExitOnFailure(hr, "Failed to allocate space for full path."); | ||
97 | |||
98 | cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); | ||
99 | if (0 == cch) | ||
100 | { | ||
101 | ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); | ||
102 | } | ||
103 | else if (cchFullPath < cch) | ||
104 | { | ||
105 | cchFullPath = cch; | ||
106 | hr = StrAlloc(&pwzFullPath, cchFullPath); | ||
107 | ExitOnFailure(hr, "Failed to re-allocate more space for full path."); | ||
108 | |||
109 | cch = ::GetFullPathNameW(pwzExpandedPath, cchFullPath, pwzFullPath, &wzFileName); | ||
110 | if (0 == cch) | ||
111 | { | ||
112 | ExitWithLastError(hr, "Failed to get full path for string: %ls", pwzExpandedPath); | ||
113 | } | ||
114 | else if (cchFullPath < cch) | ||
115 | { | ||
116 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
117 | ExitOnRootFailure(hr, "Failed to allocate buffer for full path."); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | *ppwzFullPath = pwzFullPath; | ||
122 | pwzFullPath = NULL; | ||
123 | |||
124 | LExit: | ||
125 | ReleaseStr(pwzFullPath); | ||
126 | ReleaseStr(pwzExpandedPath); | ||
127 | |||
128 | return hr; | ||
129 | } | ||
130 | |||
131 | |||
132 | /******************************************************************* | ||
133 | FileStripExtension - Strip extension from filename | ||
134 | ********************************************************************/ | ||
135 | extern "C" HRESULT DAPI FileStripExtension( | ||
136 | __in LPCWSTR wzFileName, | ||
137 | __out LPWSTR *ppwzFileNameNoExtension | ||
138 | ) | ||
139 | { | ||
140 | Assert(wzFileName && *wzFileName); | ||
141 | |||
142 | HRESULT hr = S_OK; | ||
143 | |||
144 | SIZE_T cchFileName = wcslen(wzFileName); | ||
145 | |||
146 | LPWSTR pwzFileNameNoExtension = NULL; | ||
147 | DWORD cchFileNameNoExtension = 0; | ||
148 | |||
149 | // Filename without extension can not be longer than _MAX_FNAME | ||
150 | // Filename without extension should also not be longer than filename itself | ||
151 | if (_MAX_FNAME > cchFileName) | ||
152 | { | ||
153 | cchFileNameNoExtension = (DWORD) cchFileName; | ||
154 | } | ||
155 | else | ||
156 | { | ||
157 | cchFileNameNoExtension = _MAX_FNAME; | ||
158 | } | ||
159 | |||
160 | hr = StrAlloc(&pwzFileNameNoExtension, cchFileNameNoExtension); | ||
161 | ExitOnFailure(hr, "failed to allocate space for File Name without extension"); | ||
162 | |||
163 | // _wsplitpath_s can handle drive/path/filename/extension | ||
164 | errno_t err = _wsplitpath_s(wzFileName, NULL, NULL, NULL, NULL, pwzFileNameNoExtension, cchFileNameNoExtension, NULL, NULL); | ||
165 | if (0 != err) | ||
166 | { | ||
167 | hr = E_INVALIDARG; | ||
168 | ExitOnFailure(hr, "failed to parse filename: %ls", wzFileName); | ||
169 | } | ||
170 | |||
171 | *ppwzFileNameNoExtension = pwzFileNameNoExtension; | ||
172 | pwzFileNameNoExtension = NULL; | ||
173 | |||
174 | LExit: | ||
175 | ReleaseStr(pwzFileNameNoExtension); | ||
176 | |||
177 | return hr; | ||
178 | } | ||
179 | |||
180 | |||
181 | /******************************************************************* | ||
182 | FileChangeExtension - Changes the extension of a filename | ||
183 | ********************************************************************/ | ||
184 | extern "C" HRESULT DAPI FileChangeExtension( | ||
185 | __in LPCWSTR wzFileName, | ||
186 | __in LPCWSTR wzNewExtension, | ||
187 | __out LPWSTR *ppwzFileNameNewExtension | ||
188 | ) | ||
189 | { | ||
190 | Assert(wzFileName && *wzFileName); | ||
191 | |||
192 | HRESULT hr = S_OK; | ||
193 | LPWSTR sczFileName = NULL; | ||
194 | |||
195 | hr = FileStripExtension(wzFileName, &sczFileName); | ||
196 | ExitOnFailure(hr, "Failed to strip extension from file name: %ls", wzFileName); | ||
197 | |||
198 | hr = StrAllocConcat(&sczFileName, wzNewExtension, 0); | ||
199 | ExitOnFailure(hr, "Failed to add new extension."); | ||
200 | |||
201 | *ppwzFileNameNewExtension = sczFileName; | ||
202 | sczFileName = NULL; | ||
203 | |||
204 | LExit: | ||
205 | ReleaseStr(sczFileName); | ||
206 | |||
207 | return hr; | ||
208 | } | ||
209 | |||
210 | |||
211 | /******************************************************************* | ||
212 | FileAddSuffixToBaseName - Adds a suffix the base portion of a file | ||
213 | name; e.g., file.ext to fileSuffix.ext. | ||
214 | ********************************************************************/ | ||
215 | extern "C" HRESULT DAPI FileAddSuffixToBaseName( | ||
216 | __in_z LPCWSTR wzFileName, | ||
217 | __in_z LPCWSTR wzSuffix, | ||
218 | __out_z LPWSTR* psczNewFileName | ||
219 | ) | ||
220 | { | ||
221 | Assert(wzFileName && *wzFileName); | ||
222 | |||
223 | HRESULT hr = S_OK; | ||
224 | LPWSTR sczNewFileName = NULL; | ||
225 | |||
226 | LPCWSTR wzExtension = wzFileName + lstrlenW(wzFileName); | ||
227 | while (wzFileName < wzExtension && L'.' != *wzExtension) | ||
228 | { | ||
229 | --wzExtension; | ||
230 | } | ||
231 | |||
232 | if (wzFileName < wzExtension) | ||
233 | { | ||
234 | // found an extension so add the suffix before it | ||
235 | hr = StrAllocFormatted(&sczNewFileName, L"%.*ls%ls%ls", static_cast<int>(wzExtension - wzFileName), wzFileName, wzSuffix, wzExtension); | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | // no extension, so add the suffix at the end of the whole name | ||
240 | hr = StrAllocString(&sczNewFileName, wzFileName, 0); | ||
241 | ExitOnFailure(hr, "Failed to allocate new file name."); | ||
242 | |||
243 | hr = StrAllocConcat(&sczNewFileName, wzSuffix, 0); | ||
244 | } | ||
245 | ExitOnFailure(hr, "Failed to allocate new file name with suffix."); | ||
246 | |||
247 | *psczNewFileName = sczNewFileName; | ||
248 | sczNewFileName = NULL; | ||
249 | |||
250 | LExit: | ||
251 | ReleaseStr(sczNewFileName); | ||
252 | |||
253 | return hr; | ||
254 | } | ||
255 | |||
256 | |||
257 | /******************************************************************* | ||
258 | FileVersion | ||
259 | |||
260 | ********************************************************************/ | ||
261 | extern "C" HRESULT DAPI FileVersion( | ||
262 | __in LPCWSTR wzFilename, | ||
263 | __out DWORD *pdwVerMajor, | ||
264 | __out DWORD* pdwVerMinor | ||
265 | ) | ||
266 | { | ||
267 | HRESULT hr = S_OK; | ||
268 | |||
269 | DWORD dwHandle = 0; | ||
270 | UINT cbVerBuffer = 0; | ||
271 | LPVOID pVerBuffer = NULL; | ||
272 | VS_FIXEDFILEINFO* pvsFileInfo = NULL; | ||
273 | UINT cbFileInfo = 0; | ||
274 | |||
275 | if (0 == (cbVerBuffer = ::GetFileVersionInfoSizeW(wzFilename, &dwHandle))) | ||
276 | { | ||
277 | ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); | ||
278 | } | ||
279 | |||
280 | pVerBuffer = ::GlobalAlloc(GMEM_FIXED, cbVerBuffer); | ||
281 | ExitOnNullDebugTrace(pVerBuffer, hr, E_OUTOFMEMORY, "failed to allocate version info for file: %ls", wzFilename); | ||
282 | |||
283 | if (!::GetFileVersionInfoW(wzFilename, dwHandle, cbVerBuffer, pVerBuffer)) | ||
284 | { | ||
285 | ExitOnLastErrorDebugTrace(hr, "failed to get version info for file: %ls", wzFilename); | ||
286 | } | ||
287 | |||
288 | if (!::VerQueryValueW(pVerBuffer, L"\\", (void**)&pvsFileInfo, &cbFileInfo)) | ||
289 | { | ||
290 | ExitOnLastErrorDebugTrace(hr, "failed to get version value for file: %ls", wzFilename); | ||
291 | } | ||
292 | |||
293 | *pdwVerMajor = pvsFileInfo->dwFileVersionMS; | ||
294 | *pdwVerMinor = pvsFileInfo->dwFileVersionLS; | ||
295 | |||
296 | LExit: | ||
297 | if (pVerBuffer) | ||
298 | { | ||
299 | ::GlobalFree(pVerBuffer); | ||
300 | } | ||
301 | return hr; | ||
302 | } | ||
303 | |||
304 | |||
305 | /******************************************************************* | ||
306 | FileVersionFromString | ||
307 | |||
308 | *******************************************************************/ | ||
309 | extern "C" HRESULT DAPI FileVersionFromString( | ||
310 | __in LPCWSTR wzVersion, | ||
311 | __out DWORD* pdwVerMajor, | ||
312 | __out DWORD* pdwVerMinor | ||
313 | ) | ||
314 | { | ||
315 | Assert(pdwVerMajor && pdwVerMinor); | ||
316 | |||
317 | HRESULT hr = S_OK; | ||
318 | LPCWSTR pwz = wzVersion; | ||
319 | DWORD dw; | ||
320 | |||
321 | *pdwVerMajor = 0; | ||
322 | *pdwVerMinor = 0; | ||
323 | |||
324 | if ((L'v' == *pwz) || (L'V' == *pwz)) | ||
325 | { | ||
326 | ++pwz; | ||
327 | } | ||
328 | |||
329 | dw = wcstoul(pwz, (WCHAR**)&pwz, 10); | ||
330 | if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) | ||
331 | { | ||
332 | *pdwVerMajor = dw << 16; | ||
333 | |||
334 | if (!*pwz) | ||
335 | { | ||
336 | ExitFunction1(hr = S_OK); | ||
337 | } | ||
338 | ++pwz; | ||
339 | } | ||
340 | else | ||
341 | { | ||
342 | ExitFunction1(hr = S_FALSE); | ||
343 | } | ||
344 | |||
345 | dw = wcstoul(pwz, (WCHAR**)&pwz, 10); | ||
346 | if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) | ||
347 | { | ||
348 | *pdwVerMajor |= dw; | ||
349 | |||
350 | if (!*pwz) | ||
351 | { | ||
352 | ExitFunction1(hr = S_OK); | ||
353 | } | ||
354 | ++pwz; | ||
355 | } | ||
356 | else | ||
357 | { | ||
358 | ExitFunction1(hr = S_FALSE); | ||
359 | } | ||
360 | |||
361 | dw = wcstoul(pwz, (WCHAR**)&pwz, 10); | ||
362 | if (pwz && (L'.' == *pwz && dw < 0x10000) || !*pwz) | ||
363 | { | ||
364 | *pdwVerMinor = dw << 16; | ||
365 | |||
366 | if (!*pwz) | ||
367 | { | ||
368 | ExitFunction1(hr = S_OK); | ||
369 | } | ||
370 | ++pwz; | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | ExitFunction1(hr = S_FALSE); | ||
375 | } | ||
376 | |||
377 | dw = wcstoul(pwz, (WCHAR**)&pwz, 10); | ||
378 | if (pwz && L'\0' == *pwz && dw < 0x10000) | ||
379 | { | ||
380 | *pdwVerMinor |= dw; | ||
381 | } | ||
382 | else | ||
383 | { | ||
384 | ExitFunction1(hr = S_FALSE); | ||
385 | } | ||
386 | |||
387 | LExit: | ||
388 | return hr; | ||
389 | } | ||
390 | |||
391 | |||
392 | /******************************************************************* | ||
393 | FileVersionFromStringEx | ||
394 | |||
395 | *******************************************************************/ | ||
396 | extern "C" HRESULT DAPI FileVersionFromStringEx( | ||
397 | __in LPCWSTR wzVersion, | ||
398 | __in DWORD cchVersion, | ||
399 | __out DWORD64* pqwVersion | ||
400 | ) | ||
401 | { | ||
402 | Assert(wzVersion); | ||
403 | Assert(pqwVersion); | ||
404 | |||
405 | HRESULT hr = S_OK; | ||
406 | LPCWSTR wzEnd = NULL; | ||
407 | LPCWSTR wzPartBegin = wzVersion; | ||
408 | LPCWSTR wzPartEnd = wzVersion; | ||
409 | DWORD iPart = 0; | ||
410 | USHORT us = 0; | ||
411 | DWORD64 qwVersion = 0; | ||
412 | |||
413 | // get string length if not provided | ||
414 | if (0 >= cchVersion) | ||
415 | { | ||
416 | cchVersion = lstrlenW(wzVersion); | ||
417 | if (0 >= cchVersion) | ||
418 | { | ||
419 | ExitFunction1(hr = E_INVALIDARG); | ||
420 | } | ||
421 | } | ||
422 | |||
423 | if ((L'v' == *wzVersion) || (L'V' == *wzVersion)) | ||
424 | { | ||
425 | ++wzVersion; | ||
426 | --cchVersion; | ||
427 | wzPartBegin = wzVersion; | ||
428 | wzPartEnd = wzVersion; | ||
429 | } | ||
430 | |||
431 | // save end pointer | ||
432 | wzEnd = wzVersion + cchVersion; | ||
433 | |||
434 | // loop through parts | ||
435 | for (;;) | ||
436 | { | ||
437 | if (4 <= iPart) | ||
438 | { | ||
439 | // error, too many parts | ||
440 | ExitFunction1(hr = E_INVALIDARG); | ||
441 | } | ||
442 | |||
443 | // find end of part | ||
444 | while (wzPartEnd < wzEnd && L'.' != *wzPartEnd) | ||
445 | { | ||
446 | ++wzPartEnd; | ||
447 | } | ||
448 | if (wzPartBegin == wzPartEnd) | ||
449 | { | ||
450 | // error, empty part | ||
451 | ExitFunction1(hr = E_INVALIDARG); | ||
452 | } | ||
453 | |||
454 | DWORD cchPart; | ||
455 | hr = ::PtrdiffTToDWord(wzPartEnd - wzPartBegin, &cchPart); | ||
456 | ExitOnFailure(hr, "Version number part was too long."); | ||
457 | |||
458 | // parse version part | ||
459 | hr = StrStringToUInt16(wzPartBegin, cchPart, &us); | ||
460 | ExitOnFailure(hr, "Failed to parse version number part."); | ||
461 | |||
462 | // add part to qword version | ||
463 | qwVersion |= (DWORD64)us << ((3 - iPart) * 16); | ||
464 | |||
465 | if (wzPartEnd >= wzEnd) | ||
466 | { | ||
467 | // end of string | ||
468 | break; | ||
469 | } | ||
470 | |||
471 | wzPartBegin = ++wzPartEnd; // skip over separator | ||
472 | ++iPart; | ||
473 | } | ||
474 | |||
475 | *pqwVersion = qwVersion; | ||
476 | |||
477 | LExit: | ||
478 | return hr; | ||
479 | } | ||
480 | |||
481 | /******************************************************************* | ||
482 | FileVersionFromStringEx - Formats the DWORD64 as a string version. | ||
483 | |||
484 | *******************************************************************/ | ||
485 | extern "C" HRESULT DAPI FileVersionToStringEx( | ||
486 | __in DWORD64 qwVersion, | ||
487 | __out LPWSTR* psczVersion | ||
488 | ) | ||
489 | { | ||
490 | HRESULT hr = S_OK; | ||
491 | WORD wMajor = 0; | ||
492 | WORD wMinor = 0; | ||
493 | WORD wBuild = 0; | ||
494 | WORD wRevision = 0; | ||
495 | |||
496 | // Mask and shift each WORD for each field. | ||
497 | wMajor = (WORD)(qwVersion >> 48 & 0xffff); | ||
498 | wMinor = (WORD)(qwVersion >> 32 & 0xffff); | ||
499 | wBuild = (WORD)(qwVersion >> 16 & 0xffff); | ||
500 | wRevision = (WORD)(qwVersion & 0xffff); | ||
501 | |||
502 | // Format and return the version string. | ||
503 | hr = StrAllocFormatted(psczVersion, L"%u.%u.%u.%u", wMajor, wMinor, wBuild, wRevision); | ||
504 | ExitOnFailure(hr, "Failed to allocate and format the version number."); | ||
505 | |||
506 | LExit: | ||
507 | return hr; | ||
508 | } | ||
509 | |||
510 | /******************************************************************* | ||
511 | FileSetPointer - sets the file pointer. | ||
512 | |||
513 | ********************************************************************/ | ||
514 | extern "C" HRESULT DAPI FileSetPointer( | ||
515 | __in HANDLE hFile, | ||
516 | __in DWORD64 dw64Move, | ||
517 | __out_opt DWORD64* pdw64NewPosition, | ||
518 | __in DWORD dwMoveMethod | ||
519 | ) | ||
520 | { | ||
521 | Assert(INVALID_HANDLE_VALUE != hFile); | ||
522 | |||
523 | HRESULT hr = S_OK; | ||
524 | LARGE_INTEGER liMove; | ||
525 | LARGE_INTEGER liNewPosition; | ||
526 | |||
527 | liMove.QuadPart = dw64Move; | ||
528 | if (!::SetFilePointerEx(hFile, liMove, &liNewPosition, dwMoveMethod)) | ||
529 | { | ||
530 | ExitWithLastError(hr, "Failed to set file pointer."); | ||
531 | } | ||
532 | |||
533 | if (pdw64NewPosition) | ||
534 | { | ||
535 | *pdw64NewPosition = liNewPosition.QuadPart; | ||
536 | } | ||
537 | |||
538 | LExit: | ||
539 | return hr; | ||
540 | } | ||
541 | |||
542 | |||
543 | /******************************************************************* | ||
544 | FileSize | ||
545 | |||
546 | ********************************************************************/ | ||
547 | extern "C" HRESULT DAPI FileSize( | ||
548 | __in LPCWSTR pwzFileName, | ||
549 | __out LONGLONG* pllSize | ||
550 | ) | ||
551 | { | ||
552 | HRESULT hr = S_OK; | ||
553 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
554 | |||
555 | ExitOnNull(pwzFileName, hr, E_INVALIDARG, "Attempted to check filename, but no filename was provided"); | ||
556 | |||
557 | hFile = ::CreateFileW(pwzFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | ||
558 | if (INVALID_HANDLE_VALUE == hFile) | ||
559 | { | ||
560 | ExitWithLastError(hr, "Failed to open file %ls while checking file size", pwzFileName); | ||
561 | } | ||
562 | |||
563 | hr = FileSizeByHandle(hFile, pllSize); | ||
564 | ExitOnFailure(hr, "Failed to check size of file %ls by handle", pwzFileName); | ||
565 | |||
566 | LExit: | ||
567 | ReleaseFileHandle(hFile); | ||
568 | |||
569 | return hr; | ||
570 | } | ||
571 | |||
572 | |||
573 | /******************************************************************* | ||
574 | FileSizeByHandle | ||
575 | |||
576 | ********************************************************************/ | ||
577 | extern "C" HRESULT DAPI FileSizeByHandle( | ||
578 | __in HANDLE hFile, | ||
579 | __out LONGLONG* pllSize | ||
580 | ) | ||
581 | { | ||
582 | Assert(INVALID_HANDLE_VALUE != hFile && pllSize); | ||
583 | HRESULT hr = S_OK; | ||
584 | LARGE_INTEGER li; | ||
585 | |||
586 | *pllSize = 0; | ||
587 | |||
588 | if (!::GetFileSizeEx(hFile, &li)) | ||
589 | { | ||
590 | ExitWithLastError(hr, "Failed to get size of file."); | ||
591 | } | ||
592 | |||
593 | *pllSize = li.QuadPart; | ||
594 | |||
595 | LExit: | ||
596 | return hr; | ||
597 | } | ||
598 | |||
599 | |||
600 | /******************************************************************* | ||
601 | FileExistsEx | ||
602 | |||
603 | ********************************************************************/ | ||
604 | extern "C" BOOL DAPI FileExistsEx( | ||
605 | __in LPCWSTR wzPath, | ||
606 | __out_opt DWORD *pdwAttributes | ||
607 | ) | ||
608 | { | ||
609 | Assert(wzPath && *wzPath); | ||
610 | BOOL fExists = FALSE; | ||
611 | |||
612 | WIN32_FIND_DATAW fd = { }; | ||
613 | HANDLE hff; | ||
614 | |||
615 | if (INVALID_HANDLE_VALUE != (hff = ::FindFirstFileW(wzPath, &fd))) | ||
616 | { | ||
617 | ::FindClose(hff); | ||
618 | if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) | ||
619 | { | ||
620 | if (pdwAttributes) | ||
621 | { | ||
622 | *pdwAttributes = fd.dwFileAttributes; | ||
623 | } | ||
624 | |||
625 | fExists = TRUE; | ||
626 | } | ||
627 | } | ||
628 | |||
629 | return fExists; | ||
630 | } | ||
631 | |||
632 | |||
633 | /******************************************************************* | ||
634 | FileExistsAfterRestart - checks that a file exists and will continue | ||
635 | to exist after restart. | ||
636 | |||
637 | ********************************************************************/ | ||
638 | extern "C" BOOL DAPI FileExistsAfterRestart( | ||
639 | __in_z LPCWSTR wzPath, | ||
640 | __out_opt DWORD *pdwAttributes | ||
641 | ) | ||
642 | { | ||
643 | HRESULT hr = S_OK; | ||
644 | BOOL fExists = FALSE; | ||
645 | HKEY hkPendingFileRename = NULL; | ||
646 | LPWSTR* rgsczRenames = NULL; | ||
647 | DWORD cRenames = 0; | ||
648 | int nCompare = 0; | ||
649 | |||
650 | fExists = FileExistsEx(wzPath, pdwAttributes); | ||
651 | if (fExists) | ||
652 | { | ||
653 | hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); | ||
654 | if (E_FILENOTFOUND == hr) | ||
655 | { | ||
656 | ExitFunction1(hr = S_OK); | ||
657 | } | ||
658 | ExitOnFailure(hr, "Failed to open pending file rename registry key."); | ||
659 | |||
660 | hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); | ||
661 | if (E_FILENOTFOUND == hr) | ||
662 | { | ||
663 | ExitFunction1(hr = S_OK); | ||
664 | } | ||
665 | ExitOnFailure(hr, "Failed to read pending file renames."); | ||
666 | |||
667 | // The pending file renames array is pairs of source and target paths. We only care | ||
668 | // about checking the source paths so skip the target paths (i += 2). | ||
669 | for (DWORD i = 0; i < cRenames; i += 2) | ||
670 | { | ||
671 | LPWSTR wzRename = rgsczRenames[i]; | ||
672 | if (wzRename && *wzRename) | ||
673 | { | ||
674 | // Skip the long path designator if present. | ||
675 | if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) | ||
676 | { | ||
677 | wzRename += 4; | ||
678 | } | ||
679 | |||
680 | hr = PathCompare(wzPath, wzRename, &nCompare); | ||
681 | ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); | ||
682 | |||
683 | if (CSTR_EQUAL == nCompare) | ||
684 | { | ||
685 | fExists = FALSE; | ||
686 | break; | ||
687 | } | ||
688 | } | ||
689 | } | ||
690 | } | ||
691 | |||
692 | LExit: | ||
693 | ReleaseStrArray(rgsczRenames, cRenames); | ||
694 | ReleaseRegKey(hkPendingFileRename); | ||
695 | |||
696 | return fExists; | ||
697 | } | ||
698 | |||
699 | |||
700 | /******************************************************************* | ||
701 | FileRemoveFromPendingRename - removes the file path from the pending | ||
702 | file rename list. | ||
703 | |||
704 | ********************************************************************/ | ||
705 | extern "C" HRESULT DAPI FileRemoveFromPendingRename( | ||
706 | __in_z LPCWSTR wzPath | ||
707 | ) | ||
708 | { | ||
709 | HRESULT hr = S_OK; | ||
710 | HKEY hkPendingFileRename = NULL; | ||
711 | LPWSTR* rgsczRenames = NULL; | ||
712 | DWORD cRenames = 0; | ||
713 | int nCompare = 0; | ||
714 | BOOL fRemoved = FALSE; | ||
715 | DWORD cNewRenames = 0; | ||
716 | |||
717 | hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); | ||
718 | if (E_FILENOTFOUND == hr) | ||
719 | { | ||
720 | ExitFunction1(hr = S_OK); | ||
721 | } | ||
722 | ExitOnFailure(hr, "Failed to open pending file rename registry key."); | ||
723 | |||
724 | hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); | ||
725 | if (E_FILENOTFOUND == hr) | ||
726 | { | ||
727 | ExitFunction1(hr = S_OK); | ||
728 | } | ||
729 | ExitOnFailure(hr, "Failed to read pending file renames."); | ||
730 | |||
731 | // The pending file renames array is pairs of source and target paths. We only care | ||
732 | // about checking the source paths so skip the target paths (i += 2). | ||
733 | for (DWORD i = 0; i < cRenames; i += 2) | ||
734 | { | ||
735 | LPWSTR wzRename = rgsczRenames[i]; | ||
736 | if (wzRename && *wzRename) | ||
737 | { | ||
738 | // Skip the long path designator if present. | ||
739 | if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) | ||
740 | { | ||
741 | wzRename += 4; | ||
742 | } | ||
743 | |||
744 | hr = PathCompare(wzPath, wzRename, &nCompare); | ||
745 | ExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); | ||
746 | |||
747 | // If we find our path in the list, null out the source and target slot and | ||
748 | // we'll compact the array next. | ||
749 | if (CSTR_EQUAL == nCompare) | ||
750 | { | ||
751 | ReleaseNullStr(rgsczRenames[i]); | ||
752 | ReleaseNullStr(rgsczRenames[i + 1]); | ||
753 | fRemoved = TRUE; | ||
754 | } | ||
755 | } | ||
756 | } | ||
757 | |||
758 | if (fRemoved) | ||
759 | { | ||
760 | // Compact the array by removing any nulls. | ||
761 | for (DWORD i = 0; i < cRenames; ++i) | ||
762 | { | ||
763 | LPWSTR wzRename = rgsczRenames[i]; | ||
764 | if (wzRename) | ||
765 | { | ||
766 | rgsczRenames[cNewRenames] = wzRename; | ||
767 | ++cNewRenames; | ||
768 | } | ||
769 | } | ||
770 | |||
771 | cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. | ||
772 | |||
773 | // Write the new array back to the pending file rename key. | ||
774 | hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); | ||
775 | ExitOnFailure(hr, "Failed to update pending file renames."); | ||
776 | } | ||
777 | |||
778 | LExit: | ||
779 | ReleaseStrArray(rgsczRenames, cRenames); | ||
780 | ReleaseRegKey(hkPendingFileRename); | ||
781 | |||
782 | return hr; | ||
783 | } | ||
784 | |||
785 | |||
786 | /******************************************************************* | ||
787 | FileRead - read a file into memory | ||
788 | |||
789 | ********************************************************************/ | ||
790 | extern "C" HRESULT DAPI FileRead( | ||
791 | __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, | ||
792 | __out DWORD* pcbDest, | ||
793 | __in LPCWSTR wzSrcPath | ||
794 | ) | ||
795 | { | ||
796 | HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE); | ||
797 | return hr; | ||
798 | } | ||
799 | |||
800 | /******************************************************************* | ||
801 | FileRead - read a file into memory with specified share mode | ||
802 | |||
803 | ********************************************************************/ | ||
804 | extern "C" HRESULT DAPI FileReadEx( | ||
805 | __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, | ||
806 | __out DWORD* pcbDest, | ||
807 | __in_z LPCWSTR wzSrcPath, | ||
808 | __in DWORD dwShareMode | ||
809 | ) | ||
810 | { | ||
811 | HRESULT hr = FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, FALSE, 0, 0xFFFFFFFF, FALSE, dwShareMode); | ||
812 | return hr; | ||
813 | } | ||
814 | |||
815 | /******************************************************************* | ||
816 | FileReadUntil - read a file into memory with a maximum size | ||
817 | |||
818 | ********************************************************************/ | ||
819 | extern "C" HRESULT DAPI FileReadUntil( | ||
820 | __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, | ||
821 | __out_range(<=, cbMaxRead) DWORD* pcbDest, | ||
822 | __in LPCWSTR wzSrcPath, | ||
823 | __in DWORD cbMaxRead | ||
824 | ) | ||
825 | { | ||
826 | HRESULT hr = FileReadPartial(ppbDest, pcbDest, wzSrcPath, FALSE, 0, cbMaxRead, FALSE); | ||
827 | return hr; | ||
828 | } | ||
829 | |||
830 | |||
831 | /******************************************************************* | ||
832 | FileReadPartial - read a portion of a file into memory | ||
833 | |||
834 | ********************************************************************/ | ||
835 | extern "C" HRESULT DAPI FileReadPartial( | ||
836 | __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, | ||
837 | __out_range(<=, cbMaxRead) DWORD* pcbDest, | ||
838 | __in LPCWSTR wzSrcPath, | ||
839 | __in BOOL fSeek, | ||
840 | __in DWORD cbStartPosition, | ||
841 | __in DWORD cbMaxRead, | ||
842 | __in BOOL fPartialOK | ||
843 | ) | ||
844 | { | ||
845 | return FileReadPartialEx(ppbDest, pcbDest, wzSrcPath, fSeek, cbStartPosition, cbMaxRead, fPartialOK, FILE_SHARE_READ | FILE_SHARE_DELETE); | ||
846 | } | ||
847 | |||
848 | /******************************************************************* | ||
849 | FileReadPartial - read a portion of a file into memory | ||
850 | (with specified share mode) | ||
851 | ********************************************************************/ | ||
852 | extern "C" HRESULT DAPI FileReadPartialEx( | ||
853 | __deref_out_bcount_full(*pcbDest) LPBYTE* ppbDest, | ||
854 | __out_range(<=, cbMaxRead) DWORD* pcbDest, | ||
855 | __in_z LPCWSTR wzSrcPath, | ||
856 | __in BOOL fSeek, | ||
857 | __in DWORD cbStartPosition, | ||
858 | __in DWORD cbMaxRead, | ||
859 | __in BOOL fPartialOK, | ||
860 | __in DWORD dwShareMode | ||
861 | ) | ||
862 | { | ||
863 | HRESULT hr = S_OK; | ||
864 | |||
865 | UINT er = ERROR_SUCCESS; | ||
866 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
867 | LARGE_INTEGER liFileSize = { }; | ||
868 | DWORD cbData = 0; | ||
869 | BYTE* pbData = NULL; | ||
870 | |||
871 | ExitOnNull(pcbDest, hr, E_INVALIDARG, "Invalid argument pcbDest"); | ||
872 | ExitOnNull(ppbDest, hr, E_INVALIDARG, "Invalid argument ppbDest"); | ||
873 | ExitOnNull(wzSrcPath, hr, E_INVALIDARG, "Invalid argument wzSrcPath"); | ||
874 | ExitOnNull(*wzSrcPath, hr, E_INVALIDARG, "*wzSrcPath is null"); | ||
875 | |||
876 | hFile = ::CreateFileW(wzSrcPath, GENERIC_READ, dwShareMode, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); | ||
877 | if (INVALID_HANDLE_VALUE == hFile) | ||
878 | { | ||
879 | er = ::GetLastError(); | ||
880 | if (E_FILENOTFOUND == HRESULT_FROM_WIN32(er)) | ||
881 | { | ||
882 | ExitFunction1(hr = E_FILENOTFOUND); | ||
883 | } | ||
884 | ExitOnWin32Error(er, hr, "Failed to open file: %ls", wzSrcPath); | ||
885 | } | ||
886 | |||
887 | if (!::GetFileSizeEx(hFile, &liFileSize)) | ||
888 | { | ||
889 | ExitWithLastError(hr, "Failed to get size of file: %ls", wzSrcPath); | ||
890 | } | ||
891 | |||
892 | if (fSeek) | ||
893 | { | ||
894 | if (cbStartPosition > liFileSize.QuadPart) | ||
895 | { | ||
896 | hr = E_INVALIDARG; | ||
897 | ExitOnFailure(hr, "Start position %d bigger than file '%ls' size %d", cbStartPosition, wzSrcPath, liFileSize.QuadPart); | ||
898 | } | ||
899 | |||
900 | DWORD dwErr = ::SetFilePointer(hFile, cbStartPosition, NULL, FILE_CURRENT); | ||
901 | if (INVALID_SET_FILE_POINTER == dwErr) | ||
902 | { | ||
903 | ExitOnLastError(hr, "Failed to seek position %d", cbStartPosition); | ||
904 | } | ||
905 | } | ||
906 | else | ||
907 | { | ||
908 | cbStartPosition = 0; | ||
909 | } | ||
910 | |||
911 | if (fPartialOK) | ||
912 | { | ||
913 | cbData = cbMaxRead; | ||
914 | } | ||
915 | else | ||
916 | { | ||
917 | cbData = liFileSize.LowPart - cbStartPosition; // should only need the low part because we cap at DWORD | ||
918 | if (cbMaxRead < liFileSize.QuadPart - cbStartPosition) | ||
919 | { | ||
920 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
921 | ExitOnRootFailure(hr, "Failed to load file: %ls, too large.", wzSrcPath); | ||
922 | } | ||
923 | } | ||
924 | |||
925 | if (*ppbDest) | ||
926 | { | ||
927 | if (0 == cbData) | ||
928 | { | ||
929 | ReleaseNullMem(*ppbDest); | ||
930 | *pcbDest = 0; | ||
931 | ExitFunction1(hr = S_OK); | ||
932 | } | ||
933 | |||
934 | LPVOID pv = MemReAlloc(*ppbDest, cbData, TRUE); | ||
935 | ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to re-allocate memory to read in file: %ls", wzSrcPath); | ||
936 | |||
937 | pbData = static_cast<BYTE*>(pv); | ||
938 | } | ||
939 | else | ||
940 | { | ||
941 | if (0 == cbData) | ||
942 | { | ||
943 | *pcbDest = 0; | ||
944 | ExitFunction1(hr = S_OK); | ||
945 | } | ||
946 | |||
947 | pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); | ||
948 | ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in file: %ls", wzSrcPath); | ||
949 | } | ||
950 | |||
951 | DWORD cbTotalRead = 0; | ||
952 | DWORD cbRead = 0; | ||
953 | do | ||
954 | { | ||
955 | DWORD cbRemaining = 0; | ||
956 | hr = ::ULongSub(cbData, cbTotalRead, &cbRemaining); | ||
957 | ExitOnFailure(hr, "Underflow calculating remaining buffer size."); | ||
958 | |||
959 | if (!::ReadFile(hFile, pbData + cbTotalRead, cbRemaining, &cbRead, NULL)) | ||
960 | { | ||
961 | ExitWithLastError(hr, "Failed to read from file: %ls", wzSrcPath); | ||
962 | } | ||
963 | |||
964 | cbTotalRead += cbRead; | ||
965 | } while (cbRead); | ||
966 | |||
967 | if (cbTotalRead != cbData) | ||
968 | { | ||
969 | hr = E_UNEXPECTED; | ||
970 | ExitOnFailure(hr, "Failed to completely read file: %ls", wzSrcPath); | ||
971 | } | ||
972 | |||
973 | *ppbDest = pbData; | ||
974 | pbData = NULL; | ||
975 | *pcbDest = cbData; | ||
976 | |||
977 | LExit: | ||
978 | ReleaseMem(pbData); | ||
979 | ReleaseFile(hFile); | ||
980 | |||
981 | return hr; | ||
982 | } | ||
983 | |||
984 | |||
985 | /******************************************************************* | ||
986 | FileWrite - write a file from memory | ||
987 | |||
988 | ********************************************************************/ | ||
989 | extern "C" HRESULT DAPI FileWrite( | ||
990 | __in_z LPCWSTR pwzFileName, | ||
991 | __in DWORD dwFlagsAndAttributes, | ||
992 | __in_bcount_opt(cbData) LPCBYTE pbData, | ||
993 | __in DWORD cbData, | ||
994 | __out_opt HANDLE* pHandle | ||
995 | ) | ||
996 | { | ||
997 | HRESULT hr = S_OK; | ||
998 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
999 | |||
1000 | // Open the file | ||
1001 | hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, dwFlagsAndAttributes, NULL); | ||
1002 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file: %ls", pwzFileName); | ||
1003 | |||
1004 | hr = FileWriteHandle(hFile, pbData, cbData); | ||
1005 | ExitOnFailure(hr, "Failed to write to file: %ls", pwzFileName); | ||
1006 | |||
1007 | if (pHandle) | ||
1008 | { | ||
1009 | *pHandle = hFile; | ||
1010 | hFile = INVALID_HANDLE_VALUE; | ||
1011 | } | ||
1012 | |||
1013 | LExit: | ||
1014 | ReleaseFile(hFile); | ||
1015 | |||
1016 | return hr; | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | /******************************************************************* | ||
1021 | FileWriteHandle - write to a file handle from memory | ||
1022 | |||
1023 | ********************************************************************/ | ||
1024 | extern "C" HRESULT DAPI FileWriteHandle( | ||
1025 | __in HANDLE hFile, | ||
1026 | __in_bcount_opt(cbData) LPCBYTE pbData, | ||
1027 | __in DWORD cbData | ||
1028 | ) | ||
1029 | { | ||
1030 | HRESULT hr = S_OK; | ||
1031 | DWORD cbDataWritten = 0; | ||
1032 | DWORD cbTotal = 0; | ||
1033 | |||
1034 | // Write out all of the data. | ||
1035 | do | ||
1036 | { | ||
1037 | if (!::WriteFile(hFile, pbData + cbTotal, cbData - cbTotal, &cbDataWritten, NULL)) | ||
1038 | { | ||
1039 | ExitOnLastError(hr, "Failed to write data to file handle."); | ||
1040 | } | ||
1041 | |||
1042 | cbTotal += cbDataWritten; | ||
1043 | } while (cbTotal < cbData); | ||
1044 | |||
1045 | LExit: | ||
1046 | return hr; | ||
1047 | } | ||
1048 | |||
1049 | |||
1050 | /******************************************************************* | ||
1051 | FileCopyUsingHandles | ||
1052 | |||
1053 | *******************************************************************/ | ||
1054 | extern "C" HRESULT DAPI FileCopyUsingHandles( | ||
1055 | __in HANDLE hSource, | ||
1056 | __in HANDLE hTarget, | ||
1057 | __in DWORD64 cbCopy, | ||
1058 | __out_opt DWORD64* pcbCopied | ||
1059 | ) | ||
1060 | { | ||
1061 | HRESULT hr = S_OK; | ||
1062 | DWORD64 cbTotalCopied = 0; | ||
1063 | BYTE rgbData[4 * 1024]; | ||
1064 | DWORD cbRead = 0; | ||
1065 | |||
1066 | do | ||
1067 | { | ||
1068 | cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied)); | ||
1069 | if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL)) | ||
1070 | { | ||
1071 | ExitWithLastError(hr, "Failed to read from source."); | ||
1072 | } | ||
1073 | |||
1074 | if (cbRead) | ||
1075 | { | ||
1076 | hr = FileWriteHandle(hTarget, rgbData, cbRead); | ||
1077 | ExitOnFailure(hr, "Failed to write to target."); | ||
1078 | } | ||
1079 | |||
1080 | cbTotalCopied += cbRead; | ||
1081 | } while (cbTotalCopied < cbCopy && 0 != cbRead); | ||
1082 | |||
1083 | if (pcbCopied) | ||
1084 | { | ||
1085 | *pcbCopied = cbTotalCopied; | ||
1086 | } | ||
1087 | |||
1088 | LExit: | ||
1089 | return hr; | ||
1090 | } | ||
1091 | |||
1092 | |||
1093 | /******************************************************************* | ||
1094 | FileEnsureCopy | ||
1095 | |||
1096 | *******************************************************************/ | ||
1097 | extern "C" HRESULT DAPI FileEnsureCopy( | ||
1098 | __in LPCWSTR wzSource, | ||
1099 | __in LPCWSTR wzTarget, | ||
1100 | __in BOOL fOverwrite | ||
1101 | ) | ||
1102 | { | ||
1103 | HRESULT hr = S_OK; | ||
1104 | DWORD er; | ||
1105 | |||
1106 | // try to copy the file first | ||
1107 | if (::CopyFileW(wzSource, wzTarget, !fOverwrite)) | ||
1108 | { | ||
1109 | ExitFunction(); // we're done | ||
1110 | } | ||
1111 | |||
1112 | er = ::GetLastError(); // check the error and do the right thing below | ||
1113 | if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) | ||
1114 | { | ||
1115 | // if not overwriting this is an expected error | ||
1116 | ExitFunction1(hr = S_FALSE); | ||
1117 | } | ||
1118 | else if (ERROR_PATH_NOT_FOUND == er) // if the path doesn't exist | ||
1119 | { | ||
1120 | // try to create the directory then do the copy | ||
1121 | LPWSTR pwzLastSlash = NULL; | ||
1122 | for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz) | ||
1123 | { | ||
1124 | if (*pwz == L'\\') | ||
1125 | { | ||
1126 | pwzLastSlash = pwz; | ||
1127 | } | ||
1128 | } | ||
1129 | |||
1130 | if (pwzLastSlash) | ||
1131 | { | ||
1132 | *pwzLastSlash = L'\0'; // null terminate | ||
1133 | hr = DirEnsureExists(wzTarget, NULL); | ||
1134 | *pwzLastSlash = L'\\'; // now put the slash back | ||
1135 | ExitOnFailureDebugTrace(hr, "failed to create directory while copying file: '%ls' to: '%ls'", wzSource, wzTarget); | ||
1136 | |||
1137 | // try to copy again | ||
1138 | if (!::CopyFileW(wzSource, wzTarget, fOverwrite)) | ||
1139 | { | ||
1140 | ExitOnLastErrorDebugTrace(hr, "failed to copy file: '%ls' to: '%ls'", wzSource, wzTarget); | ||
1141 | } | ||
1142 | } | ||
1143 | else // no path was specified so just return the error | ||
1144 | { | ||
1145 | hr = HRESULT_FROM_WIN32(er); | ||
1146 | } | ||
1147 | } | ||
1148 | else // unexpected error | ||
1149 | { | ||
1150 | hr = HRESULT_FROM_WIN32(er); | ||
1151 | } | ||
1152 | |||
1153 | LExit: | ||
1154 | return hr; | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | /******************************************************************* | ||
1159 | FileEnsureCopyWithRetry | ||
1160 | |||
1161 | *******************************************************************/ | ||
1162 | extern "C" HRESULT DAPI FileEnsureCopyWithRetry( | ||
1163 | __in LPCWSTR wzSource, | ||
1164 | __in LPCWSTR wzTarget, | ||
1165 | __in BOOL fOverwrite, | ||
1166 | __in DWORD cRetry, | ||
1167 | __in DWORD dwWaitMilliseconds | ||
1168 | ) | ||
1169 | { | ||
1170 | AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); | ||
1171 | |||
1172 | HRESULT hr = E_FAIL; | ||
1173 | DWORD i = 0; | ||
1174 | |||
1175 | for (i = 0; FAILED(hr) && i <= cRetry; ++i) | ||
1176 | { | ||
1177 | if (0 < i) | ||
1178 | { | ||
1179 | ::Sleep(dwWaitMilliseconds); | ||
1180 | } | ||
1181 | |||
1182 | hr = FileEnsureCopy(wzSource, wzTarget, fOverwrite); | ||
1183 | if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr || HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr | ||
1184 | || HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr || HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) == hr) | ||
1185 | { | ||
1186 | break; // no reason to retry these errors. | ||
1187 | } | ||
1188 | } | ||
1189 | ExitOnFailure(hr, "Failed to copy file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); | ||
1190 | |||
1191 | LExit: | ||
1192 | return hr; | ||
1193 | } | ||
1194 | |||
1195 | |||
1196 | /******************************************************************* | ||
1197 | FileEnsureMove | ||
1198 | |||
1199 | *******************************************************************/ | ||
1200 | extern "C" HRESULT DAPI FileEnsureMove( | ||
1201 | __in LPCWSTR wzSource, | ||
1202 | __in LPCWSTR wzTarget, | ||
1203 | __in BOOL fOverwrite, | ||
1204 | __in BOOL fAllowCopy | ||
1205 | ) | ||
1206 | { | ||
1207 | HRESULT hr = S_OK; | ||
1208 | DWORD er; | ||
1209 | |||
1210 | DWORD dwFlags = 0; | ||
1211 | |||
1212 | if (fOverwrite) | ||
1213 | { | ||
1214 | dwFlags |= MOVEFILE_REPLACE_EXISTING; | ||
1215 | } | ||
1216 | if (fAllowCopy) | ||
1217 | { | ||
1218 | dwFlags |= MOVEFILE_COPY_ALLOWED; | ||
1219 | } | ||
1220 | |||
1221 | // try to move the file first | ||
1222 | if (::MoveFileExW(wzSource, wzTarget, dwFlags)) | ||
1223 | { | ||
1224 | ExitFunction(); // we're done | ||
1225 | } | ||
1226 | |||
1227 | er = ::GetLastError(); // check the error and do the right thing below | ||
1228 | if (!fOverwrite && (ERROR_FILE_EXISTS == er || ERROR_ALREADY_EXISTS == er)) | ||
1229 | { | ||
1230 | // if not overwriting this is an expected error | ||
1231 | ExitFunction1(hr = S_FALSE); | ||
1232 | } | ||
1233 | else if (ERROR_FILE_NOT_FOUND == er) | ||
1234 | { | ||
1235 | // We are seeing some cases where ::MoveFileEx() says a file was not found | ||
1236 | // but the source file is actually present. In that case, return path not | ||
1237 | // found so we try to create the target path since that is most likely | ||
1238 | // what is missing. Otherwise, the source file is missing and we're obviously | ||
1239 | // not going to be recovering from that. | ||
1240 | if (FileExistsEx(wzSource, NULL)) | ||
1241 | { | ||
1242 | er = ERROR_PATH_NOT_FOUND; | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | // If the path doesn't exist, try to create the directory tree then do the move. | ||
1247 | if (ERROR_PATH_NOT_FOUND == er) | ||
1248 | { | ||
1249 | LPWSTR pwzLastSlash = NULL; | ||
1250 | for (LPWSTR pwz = const_cast<LPWSTR>(wzTarget); *pwz; ++pwz) | ||
1251 | { | ||
1252 | if (*pwz == L'\\') | ||
1253 | { | ||
1254 | pwzLastSlash = pwz; | ||
1255 | } | ||
1256 | } | ||
1257 | |||
1258 | if (pwzLastSlash) | ||
1259 | { | ||
1260 | *pwzLastSlash = L'\0'; // null terminate | ||
1261 | hr = DirEnsureExists(wzTarget, NULL); | ||
1262 | *pwzLastSlash = L'\\'; // now put the slash back | ||
1263 | ExitOnFailureDebugTrace(hr, "failed to create directory while moving file: '%ls' to: '%ls'", wzSource, wzTarget); | ||
1264 | |||
1265 | // try to move again | ||
1266 | if (!::MoveFileExW(wzSource, wzTarget, dwFlags)) | ||
1267 | { | ||
1268 | ExitOnLastErrorDebugTrace(hr, "failed to move file: '%ls' to: '%ls'", wzSource, wzTarget); | ||
1269 | } | ||
1270 | } | ||
1271 | else // no path was specified so just return the error | ||
1272 | { | ||
1273 | hr = HRESULT_FROM_WIN32(er); | ||
1274 | } | ||
1275 | } | ||
1276 | else // unexpected error | ||
1277 | { | ||
1278 | hr = HRESULT_FROM_WIN32(er); | ||
1279 | } | ||
1280 | |||
1281 | LExit: | ||
1282 | return hr; | ||
1283 | } | ||
1284 | |||
1285 | |||
1286 | /******************************************************************* | ||
1287 | FileEnsureMoveWithRetry | ||
1288 | |||
1289 | *******************************************************************/ | ||
1290 | extern "C" HRESULT DAPI FileEnsureMoveWithRetry( | ||
1291 | __in LPCWSTR wzSource, | ||
1292 | __in LPCWSTR wzTarget, | ||
1293 | __in BOOL fOverwrite, | ||
1294 | __in BOOL fAllowCopy, | ||
1295 | __in DWORD cRetry, | ||
1296 | __in DWORD dwWaitMilliseconds | ||
1297 | ) | ||
1298 | { | ||
1299 | AssertSz(cRetry != DWORD_MAX, "Cannot pass DWORD_MAX for retry."); | ||
1300 | |||
1301 | HRESULT hr = E_FAIL; | ||
1302 | DWORD i = 0; | ||
1303 | |||
1304 | for (i = 0; FAILED(hr) && i < cRetry + 1; ++i) | ||
1305 | { | ||
1306 | if (0 < i) | ||
1307 | { | ||
1308 | ::Sleep(dwWaitMilliseconds); | ||
1309 | } | ||
1310 | |||
1311 | hr = FileEnsureMove(wzSource, wzTarget, fOverwrite, fAllowCopy); | ||
1312 | } | ||
1313 | ExitOnFailure(hr, "Failed to move file: '%ls' to: '%ls' after %u retries.", wzSource, wzTarget, i); | ||
1314 | |||
1315 | LExit: | ||
1316 | return hr; | ||
1317 | } | ||
1318 | |||
1319 | |||
1320 | /******************************************************************* | ||
1321 | FileCreateTemp - creates an empty temp file | ||
1322 | |||
1323 | NOTE: uses ANSI functions internally so it is Win9x safe | ||
1324 | ********************************************************************/ | ||
1325 | extern "C" HRESULT DAPI FileCreateTemp( | ||
1326 | __in LPCWSTR wzPrefix, | ||
1327 | __in LPCWSTR wzExtension, | ||
1328 | __deref_opt_out_z LPWSTR* ppwzTempFile, | ||
1329 | __out_opt HANDLE* phTempFile | ||
1330 | ) | ||
1331 | { | ||
1332 | Assert(wzPrefix && *wzPrefix); | ||
1333 | HRESULT hr = S_OK; | ||
1334 | LPSTR pszTempPath = NULL; | ||
1335 | DWORD cchTempPath = MAX_PATH; | ||
1336 | |||
1337 | HANDLE hTempFile = INVALID_HANDLE_VALUE; | ||
1338 | LPSTR pszTempFile = NULL; | ||
1339 | |||
1340 | int i = 0; | ||
1341 | |||
1342 | hr = StrAnsiAlloc(&pszTempPath, cchTempPath); | ||
1343 | ExitOnFailure(hr, "failed to allocate memory for the temp path"); | ||
1344 | ::GetTempPathA(cchTempPath, pszTempPath); | ||
1345 | |||
1346 | for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) | ||
1347 | { | ||
1348 | hr = StrAnsiAllocFormatted(&pszTempFile, "%s%ls%05d.%ls", pszTempPath, wzPrefix, i, wzExtension); | ||
1349 | ExitOnFailure(hr, "failed to allocate memory for log file"); | ||
1350 | |||
1351 | hTempFile = ::CreateFileA(pszTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); | ||
1352 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
1353 | { | ||
1354 | // if the file already exists, just try again | ||
1355 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
1356 | if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) | ||
1357 | { | ||
1358 | hr = S_OK; | ||
1359 | continue; | ||
1360 | } | ||
1361 | ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pszTempFile); | ||
1362 | } | ||
1363 | } | ||
1364 | |||
1365 | if (ppwzTempFile) | ||
1366 | { | ||
1367 | hr = StrAllocStringAnsi(ppwzTempFile, pszTempFile, 0, CP_UTF8); | ||
1368 | } | ||
1369 | |||
1370 | if (phTempFile) | ||
1371 | { | ||
1372 | *phTempFile = hTempFile; | ||
1373 | hTempFile = INVALID_HANDLE_VALUE; | ||
1374 | } | ||
1375 | |||
1376 | LExit: | ||
1377 | ReleaseFile(hTempFile); | ||
1378 | ReleaseStr(pszTempFile); | ||
1379 | ReleaseStr(pszTempPath); | ||
1380 | |||
1381 | return hr; | ||
1382 | } | ||
1383 | |||
1384 | |||
1385 | /******************************************************************* | ||
1386 | FileCreateTempW - creates an empty temp file | ||
1387 | |||
1388 | *******************************************************************/ | ||
1389 | extern "C" HRESULT DAPI FileCreateTempW( | ||
1390 | __in LPCWSTR wzPrefix, | ||
1391 | __in LPCWSTR wzExtension, | ||
1392 | __deref_opt_out_z LPWSTR* ppwzTempFile, | ||
1393 | __out_opt HANDLE* phTempFile | ||
1394 | ) | ||
1395 | { | ||
1396 | Assert(wzPrefix && *wzPrefix); | ||
1397 | HRESULT hr = E_FAIL; | ||
1398 | |||
1399 | WCHAR wzTempPath[MAX_PATH]; | ||
1400 | DWORD cchTempPath = countof(wzTempPath); | ||
1401 | LPWSTR pwzTempFile = NULL; | ||
1402 | |||
1403 | HANDLE hTempFile = INVALID_HANDLE_VALUE; | ||
1404 | int i = 0; | ||
1405 | |||
1406 | if (!::GetTempPathW(cchTempPath, wzTempPath)) | ||
1407 | { | ||
1408 | ExitOnLastError(hr, "failed to get temp path"); | ||
1409 | } | ||
1410 | |||
1411 | for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) | ||
1412 | { | ||
1413 | hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); | ||
1414 | ExitOnFailure(hr, "failed to allocate memory for temp filename"); | ||
1415 | |||
1416 | hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); | ||
1417 | if (INVALID_HANDLE_VALUE == hTempFile) | ||
1418 | { | ||
1419 | // if the file already exists, just try again | ||
1420 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
1421 | if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr) | ||
1422 | { | ||
1423 | hr = S_OK; | ||
1424 | continue; | ||
1425 | } | ||
1426 | ExitOnFailureDebugTrace(hr, "failed to create file: %ls", pwzTempFile); | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | if (phTempFile) | ||
1431 | { | ||
1432 | *phTempFile = hTempFile; | ||
1433 | hTempFile = INVALID_HANDLE_VALUE; | ||
1434 | } | ||
1435 | |||
1436 | if (ppwzTempFile) | ||
1437 | { | ||
1438 | *ppwzTempFile = pwzTempFile; | ||
1439 | pwzTempFile = NULL; | ||
1440 | } | ||
1441 | |||
1442 | LExit: | ||
1443 | ReleaseFile(hTempFile); | ||
1444 | ReleaseStr(pwzTempFile); | ||
1445 | |||
1446 | return hr; | ||
1447 | } | ||
1448 | |||
1449 | |||
1450 | /******************************************************************* | ||
1451 | FileIsSame | ||
1452 | |||
1453 | ********************************************************************/ | ||
1454 | extern "C" HRESULT DAPI FileIsSame( | ||
1455 | __in LPCWSTR wzFile1, | ||
1456 | __in LPCWSTR wzFile2, | ||
1457 | __out LPBOOL lpfSameFile | ||
1458 | ) | ||
1459 | { | ||
1460 | HRESULT hr = S_OK; | ||
1461 | HANDLE hFile1 = NULL; | ||
1462 | HANDLE hFile2 = NULL; | ||
1463 | BY_HANDLE_FILE_INFORMATION fileInfo1 = { }; | ||
1464 | BY_HANDLE_FILE_INFORMATION fileInfo2 = { }; | ||
1465 | |||
1466 | hFile1 = ::CreateFileW(wzFile1, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); | ||
1467 | ExitOnInvalidHandleWithLastError(hFile1, hr, "Failed to open file 1. File = '%ls'", wzFile1); | ||
1468 | |||
1469 | hFile2 = ::CreateFileW(wzFile2, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); | ||
1470 | ExitOnInvalidHandleWithLastError(hFile2, hr, "Failed to open file 2. File = '%ls'", wzFile2); | ||
1471 | |||
1472 | if (!::GetFileInformationByHandle(hFile1, &fileInfo1)) | ||
1473 | { | ||
1474 | ExitWithLastError(hr, "Failed to get information for file 1. File = '%ls'", wzFile1); | ||
1475 | } | ||
1476 | |||
1477 | if (!::GetFileInformationByHandle(hFile2, &fileInfo2)) | ||
1478 | { | ||
1479 | ExitWithLastError(hr, "Failed to get information for file 2. File = '%ls'", wzFile2); | ||
1480 | } | ||
1481 | |||
1482 | *lpfSameFile = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber && | ||
1483 | fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh && | ||
1484 | fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow ? TRUE : FALSE; | ||
1485 | |||
1486 | LExit: | ||
1487 | ReleaseFile(hFile1); | ||
1488 | ReleaseFile(hFile2); | ||
1489 | |||
1490 | return hr; | ||
1491 | } | ||
1492 | |||
1493 | /******************************************************************* | ||
1494 | FileEnsureDelete - deletes a file, first removing read-only, | ||
1495 | hidden, or system attributes if necessary. | ||
1496 | ********************************************************************/ | ||
1497 | extern "C" HRESULT DAPI FileEnsureDelete( | ||
1498 | __in LPCWSTR wzFile | ||
1499 | ) | ||
1500 | { | ||
1501 | HRESULT hr = S_OK; | ||
1502 | |||
1503 | DWORD dwAttrib = INVALID_FILE_ATTRIBUTES; | ||
1504 | if (FileExistsEx(wzFile, &dwAttrib)) | ||
1505 | { | ||
1506 | if (dwAttrib & FILE_ATTRIBUTE_READONLY || dwAttrib & FILE_ATTRIBUTE_HIDDEN || dwAttrib & FILE_ATTRIBUTE_SYSTEM) | ||
1507 | { | ||
1508 | if (!::SetFileAttributesW(wzFile, FILE_ATTRIBUTE_NORMAL)) | ||
1509 | { | ||
1510 | ExitOnLastError(hr, "Failed to remove attributes from file: %ls", wzFile); | ||
1511 | } | ||
1512 | } | ||
1513 | |||
1514 | if (!::DeleteFileW(wzFile)) | ||
1515 | { | ||
1516 | ExitOnLastError(hr, "Failed to delete file: %ls", wzFile); | ||
1517 | } | ||
1518 | } | ||
1519 | |||
1520 | LExit: | ||
1521 | return hr; | ||
1522 | } | ||
1523 | |||
1524 | /******************************************************************* | ||
1525 | FileGetTime - Gets the file time of a specified file | ||
1526 | ********************************************************************/ | ||
1527 | extern "C" HRESULT DAPI FileGetTime( | ||
1528 | __in LPCWSTR wzFile, | ||
1529 | __out_opt LPFILETIME lpCreationTime, | ||
1530 | __out_opt LPFILETIME lpLastAccessTime, | ||
1531 | __out_opt LPFILETIME lpLastWriteTime | ||
1532 | ) | ||
1533 | { | ||
1534 | HRESULT hr = S_OK; | ||
1535 | HANDLE hFile = NULL; | ||
1536 | |||
1537 | hFile = ::CreateFileW(wzFile, FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); | ||
1538 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); | ||
1539 | |||
1540 | if (!::GetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) | ||
1541 | { | ||
1542 | ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); | ||
1543 | } | ||
1544 | |||
1545 | LExit: | ||
1546 | ReleaseFile(hFile); | ||
1547 | return hr; | ||
1548 | } | ||
1549 | |||
1550 | /******************************************************************* | ||
1551 | FileSetTime - Sets the file time of a specified file | ||
1552 | ********************************************************************/ | ||
1553 | extern "C" HRESULT DAPI FileSetTime( | ||
1554 | __in LPCWSTR wzFile, | ||
1555 | __in_opt const FILETIME *lpCreationTime, | ||
1556 | __in_opt const FILETIME *lpLastAccessTime, | ||
1557 | __in_opt const FILETIME *lpLastWriteTime | ||
1558 | ) | ||
1559 | { | ||
1560 | HRESULT hr = S_OK; | ||
1561 | HANDLE hFile = NULL; | ||
1562 | |||
1563 | hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); | ||
1564 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); | ||
1565 | |||
1566 | if (!::SetFileTime(hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime)) | ||
1567 | { | ||
1568 | ExitWithLastError(hr, "Failed to set file time for file. File = '%ls'", wzFile); | ||
1569 | } | ||
1570 | |||
1571 | LExit: | ||
1572 | ReleaseFile(hFile); | ||
1573 | return hr; | ||
1574 | } | ||
1575 | |||
1576 | /******************************************************************* | ||
1577 | FileReSetTime - ReSets a file's last acess and modified time to the | ||
1578 | creation time of the file | ||
1579 | ********************************************************************/ | ||
1580 | extern "C" HRESULT DAPI FileResetTime( | ||
1581 | __in LPCWSTR wzFile | ||
1582 | ) | ||
1583 | { | ||
1584 | HRESULT hr = S_OK; | ||
1585 | HANDLE hFile = NULL; | ||
1586 | FILETIME ftCreateTime; | ||
1587 | |||
1588 | hFile = ::CreateFileW(wzFile, FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); | ||
1589 | ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open file. File = '%ls'", wzFile); | ||
1590 | |||
1591 | if (!::GetFileTime(hFile, &ftCreateTime, NULL, NULL)) | ||
1592 | { | ||
1593 | ExitWithLastError(hr, "Failed to get file time for file. File = '%ls'", wzFile); | ||
1594 | } | ||
1595 | |||
1596 | if (!::SetFileTime(hFile, NULL, NULL, &ftCreateTime)) | ||
1597 | { | ||
1598 | ExitWithLastError(hr, "Failed to reset file time for file. File = '%ls'", wzFile); | ||
1599 | } | ||
1600 | |||
1601 | LExit: | ||
1602 | ReleaseFile(hFile); | ||
1603 | return hr; | ||
1604 | } | ||
1605 | |||
1606 | |||
1607 | /******************************************************************* | ||
1608 | FileExecutableArchitecture | ||
1609 | |||
1610 | *******************************************************************/ | ||
1611 | extern "C" HRESULT DAPI FileExecutableArchitecture( | ||
1612 | __in LPCWSTR wzFile, | ||
1613 | __out FILE_ARCHITECTURE *pArchitecture | ||
1614 | ) | ||
1615 | { | ||
1616 | HRESULT hr = S_OK; | ||
1617 | |||
1618 | HANDLE hFile = INVALID_HANDLE_VALUE; | ||
1619 | DWORD cbRead = 0; | ||
1620 | IMAGE_DOS_HEADER DosImageHeader = { }; | ||
1621 | IMAGE_NT_HEADERS NtImageHeader = { }; | ||
1622 | |||
1623 | hFile = ::CreateFileW(wzFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); | ||
1624 | if (hFile == INVALID_HANDLE_VALUE) | ||
1625 | { | ||
1626 | ExitWithLastError(hr, "Failed to open file: %ls", wzFile); | ||
1627 | } | ||
1628 | |||
1629 | if (!::ReadFile(hFile, &DosImageHeader, sizeof(DosImageHeader), &cbRead, NULL)) | ||
1630 | { | ||
1631 | ExitWithLastError(hr, "Failed to read DOS header from file: %ls", wzFile); | ||
1632 | } | ||
1633 | |||
1634 | if (DosImageHeader.e_magic != IMAGE_DOS_SIGNATURE) | ||
1635 | { | ||
1636 | hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); | ||
1637 | ExitOnRootFailure(hr, "Read invalid DOS header from file: %ls", wzFile); | ||
1638 | } | ||
1639 | |||
1640 | if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hFile, DosImageHeader.e_lfanew, NULL, FILE_BEGIN)) | ||
1641 | { | ||
1642 | ExitWithLastError(hr, "Failed to seek the NT header in file: %ls", wzFile); | ||
1643 | } | ||
1644 | |||
1645 | if (!::ReadFile(hFile, &NtImageHeader, sizeof(NtImageHeader), &cbRead, NULL)) | ||
1646 | { | ||
1647 | ExitWithLastError(hr, "Failed to read NT header from file: %ls", wzFile); | ||
1648 | } | ||
1649 | |||
1650 | if (NtImageHeader.Signature != IMAGE_NT_SIGNATURE) | ||
1651 | { | ||
1652 | hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); | ||
1653 | ExitOnRootFailure(hr, "Read invalid NT header from file: %ls", wzFile); | ||
1654 | } | ||
1655 | |||
1656 | if (IMAGE_SUBSYSTEM_NATIVE == NtImageHeader.OptionalHeader.Subsystem || | ||
1657 | IMAGE_SUBSYSTEM_WINDOWS_GUI == NtImageHeader.OptionalHeader.Subsystem || | ||
1658 | IMAGE_SUBSYSTEM_WINDOWS_CUI == NtImageHeader.OptionalHeader.Subsystem) | ||
1659 | { | ||
1660 | switch (NtImageHeader.FileHeader.Machine) | ||
1661 | { | ||
1662 | case IMAGE_FILE_MACHINE_I386: | ||
1663 | *pArchitecture = FILE_ARCHITECTURE_X86; | ||
1664 | break; | ||
1665 | case IMAGE_FILE_MACHINE_IA64: | ||
1666 | *pArchitecture = FILE_ARCHITECTURE_IA64; | ||
1667 | break; | ||
1668 | case IMAGE_FILE_MACHINE_AMD64: | ||
1669 | *pArchitecture = FILE_ARCHITECTURE_X64; | ||
1670 | break; | ||
1671 | default: | ||
1672 | hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); | ||
1673 | break; | ||
1674 | } | ||
1675 | } | ||
1676 | else | ||
1677 | { | ||
1678 | hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); | ||
1679 | } | ||
1680 | ExitOnFailure(hr, "Unexpected subsystem: %d machine type: %d specified in NT header from file: %ls", NtImageHeader.OptionalHeader.Subsystem, NtImageHeader.FileHeader.Machine, wzFile); | ||
1681 | |||
1682 | LExit: | ||
1683 | if (hFile != INVALID_HANDLE_VALUE) | ||
1684 | { | ||
1685 | ::CloseHandle(hFile); | ||
1686 | } | ||
1687 | |||
1688 | return hr; | ||
1689 | } | ||
1690 | |||
1691 | /******************************************************************* | ||
1692 | FileToString | ||
1693 | |||
1694 | *******************************************************************/ | ||
1695 | extern "C" HRESULT DAPI FileToString( | ||
1696 | __in_z LPCWSTR wzFile, | ||
1697 | __out LPWSTR *psczString, | ||
1698 | __out_opt FILE_ENCODING *pfeEncoding | ||
1699 | ) | ||
1700 | { | ||
1701 | HRESULT hr = S_OK; | ||
1702 | BYTE *pbFullFileBuffer = NULL; | ||
1703 | DWORD cbFullFileBuffer = 0; | ||
1704 | BOOL fNullCharFound = FALSE; | ||
1705 | LPWSTR sczFileText = NULL; | ||
1706 | |||
1707 | // Check if the file is ANSI | ||
1708 | hr = FileRead(&pbFullFileBuffer, &cbFullFileBuffer, wzFile); | ||
1709 | ExitOnFailure(hr, "Failed to read file: %ls", wzFile); | ||
1710 | |||
1711 | if (0 == cbFullFileBuffer) | ||
1712 | { | ||
1713 | *psczString = NULL; | ||
1714 | ExitFunction1(hr = S_OK); | ||
1715 | } | ||
1716 | |||
1717 | // UTF-8 BOM | ||
1718 | if (cbFullFileBuffer > sizeof(UTF8BOM) && 0 == memcmp(pbFullFileBuffer, UTF8BOM, sizeof(UTF8BOM))) | ||
1719 | { | ||
1720 | if (pfeEncoding) | ||
1721 | { | ||
1722 | *pfeEncoding = FILE_ENCODING_UTF8_WITH_BOM; | ||
1723 | } | ||
1724 | |||
1725 | hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer + 3), cbFullFileBuffer - 3, CP_UTF8); | ||
1726 | ExitOnFailure(hr, "Failed to convert file %ls from UTF-8 as its BOM indicated", wzFile); | ||
1727 | |||
1728 | *psczString = sczFileText; | ||
1729 | sczFileText = NULL; | ||
1730 | } | ||
1731 | // UTF-16 BOM, little endian (windows regular UTF-16) | ||
1732 | else if (cbFullFileBuffer > sizeof(UTF16BOM) && 0 == memcmp(pbFullFileBuffer, UTF16BOM, sizeof(UTF16BOM))) | ||
1733 | { | ||
1734 | if (pfeEncoding) | ||
1735 | { | ||
1736 | *pfeEncoding = FILE_ENCODING_UTF16_WITH_BOM; | ||
1737 | } | ||
1738 | |||
1739 | hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer + 2), (cbFullFileBuffer - 2) / sizeof(WCHAR)); | ||
1740 | ExitOnFailure(hr, "Failed to allocate copy of string"); | ||
1741 | } | ||
1742 | // No BOM, let's try to detect | ||
1743 | else | ||
1744 | { | ||
1745 | for (DWORD i = 0; i < cbFullFileBuffer; ++i) | ||
1746 | { | ||
1747 | if (pbFullFileBuffer[i] == '\0') | ||
1748 | { | ||
1749 | fNullCharFound = TRUE; | ||
1750 | break; | ||
1751 | } | ||
1752 | } | ||
1753 | |||
1754 | if (!fNullCharFound) | ||
1755 | { | ||
1756 | if (pfeEncoding) | ||
1757 | { | ||
1758 | *pfeEncoding = FILE_ENCODING_UTF8; | ||
1759 | } | ||
1760 | |||
1761 | hr = StrAllocStringAnsi(&sczFileText, reinterpret_cast<LPCSTR>(pbFullFileBuffer), cbFullFileBuffer, CP_UTF8); | ||
1762 | if (FAILED(hr)) | ||
1763 | { | ||
1764 | if (E_OUTOFMEMORY == hr) | ||
1765 | { | ||
1766 | ExitOnFailure(hr, "Failed to convert file %ls from UTF-8", wzFile); | ||
1767 | } | ||
1768 | } | ||
1769 | else | ||
1770 | { | ||
1771 | *psczString = sczFileText; | ||
1772 | sczFileText = NULL; | ||
1773 | } | ||
1774 | } | ||
1775 | else if (NULL == *psczString) | ||
1776 | { | ||
1777 | if (pfeEncoding) | ||
1778 | { | ||
1779 | *pfeEncoding = FILE_ENCODING_UTF16; | ||
1780 | } | ||
1781 | |||
1782 | hr = StrAllocString(psczString, reinterpret_cast<LPWSTR>(pbFullFileBuffer), cbFullFileBuffer / sizeof(WCHAR)); | ||
1783 | ExitOnFailure(hr, "Failed to allocate copy of string"); | ||
1784 | } | ||
1785 | } | ||
1786 | |||
1787 | LExit: | ||
1788 | ReleaseStr(sczFileText); | ||
1789 | ReleaseMem(pbFullFileBuffer); | ||
1790 | |||
1791 | return hr; | ||
1792 | } | ||
1793 | |||
1794 | /******************************************************************* | ||
1795 | FileFromString | ||
1796 | |||
1797 | *******************************************************************/ | ||
1798 | extern "C" HRESULT DAPI FileFromString( | ||
1799 | __in_z LPCWSTR wzFile, | ||
1800 | __in DWORD dwFlagsAndAttributes, | ||
1801 | __in_z LPCWSTR sczString, | ||
1802 | __in FILE_ENCODING feEncoding | ||
1803 | ) | ||
1804 | { | ||
1805 | HRESULT hr = S_OK; | ||
1806 | LPSTR sczUtf8String = NULL; | ||
1807 | BYTE *pbFullFileBuffer = NULL; | ||
1808 | const BYTE *pcbFullFileBuffer = NULL; | ||
1809 | DWORD cbFullFileBuffer = 0; | ||
1810 | DWORD cbStrLen = 0; | ||
1811 | |||
1812 | switch (feEncoding) | ||
1813 | { | ||
1814 | case FILE_ENCODING_UTF8: | ||
1815 | hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); | ||
1816 | ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); | ||
1817 | |||
1818 | cbFullFileBuffer = lstrlenA(sczUtf8String); | ||
1819 | pcbFullFileBuffer = reinterpret_cast<BYTE *>(sczUtf8String); | ||
1820 | break; | ||
1821 | case FILE_ENCODING_UTF8_WITH_BOM: | ||
1822 | hr = StrAnsiAllocString(&sczUtf8String, sczString, 0, CP_UTF8); | ||
1823 | ExitOnFailure(hr, "Failed to convert string to UTF-8 to write UTF-8 file"); | ||
1824 | |||
1825 | cbStrLen = lstrlenA(sczUtf8String); | ||
1826 | cbFullFileBuffer = sizeof(UTF8BOM) + cbStrLen; | ||
1827 | |||
1828 | pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE)); | ||
1829 | ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); | ||
1830 | |||
1831 | memcpy_s(pbFullFileBuffer, sizeof(UTF8BOM), UTF8BOM, sizeof(UTF8BOM)); | ||
1832 | memcpy_s(pbFullFileBuffer + sizeof(UTF8BOM), cbStrLen, sczUtf8String, cbStrLen); | ||
1833 | pcbFullFileBuffer = pbFullFileBuffer; | ||
1834 | break; | ||
1835 | case FILE_ENCODING_UTF16: | ||
1836 | cbFullFileBuffer = lstrlenW(sczString) * sizeof(WCHAR); | ||
1837 | pcbFullFileBuffer = reinterpret_cast<const BYTE *>(sczString); | ||
1838 | break; | ||
1839 | case FILE_ENCODING_UTF16_WITH_BOM: | ||
1840 | cbStrLen = lstrlenW(sczString) * sizeof(WCHAR); | ||
1841 | cbFullFileBuffer = sizeof(UTF16BOM) + cbStrLen; | ||
1842 | |||
1843 | pbFullFileBuffer = reinterpret_cast<BYTE *>(MemAlloc(cbFullFileBuffer, TRUE)); | ||
1844 | ExitOnNull(pbFullFileBuffer, hr, E_OUTOFMEMORY, "Failed to allocate memory for output file buffer"); | ||
1845 | |||
1846 | memcpy_s(pbFullFileBuffer, sizeof(UTF16BOM), UTF16BOM, sizeof(UTF16BOM)); | ||
1847 | memcpy_s(pbFullFileBuffer + sizeof(UTF16BOM), cbStrLen, sczString, cbStrLen); | ||
1848 | pcbFullFileBuffer = pbFullFileBuffer; | ||
1849 | break; | ||
1850 | } | ||
1851 | |||
1852 | hr = FileWrite(wzFile, dwFlagsAndAttributes, pcbFullFileBuffer, cbFullFileBuffer, NULL); | ||
1853 | ExitOnFailure(hr, "Failed to write file from string to: %ls", wzFile); | ||
1854 | |||
1855 | LExit: | ||
1856 | ReleaseStr(sczUtf8String); | ||
1857 | ReleaseMem(pbFullFileBuffer); | ||
1858 | |||
1859 | return hr; | ||
1860 | } | ||