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