Bug 1127481 - Run the updater from the install directory instead of copying it. r=spohl, a=abillings
authorRobert Strong <robert.bugzilla@gmail.com>
Fri, 01 May 2015 20:26:41 -0400
changeset 260351 dd9d5b512e0e
parent 260349 a1efc72ea226
child 260352 538fd67bb637
push id759
push userryanvm@gmail.com
push date2015-05-02 00:27 +0000
treeherdermozilla-release@dd9d5b512e0e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl, abillings
bugs1127481
milestone38.0
Bug 1127481 - Run the updater from the install directory instead of copying it. r=spohl, a=abillings
toolkit/mozapps/update/tests/unit_aus_update/head_update.js
toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
toolkit/mozapps/update/tests/unit_service_updater/marAppApplyUpdateStageSuccessSvc.js
toolkit/mozapps/update/updater/updater.cpp
toolkit/xre/nsUpdateDriver.cpp
--- a/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
+++ b/toolkit/mozapps/update/tests/unit_aus_update/head_update.js
@@ -1476,28 +1476,33 @@ function runUpdate(aExpectedExitValue, a
   if (!updater.exists()) {
     updater = binDir.clone();
     updater.append(FILE_UPDATER_BIN);
     if (!updater.exists()) {
       do_throw("Unable to find updater binary!");
     }
   }
 
-  let updatesDir = getUpdatesPatchDir();
-  updater.copyToFollowingLinks(updatesDir, updater.leafName);
-  let updateBin = updatesDir.clone();
-  updateBin.append(updater.leafName);
-  if (updateBin.leafName == "updater.app") {
-    updateBin.append("Contents");
-    updateBin.append("MacOS");
-    updateBin.append("updater");
-    if (!updateBin.exists()) {
-      do_throw("Unable to find the updater executable!");
+   let updatesDir = getUpdatesPatchDir();
+  let updateBin;
+  if (IS_WIN) {
+    updateBin = updater.clone();
+  } else {
+    updater.copyToFollowingLinks(updatesDir, updater.leafName);
+    updateBin = updatesDir.clone();
+    updateBin.append(updater.leafName);
+    if (updateBin.leafName == "updater.app") {
+      updateBin.append("Contents");
+      updateBin.append("MacOS");
+      updateBin.append("updater");
     }
   }
+  if (!updateBin.exists()) {
+    do_throw("Unable to find the updater executable!");
+  }
 
   let applyToDir = getApplyDirFile(null, true);
   let applyToDirPath = applyToDir.path;
 
   let stageDir = getStageDirFile(null, true);
   let stageDirPath = stageDir.path;
 
   if (IS_WIN) {
@@ -2466,16 +2471,20 @@ function checkUpdateLogContents(aCompare
   if (IS_MACOSX) {
     // Skip lines that log moving the distribution directory for Mac v2 signing.
     updateLogContents = updateLogContents.replace(/Moving old [^\n]*\nrename_file: .*/g, "");
     updateLogContents = updateLogContents.replace(/New distribution directory .*/g, "");
   }
   if (gSwitchApp) {
     // Remove the lines which contain absolute paths
     updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, "");
+    updateLogContents = updateLogContents.replace(/^ensure_remove: failed to remove file: .*$/mg, "");
+    updateLogContents = updateLogContents.replace(/^ensure_remove_recursive: unable to remove directory: .*$/mg, "");
+    updateLogContents = updateLogContents.replace(/^Removing tmpDir failed, err: -1$/mg, "");
+    updateLogContents = updateLogContents.replace(/^remove_recursive_on_reboot: .*$/mg, "");
   }
   updateLogContents = updateLogContents.replace(/\r/g, "");
   // Replace error codes since they are different on each platform.
   updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n");
   // Replace to make the log parsing happy.
   updateLogContents = updateLogContents.replace(/non-fatal error /g, "");
   // The FindFile results when enumerating the filesystem on Windows is not
   // determistic so the results matching the following need to be ignored.
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppApplyUpdateStageSuccess.js
@@ -226,17 +226,17 @@ 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);
   }
 
-  checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
+  checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   gSwitchApp = true;
   checkUpdateLogContents();
   gSwitchApp = false;
   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
@@ -238,17 +238,17 @@ function finishCheckUpdateApplied() {
 
   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);
+  checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
   gSwitchApp = true;
   checkUpdateLogContents();
   gSwitchApp = false;
   checkCallbackAppLog();
 
   standardInit();
 
   let update = gUpdateManager.getUpdateAt(0);
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -464,17 +464,18 @@ static int ensure_remove(const NS_tchar 
   int rv = NS_tremove(path);
   if (rv)
     LOG(("ensure_remove: failed to remove file: " LOG_S ", rv: %d, err: %d",
          path, rv, errno));
   return rv;
 }
 
 // Remove the directory pointed to by path and all of its files and sub-directories.
-static int ensure_remove_recursive(const NS_tchar *path)
+static int ensure_remove_recursive(const NS_tchar *path,
+                                   bool continueEnumOnFailure = false)
 {
   // We use lstat rather than stat here so that we can successfully remove
   // symlinks.
   struct NS_tstat_t sInfo;
   int rv = NS_tlstat(path, &sInfo);
   if (rv) {
     // This error is benign
     return rv;
@@ -483,42 +484,42 @@ static int ensure_remove_recursive(const
     return ensure_remove(path);
   }
 
   NS_tDIR *dir;
   NS_tdirent *entry;
 
   dir = NS_topendir(path);
   if (!dir) {
-    LOG(("ensure_remove_recursive: path is not a directory: " LOG_S ", rv: %d, err: %d",
-         path, rv, errno));
+    LOG(("ensure_remove_recursive: unable to open directory: " LOG_S
+         ", rv: %d, err: %d", path, rv, errno));
     return rv;
   }
 
   while ((entry = NS_treaddir(dir)) != 0) {
     if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
         NS_tstrcmp(entry->d_name, NS_T(".."))) {
       NS_tchar childPath[MAXPATHLEN];
       NS_tsnprintf(childPath, sizeof(childPath)/sizeof(childPath[0]),
                    NS_T("%s/%s"), path, entry->d_name);
       rv = ensure_remove_recursive(childPath);
-      if (rv) {
+      if (rv && !continueEnumOnFailure) {
         break;
       }
     }
   }
 
   NS_tclosedir(dir);
 
   if (rv == OK) {
     ensure_write_permissions(path);
     rv = NS_trmdir(path);
     if (rv) {
-      LOG(("ensure_remove_recursive: path is not a directory: " LOG_S ", rv: %d, err: %d",
-           path, rv, errno));
+      LOG(("ensure_remove_recursive: unable to remove directory: " LOG_S
+           ", rv: %d, err: %d", path, rv, errno));
     }
   }
   return rv;
 }
 
 static bool is_read_only(const NS_tchar *flags)
 {
   size_t length = NS_tstrlen(flags);
@@ -840,16 +841,83 @@ static int rename_file(const NS_tchar *s
     LOG(("rename_file: failed to rename file - src: " LOG_S ", " \
          "dst:" LOG_S ", err: %d", spath, dpath, errno));
     return WRITE_ERROR;
   }
 
   return OK;
 }
 
+#ifdef XP_WIN
+// Remove the directory pointed to by path and all of its files and
+// sub-directories. If a file is in use move it to the tobedeleted directory
+// and attempt to schedule removal of the file on reboot
+static int remove_recursive_on_reboot(const NS_tchar *path, const NS_tchar *deleteDir)
+{
+  struct NS_tstat_t sInfo;
+  int rv = NS_tlstat(path, &sInfo);
+  if (rv) {
+    // This error is benign
+    return rv;
+  }
+
+  if (!S_ISDIR(sInfo.st_mode)) {
+    NS_tchar tmpDeleteFile[MAXPATHLEN];
+    GetTempFileNameW(deleteDir, L"rep", 0, tmpDeleteFile);
+    NS_tremove(tmpDeleteFile);
+    rv = rename_file(path, tmpDeleteFile, false);
+    if (MoveFileEx(rv ? path : tmpDeleteFile, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
+      LOG(("remove_recursive_on_reboot: file will be removed on OS reboot: "
+           LOG_S, rv ? path : tmpDeleteFile));
+    } else {
+      LOG(("remove_recursive_on_reboot: failed to schedule OS reboot removal of "
+           "file: " LOG_S, rv ? path : tmpDeleteFile));
+    }
+    return rv;
+  }
+
+  NS_tDIR *dir;
+  NS_tdirent *entry;
+
+  dir = NS_topendir(path);
+  if (!dir) {
+    LOG(("remove_recursive_on_reboot: unable to open directory: " LOG_S
+         ", rv: %d, err: %d",
+         path, rv, errno));
+    return rv;
+  }
+
+  while ((entry = NS_treaddir(dir)) != 0) {
+    if (NS_tstrcmp(entry->d_name, NS_T(".")) &&
+        NS_tstrcmp(entry->d_name, NS_T(".."))) {
+      NS_tchar childPath[MAXPATHLEN];
+      NS_tsnprintf(childPath, sizeof(childPath)/sizeof(childPath[0]),
+                   NS_T("%s/%s"), path, entry->d_name);
+      // There is no need to check the return value of this call since this
+      // function is only called after an update is successful and there is not
+      // much that can be done to recover if it isn't successful. There is also
+      // no need to log the value since it will have already been logged.
+      remove_recursive_on_reboot(childPath, deleteDir);
+    }
+  }
+
+  NS_tclosedir(dir);
+
+  if (rv == OK) {
+    ensure_write_permissions(path);
+    rv = NS_trmdir(path);
+    if (rv) {
+      LOG(("remove_recursive_on_reboot: unable to remove directory: " LOG_S
+           ", rv: %d, err: %d", path, rv, errno));
+    }
+  }
+  return rv;
+}
+#endif
+
 //-----------------------------------------------------------------------------
 
 // Create a backup of the specified file by renaming it.
 static int backup_create(const NS_tchar *path)
 {
   NS_tchar backup[MAXPATHLEN];
   NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
                NS_T("%s") BACKUP_EXT, path);
@@ -2012,26 +2080,30 @@ ProcessReplaceRequest()
     int rv2 = rename_file(tmpDir, destDir, true);
     if (rv2) {
       LOG(("Moving tmpDir back to destDir failed, err: %d", rv2));
     }
     return rv;
   }
 
   LOG(("Now, remove the tmpDir"));
-  rv = ensure_remove_recursive(tmpDir);
+  rv = ensure_remove_recursive(tmpDir, true);
   if (rv) {
     LOG(("Removing tmpDir failed, err: %d", rv));
 #ifdef XP_WIN
-    if (MoveFileExW(tmpDir, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
-      LOG(("tmpDir will be removed on OS reboot: " LOG_S, tmpDir));
-    } else {
-      LOG(("Failed to schedule OS reboot removal of directory: " LOG_S,
-           tmpDir));
+    NS_tchar deleteDir[MAXPATHLEN];
+    NS_tsnprintf(deleteDir, sizeof(deleteDir)/sizeof(deleteDir[0]),
+                 NS_T("%s\\%s"), destDir, DELETE_DIR);
+    // Attempt to remove the tobedeleted directory and then recreate it if it
+    // was successfully removed.
+    _wrmdir(deleteDir);
+    if (NS_taccess(deleteDir, F_OK)) {
+      NS_tmkdir(deleteDir, 0755);
     }
+    remove_recursive_on_reboot(tmpDir, deleteDir);
 #endif
   }
 
 #ifdef XP_MACOSX
   // On OS X, we we need to remove the staging directory after its Contents
   // directory has been moved.
   NS_tchar updatedAppDir[MAXPATHLEN];
   NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]),
@@ -2467,19 +2539,19 @@ int NS_main(int argc, NS_tchar **argv)
   if (pid > 0)
     waitpid(pid, nullptr, 0);
 #endif
 
   if (sReplaceRequest) {
 #ifdef XP_WIN
     // On Windows, the current working directory of the process should be changed
     // so that it's not locked.
-    NS_tchar tmpDir[MAXPATHLEN];
-    if (GetTempPathW(MAXPATHLEN, tmpDir)) {
-      NS_tchdir(tmpDir);
+    NS_tchar sysDir[MAX_PATH + 1] = { L'\0' };
+    if (GetSystemDirectoryW(sysDir, MAX_PATH + 1)) {
+      NS_tchdir(sysDir);
     }
 #endif
   }
 
   // The callback is the remaining arguments starting at callbackIndex.
   // The argument specified by callbackIndex is the callback executable and the
   // argument prior to callbackIndex is the working directory.
   const int callbackIndex = 6;
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -424,19 +424,20 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir
  */
 static void
 SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir,
                    nsIFile *appDir, int appArgc, char **appArgv)
 {
   nsresult rv;
 
   // Steps:
-  //  - copy updater into updates/0/MozUpdater/bgupdate/ dir
+  //  - copy updater into updates/0/MozUpdater/bgupdate/ dir on all platforms
+  //    except Windows
   //  - run updater with the correct arguments
-
+#ifndef XP_WIN
   nsCOMPtr<nsIFile> mozUpdaterDir;
   rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir));
   if (NS_FAILED(rv)) {
     LOG(("failed cloning update dir\n"));
     return;
   }
 
   // Create a new directory named MozUpdater in the updates/0 directory to copy
@@ -449,16 +450,17 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIF
   mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate"));
   mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
 
   nsCOMPtr<nsIFile> updater;
   if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) {
     LOG(("failed copying updater\n"));
     return;
   }
+#endif
 
   // We need to use the value returned from XRE_GetBinaryPath when attempting
   // to restart the running application.
   nsCOMPtr<nsIFile> appFile;
 
 #if defined(XP_MACOSX)
   // On OS X we need to pass the location of the xulrunner-stub executable
   // rather than xulrunner-bin. See bug 349737.
@@ -468,25 +470,38 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIF
 #endif
 
   if (!appFile)
     return;
 
 #ifdef XP_WIN
   nsAutoString appFilePathW;
   rv = appFile->GetPath(appFilePathW);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return;
+  }
   NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
 
+  nsCOMPtr<nsIFile> updater;
+  rv = greDir->Clone(getter_AddRefs(updater));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsDependentCString leaf(kUpdaterBin);
+  rv = updater->AppendNative(leaf);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   nsAutoString updaterPathW;
   rv = updater->GetPath(updaterPathW);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return;
-
+  }
   NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
 #else
 
   nsAutoCString appFilePath;
 #if defined(MOZ_WIDGET_GONK)
   appFilePath.Assign(kB2GServiceArgv[0]);
   appArgc = kB2GServiceArgc;
   appArgv = const_cast<char**>(kB2GServiceArgv);
@@ -700,24 +715,25 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
             nsIFile *appDir, int appArgc, char **appArgv,
             bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
             ProcessType *outpid)
 {
   nsresult rv;
 
   // Steps:
   //  - mark update as 'applying'
-  //  - copy updater into update dir
+  //  - copy updater into update dir on all platforms except Windows
   //  - run updater w/ appDir as the current working dir
-
+#ifndef XP_WIN
   nsCOMPtr<nsIFile> updater;
   if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
     LOG(("failed copying updater\n"));
     return;
   }
+#endif
 
   // We need to use the value returned from XRE_GetBinaryPath when attempting
   // to restart the running application.
   nsCOMPtr<nsIFile> appFile;
 
 #if defined(XP_MACOSX)
   // On OS X we need to pass the location of the xulrunner-stub executable
   // rather than xulrunner-bin. See bug 349737.
@@ -727,27 +743,39 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
 #endif
 
   if (!appFile)
     return;
 
 #ifdef XP_WIN
   nsAutoString appFilePathW;
   rv = appFile->GetPath(appFilePathW);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return;
+  }
   NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
 
+  nsCOMPtr<nsIFile> updater;
+  rv = greDir->Clone(getter_AddRefs(updater));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsDependentCString leaf(kUpdaterBin);
+  rv = updater->AppendNative(leaf);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
   nsAutoString updaterPathW;
   rv = updater->GetPath(updaterPathW);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return;
-
+  }
   NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
-
 #else
   nsAutoCString appFilePath;
   rv = appFile->GetNativePath(appFilePath);
   if (NS_FAILED(rv))
     return;
   
   nsAutoCString updaterPath;
   rv = updater->GetNativePath(updaterPath);