summaryrefslogtreecommitdiff
path: root/toolkit/mozapps/update
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/update')
-rw-r--r--toolkit/mozapps/update/common/updatecommon.cpp4
-rw-r--r--toolkit/mozapps/update/common/updatedefines.h4
-rw-r--r--toolkit/mozapps/update/nsUpdateService.js137
-rw-r--r--toolkit/mozapps/update/updater/progressui.h3
-rw-r--r--toolkit/mozapps/update/updater/updater.cpp277
5 files changed, 412 insertions, 13 deletions
diff --git a/toolkit/mozapps/update/common/updatecommon.cpp b/toolkit/mozapps/update/common/updatecommon.cpp
index 17a57eae38..aff7d72602 100644
--- a/toolkit/mozapps/update/common/updatecommon.cpp
+++ b/toolkit/mozapps/update/common/updatecommon.cpp
@@ -41,6 +41,8 @@ void UpdateLog::Init(NS_tchar* sourcePath,
// updater process if the elevated updater process has written the log.
DeleteFileW(mDstFilePath);
}
+#elif XP_MACOSX
+ logFP = NS_tfopen(mDstFilePath, NS_T("w"));
#else
// On platforms that have an updates directory in the installation directory
// (e.g. platforms other than Windows and Mac) the update log is written to
@@ -59,7 +61,7 @@ void UpdateLog::Finish()
return;
}
-#if !defined(XP_WIN)
+#if !defined(XP_WIN) && !defined(XP_MACOSX)
const int blockSize = 1024;
char buffer[blockSize];
fflush(logFP);
diff --git a/toolkit/mozapps/update/common/updatedefines.h b/toolkit/mozapps/update/common/updatedefines.h
index 0db182219a..49fbde6f2f 100644
--- a/toolkit/mozapps/update/common/updatedefines.h
+++ b/toolkit/mozapps/update/common/updatedefines.h
@@ -100,6 +100,10 @@ static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...)
#endif
# include <dirent.h>
+#ifdef XP_MACOSX
+# include <sys/time.h>
+#endif
+
# define LOG_S "%s"
# define NS_T(str) str
# define NS_SLASH NS_T('/')
diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js
index 9bea453dbb..ce9be2c512 100644
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -315,6 +315,36 @@ function areDirectoryEntriesWriteable(aDir) {
}
/**
+ * OSX only function to determine if the user requires elevation to be able to
+ * write to the application bundle.
+ *
+ * @return true if elevation is required, false otherwise
+ */
+function getElevationRequired() {
+#ifdef XP_MACOSX
+ try {
+ // Recursively check that the application bundle (and its descendants) can
+ // be written to.
+ LOG("getElevationRequired - recursively testing write access on " +
+ getInstallDirRoot().path);
+ if (!getInstallDirRoot().isWritable() ||
+ !areDirectoryEntriesWriteable(getInstallDirRoot())) {
+ LOG("getElevationRequired - unable to write to application bundle, " +
+ "elevation required");
+ return true;
+ }
+ } catch (ex) {
+ LOG("getElevationRequired - unable to write to application bundle, " +
+ "elevation required. Exception: " + ex);
+ return true;
+ }
+ LOG("getElevationRequired - able to write to application bundle, elevation " +
+ "not required");
+#endif
+ return false;
+}
+
+/**
* Determines whether or not an update can be applied. This is always true on
* Windows when the service is used. Also, this is always true on OSX because we
* offer users the option to perform an elevated update when necessary.
@@ -322,6 +352,7 @@ function areDirectoryEntriesWriteable(aDir) {
* @return true if an update can be applied, false otherwise
*/
function getCanApplyUpdates() {
+#ifndef XP_MACOSX
try {
let updateTestFile = getUpdateFile([FILE_UPDATE_TEST]);
LOG("getCanApplyUpdates - testing write access " + updateTestFile.path);
@@ -400,6 +431,7 @@ function getCanApplyUpdates() {
// No write privileges to install directory
return false;
}
+#endif // !XP_MACOSX
LOG("getCanApplyUpdates - able to apply updates");
return true;
@@ -412,17 +444,28 @@ function getCanApplyUpdates() {
* @return true if updates can be staged for this session.
*/
XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession", function aus_gCSUS() {
+ if (getElevationRequired()) {
+ LOG("gCanStageUpdatesSession - unable to stage updates because elevation " +
+ "is required.");
+ return false;
+ }
+
try {
let updateTestFile;
+#ifdef XP_MACOSX
+ updateTestFile = getUpdateFile([FILE_UPDATE_TEST]);
+#else
updateTestFile = getInstallDirRoot();
updateTestFile.append(FILE_UPDATE_TEST);
+#endif
LOG("gCanStageUpdatesSession - testing write access " +
updateTestFile.path);
testWriteAccess(updateTestFile, true);
- // On all platforms, we need to test the parent directory as well,
- // as we need to be able to move files in that directory during the
+#ifndef XP_MACOSX
+ // On all platforms except Mac, we need to test the parent directory as
+ // well, as we need to be able to move files in that directory during the
// replacing step.
updateTestFile = getInstallDirRoot().parent;
updateTestFile.append(FILE_UPDATE_TEST);
@@ -431,6 +474,7 @@ XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession", function aus_gCSUS(
updateTestFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE,
FileUtils.PERMS_DIRECTORY);
updateTestFile.remove(false);
+#endif // !XP_MACOSX
} catch (e) {
LOG("gCanStageUpdatesSession - unable to stage updates. Exception: " +
e);
@@ -549,6 +593,10 @@ function getAppBaseDir() {
*/
function getInstallDirRoot() {
let dir = getAppBaseDir();
+#ifdef XP_MACOSX
+ // On Mac, we store the Updated.app directory inside the bundle directory.
+ dir = dir.parent.parent;
+#endif
return dir;
}
@@ -832,7 +880,31 @@ function handleUpdateFailure(update, errorCode) {
cancelations++;
Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations);
+#ifdef XP_MACOSX
+ let osxCancelations = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX, 0);
+ osxCancelations++;
+ Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX,
+ osxCancelations);
+ let maxCancels = Services.prefs.getIntPref(
+ PREF_APP_UPDATE_CANCELATIONS_OSX_MAX,
+ DEFAULT_CANCELATIONS_OSX_MAX);
+ // Prevent the preference from setting a value greater than 5.
+ maxCancels = Math.min(maxCancels, 5);
+ if (osxCancelations >= maxCancels) {
+ cleanupActiveUpdate();
+ } else {
+ writeStatusFile(getUpdatesDir(),
+ update.state = STATE_PENDING_ELEVATE);
+ }
+ update.statusText = gUpdateBundle.GetStringFromName("elevationFailure");
+ update.QueryInterface(Ci.nsIWritablePropertyBag);
+ update.setProperty("patchingFailed", "elevationFailure");
+ let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
+ createInstance(Ci.nsIUpdatePrompt);
+ prompter.showUpdateError(update);
+#else
writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
+#endif
return true;
}
@@ -1778,6 +1850,58 @@ UpdateService.prototype = {
});
let update = minorUpdate || majorUpdate;
+#ifdef XP_MACOSX
+ if (update) {
+ if (getElevationRequired()) {
+ let installAttemptVersion = Services.prefs.getCharPref(
+ PREF_APP_UPDATE_ELEVATE_VERSION,
+ "");
+ if (vc.compare(installAttemptVersion, update.appVersion) != 0) {
+ Services.prefs.setCharPref(PREF_APP_UPDATE_ELEVATE_VERSION,
+ update.appVersion);
+ if (Services.prefs.prefHasUserValue(
+ PREF_APP_UPDATE_CANCELATIONS_OSX)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
+ }
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
+ }
+ } else {
+ let numCancels = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX, 0);
+ let rejectedVersion = Services.prefs.getCharPref(PREF_APP_UPDATE_ELEVATE_NEVER, "");
+ let maxCancels = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX_MAX,
+ DEFAULT_CANCELATIONS_OSX_MAX);
+ if (numCancels >= maxCancels) {
+ LOG("UpdateService:selectUpdate - the user requires elevation to " +
+ "install this update, but the user has exceeded the max " +
+ "number of elevation attempts.");
+ update.elevationFailure = true;
+ } else if (vc.compare(rejectedVersion, update.appVersion) == 0) {
+ LOG("UpdateService:selectUpdate - the user requires elevation to " +
+ "install this update, but elevation is disabled for this " +
+ "version.");
+ update.elevationFailure = true;
+ } else {
+ LOG("UpdateService:selectUpdate - the user requires elevation to " +
+ "install the update.");
+ }
+ }
+ } else {
+ // Clear elevation-related prefs since they no longer apply (the user
+ // may have gained write access to the Firefox directory or an update
+ // was executed with a different profile).
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_VERSION)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_VERSION);
+ }
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
+ }
+ if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
+ }
+ }
+ }
+#endif
return update;
},
@@ -1895,8 +2019,7 @@ UpdateService.prototype = {
* See nsIUpdateService.idl
*/
get elevationRequired() {
- /** Mac Stub, but keeping this for now to not break the API **/
- return false;
+ return getElevationRequired();
},
/**
@@ -3147,7 +3270,11 @@ Downloader.prototype = {
"max fail: " + maxFail + ", " + "retryTimeout: " + retryTimeout);
if (Components.isSuccessCode(status)) {
if (this._verifyDownload()) {
- state = STATE_PENDING;
+ if (getElevationRequired()) {
+ state = STATE_PENDING_ELEVATE;
+ } else {
+ state = STATE_PENDING;
+ }
if (this.background) {
shouldShowPrompt = !getCanStageUpdates();
}
diff --git a/toolkit/mozapps/update/updater/progressui.h b/toolkit/mozapps/update/updater/progressui.h
index 6dc20e06bc..5462815dee 100644
--- a/toolkit/mozapps/update/updater/progressui.h
+++ b/toolkit/mozapps/update/updater/progressui.h
@@ -24,6 +24,9 @@ int InitProgressUI(int *argc, NS_tchar ***argv);
// Called on the main thread at startup
int ShowProgressUI(bool indeterminate = false, bool initUIStrings = true);
int InitProgressUIStrings();
+#elif defined(XP_MACOSX)
+ // Called on the main thread at startup
+ int ShowProgressUI(bool indeterminate = false);
#else
// Called on the main thread at startup
int ShowProgressUI();
diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp
index 13d829243f..f5f71935dc 100644
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -53,6 +53,9 @@
#include <algorithm>
#include "updatecommon.h"
+#ifdef XP_MACOSX
+#include "updaterfileutils_osx.h"
+#endif // XP_MACOSX
#include "mozilla/Compiler.h"
#include "mozilla/Types.h"
@@ -72,6 +75,23 @@
#define PARENT_WAIT 10000
#endif
+#if defined(XP_MACOSX)
+// These functions are defined in launchchild_osx.mm
+void CleanupElevatedMacUpdate(bool aFailureOccurred);
+bool IsOwnedByGroupAdmin(const char* aAppBundle);
+bool IsRecursivelyWritable(const char* aPath);
+void LaunchChild(int argc, const char** argv);
+void LaunchMacPostProcess(const char* aAppBundle);
+bool ObtainUpdaterArguments(int* argc, char*** argv);
+bool ServeElevatedUpdate(int argc, const char** argv);
+void SetGroupOwnershipAndPermissions(const char* aAppBundle);
+struct UpdateServerThreadArgs
+{
+ int argc;
+ const NS_tchar** argv;
+};
+#endif
+
#ifndef _O_BINARY
# define _O_BINARY 0
#endif
@@ -86,13 +106,14 @@
// We want to use execv to invoke the callback executable on platforms where
// we were launched using execv. See nsUpdateDriver.cpp.
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
#define USE_EXECV
#endif
# define MAYBE_USE_HARD_LINKS 0
-#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN)
+#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
+ !defined(XP_MACOSX)
#include "nss.h"
#include "prerror.h"
#endif
@@ -1675,6 +1696,21 @@ PatchFile::Execute()
AutoFile ofile(ensure_open(mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
ss.st_mode));
+#elif defined(XP_MACOSX)
+ AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
+ // Modified code from FileUtils.cpp
+ fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen};
+ // Try to get a continous chunk of disk space
+ rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
+ if (rv == -1) {
+ // OK, perhaps we are too fragmented, allocate non-continuous
+ store.fst_flags = F_ALLOCATEALL;
+ rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store);
+ }
+
+ if (rv != -1) {
+ ftruncate(fileno((FILE *)ofile), header.dlen);
+ }
#else
AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
#endif
@@ -2055,6 +2091,8 @@ LaunchCallbackApp(const NS_tchar *workingDir,
#if defined(USE_EXECV)
execv(argv[0], argv);
+#elif defined(XP_MACOSX)
+ LaunchChild(argc, (const char**)argv);
#elif defined(XP_WIN)
WinLaunchChild(argv[0], argc, argv, nullptr);
#else
@@ -2170,15 +2208,19 @@ CopyInstallDirToDestDir()
// These files should not be copied over to the updated app
#ifdef XP_WIN
#define SKIPLIST_COUNT 3
+#elif XP_MACOSX
+#define SKIPLIST_COUNT 0
#else
#define SKIPLIST_COUNT 2
#endif
copy_recursive_skiplist<SKIPLIST_COUNT> skiplist;
+#ifndef XP_MACOSX
skiplist.append(0, gInstallDirPath, NS_T("updated"));
skiplist.append(1, gInstallDirPath, NS_T("updates/0"));
#ifdef XP_WIN
skiplist.append(2, gInstallDirPath, NS_T("updated.update_in_progress.lock"));
#endif
+#endif
return ensure_copy_recursive(gInstallDirPath, gWorkingDirPath, skiplist);
}
@@ -2197,7 +2239,11 @@ ProcessReplaceRequest()
// 2. Move newDir to destDir. In case of failure, revert step 1 and abort.
// 3. Delete tmpDir (or defer it to the next reboot).
-#if XP_WIN
+#ifdef XP_MACOSX
+ NS_tchar destDir[MAXPATHLEN];
+ NS_tsnprintf(destDir, sizeof(destDir)/sizeof(destDir[0]),
+ NS_T("%s/Contents"), gInstallDirPath);
+#elif XP_WIN
// Windows preserves the case of the file/directory names. We use the
// GetLongPathName API in order to get the correct case for the directory
// name, so that if the user has used a different case when launching the
@@ -2217,8 +2263,13 @@ ProcessReplaceRequest()
NS_tchar newDir[MAXPATHLEN];
NS_tsnprintf(newDir, sizeof(newDir)/sizeof(newDir[0]),
+#ifdef XP_MACOSX
+ NS_T("%s/Contents"),
+ gWorkingDirPath);
+#else
NS_T("%s.bak/updated"),
gInstallDirPath);
+#endif
// First try to remove the possibly existing temp directory, because if this
// directory exists, we will fail to rename destDir.
@@ -2256,6 +2307,14 @@ ProcessReplaceRequest()
LOG(("Begin moving newDir (" LOG_S ") to destDir (" LOG_S ")",
newDir, destDir));
rv = rename_file(newDir, destDir, true);
+#ifdef XP_MACOSX
+ if (rv) {
+ LOG(("Moving failed. Begin copying newDir (" LOG_S ") to destDir (" LOG_S ")",
+ newDir, destDir));
+ copy_recursive_skiplist<0> skiplist;
+ rv = ensure_copy_recursive(newDir, destDir, skiplist);
+ }
+#endif
if (rv) {
LOG(("Moving newDir to destDir failed, err: %d", rv));
LOG(("Now, try to move tmpDir back to destDir"));
@@ -2269,7 +2328,7 @@ ProcessReplaceRequest()
return rv;
}
-#if !defined(XP_WIN)
+#if !defined(XP_WIN) && !defined(XP_MACOSX)
// Platforms that have their updates directory in the installation directory
// need to have the last-update.log and backup-update.log files moved from the
// old installation directory to the new installation directory.
@@ -2303,6 +2362,15 @@ ProcessReplaceRequest()
#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]),
+ NS_T("%s/Updated.app"), gPatchDirPath);
+ ensure_remove_recursive(updatedAppDir);
+#endif
+
gSucceeded = true;
return 0;
@@ -2412,7 +2480,11 @@ UpdateThreadFunc(void *param)
NS_tchar updateSettingsPath[MAX_TEXT_LEN];
NS_tsnprintf(updateSettingsPath,
sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]),
+#ifdef XP_MACOSX
+ NS_T("%s/Contents/Resources/update-settings.ini"),
+#else
NS_T("%s/update-settings.ini"),
+#endif
gWorkingDirPath);
MARChannelStringTable MARStrings;
if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
@@ -2484,6 +2556,15 @@ UpdateThreadFunc(void *param)
if (rv) {
LOG(("failed: %d", rv));
} else {
+#ifdef XP_MACOSX
+ // If the update was successful we need to update the timestamp on the
+ // top-level Mac OS X bundle directory so that Mac OS X's Launch Services
+ // picks up any major changes when the bundle is updated.
+ if (!sStagedUpdate && utimes(gInstallDirPath, nullptr) != 0) {
+ LOG(("Couldn't set access/modification time on application bundle."));
+ }
+#endif
+
LOG(("succeeded"));
}
WriteStatusFile(rv);
@@ -2493,11 +2574,34 @@ UpdateThreadFunc(void *param)
QuitProgressUI();
}
+#ifdef XP_MACOSX
+static void
+ServeElevatedUpdateThreadFunc(void* param)
+{
+ UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param;
+ gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv);
+ if (!gSucceeded) {
+ WriteStatusFile(ELEVATION_CANCELED);
+ }
+ QuitProgressUI();
+}
+
+void freeArguments(int argc, char** argv)
+{
+ for (int i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+#endif
+
int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv,
int callbackIndex
#ifdef XP_WIN
, const WCHAR* elevatedLockFilePath
, HANDLE updateLockFileHandle
+#elif XP_MACOSX
+ , bool isElevated
#endif
)
{
@@ -2509,11 +2613,19 @@ int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv,
}
}
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
+#elif XP_MACOSX
+ if (!isElevated) {
+ if (gSucceeded) {
+ LaunchMacPostProcess(gInstallDirPath);
+ }
#endif
LaunchCallbackApp(argv[5],
argc - callbackIndex,
argv + callbackIndex);
+#ifdef XP_MACOSX
+ } // if (!isElevated)
+#endif /* XP_MACOSX */
}
return 0;
}
@@ -2525,7 +2637,20 @@ int NS_main(int argc, NS_tchar **argv)
// argument prior to callbackIndex is the working directory.
const int callbackIndex = 6;
-#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN)
+#ifdef XP_MACOSX
+ bool isElevated =
+ strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0;
+ if (isElevated) {
+ if (!ObtainUpdaterArguments(&argc, &argv)) {
+ // Won't actually get here because ObtainUpdaterArguments will terminate
+ // the current process on failure.
+ return 1;
+ }
+ }
+#endif
+
+#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
+ !defined(XP_MACOSX)
// On Windows and Mac we rely on native APIs to do verifications so we don't
// need to initialize NSS at all there.
// Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS
@@ -2538,7 +2663,13 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
- InitProgressUI(&argc, &argv);
+#ifdef XP_MACOSX
+ if (!isElevated) {
+#endif
+ InitProgressUI(&argc, &argv);
+#ifdef XP_MACOSX
+ }
+#endif
// To process an update the updater command line must at a minimum have the
// directory path containing the updater.mar file to process as the first
@@ -2556,6 +2687,12 @@ int NS_main(int argc, NS_tchar **argv)
// launched.
if (argc < 4) {
fprintf(stderr, "Usage: updater patch-dir install-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
@@ -2564,6 +2701,12 @@ int NS_main(int argc, NS_tchar **argv)
// directory is invalid don't write the status file.
fprintf(stderr, "The patch directory path is not valid for this " \
"application (" LOG_S ")\n", argv[1]);
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
// The directory containing the update information.
@@ -2573,6 +2716,12 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(INVALID_INSTALL_DIR_PATH_ERROR);
fprintf(stderr, "The install directory path is not valid for this " \
"application (" LOG_S ")\n", argv[2]);
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
// The directory we're going to update to.
@@ -2635,6 +2784,12 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(INVALID_WORKING_DIR_PATH_ERROR);
fprintf(stderr, "The working directory path is not valid for this " \
"application (" LOG_S ")\n", argv[3]);
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
// The directory we're going to update to.
@@ -2653,6 +2808,12 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(INVALID_CALLBACK_PATH_ERROR);
fprintf(stderr, "The callback file path is not valid for this " \
"application (" LOG_S ")\n", argv[callbackIndex]);
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
@@ -2663,10 +2824,37 @@ int NS_main(int argc, NS_tchar **argv)
WriteStatusFile(INVALID_CALLBACK_DIR_ERROR);
fprintf(stderr, "The callback file must be located in the " \
"installation directory (" LOG_S ")\n", argv[callbackIndex]);
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
}
+#ifdef XP_MACOSX
+ if (!isElevated && !IsRecursivelyWritable(argv[2])) {
+ // If the app directory isn't recursively writeable, an elevated update is
+ // required.
+ UpdateServerThreadArgs threadArgs;
+ threadArgs.argc = argc;
+ threadArgs.argv = const_cast<const NS_tchar**>(argv);
+
+ Thread t1;
+ if (t1.Run(ServeElevatedUpdateThreadFunc, &threadArgs) == 0) {
+ // Show an indeterminate progress bar while an elevated update is in
+ // progress.
+ ShowProgressUI(true);
+ }
+ t1.Join();
+
+ LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex, false);
+ return gSucceeded ? 0 : 1;
+ }
+#endif
+
if (EnvHasValue("MOZ_OS_UPDATE")) {
sIsOSUpdate = true;
putenv(const_cast<char*>("MOZ_OS_UPDATE="));
@@ -2676,6 +2864,12 @@ int NS_main(int argc, NS_tchar **argv)
if (!WriteStatusFile("applying")) {
LOG(("failed setting status to 'applying'"));
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
@@ -2912,6 +3106,12 @@ int NS_main(int argc, NS_tchar **argv)
// Try to create the destination directory if it doesn't exist
int rv = NS_tmkdir(gWorkingDirPath, 0755);
if (rv != OK && errno != EEXIST) {
+#ifdef XP_MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
}
@@ -3152,7 +3352,11 @@ int NS_main(int argc, NS_tchar **argv)
// is an elevated process on OSX.
Thread t;
if (t.Run(UpdateThreadFunc, nullptr) == 0) {
- if (!sStagedUpdate && !sReplaceRequest) {
+ if (!sStagedUpdate && !sReplaceRequest
+#ifdef XP_MACOSX
+ && !isElevated
+#endif
+ ) {
ShowProgressUI();
}
}
@@ -3189,12 +3393,66 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif /* XP_WIN */
+#ifdef XP_MACOSX
+ // When the update is successful remove the precomplete file in the root of
+ // the application bundle and move the distribution directory from
+ // Contents/MacOS to Contents/Resources and if both exist delete the
+ // directory under Contents/MacOS (see Bug 1068439).
+ if (gSucceeded && !sStagedUpdate) {
+ NS_tchar oldPrecomplete[MAXPATHLEN];
+ NS_tsnprintf(oldPrecomplete, sizeof(oldPrecomplete)/sizeof(oldPrecomplete[0]),
+ NS_T("%s/precomplete"), gInstallDirPath);
+ NS_tremove(oldPrecomplete);
+
+ NS_tchar oldDistDir[MAXPATHLEN];
+ NS_tsnprintf(oldDistDir, sizeof(oldDistDir)/sizeof(oldDistDir[0]),
+ NS_T("%s/Contents/MacOS/distribution"), gInstallDirPath);
+ int rv = NS_taccess(oldDistDir, F_OK);
+ if (!rv) {
+ NS_tchar newDistDir[MAXPATHLEN];
+ NS_tsnprintf(newDistDir, sizeof(newDistDir)/sizeof(newDistDir[0]),
+ NS_T("%s/Contents/Resources/distribution"), gInstallDirPath);
+ rv = NS_taccess(newDistDir, F_OK);
+ if (!rv) {
+ LOG(("New distribution directory already exists... removing old " \
+ "distribution directory: " LOG_S, oldDistDir));
+ rv = ensure_remove_recursive(oldDistDir);
+ if (rv) {
+ LOG(("Removing old distribution directory failed - err: %d", rv));
+ }
+ } else {
+ LOG(("Moving old distribution directory to new location. src: " LOG_S \
+ ", dst:" LOG_S, oldDistDir, newDistDir));
+ rv = rename_file(oldDistDir, newDistDir, true);
+ if (rv) {
+ LOG(("Moving old distribution directory to new location failed - " \
+ "err: %d", rv));
+ }
+ }
+ }
+ }
+
+ if (isElevated) {
+ SetGroupOwnershipAndPermissions(gInstallDirPath);
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(false);
+ } else if (IsOwnedByGroupAdmin(gInstallDirPath)) {
+ // If the group ownership of the Firefox .app bundle was set to the "admin"
+ // group during a previous elevated update, we need to ensure that all files
+ // in the bundle have group ownership of "admin" as well as write permission
+ // for the group to not break updates in the future.
+ SetGroupOwnershipAndPermissions(gInstallDirPath);
+ }
+#endif /* XP_MACOSX */
+
LogFinish();
int retVal = LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex
#ifdef XP_WIN
, elevatedLockFilePath
, updateLockFileHandle
+#elif XP_MACOSX
+ , isElevated
#endif
);
@@ -3650,8 +3908,13 @@ int AddPreCompleteActions(ActionList *list)
return OK;
}
+#ifdef XP_MACOSX
+ mozilla::UniquePtr<NS_tchar[]> manifestPath(get_full_path(
+ NS_T("Contents/Resources/precomplete")));
+#else
mozilla::UniquePtr<NS_tchar[]> manifestPath(get_full_path(
NS_T("precomplete")));
+#endif
NS_tchar *rb = GetManifestContents(manifestPath.get());
if (rb == nullptr) {