From 0c2b4cf3a439eda3e19d20fadfc65ddc7d0394c0 Mon Sep 17 00:00:00 2001
From: Rob Mensching <rob@firegiant.com>
Date: Sat, 10 Apr 2021 16:05:23 -0700
Subject: Integrate fixes that make dutil a little more robust to failure

---
 src/dutil/cabutil.cpp    |  52 +++++++++++++++----
 src/dutil/fileutil.cpp   | 128 +++++++++++++++++++++++++++++++++++++++++++++++
 src/dutil/inc/fileutil.h |   9 ++++
 src/dutil/inc/regutil.h  |  13 +++++
 src/dutil/regutil.cpp    |  59 +++++++++++++++++++++-
 src/dutil/wiutil.cpp     |  18 +++++--
 6 files changed, 265 insertions(+), 14 deletions(-)

diff --git a/src/dutil/cabutil.cpp b/src/dutil/cabutil.cpp
index 4a6f7b7b..5d77e483 100644
--- a/src/dutil/cabutil.cpp
+++ b/src/dutil/cabutil.cpp
@@ -102,6 +102,29 @@ LExit:
 }
 
 
+static HANDLE OpenFileWithRetry(
+    __in LPCWSTR wzPath,
+    __in DWORD dwDesiredAccess,
+    __in DWORD dwCreationDisposition
+)
+{
+    HANDLE hFile = INVALID_HANDLE_VALUE;
+
+    for (DWORD i = 0; i < 30; ++i)
+    {
+        hFile = ::CreateFileW(wzPath, dwDesiredAccess, FILE_SHARE_READ, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
+        if (INVALID_HANDLE_VALUE != hFile)
+        {
+            break;
+        }
+
+        ::Sleep(100);
+    }
+
+    return hFile;
+}
+
+
 /********************************************************************
  CabInitialize - initializes internal static variables
 
@@ -340,6 +363,7 @@ static __callback void DIAMONDAPI CabExtractFree(__in LPVOID pvData)
 static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __in int oflag, __in int pmode)
 {
     HRESULT hr = S_OK;
+    HANDLE hFile = INVALID_HANDLE_VALUE;
     INT_PTR pFile = -1;
     LPWSTR sczCabFile = NULL;
 
@@ -353,19 +377,24 @@ static __callback INT_PTR FAR DIAMONDAPI CabExtractOpen(__in_z PSTR pszFile, __i
     hr = StrAllocStringAnsi(&sczCabFile, pszFile, 0, CP_UTF8);
     CabExitOnFailure(hr, "Failed to convert UTF8 cab file name to wide character string");
 
-    pFile = reinterpret_cast<INT_PTR>(::CreateFileW(sczCabFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
-    if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
+    hFile = OpenFileWithRetry(sczCabFile, GENERIC_READ, OPEN_EXISTING);
+    if (INVALID_HANDLE_VALUE == hFile)
     {
         CabExitWithLastError(hr, "failed to open file: %ls", sczCabFile);
     }
 
+    pFile = reinterpret_cast<INT_PTR>(hFile);
+
     if (vdw64EmbeddedOffset)
     {
         hr = CabExtractSeek(pFile, 0, 0);
         CabExitOnFailure(hr, "Failed to seek to embedded offset %I64d", vdw64EmbeddedOffset);
     }
 
+    hFile = INVALID_HANDLE_VALUE;
+
 LExit:
+    ReleaseFileHandle(hFile);
     ReleaseStr(sczCabFile);
 
     return FAILED(hr) ? -1 : pFile;
@@ -460,6 +489,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
     Assert(pFDINotify->pv);
 
     HRESULT hr = S_OK;
+    HANDLE hFile = INVALID_HANDLE_VALUE;
     INT_PTR ipResult = 0;   // result to return on success
 
     CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
@@ -503,7 +533,6 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
                 CabExitWithLastError(hr, "failed to get time for resource: %ls", wz);
             }
             ::LocalFileTimeToFileTime(&ftLocal, &ft);
-            
 
             WCHAR wzPath[MAX_PATH];
             hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir);
@@ -511,21 +540,24 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
             hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
             CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
 
-            ipResult = reinterpret_cast<INT_PTR>(::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
-            if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(ipResult))
+            hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS);
+            if (INVALID_HANDLE_VALUE == hFile)
             {
                 CabExitWithLastError(hr, "failed to create file: %ls", wzPath);
             }
 
-            ::SetFileTime(reinterpret_cast<HANDLE>(ipResult), &ft, &ft, &ft);   // try to set the file time (who cares if it fails)
+            ::SetFileTime(hFile, &ft, &ft, &ft);   // try to set the file time (who cares if it fails)
 
-            if (::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), pFDINotify->cb, NULL, FILE_BEGIN))   // try to set the end of the file (don't worry if this fails)
+            if (::SetFilePointer(hFile, pFDINotify->cb, NULL, FILE_BEGIN))   // try to set the end of the file (don't worry if this fails)
             {
-                if (::SetEndOfFile(reinterpret_cast<HANDLE>(ipResult)))
+                if (::SetEndOfFile(hFile))
                 {
-                    ::SetFilePointer(reinterpret_cast<HANDLE>(ipResult), 0, NULL, FILE_BEGIN);  // reset the file pointer
+                    ::SetFilePointer(hFile, 0, NULL, FILE_BEGIN);  // reset the file pointer
                 }
             }
+
+            ipResult = reinterpret_cast<INT_PTR>(hFile);
+            hFile = INVALID_HANDLE_VALUE;
         }
         else  // resource wasn't requested, skip it
         {
@@ -579,5 +611,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
     };
 
 LExit:
+    ReleaseFileHandle(hFile);
+
     return (S_OK == hr) ? ipResult : -1;
 }
diff --git a/src/dutil/fileutil.cpp b/src/dutil/fileutil.cpp
index c76017de..06a44b45 100644
--- a/src/dutil/fileutil.cpp
+++ b/src/dutil/fileutil.cpp
@@ -1105,6 +1105,134 @@ LExit:
 }
 
 
+/*******************************************************************
+ FileCopyUsingHandlesWithProgress
+
+*******************************************************************/
+extern "C" HRESULT DAPI FileCopyUsingHandlesWithProgress(
+    __in HANDLE hSource,
+    __in HANDLE hTarget,
+    __in DWORD64 cbCopy,
+    __in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
+    __in_opt LPVOID lpData,
+    __in_opt LPBOOL pbCancel,
+    __out_opt DWORD64* pcbCopied
+)
+{
+    HRESULT hr = S_OK;
+    BOOL fStop = FALSE;
+    BOOL fCanceled = FALSE;
+    DWORD64 cbTotalCopied = 0;
+    BYTE rgbData[64 * 1024];
+    DWORD cbRead = 0;
+
+    LARGE_INTEGER liSourceSize = { };
+    LARGE_INTEGER liTotalCopied = { };
+    LARGE_INTEGER liZero = { };
+    DWORD dwResult = 0;
+
+    hr = FileSizeByHandle(hSource, &liSourceSize.QuadPart);
+    FileExitOnFailure(hr, "Failed to get size of source.");
+
+    if (0 < cbCopy && cbCopy < (DWORD64)liSourceSize.QuadPart)
+    {
+        liSourceSize.QuadPart = cbCopy;
+    }
+
+    dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_STREAM_SWITCH, hSource, hTarget, lpData);
+    switch (dwResult)
+    {
+    case PROGRESS_CONTINUE:
+        break;
+
+    case PROGRESS_CANCEL:
+        fCanceled = TRUE;
+        fStop = TRUE;
+        break;
+
+    case PROGRESS_STOP:
+        fStop = TRUE;
+        break;
+
+    case PROGRESS_QUIET:
+        lpProgressRoutine = NULL;
+        break;
+    }
+
+    // Set size of the target file.
+    ::SetFilePointerEx(hTarget, liSourceSize, NULL, FILE_BEGIN);
+
+    if (!::SetEndOfFile(hTarget))
+    {
+        FileExitWithLastError(hr, "Failed to set end of target file.");
+    }
+
+    if (!::SetFilePointerEx(hTarget, liZero, NULL, FILE_BEGIN))
+    {
+        FileExitWithLastError(hr, "Failed to reset target file pointer.");
+    }
+
+    // Copy with progress.
+    while (!fStop && (0 == cbCopy || cbTotalCopied < cbCopy))
+    {
+        cbRead = static_cast<DWORD>((0 == cbCopy) ? countof(rgbData) : min(countof(rgbData), cbCopy - cbTotalCopied));
+        if (!::ReadFile(hSource, rgbData, cbRead, &cbRead, NULL))
+        {
+            FileExitWithLastError(hr, "Failed to read from source.");
+        }
+
+        if (cbRead)
+        {
+            hr = FileWriteHandle(hTarget, rgbData, cbRead);
+            FileExitOnFailure(hr, "Failed to write to target.");
+
+            cbTotalCopied += cbRead;
+
+            if (lpProgressRoutine)
+            {
+                liTotalCopied.QuadPart = cbTotalCopied;
+                dwResult = lpProgressRoutine(liSourceSize, liTotalCopied, liZero, liZero, 0, CALLBACK_CHUNK_FINISHED, hSource, hTarget, lpData);
+                switch (dwResult)
+                {
+                case PROGRESS_CONTINUE:
+                    break;
+
+                case PROGRESS_CANCEL:
+                    fCanceled = TRUE;
+                    fStop = TRUE;
+                    break;
+
+                case PROGRESS_STOP:
+                    fStop = TRUE;
+                    break;
+
+                case PROGRESS_QUIET:
+                    lpProgressRoutine = NULL;
+                    break;
+                }
+            }
+        }
+        else
+        {
+            fStop = TRUE;
+        }
+    }
+
+LExit:
+    if (pbCancel)
+    {
+        *pbCancel = fCanceled;
+    }
+
+    if (pcbCopied)
+    {
+        *pcbCopied = cbTotalCopied;
+    }
+
+    return hr;
+}
+
+
 /*******************************************************************
  FileEnsureCopy
 
diff --git a/src/dutil/inc/fileutil.h b/src/dutil/inc/fileutil.h
index 7caa62b8..48830043 100644
--- a/src/dutil/inc/fileutil.h
+++ b/src/dutil/inc/fileutil.h
@@ -148,6 +148,15 @@ HRESULT DAPI FileCopyUsingHandles(
     __in DWORD64 cbCopy,
     __out_opt DWORD64* pcbCopied
     );
+HRESULT DAPI FileCopyUsingHandlesWithProgress(
+    __in HANDLE hSource,
+    __in HANDLE hTarget,
+    __in DWORD64 cbCopy,
+    __in_opt LPPROGRESS_ROUTINE lpProgressRoutine,
+    __in_opt LPVOID lpData,
+    __in_opt LPBOOL pbCancel,
+    __out_opt DWORD64* pcbCopied
+    );
 HRESULT DAPI FileEnsureCopy(
     __in_z LPCWSTR wzSource,
     __in_z LPCWSTR wzTarget,
diff --git a/src/dutil/inc/regutil.h b/src/dutil/inc/regutil.h
index 2f09d244..75284940 100644
--- a/src/dutil/inc/regutil.h
+++ b/src/dutil/inc/regutil.h
@@ -226,6 +226,19 @@ HRESULT DAPI RegQueryKey(
     __out_opt DWORD* pcSubKeys,
     __out_opt DWORD* pcValues
     );
+HRESULT DAPI RegKeyReadNumber(
+    __in HKEY hk,
+    __in_z LPCWSTR wzSubKey,
+    __in_z_opt LPCWSTR wzName,
+    __in BOOL f64Bit,
+    __out DWORD* pdwValue
+    );
+BOOL DAPI RegValueExists(
+    __in HKEY hk,
+    __in_z LPCWSTR wzSubKey,
+    __in_z_opt LPCWSTR wzName,
+    __in BOOL f64Bit
+    );
 
 #ifdef __cplusplus
 }
diff --git a/src/dutil/regutil.cpp b/src/dutil/regutil.cpp
index e1ef19e8..afd2d089 100644
--- a/src/dutil/regutil.cpp
+++ b/src/dutil/regutil.cpp
@@ -283,7 +283,7 @@ LExit:
 
 
 /********************************************************************
- RegKeyEnum - enumerates a registry key.
+ RegKeyEnum - enumerates child registry keys.
 
 *********************************************************************/
 extern "C" HRESULT DAPI RegKeyEnum(
@@ -340,7 +340,7 @@ LExit:
 
 
 /********************************************************************
- RegValueEnum - enumerates a registry value.
+ RegValueEnum - enumerates registry values.
 
 *********************************************************************/
 HRESULT DAPI RegValueEnum(
@@ -939,6 +939,61 @@ LExit:
     return hr;
 }
 
+/********************************************************************
+RegKeyReadNumber - reads a DWORD registry key value as a number from
+a specified subkey.
+
+*********************************************************************/
+extern "C" HRESULT DAPI RegKeyReadNumber(
+    __in HKEY hk,
+    __in_z LPCWSTR wzSubKey,
+    __in_z_opt LPCWSTR wzName,
+    __in BOOL f64Bit,
+    __out DWORD* pdwValue
+    )
+{
+    HRESULT hr = S_OK;
+    HKEY hkKey = NULL;
+
+    hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey);
+    RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
+
+    hr = RegReadNumber(hkKey, wzName, pdwValue);
+    RegExitOnFailure(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName);
+
+LExit:
+    ReleaseRegKey(hkKey);
+
+    return hr;
+}
+
+/********************************************************************
+RegValueExists - determines whether a named value exists in a 
+specified subkey.
+
+*********************************************************************/
+extern "C" BOOL DAPI RegValueExists(
+    __in HKEY hk,
+    __in_z LPCWSTR wzSubKey,
+    __in_z_opt LPCWSTR wzName,
+    __in BOOL f64Bit
+    )
+{
+    HRESULT hr = S_OK;
+    HKEY hkKey = NULL;
+    DWORD dwType = 0;
+
+    hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey);
+    RegExitOnFailure(hr, "Failed to open key: %ls", wzSubKey);
+
+    hr = RegGetType(hkKey, wzName, &dwType);
+    RegExitOnFailure(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName);
+
+LExit:
+    ReleaseRegKey(hkKey);
+
+    return SUCCEEDED(hr);
+}
 
 static HRESULT WriteStringToRegistry(
     __in HKEY hk,
diff --git a/src/dutil/wiutil.cpp b/src/dutil/wiutil.cpp
index ffbfe85a..f1984266 100644
--- a/src/dutil/wiutil.cpp
+++ b/src/dutil/wiutil.cpp
@@ -4,6 +4,7 @@
 
 
 // Exit macros
+#define WiuExitTrace(x, s, ...) ExitTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
 #define WiuExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
 #define WiuExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
 #define WiuExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_WIUTIL, x, s, __VA_ARGS__)
@@ -692,12 +693,23 @@ extern "C" HRESULT DAPI WiuEnumRelatedProductCodes(
 
         if (fReturnHighestVersionOnly)
         {
-            // get the version
+            // try to get the version but if the product registration is broken
+            // (for whatever reason), skip this product
             hr = WiuGetProductInfo(wzCurrentProductCode, L"VersionString", &sczInstalledVersion);
-            WiuExitOnFailure(hr, "Failed to get version for product code: %ls", wzCurrentProductCode);
+            if (FAILED(hr))
+            {
+                WiuExitTrace(hr, "Could not get product version for product code: %ls, skipping...", wzCurrentProductCode);
+                continue;
+            }
 
+            // try to parse the product version but if it is corrupt (for whatever
+            // reason), skip it
             hr = FileVersionFromStringEx(sczInstalledVersion, 0, &qwCurrentVersion);
-            WiuExitOnFailure(hr, "Failed to convert version: %ls to DWORD64 for product code: %ls", sczInstalledVersion, wzCurrentProductCode);
+            if (FAILED(hr))
+            {
+                WiuExitTrace(hr, "Could not convert version: %ls to DWORD64 for product code: %ls, skipping...", sczInstalledVersion, wzCurrentProductCode);
+                continue;
+            }
 
             // if this is the first product found then it is the highest version (for now)
             if (0 == *pcRelatedProducts)
-- 
cgit v1.2.3-55-g6feb