Bug 437349 - updater.exe lacks elevation manifest and fails to start when installer detection is disabled r=jmathies r=ted.mielczarek
authorRobert Strong <robert.bugzilla@gmail.com>
Mon, 23 Jun 2008 12:06:37 -0700
changeset 15484 9737aa36303755d1fd38beb7950dd7ca44efdf6c
parent 15483 9b33be091bf5d2c1f25ea8e5d75653d7f7ee1fa3
child 15485 9756a45384e63429723dafb1604452995a4d33b1
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies, ted.mielczarek
bugs437349
milestone1.9.1a1pre
Bug 437349 - updater.exe lacks elevation manifest and fails to start when installer detection is disabled r=jmathies r=ted.mielczarek
toolkit/mozapps/update/src/updater/updater.cpp
toolkit/mozapps/update/src/updater/updater.exe.manifest
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsUpdateDriver.cpp
--- a/toolkit/mozapps/update/src/updater/updater.cpp
+++ b/toolkit/mozapps/update/src/updater/updater.cpp
@@ -16,16 +16,17 @@
  * The Initial Developer of the Original Code is
  * Benjamin Smedberg <benjamin@smedbergs.us>
  *
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
+ *  Robert Strong <robert.bugzilla@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -1096,17 +1097,17 @@ LaunchWinPostProcess(const WCHAR *appExe
 
   static int    argc = 2;
   static WCHAR* argv[3] = {
     L"argv0ignoredbywinlaunchchild",
     exearg,
     L"\0"
   };
 
-  WinLaunchChild(exefullpath, argc, argv, 1);
+  WinLaunchChild(exefullpath, argc, argv, 0);
   free(argv);
 }
 #endif
 
 static void
 LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv)
 {
   putenv("NO_EM_RESTART=");
@@ -1115,17 +1116,17 @@ LaunchCallbackApp(const NS_tchar *workin
   // Run from the specified working directory (see bug 312360).
   NS_tchdir(workingDir);
 
 #if defined(USE_EXECV)
   execv(argv[0], argv);
 #elif defined(XP_MACOSX)
   LaunchChild(argc, argv);
 #elif defined(XP_WIN)
-  WinLaunchChild(argv[0], argc, argv, -1);
+  WinLaunchChild(argv[0], argc, argv, 0);
 #else
 # warning "Need implementaton of LaunchCallbackApp"
 #endif
 }
 
 static void
 WriteStatusFile(int status)
 {
@@ -1207,16 +1208,101 @@ int NS_main(int argc, NS_tchar **argv)
       Sleep(50);
     }
 #else
     int status;
     waitpid(pid, &status, 0);
 #endif
   }
 
+#ifdef XP_WIN
+  // Launch a second instance of the updater with the runas verb on Windows
+  // when write access is denied to the installation directory.
+
+  NS_tchar updateLockFilePath[MAXPATHLEN];
+  NS_tsnprintf(updateLockFilePath, MAXPATHLEN,
+               NS_T("%s/update_in_progress.lock"), argv[3]);
+
+  // The update_in_progress.lock file should only exist during an update. In
+  // case it exists attempt to remove it and exit if that fails to prevent
+  // simultaneous updates occurring.
+  if (!_waccess(updateLockFilePath, F_OK) &&
+      NS_tremove(updateLockFilePath) != 0) {
+    fprintf(stderr, "Update already in progress! Exiting\n");
+    return 1;
+  }
+
+  HANDLE updateLockFileHandle;
+  updateLockFileHandle = CreateFileW(updateLockFilePath,
+                                     GENERIC_READ | GENERIC_WRITE,
+                                     0,
+                                     NULL,
+                                     OPEN_ALWAYS,
+                                     FILE_FLAG_DELETE_ON_CLOSE,
+                                     NULL);
+
+  NS_tchar elevatedLockFilePath[MAXPATHLEN];
+  NS_tsnprintf(elevatedLockFilePath, MAXPATHLEN,
+               NS_T("%s/update_elevated.lock"), argv[1]);
+
+  if (updateLockFileHandle == INVALID_HANDLE_VALUE) {
+    if (!_waccess(elevatedLockFilePath, F_OK) &&
+        NS_tremove(elevatedLockFilePath) != 0) {
+      fprintf(stderr, "Update already elevated! Exiting\n");
+      return 1;
+    }
+
+    HANDLE elevatedFileHandle;
+    elevatedFileHandle = CreateFileW(elevatedLockFilePath,
+                                     GENERIC_READ | GENERIC_WRITE,
+                                     0,
+                                     NULL,
+                                     OPEN_ALWAYS,
+                                     FILE_FLAG_DELETE_ON_CLOSE,
+                                     NULL);
+
+    if (elevatedFileHandle == INVALID_HANDLE_VALUE) {
+      fprintf(stderr, "Unable to create elevated lock file! Exiting\n");
+      return 1;
+    }
+
+    PRUnichar *cmdLine = MakeCommandLine(argc - 1, argv + 1);
+    if (!cmdLine) {
+      CloseHandle(elevatedFileHandle);
+      return 1;
+    }
+
+    SHELLEXECUTEINFO sinfo;
+    memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO));
+    sinfo.cbSize       = sizeof(SHELLEXECUTEINFO);
+    sinfo.fMask        = SEE_MASK_NOASYNC |
+                         SEE_MASK_FLAG_NO_UI |
+                         SEE_MASK_NOCLOSEPROCESS;
+    sinfo.hwnd         = NULL;
+    sinfo.lpFile       = argv[0];
+    sinfo.lpParameters = cmdLine;
+    sinfo.lpVerb       = L"runas";
+    sinfo.nShow        = SW_SHOWNORMAL;
+
+    BOOL result = ShellExecuteEx(&sinfo);
+    free(cmdLine);
+
+    if (result) {
+      WaitForSingleObject(sinfo.hProcess, INFINITE);
+      CloseHandle(sinfo.hProcess);
+    }
+
+    if (argc > 4)
+      LaunchCallbackApp(argv[3], argc - 4, argv + 4);
+
+    CloseHandle(elevatedFileHandle);
+    return 0;
+  }
+#endif
+
   gSourcePath = argv[1];
 
   LogInit();
 
   // Run update process on a background thread.  ShowProgressUI may return
   // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
   // terminate.
   Thread t;
@@ -1224,16 +1310,22 @@ int NS_main(int argc, NS_tchar **argv)
     ShowProgressUI();
   t.Join();
 
   LogFinish();
 
 #ifdef XP_WIN
   if (gSucceeded && argc > 4)
     LaunchWinPostProcess(argv[4]);
+  CloseHandle(updateLockFileHandle);
+  // If elevated return early and let the process that launched this process
+  // launch the callback application.
+  if (!_waccess(elevatedLockFilePath, F_OK) &&
+      NS_tremove(elevatedLockFilePath) != 0)
+    return 0;
 #endif
 
   // The callback to execute is given as the last N arguments of our command
   // line.  The first of those arguments specifies the working directory for
   // the callback.
   if (argc > 4)
     LaunchCallbackApp(argv[3], argc - 4, argv + 4);
 
--- a/toolkit/mozapps/update/src/updater/updater.exe.manifest
+++ b/toolkit/mozapps/update/src/updater/updater.exe.manifest
@@ -14,9 +14,17 @@
                         name="Microsoft.Windows.Common-Controls"
                         version="6.0.0.0"
                         processorArchitecture="*"
                         publicKeyToken="6595b64144ccf1df"
                         language="*"
                 />
         </dependentAssembly>
 </dependency>
+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
+  <ms_asmv3:security>
+    <ms_asmv3:requestedPrivileges>
+      <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false">
+      </ms_asmv3:requestedExecutionLevel>
+    </ms_asmv3:requestedPrivileges>
+  </ms_asmv3:security>
+</ms_asmv3:trustInfo>
 </assembly>
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1539,18 +1539,17 @@ int OS2LaunchChild(const char *aExePath,
   return 0;
 }
 #endif
 
 // If aBlankCommandLine is true, then the application will be launched with a
 // blank command line instead of being launched with the same command line that
 // it was initially started with.
 static nsresult LaunchChild(nsINativeAppSupport* aNative,
-                            PRBool aBlankCommandLine = PR_FALSE,
-                            int needElevation = 0)
+                            PRBool aBlankCommandLine = PR_FALSE)
 {
   aNative->Quit(); // release DDE mutex, if we're holding it
 
   // Restart this process by exec'ing it into the current process
   // if supported by the platform.  Otherwise, use NSPR.
  
   if (aBlankCommandLine) {
     gRestartArgc = 1;
@@ -1568,17 +1567,17 @@ static nsresult LaunchChild(nsINativeApp
     return rv;
 
 #if defined(XP_WIN)
   nsAutoString exePath;
   rv = lf->GetPath(exePath);
   if (NS_FAILED(rv))
     return rv;
 
-  if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, needElevation))
+  if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv, 0))
     return NS_ERROR_FAILURE;
 
 #else
   nsCAutoString exePath;
   rv = lf->GetNativePath(exePath);
   if (NS_FAILED(rv))
     return rv;
 
@@ -3270,17 +3269,17 @@ XRE_main(int argc, char* argv[], const n
         }
       }
 #endif
 
 #ifdef MOZ_WIDGET_GTK2
       MOZ_gdk_display_close(display);
 #endif
 
-      rv = LaunchChild(nativeApp, appInitiatedRestart, upgraded ? -1 : 0);
+      rv = LaunchChild(nativeApp, appInitiatedRestart);
 
 #ifdef MOZ_CRASHREPORTER
       if (appData.flags & NS_XRE_ENABLE_CRASH_REPORTER)
         CrashReporter::UnsetExceptionHandler();
 #endif
 
       return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1;
     }
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -469,17 +469,17 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
 
 #if defined(USE_EXECV)
   chdir(applyToDir.get());
   execv(updaterPath.get(), argv);
 #elif defined(XP_WIN)
   _wchdir(applyToDir.get());
 
-  if (!WinLaunchChild(updaterPathW.get(), appArgc + 4, argv, 1))
+  if (!WinLaunchChild(updaterPathW.get(), appArgc + 4, argv, 0))
     return;
   _exit(0);
 #else
   PRStatus status;
   PRProcessAttr *attr;
   
   attr = PR_NewProcessAttr();
   if (!attr)