Bug 1177861 - remove remaining unused declarations and cleanup update staging. r=spohl, a=rkothari
authorRobert Strong <robert.bugzilla@gmail.com>
Tue, 25 Aug 2015 21:32:51 -0700
changeset 260706 be6396f0eb7cba78a3895ff268181e925da70b44
parent 260705 60aec55a27cc173ca19ad47ed6a500c3b6cfc4fa
child 260709 61e613c47cc62470281fb1b3e54abac15bdb2a7c
push id143
push userrstrong@mozilla.com
push dateWed, 26 Aug 2015 04:33:10 +0000
treeherdermozilla-esr38@be6396f0eb7c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl, rkothari
bugs1177861
milestone38.2.0
Bug 1177861 - remove remaining unused declarations and cleanup update staging. r=spohl, a=rkothari
toolkit/components/maintenanceservice/certificatecheck.cpp
toolkit/components/maintenanceservice/certificatecheck.h
toolkit/components/maintenanceservice/moz.build
toolkit/components/maintenanceservice/registrycertificates.cpp
toolkit/components/maintenanceservice/registrycertificates.h
toolkit/components/maintenanceservice/workmonitor.cpp
toolkit/mozapps/update/common/certificatecheck.cpp
toolkit/mozapps/update/common/certificatecheck.h
toolkit/mozapps/update/common/errors.h
toolkit/mozapps/update/common/moz.build
toolkit/mozapps/update/common/registrycertificates.cpp
toolkit/mozapps/update/common/registrycertificates.h
toolkit/mozapps/update/common/sources.mozbuild
toolkit/mozapps/update/common/updatehelper.cpp
toolkit/mozapps/update/common/updatehelper.h
toolkit/mozapps/update/tests/TestAUSHelper.cpp
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
toolkit/mozapps/update/updater/updater.cpp
--- a/toolkit/components/maintenanceservice/moz.build
+++ b/toolkit/components/maintenanceservice/moz.build
@@ -2,19 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 Program('maintenanceservice')
 
 SOURCES += [
-    'certificatecheck.cpp',
     'maintenanceservice.cpp',
-    'registrycertificates.cpp',
     'servicebase.cpp',
     'serviceinstall.cpp',
     'workmonitor.cpp',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     USE_LIBS += [
         'updatecommon-standalone',
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -201,45 +201,26 @@ StartUpdateProcess(int argc,
   // across all OS if it's of no harm.
   if (argc >= index) {
     // Setting the desktop to blank will ensure no GUI is displayed
     si.lpDesktop = L"";
     si.dwFlags |= STARTF_USESHOWWINDOW;
     si.wShowWindow = SW_HIDE;
   }
 
-  // We move the updater.ini file out of the way because we will handle
-  // executing PostUpdate through the service.  We handle PostUpdate from
-  // the service because there are some per user things that happen that
-  // can't run in session 0 which we run updater.exe in.
-  // Once we are done running updater.exe we rename updater.ini back so
-  // that if there were any errors the next updater.exe will run correctly.
-  WCHAR updaterINI[MAX_PATH + 1];
-  WCHAR updaterINITemp[MAX_PATH + 1];
-  BOOL selfHandlePostUpdate = FALSE;
-  // We use the updater.ini from the same directory as the updater.exe
-  // because of background updates.
-  if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") &&
-      PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) {
-    selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp,
-                                       MOVEFILE_REPLACE_EXISTING);
-  }
-
   // Add an env var for MOZ_USING_SERVICE so the updater.exe can
   // do anything special that it needs to do for service updates.
   // Search in updater.cpp for more info on MOZ_USING_SERVICE.
   putenv(const_cast<char*>("MOZ_USING_SERVICE=1"));
   LOG(("Starting service with cmdline: %ls", cmdLine));
   processStarted = CreateProcessW(argv[0], cmdLine,
                                   nullptr, nullptr, FALSE,
                                   CREATE_DEFAULT_ERROR_MODE,
                                   nullptr,
                                   nullptr, &si, &pi);
-  // Empty value on putenv is how you remove an env variable in Windows
-  putenv(const_cast<char*>("MOZ_USING_SERVICE="));
 
   BOOL updateWasSuccessful = FALSE;
   if (processStarted) {
     // Wait for the updater process to finish
     LOG(("Process was started... waiting on result."));
     DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
     if (WAIT_TIMEOUT == waitRes) {
       // We waited a long period of time for updater.exe and it never finished
@@ -284,48 +265,18 @@ StartUpdateProcess(int argc,
       }
     }
   } else {
     DWORD lastError = GetLastError();
     LOG_WARN(("Could not create process as current user, "
               "updaterPath: %ls; cmdLine: %ls.  (%d)",
               argv[0], cmdLine, lastError));
   }
-
-  // Now that we're done with the update, restore back the updater.ini file
-  // We use it ourselves, and also we want it back in case we had any type
-  // of error so that the normal update process can use it.
-  if (selfHandlePostUpdate) {
-    MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
-
-    // Only run the PostUpdate if the update was successful
-    if (updateWasSuccessful && argc > index) {
-      LPCWSTR updateInfoDir = argv[1];
-      bool stagingUpdate = IsUpdateBeingStaged(argc, argv);
-
-      // Launch the PostProcess with admin access in session 0.  This is
-      // actually launching the post update process but it takes in the
-      // callback app path to figure out where to apply to.
-      // The PostUpdate process with user only access will be done inside
-      // the unelevated updater.exe after the update process is complete
-      // from the service.  We don't know here which session to start
-      // the user PostUpdate process from.
-      // Note that we don't need to do this if we're just staging the
-      // update in the background, as the PostUpdate step runs when
-      // performing the replacing in that case.
-      if (!stagingUpdate) {
-        LOG(("Launching post update process as the service in session 0."));
-        if (!LaunchWinPostProcess(installDir, updateInfoDir, true, nullptr)) {
-          LOG_WARN(("The post update process could not be launched."
-                    " installDir: %ls, updateInfoDir: %ls",
-                    installDir, updateInfoDir));
-        }
-      }
-    }
-  }
+  // Empty value on putenv is how you remove an env variable in Windows
+  putenv(const_cast<char*>("MOZ_USING_SERVICE="));
 
   free(cmdLine);
   return updateWasSuccessful;
 }
 
 /**
  * Processes a software update command
  *
rename from toolkit/components/maintenanceservice/certificatecheck.cpp
rename to toolkit/mozapps/update/common/certificatecheck.cpp
--- a/toolkit/components/maintenanceservice/certificatecheck.cpp
+++ b/toolkit/mozapps/update/common/certificatecheck.cpp
@@ -4,17 +4,17 @@
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <windows.h>
 #include <softpub.h>
 #include <wintrust.h>
 
 #include "certificatecheck.h"
-#include "servicebase.h"
+#include "updatelogging.h"
 
 #pragma comment(lib, "wintrust.lib")
 #pragma comment(lib, "crypt32.lib")
 
 static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
 
 /**
  * Checks to see if a file stored at filePath matches the specified info.
@@ -118,40 +118,40 @@ cleanup:
  * @param  infoToMatch  The acceptable information to match
  * @return FALSE if the info does not match or if any error occurs in the check
  */
 BOOL 
 DoCertificateAttributesMatch(PCCERT_CONTEXT certContext, 
                              CertificateCheckInfo &infoToMatch)
 {
   DWORD dwData;
-  LPTSTR szName = nullptr;
+  LPWSTR szName = nullptr;
 
   if (infoToMatch.issuer) {
     // Pass in nullptr to get the needed size of the issuer buffer.
     dwData = CertGetNameString(certContext, 
                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                CERT_NAME_ISSUER_FLAG, nullptr,
                                nullptr, 0);
 
     if (!dwData) {
       LOG_WARN(("CertGetNameString failed.  (%d)", GetLastError()));
       return FALSE;
     }
 
     // Allocate memory for Issuer name buffer.
-    LPTSTR szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
+    szName = (LPWSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
     if (!szName) {
       LOG_WARN(("Unable to allocate memory for issuer name.  (%d)",
                 GetLastError()));
       return FALSE;
     }
 
     // Get Issuer name.
-    if (!CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
+    if (!CertGetNameStringW(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
                            CERT_NAME_ISSUER_FLAG, nullptr, szName, dwData)) {
       LOG_WARN(("CertGetNameString failed.  (%d)", GetLastError()));
       LocalFree(szName);
       return FALSE;
     }
 
     // If the issuer does not match, return a failure.
     if (!infoToMatch.issuer ||
@@ -169,25 +169,25 @@ DoCertificateAttributesMatch(PCCERT_CONT
     dwData = CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                0, nullptr, nullptr, 0);
     if (!dwData) {
       LOG_WARN(("CertGetNameString failed.  (%d)", GetLastError()));
       return FALSE;
     }
 
     // Allocate memory for the name buffer.
-    szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
+    szName = (LPWSTR)LocalAlloc(LPTR, dwData * sizeof(WCHAR));
     if (!szName) {
       LOG_WARN(("Unable to allocate memory for subject name.  (%d)",
                 GetLastError()));
       return FALSE;
     }
 
     // Obtain the name.
-    if (!(CertGetNameString(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+    if (!(CertGetNameStringW(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
                             nullptr, szName, dwData))) {
       LOG_WARN(("CertGetNameString failed.  (%d)", GetLastError()));
       LocalFree(szName);
       return FALSE;
     }
 
     // If the issuer does not match, return a failure.
     if (!infoToMatch.name || 
@@ -200,33 +200,16 @@ DoCertificateAttributesMatch(PCCERT_CONT
     LocalFree(szName);
   }
 
   // If there were any errors we would have aborted by now.
   return TRUE;
 }
 
 /**
- * Duplicates the specified string
- *
- * @param  inputString The string to duplicate
- * @return The duplicated string which should be freed by the caller.
- */
-LPWSTR 
-AllocateAndCopyWideString(LPCWSTR inputString)
-{
-  LPWSTR outputString = 
-    (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR));
-  if (outputString) {
-    lstrcpyW(outputString, inputString);
-  }
-  return outputString;
-}
-
-/**
  * Verifies the trust of the specified file path.
  *
  * @param  filePath  The file path to check.
  * @return ERROR_SUCCESS if successful, or the last error code otherwise.
  */
 DWORD
 VerifyCertificateTrustForFile(LPCWSTR filePath)
 {
rename from toolkit/components/maintenanceservice/certificatecheck.h
rename to toolkit/mozapps/update/common/certificatecheck.h
--- a/toolkit/mozapps/update/common/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -63,16 +63,18 @@
 #define UNEXPECTED_BSPATCH_ERROR 41
 #define UNEXPECTED_FILE_OPERATION_ERROR 42
 #define FILESYSTEM_MOUNT_READWRITE_ERROR 43
 #define FOTA_GENERAL_ERROR 44
 #define FOTA_UNKNOWN_ERROR 45
 #define WRITE_ERROR_SHARING_VIOLATION_SIGNALED 46
 #define WRITE_ERROR_SHARING_VIOLATION_NOPROCESSFORPID 47
 #define WRITE_ERROR_SHARING_VIOLATION_NOPID 48
+#define REMOVE_FILE_SPEC_ERROR 71
+#define INVALID_STAGED_PARENT_ERROR 72
 
 // The following error codes are only used by updater.exe
 // when a fallback key exists and XPCShell tests are being run.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
 #define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
 #define FALLBACKKEY_LAUNCH_ERROR 104
--- a/toolkit/mozapps/update/common/moz.build
+++ b/toolkit/mozapps/update/common/moz.build
@@ -7,21 +7,23 @@
 EXPORTS += [
     'readstrings.h',
     'updatedefines.h',
     'updatelogging.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXPORTS += [
+        'certificatecheck.h',
         'pathhash.h',
+        'registrycertificates.h',
         'uachelper.h',
         'updatehelper.cpp',
         'updatehelper.h',
     ]
 
 Library('updatecommon')
 
+DEFINES['NS_NO_XPCOM'] = True
+
 srcdir = '.'
 
 include('sources.mozbuild')
-
-FINAL_LIBRARY = 'xul'
rename from toolkit/components/maintenanceservice/registrycertificates.cpp
rename to toolkit/mozapps/update/common/registrycertificates.cpp
--- a/toolkit/components/maintenanceservice/registrycertificates.cpp
+++ b/toolkit/mozapps/update/common/registrycertificates.cpp
@@ -3,18 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <windows.h>
 
 #include "registrycertificates.h"
 #include "pathhash.h"
-#include "nsWindowsHelpers.h"
-#include "servicebase.h"
+#include "updatelogging.h"
 #include "updatehelper.h"
 #define MAX_KEY_LENGTH 255
 
 /**
  * Verifies if the file path matches any certificate stored in the registry.
  *
  * @param  filePath The file path of the application to check if allowed.
  * @return TRUE if the binary matches any of the allowed certificates.
@@ -29,108 +28,114 @@ DoesBinaryMatchAllowedCertificates(LPCWS
   }
 
   // We use KEY_WOW64_64KEY to always force 64-bit view.
   // The user may have both x86 and x64 applications installed
   // which each register information.  We need a consistent place
   // to put those certificate attributes in and hence why we always
   // force the non redirected registry under Wow6432Node.
   // This flag is ignored on 32bit systems.
-  HKEY baseKeyRaw;
+  HKEY baseKey;
   LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
                                maintenanceServiceKey, 0, 
-                               KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
+                               KEY_READ | KEY_WOW64_64KEY, &baseKey);
   if (retCode != ERROR_SUCCESS) {
     LOG_WARN(("Could not open key.  (%d)", retCode));
     // Our tests run with a different apply directory for each test.
     // We use this registry key on our test slaves to store the 
     // allowed name/issuers.
     retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
                             TEST_ONLY_FALLBACK_KEY_PATH, 0,
-                            KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
+                            KEY_READ | KEY_WOW64_64KEY, &baseKey);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Could not open fallback key.  (%d)", retCode));
       return FALSE;
     }
   }
-  nsAutoRegKey baseKey(baseKeyRaw);
 
   // Get the number of subkeys.
   DWORD subkeyCount = 0;
   retCode = RegQueryInfoKeyW(baseKey, nullptr, nullptr, nullptr, &subkeyCount,
                              nullptr, nullptr, nullptr, nullptr, nullptr,
                              nullptr, nullptr);
   if (retCode != ERROR_SUCCESS) {
     LOG_WARN(("Could not query info key.  (%d)", retCode));
+    RegCloseKey(baseKey);
     return FALSE;
   }
 
   // Enumerate the subkeys, each subkey represents an allowed certificate.
   for (DWORD i = 0; i < subkeyCount; i++) { 
     WCHAR subkeyBuffer[MAX_KEY_LENGTH];
     DWORD subkeyBufferCount = MAX_KEY_LENGTH;  
     retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer, 
                             &subkeyBufferCount, nullptr, 
                             nullptr, nullptr, nullptr); 
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Could not enum certs.  (%d)", retCode));
+      RegCloseKey(baseKey);
       return FALSE;
     }
 
     // Open the subkey for the current certificate
-    HKEY subKeyRaw;
+    HKEY subKey;
     retCode = RegOpenKeyExW(baseKey, 
                             subkeyBuffer, 
                             0, 
                             KEY_READ | KEY_WOW64_64KEY, 
-                            &subKeyRaw);
-    nsAutoRegKey subKey(subKeyRaw);
+                            &subKey);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Could not open subkey.  (%d)", retCode));
       continue; // Try the next subkey
     }
 
     const int MAX_CHAR_COUNT = 256;
     DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
     WCHAR name[MAX_CHAR_COUNT] = { L'\0' };
     WCHAR issuer[MAX_CHAR_COUNT] = { L'\0' };
 
     // Get the name from the registry
     retCode = RegQueryValueExW(subKey, L"name", 0, nullptr, 
                                (LPBYTE)name, &valueBufSize);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Could not obtain name from registry.  (%d)", retCode));
+      RegCloseKey(subKey);
       continue; // Try the next subkey
     }
 
     // Get the issuer from the registry
     valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
     retCode = RegQueryValueExW(subKey, L"issuer", 0, nullptr, 
                                (LPBYTE)issuer, &valueBufSize);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Could not obtain issuer from registry.  (%d)", retCode));
+      RegCloseKey(subKey);
       continue; // Try the next subkey
     }
 
     CertificateCheckInfo allowedCertificate = {
       name, 
       issuer, 
     };
 
     retCode = CheckCertificateForPEFile(filePath, allowedCertificate);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Error on certificate check.  (%d)", retCode));
+      RegCloseKey(subKey);
       continue; // Try the next subkey
     }
 
     retCode = VerifyCertificateTrustForFile(filePath);
     if (retCode != ERROR_SUCCESS) {
       LOG_WARN(("Error on certificate trust check.  (%d)", retCode));
+      RegCloseKey(subKey);
       continue; // Try the next subkey
     }
 
+    RegCloseKey(baseKey);
     // Raise the roof, we found a match!
     return TRUE; 
   }
-  
+
+  RegCloseKey(baseKey);
   // No certificates match, :'(
   return FALSE;
 }
rename from toolkit/components/maintenanceservice/registrycertificates.h
rename to toolkit/mozapps/update/common/registrycertificates.h
--- a/toolkit/mozapps/update/common/sources.mozbuild
+++ b/toolkit/mozapps/update/common/sources.mozbuild
@@ -1,17 +1,19 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 sources = []
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     sources += [
+        'certificatecheck.cpp',
         'pathhash.cpp',
+        'registrycertificates.cpp',
         'uachelper.cpp',
         'updatehelper.cpp',
     ]
 
 sources += [
     'readstrings.cpp',
     'updatelogging.cpp',
 ]
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -27,18 +27,20 @@
 #include "mozilla/UniquePtr.h"
 
 // Needed for PathAppendW
 #include <shlwapi.h>
 
 using mozilla::MakeUnique;
 using mozilla::UniquePtr;
 
-WCHAR* MakeCommandLine(int argc, WCHAR **argv);
 BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
+BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,
+                            LPCWSTR siblingFilePath,
+                            LPCWSTR newFileName);
 
 /**
  * Obtains the path of a file in the same directory as the specified file.
  *
  * @param  destinationBuffer A buffer of size MAX_PATH + 1 to store the result.
  * @param  siblingFIlePath   The path of another file in the same directory
  * @param  newFileName       The filename of another file in the same directory
  * @return TRUE if successful
@@ -60,147 +62,16 @@ PathGetSiblingFilePath(LPWSTR destinatio
   if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) {
     return FALSE;
   }
 
   return PathAppendSafe(destinationBuffer, newFileName);
 }
 
 /**
- * Launch the post update application as the specified user (helper.exe).
- * It takes in the path of the callback application to calculate the path
- * of helper.exe.  For service updates this is called from both the system
- * account and the current user account.
- *
- * @param  installationDir The path to the callback application binary.
- * @param  updateInfoDir   The directory where update info is stored.
- * @param  forceSync       If true even if the ini file specifies async, the
- *                         process will wait for termination of PostUpdate.
- * @param  userToken       The user token to run as, if nullptr the current
- *                         user will be used.
- * @return TRUE if there was no error starting the process.
- */
-BOOL
-LaunchWinPostProcess(const WCHAR *installationDir,
-                     const WCHAR *updateInfoDir,
-                     bool forceSync,
-                     HANDLE userToken)
-{
-  WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
-  wcsncpy(workingDirectory, installationDir, MAX_PATH);
-
-  // Launch helper.exe to perform post processing (e.g. registry and log file
-  // modifications) for the update.
-  WCHAR inifile[MAX_PATH + 1] = { L'\0' };
-  wcsncpy(inifile, installationDir, MAX_PATH);
-  if (!PathAppendSafe(inifile, L"updater.ini")) {
-    return FALSE;
-  }
-
-  WCHAR exefile[MAX_PATH + 1];
-  WCHAR exearg[MAX_PATH + 1];
-  WCHAR exeasync[10];
-  bool async = true;
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
-                                exefile, MAX_PATH + 1, inifile)) {
-    return FALSE;
-  }
-
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
-                                MAX_PATH + 1, inifile)) {
-    return FALSE;
-  }
-
-  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
-                                exeasync,
-                                sizeof(exeasync)/sizeof(exeasync[0]),
-                                inifile)) {
-    return FALSE;
-  }
-
-  WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
-  wcsncpy(exefullpath, installationDir, MAX_PATH);
-  if (!PathAppendSafe(exefullpath, exefile)) {
-    return false;
-  }
-
-  WCHAR dlogFile[MAX_PATH + 1];
-  if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) {
-    return FALSE;
-  }
-
-  WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
-  wcsncpy(slogFile, updateInfoDir, MAX_PATH);
-  if (!PathAppendSafe(slogFile, L"update.log")) {
-    return FALSE;
-  }
-
-  WCHAR dummyArg[14] = { L'\0' };
-  wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
-
-  size_t len = wcslen(exearg) + wcslen(dummyArg);
-  WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
-  if (!cmdline) {
-    return FALSE;
-  }
-
-  wcsncpy(cmdline, dummyArg, len);
-  wcscat(cmdline, exearg);
-
-  if (forceSync ||
-      !_wcsnicmp(exeasync, L"false", 6) ||
-      !_wcsnicmp(exeasync, L"0", 2)) {
-    async = false;
-  }
-
-  // We want to launch the post update helper app to update the Windows
-  // registry even if there is a failure with removing the uninstall.update
-  // file or copying the update.log file.
-  CopyFileW(slogFile, dlogFile, false);
-
-  STARTUPINFOW si = {sizeof(si), 0};
-  si.lpDesktop = L"";
-  PROCESS_INFORMATION pi = {0};
-
-  bool ok;
-  if (userToken) {
-    ok = CreateProcessAsUserW(userToken,
-                              exefullpath,
-                              cmdline,
-                              nullptr,  // no special security attributes
-                              nullptr,  // no special thread attributes
-                              false,    // don't inherit filehandles
-                              0,        // No special process creation flags
-                              nullptr,  // inherit my environment
-                              workingDirectory,
-                              &si,
-                              &pi);
-  } else {
-    ok = CreateProcessW(exefullpath,
-                        cmdline,
-                        nullptr,  // no special security attributes
-                        nullptr,  // no special thread attributes
-                        false,    // don't inherit filehandles
-                        0,        // No special process creation flags
-                        nullptr,  // inherit my environment
-                        workingDirectory,
-                        &si,
-                        &pi);
-  }
-  free(cmdline);
-  if (ok) {
-    if (!async)
-      WaitForSingleObject(pi.hProcess, INFINITE);
-    CloseHandle(pi.hProcess);
-    CloseHandle(pi.hThread);
-  }
-  return ok;
-}
-
-/**
  * Starts the upgrade process for update of the service if it is
  * already installed.
  *
  * @param  installDir the installation directory where
  *         maintenanceservice_installer.exe is located.
  * @return TRUE if successful
  */
 BOOL
--- a/toolkit/mozapps/update/common/updatehelper.h
+++ b/toolkit/mozapps/update/common/updatehelper.h
@@ -1,18 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-BOOL LaunchWinPostProcess(const WCHAR *installationDir,
-                          const WCHAR *updateInfoDir,
-                          bool forceSync,
-                          HANDLE userToken);
 BOOL StartServiceUpdate(LPCWSTR installDir);
-BOOL GetUpdateDirectoryPath(LPWSTR path);
 DWORD LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR *argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 DWORD WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds);
 DWORD WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds);
 BOOL DoesFallbackKeyExist();
 BOOL IsLocalFile(LPCWSTR file, BOOL &isLocal);
 DWORD StartServiceCommand(int argc, LPCWSTR* argv);
 BOOL IsUnpromptedElevation(BOOL &isUnpromptedElevation);
--- a/toolkit/mozapps/update/tests/TestAUSHelper.cpp
+++ b/toolkit/mozapps/update/tests/TestAUSHelper.cpp
@@ -29,16 +29,17 @@
 # define NS_tfopen _wfopen
 # define NS_tstrcmp wcscmp
 # define NS_ttoi _wtoi
 # define NS_tstat _wstat
 # define NS_tgetcwd _wgetcwd
 # define LOG_S "%S"
 
 #include "../common/updatehelper.h"
+#include "../common/certificatecheck.h"
 
 #else
 # include <unistd.h>
 # define NS_main main
   typedef char NS_tchar;
 # define NS_T(str) str
 # define NS_tsnprintf snprintf
 # define NS_taccess access
@@ -117,71 +118,42 @@ CheckMsg(const NS_tchar *path, const cha
   mbuf[ms.st_size] = '\0';
   rb = mbuf;
 
   fclose(inFP);
   inFP = nullptr;
   return strcmp(rb, expected) == 0;
 }
 
-#ifdef XP_WIN
-/**
- * Verifies the trust of the specified file path.
- *
- * @param  filePath  The file path to check.
- * @return ERROR_SUCCESS if successful, or the last error code otherwise.
- */
-DWORD
-VerifyCertificateTrustForFile(LPCWSTR filePath)
-{
-  // Setup the file to check.
-  WINTRUST_FILE_INFO fileToCheck;
-  ZeroMemory(&fileToCheck, sizeof(fileToCheck));
-  fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO);
-  fileToCheck.pcwszFilePath = filePath;
-
-  // Setup what to check, we want to check it is signed and trusted.
-  WINTRUST_DATA trustData;
-  ZeroMemory(&trustData, sizeof(trustData));
-  trustData.cbStruct = sizeof(trustData);
-  trustData.pPolicyCallbackData = nullptr;
-  trustData.pSIPClientData = nullptr;
-  trustData.dwUIChoice = WTD_UI_NONE;
-  trustData.fdwRevocationChecks = WTD_REVOKE_NONE; 
-  trustData.dwUnionChoice = WTD_CHOICE_FILE;
-  trustData.dwStateAction = 0;
-  trustData.hWVTStateData = nullptr;
-  trustData.pwszURLReference = nullptr;
-  // no UI
-  trustData.dwUIContext = 0;
-  trustData.pFile = &fileToCheck;
-
-  GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
-  // Check if the file is signed by something that is trusted.
-  return WinVerifyTrust(nullptr, &policyGUID, &trustData);
-}
-
-#endif
-
 int NS_main(int argc, NS_tchar **argv)
 {
   if (argc == 2) {
     if (!NS_tstrcmp(argv[1], NS_T("post-update-async")) ||
         !NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
       NS_tchar exePath[MAXPATHLEN];
 #ifdef XP_WIN
       if (!::GetModuleFileNameW(0, exePath, MAXPATHLEN)) {
         return 1;
       }
 #else
       strcpy(exePath, argv[0]);
 #endif
       NS_tchar runFilePath[MAXPATHLEN];
       NS_tsnprintf(runFilePath, sizeof(runFilePath)/sizeof(runFilePath[0]),
                    NS_T("%s.running"), exePath);
+#ifdef XP_WIN
+      if (!NS_taccess(runFilePath, F_OK)) {
+        // This makes it possible to check if the post update process was
+        // launched twice which happens when the service performs an update.
+        NS_tchar runFilePathBak[MAXPATHLEN];
+        NS_tsnprintf(runFilePathBak, sizeof(runFilePathBak)/sizeof(runFilePathBak[0]),
+                     NS_T("%s.bak"), runFilePath);
+        MoveFileExW(runFilePath, runFilePathBak, MOVEFILE_REPLACE_EXISTING);
+      }
+#endif
       WriteMsg(runFilePath, "running");
 
       if (!NS_tstrcmp(argv[1], NS_T("post-update-sync"))) {
 #ifdef XP_WIN
         Sleep(2000);
 #else
         sleep(2);
 #endif
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateAppBinInUseStageSuccessSvc_win.js
@@ -19,17 +19,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
-  createUpdaterINI(true);
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI(true);
 
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING_SVC);
@@ -188,22 +189,24 @@ function checkUpdateApplied() {
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateFinished() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateFinished;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateFinished();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateFinished;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateFinished();
+//  }
+    do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateFinished);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateFinished() {
   gTimeoutRuns++;
@@ -248,31 +251,47 @@ function finishCheckUpdateFinished() {
         } catch (e) {
           do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished);
           return;
         }
       }
     }
   }
 
+// Added to make this test pass on esr38
+  if (IS_WIN) {
+    // Don't proceed until the tobedeleted dir no longer exists.
+    let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true);
+    if (toBeDeletedDir.exists()) {
+      if (gTimeoutRuns > MAX_TIMEOUT_RUNS) {
+        do_throw("Exceeded while waiting for the tobedeleted dir to no " +
+                 "longer exist");
+      } else {
+        do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished);
+        return;
+      }
+    }
+  }
+
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
-  if (IS_WIN || IS_MACOSX) {
-    let running = getPostUpdateFile(".running");
-    logTestInfo("checking that the post update process running file exists. " +
-                "Path: " + running.path);
-    do_check_true(running.exists());
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    let running = getPostUpdateFile(".running");
+//    logTestInfo("checking that the post update process running file exists. " +
+//                "Path: " + running.path);
+//    do_check_true(running.exists());
+//  }
 
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   checkCallbackAppLog();
 
   standardInit();
 
   let update = gUpdateManager.getUpdateAt(0);
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
@@ -17,17 +17,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
-  createUpdaterINI(false);
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI(false);
 
   if (IS_WIN) {
     Services.prefs.setBoolPref(PREF_APP_UPDATE_SERVICE_ENABLED, true);
   }
 
   let channel = Services.prefs.getCharPref(PREF_APP_UPDATE_CHANNEL);
   let patches = getLocalPatchString(null, null, null, null, null, "true",
                                     STATE_PENDING_SVC);
@@ -163,22 +164,24 @@ function checkUpdateApplied() {
   do_timeout(TEST_CHECK_TIMEOUT, launchAppToApplyUpdate);
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateFinished() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateApplied;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateApplied();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateApplied;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateApplied();
+//  }
+  do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateApplied);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateApplied() {
   gTimeoutRuns++;
@@ -231,22 +234,23 @@ function finishCheckUpdateApplied() {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
-  if (IS_WIN || IS_MACOSX) {
-    let running = getPostUpdateFile(".running");
-    logTestInfo("checking that the post update process running file exists. " +
-                "Path: " + running.path);
-    do_check_true(running.exists());
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    let running = getPostUpdateFile(".running");
+//    logTestInfo("checking that the post update process running file exists. " +
+//                "Path: " + running.path);
+//    do_check_true(running.exists());
+//  }
 
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   gSwitchApp = true;
   checkUpdateLogContents();
   gSwitchApp = false;
   checkCallbackAppLog();
 
   standardInit();
--- a/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateSuccessSvc.js
@@ -17,17 +17,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
-  createUpdaterINI();
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI();
 
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
@@ -51,22 +52,25 @@ function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateFinished() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateFinished;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateFinished();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateFinished;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateFinished();
+//  }
+  do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateFinished);
+
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateFinished() {
   gTimeoutRuns++;
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessCompleteSvc.js
@@ -14,17 +14,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestFiles[gTestFiles.length - 1].originalContents = null;
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
-  createUpdaterINI(false);
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI(false);
 
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
@@ -77,44 +78,47 @@ function checkUpdateFinished() {
   });
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateApplied() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateApplied;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateApplied();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateApplied;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateApplied();
+//  }
+    do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateApplied);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
-  if (IS_WIN || IS_MACOSX) {
-    let running = getPostUpdateFile(".running");
-    logTestInfo("checking that the post update process running file exists. " +
-                "Path: " + running.path);
-    do_check_true(running.exists());
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    let running = getPostUpdateFile(".running");
+//    logTestInfo("checking that the post update process running file exists. " +
+//                "Path: " + running.path);
+//    do_check_true(running.exists());
+//  }
 
   if (IS_UNIX && !IS_MACOSX) {
     checkSymlink();
   }
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   checkCallbackAppLog();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marStageSuccessPartialSvc.js
@@ -14,17 +14,18 @@ function run_test() {
   setupTestCommon();
   gTestFiles = gTestFilesPartialSuccess;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR);
 
-  createUpdaterINI(false);
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI(false);
 
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
@@ -57,41 +58,44 @@ function checkUpdateFinished() {
   });
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateApplied() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateApplied;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateApplied();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateApplied;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateApplied();
+//  }
+  do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateApplied);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateApplied() {
   if (IS_MACOSX) {
     logTestInfo("testing last modified time on the apply to directory has " +
                 "changed after a successful update (bug 600098)");
     let now = Date.now();
     let applyToDir = getApplyDirFile();
     let timeDiff = Math.abs(applyToDir.lastModifiedTime - now);
     do_check_true(timeDiff < MAC_MAX_TIME_DIFFERENCE);
   }
 
-  if (IS_WIN || IS_MACOSX) {
-    let running = getPostUpdateFile(".running");
-    logTestInfo("checking that the post update process running file exists. " +
-                "Path: " + running.path);
-    do_check_true(running.exists());
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    let running = getPostUpdateFile(".running");
+//    logTestInfo("checking that the post update process running file exists. " +
+//                "Path: " + running.path);
+//    do_check_true(running.exists());
+//  }
 
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   checkUpdateLogContents(LOG_PARTIAL_SUCCESS);
   checkCallbackAppLog();
 }
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessCompleteSvc.js
@@ -10,17 +10,18 @@ function run_test() {
     return;
   }
 
   setupTestCommon();
   gTestFiles = gTestFilesCompleteSuccess;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
-  createUpdaterINI();
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI();
 
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on a successful
   // update (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
@@ -34,22 +35,24 @@ function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateFinished() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateFinished;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateFinished();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateFinished;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateFinished();
+//  }
+  do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateFinished);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateFinished() {
   if (IS_MACOSX) {
--- a/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
+++ b/toolkit/mozapps/update/tests/unit_service_updater/marSuccessPartialSvc.js
@@ -16,17 +16,18 @@ function run_test() {
   gTestFiles[gTestFiles.length - 1].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestFiles[gTestFiles.length - 2].originalContents = null;
   gTestFiles[gTestFiles.length - 2].compareContents = "FromPartial\n";
   gTestFiles[gTestFiles.length - 2].comparePerms = 0o644;
   gTestDirs = gTestDirsPartialSuccess;
   setupUpdaterTest(FILE_PARTIAL_MAR);
 
-  createUpdaterINI(true);
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  createUpdaterINI(true);
 
   // For Mac OS X set the last modified time for the root directory to a date in
   // the past to test that the last modified time is updated on all updates since
   // the precomplete file in the root of the bundle is renamed, etc. (bug 600098).
   if (IS_MACOSX) {
     let now = Date.now();
     let yesterday = now - (1000 * 60 * 60 * 24);
     let applyToDir = getApplyDirFile();
@@ -40,22 +41,24 @@ function setupAppFilesFinished() {
   runUpdateUsingService(STATE_PENDING_SVC, STATE_SUCCEEDED);
 }
 
 /**
  * Checks if the post update binary was properly launched for the platforms that
  * support launching post update process.
  */
 function checkUpdateFinished() {
-  if (IS_WIN || IS_MACOSX) {
-    gCheckFunc = finishCheckUpdateFinished;
-    checkPostUpdateAppLog();
-  } else {
-    finishCheckUpdateFinished();
-  }
+// This is commented out on mozilla-esr38 since it doesn't have the test updater
+//  if (IS_WIN || IS_MACOSX) {
+//    gCheckFunc = finishCheckUpdateFinished;
+//    checkPostUpdateAppLog();
+//  } else {
+//    finishCheckUpdateFinished();
+//  }
+  do_timeout(TEST_HELPER_TIMEOUT, finishCheckUpdateFinished);
 }
 
 /**
  * Checks if the update has finished and if it has finished performs checks for
  * the test.
  */
 function finishCheckUpdateFinished() {
   if (IS_MACOSX) {
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -117,27 +117,33 @@ static int ioprio_set(int which, int who
 
 # define MAYBE_USE_HARD_LINKS 1
 static bool sUseHardLinks = true;
 #else
 # define MAYBE_USE_HARD_LINKS 0
 #endif
 
 #ifdef XP_WIN
+#include "registrycertificates.h"
+BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
+BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,
+                            LPCWSTR siblingFilePath,
+                            LPCWSTR newFileName);
 #include "updatehelper.h"
 
 // Closes the handle if valid and if the updater is elevated returns with the
 // return code specified. This prevents multiple launches of the callback
 // application by preventing the elevated process from launching the callback.
 #define EXIT_WHEN_ELEVATED(path, handle, retCode) \
   { \
       if (handle != INVALID_HANDLE_VALUE) { \
         CloseHandle(handle); \
       } \
       if (_waccess(path, F_OK) == 0 && NS_tremove(path) != 0) { \
+        LogFinish(); \
         return retCode; \
       } \
   }
 #endif
 
 //-----------------------------------------------------------------------------
 
 // This variable lives in libbz2.  It's declared in bzlib_private.h, so we just
@@ -1810,16 +1816,140 @@ IsUpdateFromMetro(int argc, NS_tchar **a
   for (int i = 0; i < argc; i++) {
     if (!wcsicmp(L"-ServerName:DefaultBrowserServer", argv[i])) {
       return true;
     }
   }
   return false;
 }
 #endif
+
+/**
+ * Launch the post update application (helper.exe). It takes in the path of the
+ * callback application to calculate the path of helper.exe. For service updates
+ * this is called from both the system account and the current user account.
+ *
+ * @param  installationDir The path to the callback application binary.
+ * @param  updateInfoDir   The directory where update info is stored.
+ * @return true if there was no error starting the process.
+ */
+bool
+LaunchWinPostProcess(const WCHAR *installationDir,
+                     const WCHAR *updateInfoDir)
+{
+  WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
+  wcsncpy(workingDirectory, installationDir, MAX_PATH);
+
+  // Launch helper.exe to perform post processing (e.g. registry and log file
+  // modifications) for the update.
+  WCHAR inifile[MAX_PATH + 1] = { L'\0' };
+  wcsncpy(inifile, installationDir, MAX_PATH);
+  if (!PathAppendSafe(inifile, L"updater.ini")) {
+    return false;
+  }
+
+  WCHAR exefile[MAX_PATH + 1];
+  WCHAR exearg[MAX_PATH + 1];
+  WCHAR exeasync[10];
+  bool async = true;
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
+                                exefile, MAX_PATH + 1, inifile)) {
+    return false;
+  }
+
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
+                                MAX_PATH + 1, inifile)) {
+    return false;
+  }
+
+  if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
+                                exeasync,
+                                sizeof(exeasync)/sizeof(exeasync[0]),
+                                inifile)) {
+    return false;
+  }
+
+  // Verify that exeFile doesn't contain relative paths
+  if (wcsstr(exefile, L"..") != nullptr) {
+    return false;
+  }
+
+  WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
+  wcsncpy(exefullpath, installationDir, MAX_PATH);
+  if (!PathAppendSafe(exefullpath, exefile)) {
+    return false;
+  }
+
+// TEST_UPDATER is not available on esr38
+//#if !defined(TEST_UPDATER)
+  if (sUsingService &&
+      !DoesBinaryMatchAllowedCertificates(installationDir, exefullpath)) {
+    return false;
+  }
+//#endif
+
+  WCHAR dlogFile[MAX_PATH + 1];
+  if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) {
+    return false;
+  }
+
+  WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
+  wcsncpy(slogFile, updateInfoDir, MAX_PATH);
+  if (!PathAppendSafe(slogFile, L"update.log")) {
+    return false;
+  }
+
+  WCHAR dummyArg[14] = { L'\0' };
+  wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
+
+  size_t len = wcslen(exearg) + wcslen(dummyArg);
+  WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
+  if (!cmdline) {
+    return false;
+  }
+
+  wcsncpy(cmdline, dummyArg, len);
+  wcscat(cmdline, exearg);
+
+  if (sUsingService ||
+      !_wcsnicmp(exeasync, L"false", 6) ||
+      !_wcsnicmp(exeasync, L"0", 2)) {
+    async = false;
+  }
+
+  // We want to launch the post update helper app to update the Windows
+  // registry even if there is a failure with removing the uninstall.update
+  // file or copying the update.log file.
+  CopyFileW(slogFile, dlogFile, false);
+
+  STARTUPINFOW si = {sizeof(si), 0};
+  si.lpDesktop = L"";
+  PROCESS_INFORMATION pi = {0};
+
+  bool ok = CreateProcessW(exefullpath,
+                           cmdline,
+                           nullptr,  // no special security attributes
+                           nullptr,  // no special thread attributes
+                           false,    // don't inherit filehandles
+                           0,        // No special process creation flags
+                           nullptr,  // inherit my environment
+                           workingDirectory,
+                           &si,
+                           &pi);
+  free(cmdline);
+  if (ok) {
+    if (!async) {
+      WaitForSingleObject(pi.hProcess, INFINITE);
+    }
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+  }
+  return ok;
+}
+
 #endif
 
 static void
 LaunchCallbackApp(const NS_tchar *workingDir,
                   int argc,
                   NS_tchar **argv,
                   bool usingService)
 {
@@ -2509,16 +2639,38 @@ int NS_main(int argc, NS_tchar **argv)
   } else if (sReplaceRequest) {
     LOG(("Performing a replace request"));
   }
 
   LOG(("PATCH DIRECTORY " LOG_S, gPatchDirPath));
   LOG(("INSTALLATION DIRECTORY " LOG_S, gInstallDirPath));
   LOG(("WORKING DIRECTORY " LOG_S, gWorkingDirPath));
 
+#if defined(XP_WIN)
+  if (sReplaceRequest || sStagedUpdate) {
+    NS_tchar stagedParent[MAX_PATH];
+    NS_tsnprintf(stagedParent, sizeof(stagedParent)/sizeof(stagedParent[0]),
+                 NS_T("%s"), gWorkingDirPath);
+    if (!PathRemoveFileSpecW(stagedParent)) {
+      WriteStatusFile(REMOVE_FILE_SPEC_ERROR);
+      LOG(("Error calling PathRemoveFileSpecW: %d", GetLastError()));
+      LogFinish();
+      return 1;
+    }
+
+    if (_wcsnicmp(stagedParent, gInstallDirPath, MAX_PATH) != 0) {
+      WriteStatusFile(INVALID_STAGED_PARENT_ERROR);
+      LOG(("Stage and Replace requests require that the working directory " \
+           "is a sub-directory of the installation directory! Exiting"));
+      LogFinish();
+      return 1;
+    }
+  }
+#endif
+
 #ifdef MOZ_WIDGET_GONK
   const char *prioEnv = getenv("MOZ_UPDATER_PRIO");
   if (prioEnv) {
     int32_t prioVal;
     int32_t oomScoreAdj;
     int32_t ioprioClass;
     int32_t ioprioLevel;
     if (sscanf(prioEnv, "%d/%d/%d/%d",
@@ -2805,17 +2957,17 @@ int NS_main(int argc, NS_tchar **argv)
       // current user's session of this unelevated updater.exe the
       // current process is running as.
       // Note that we don't need to do this if we're just staging the update,
       // as the PostUpdate step runs when performing the replacing in that case.
       if (useService && !sStagedUpdate) {
         bool updateStatusSucceeded = false;
         if (IsUpdateStatusSucceeded(updateStatusSucceeded) &&
             updateStatusSucceeded) {
-          if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath, false, nullptr)) {
+          if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
             fprintf(stderr, "The post update process which runs as the user"
                     " for service update could not be launched.");
           }
         }
       }
 
       // If we didn't want to use the service at all, or if an update was
       // already happening, or launching the service command failed, then
@@ -3199,28 +3351,28 @@ int NS_main(int argc, NS_tchar **argv)
   }
 #endif /* XP_MACOSX */
 
   LogFinish();
 
   if (argc > callbackIndex) {
 #if defined(XP_WIN)
     if (gSucceeded) {
+      if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
+        fprintf(stderr, "The post update process was not launched");
+      }
+
       // The service update will only be executed if it is already installed.
       // For first time installs of the service, the install will happen from
       // the PostUpdate process. We do the service update process here
       // because it's possible we are updating with updater.exe without the
       // service if the service failed to apply the update. We want to update
       // the service to a newer version in that case. If we are not running
       // through the service, then MOZ_USING_SERVICE will not exist.
       if (!sUsingService) {
-        if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath, false, nullptr)) {
-          LOG(("NS_main: The post update process could not be launched."));
-        }
-
         StartServiceUpdate(gInstallDirPath);
       }
     }
     EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
 #endif /* XP_WIN */
 #ifdef XP_MACOSX
     if (gSucceeded) {
       LaunchMacPostProcess(gInstallDirPath);