aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/fileutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dutil/fileutil.cpp')
-rw-r--r--src/dutil/fileutil.cpp1860
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
7const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF};
8const BYTE UTF16BOM[] = {0xFF, 0xFE};
9
10const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
11const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations";
12
13/*******************************************************************
14 FileFromPath - returns a pointer to the file part of the path
15
16********************************************************************/
17extern "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********************************************************************/
44extern "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
124LExit:
125 ReleaseStr(pwzFullPath);
126 ReleaseStr(pwzExpandedPath);
127
128 return hr;
129}
130
131
132/*******************************************************************
133FileStripExtension - Strip extension from filename
134********************************************************************/
135extern "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
174LExit:
175 ReleaseStr(pwzFileNameNoExtension);
176
177 return hr;
178}
179
180
181/*******************************************************************
182FileChangeExtension - Changes the extension of a filename
183********************************************************************/
184extern "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
204LExit:
205 ReleaseStr(sczFileName);
206
207 return hr;
208}
209
210
211/*******************************************************************
212FileAddSuffixToBaseName - Adds a suffix the base portion of a file
213name; e.g., file.ext to fileSuffix.ext.
214********************************************************************/
215extern "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
250LExit:
251 ReleaseStr(sczNewFileName);
252
253 return hr;
254}
255
256
257/*******************************************************************
258 FileVersion
259
260********************************************************************/
261extern "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
296LExit:
297 if (pVerBuffer)
298 {
299 ::GlobalFree(pVerBuffer);
300 }
301 return hr;
302}
303
304
305/*******************************************************************
306 FileVersionFromString
307
308*******************************************************************/
309extern "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
387LExit:
388 return hr;
389}
390
391
392/*******************************************************************
393 FileVersionFromStringEx
394
395*******************************************************************/
396extern "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
477LExit:
478 return hr;
479}
480
481/*******************************************************************
482 FileVersionFromStringEx - Formats the DWORD64 as a string version.
483
484*******************************************************************/
485extern "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
506LExit:
507 return hr;
508}
509
510/*******************************************************************
511 FileSetPointer - sets the file pointer.
512
513********************************************************************/
514extern "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
538LExit:
539 return hr;
540}
541
542
543/*******************************************************************
544 FileSize
545
546********************************************************************/
547extern "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
566LExit:
567 ReleaseFileHandle(hFile);
568
569 return hr;
570}
571
572
573/*******************************************************************
574 FileSizeByHandle
575
576********************************************************************/
577extern "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
595LExit:
596 return hr;
597}
598
599
600/*******************************************************************
601 FileExistsEx
602
603********************************************************************/
604extern "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********************************************************************/
638extern "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
692LExit:
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********************************************************************/
705extern "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
778LExit:
779 ReleaseStrArray(rgsczRenames, cRenames);
780 ReleaseRegKey(hkPendingFileRename);
781
782 return hr;
783}
784
785
786/*******************************************************************
787 FileRead - read a file into memory
788
789********************************************************************/
790extern "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********************************************************************/
804extern "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********************************************************************/
819extern "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********************************************************************/
835extern "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********************************************************************/
852extern "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
977LExit:
978 ReleaseMem(pbData);
979 ReleaseFile(hFile);
980
981 return hr;
982}
983
984
985/*******************************************************************
986 FileWrite - write a file from memory
987
988********************************************************************/
989extern "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
1013LExit:
1014 ReleaseFile(hFile);
1015
1016 return hr;
1017}
1018
1019
1020/*******************************************************************
1021 FileWriteHandle - write to a file handle from memory
1022
1023********************************************************************/
1024extern "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
1045LExit:
1046 return hr;
1047}
1048
1049
1050/*******************************************************************
1051 FileCopyUsingHandles
1052
1053*******************************************************************/
1054extern "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
1088LExit:
1089 return hr;
1090}
1091
1092
1093/*******************************************************************
1094 FileEnsureCopy
1095
1096*******************************************************************/
1097extern "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
1153LExit:
1154 return hr;
1155}
1156
1157
1158/*******************************************************************
1159 FileEnsureCopyWithRetry
1160
1161*******************************************************************/
1162extern "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
1191LExit:
1192 return hr;
1193}
1194
1195
1196/*******************************************************************
1197 FileEnsureMove
1198
1199*******************************************************************/
1200extern "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
1281LExit:
1282 return hr;
1283}
1284
1285
1286/*******************************************************************
1287 FileEnsureMoveWithRetry
1288
1289*******************************************************************/
1290extern "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
1315LExit:
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********************************************************************/
1325extern "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
1376LExit:
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*******************************************************************/
1389extern "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
1442LExit:
1443 ReleaseFile(hTempFile);
1444 ReleaseStr(pwzTempFile);
1445
1446 return hr;
1447}
1448
1449
1450/*******************************************************************
1451 FileIsSame
1452
1453********************************************************************/
1454extern "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
1486LExit:
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********************************************************************/
1497extern "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
1520LExit:
1521 return hr;
1522}
1523
1524/*******************************************************************
1525 FileGetTime - Gets the file time of a specified file
1526********************************************************************/
1527extern "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
1545LExit:
1546 ReleaseFile(hFile);
1547 return hr;
1548}
1549
1550/*******************************************************************
1551 FileSetTime - Sets the file time of a specified file
1552********************************************************************/
1553extern "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
1571LExit:
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********************************************************************/
1580extern "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
1601LExit:
1602 ReleaseFile(hFile);
1603 return hr;
1604}
1605
1606
1607/*******************************************************************
1608 FileExecutableArchitecture
1609
1610*******************************************************************/
1611extern "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
1682LExit:
1683 if (hFile != INVALID_HANDLE_VALUE)
1684 {
1685 ::CloseHandle(hFile);
1686 }
1687
1688 return hr;
1689}
1690
1691/*******************************************************************
1692 FileToString
1693
1694*******************************************************************/
1695extern "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
1787LExit:
1788 ReleaseStr(sczFileText);
1789 ReleaseMem(pbFullFileBuffer);
1790
1791 return hr;
1792}
1793
1794/*******************************************************************
1795 FileFromString
1796
1797*******************************************************************/
1798extern "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
1855LExit:
1856 ReleaseStr(sczUtf8String);
1857 ReleaseMem(pbFullFileBuffer);
1858
1859 return hr;
1860}