зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1732435 - r=nalexander,application-update-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D127894
This commit is contained in:
Родитель
3e2edea9fd
Коммит
b3546869e2
|
@ -801,25 +801,6 @@ BOOL ExecuteServiceCommand(int argc, LPWSTR* argv) {
|
||||||
// because the service self updates itself and the service
|
// because the service self updates itself and the service
|
||||||
// installer will stop the service.
|
// installer will stop the service.
|
||||||
LOG(("Service command %ls complete.", argv[2]));
|
LOG(("Service command %ls complete.", argv[2]));
|
||||||
} else if (!lstrcmpi(argv[2], L"fix-update-directory-perms")) {
|
|
||||||
bool gotInstallDir = true;
|
|
||||||
mozilla::UniquePtr<wchar_t[]> updateDir;
|
|
||||||
if (argc <= 3) {
|
|
||||||
LOG_WARN(("Didn't get an install dir for fix-update-directory-perms"));
|
|
||||||
gotInstallDir = false;
|
|
||||||
}
|
|
||||||
HRESULT permResult =
|
|
||||||
GetCommonUpdateDirectory(gotInstallDir ? argv[3] : nullptr,
|
|
||||||
SetPermissionsOf::AllFilesAndDirs, updateDir);
|
|
||||||
if (FAILED(permResult)) {
|
|
||||||
LOG_WARN(
|
|
||||||
("Unable to set the permissions on the update directory "
|
|
||||||
"('%S'): %d",
|
|
||||||
updateDir.get(), permResult));
|
|
||||||
result = FALSE;
|
|
||||||
} else {
|
|
||||||
result = TRUE;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOG_WARN(("Service command not recognized: %ls.", argv[2]));
|
LOG_WARN(("Service command not recognized: %ls.", argv[2]));
|
||||||
// result is already set to FALSE
|
// result is already set to FALSE
|
||||||
|
|
|
@ -301,14 +301,7 @@ const ONLY_INSTANCE_CHECK_DEFAULT_TIMEOUT_MS = 6 * 60 * 60 * 1000; // 6 hours
|
||||||
// that value to this so that the pref can't effectively disable the feature.
|
// that value to this so that the pref can't effectively disable the feature.
|
||||||
const ONLY_INSTANCE_CHECK_MAX_TIMEOUT_MS = 2 * 24 * 60 * 60 * 1000; // 2 days
|
const ONLY_INSTANCE_CHECK_MAX_TIMEOUT_MS = 2 * 24 * 60 * 60 * 1000; // 2 days
|
||||||
|
|
||||||
// Object to keep track of the current phase of the update and whether there
|
|
||||||
// has been a write failure for the phase so only one telemetry ping is made
|
|
||||||
// for the phase.
|
|
||||||
var gUpdateFileWriteInfo = { phase: null, failure: false };
|
|
||||||
var gUpdateMutexHandle = null;
|
var gUpdateMutexHandle = null;
|
||||||
// The permissions of the update directory should be fixed no more than once per
|
|
||||||
// session
|
|
||||||
var gUpdateDirPermissionFixAttempted = false;
|
|
||||||
// This is the file stream used for the log file.
|
// This is the file stream used for the log file.
|
||||||
var gLogfileOutputStream;
|
var gLogfileOutputStream;
|
||||||
// This value will be set to true if it appears that BITS is being used by
|
// This value will be set to true if it appears that BITS is being used by
|
||||||
|
@ -745,10 +738,6 @@ function getCanApplyUpdates() {
|
||||||
"access to the update directory. Exception: " +
|
"access to the update directory. Exception: " +
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
// Attempt to fix the update directory permissions. If successful the next
|
|
||||||
// time this function is called the write access check to the update
|
|
||||||
// directory will succeed.
|
|
||||||
fixUpdateDirectoryPermissions();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1171,10 +1160,7 @@ function readBinaryTransparencyResult(dir) {
|
||||||
function writeStatusFile(dir, state) {
|
function writeStatusFile(dir, state) {
|
||||||
let statusFile = dir.clone();
|
let statusFile = dir.clone();
|
||||||
statusFile.append(FILE_UPDATE_STATUS);
|
statusFile.append(FILE_UPDATE_STATUS);
|
||||||
let success = writeStringToFile(statusFile, state);
|
writeStringToFile(statusFile, state);
|
||||||
if (!success) {
|
|
||||||
handleCriticalWriteFailure(statusFile.path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1195,10 +1181,7 @@ function writeStatusFile(dir, state) {
|
||||||
function writeVersionFile(dir, version) {
|
function writeVersionFile(dir, version) {
|
||||||
let versionFile = dir.clone();
|
let versionFile = dir.clone();
|
||||||
versionFile.append(FILE_UPDATE_VERSION);
|
versionFile.append(FILE_UPDATE_VERSION);
|
||||||
let success = writeStringToFile(versionFile, version);
|
writeStringToFile(versionFile, version);
|
||||||
if (!success) {
|
|
||||||
handleCriticalWriteFailure(versionFile.path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1811,122 +1794,6 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
|
||||||
AUSTLMY.pingStateCode(suffix, stateCode);
|
AUSTLMY.pingStateCode(suffix, stateCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously fixes the update directory permissions. This is currently only
|
|
||||||
* available on Windows.
|
|
||||||
*
|
|
||||||
* @return true if the permission-fixing process was started, and false if the
|
|
||||||
* permission-fixing process was not started or the platform is not
|
|
||||||
* supported.
|
|
||||||
*/
|
|
||||||
function fixUpdateDirectoryPermissions() {
|
|
||||||
if (AppConstants.platform != "win") {
|
|
||||||
LOG(
|
|
||||||
"There is currently no implementation for fixing update directory " +
|
|
||||||
"permissions on this platform"
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gUpdateDirPermissionFixAttempted) {
|
|
||||||
// Never try to fix permissions more than one time during a session.
|
|
||||||
gUpdateDirPermissionFixAttempted = true;
|
|
||||||
|
|
||||||
LOG("Attempting to fix update directory permissions");
|
|
||||||
try {
|
|
||||||
Cc["@mozilla.org/updates/update-processor;1"]
|
|
||||||
.createInstance(Ci.nsIUpdateProcessor)
|
|
||||||
.fixUpdateDirectoryPerms(shouldUseService());
|
|
||||||
} catch (e) {
|
|
||||||
LOG(
|
|
||||||
"Attempt to fix update directory permissions failed. Exception: " + e
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function should be called whenever we fail to write to a file required
|
|
||||||
* for update to function. This function will, if possible, attempt to fix the
|
|
||||||
* file permissions. If the file permissions cannot be fixed, the user will be
|
|
||||||
* prompted to reinstall.
|
|
||||||
*
|
|
||||||
* All functionality happens asynchronously.
|
|
||||||
*
|
|
||||||
* Returns false if the permission-fixing process cannot be started. Since this
|
|
||||||
* is asynchronous, a true return value does not mean that the permissions were
|
|
||||||
* actually fixed.
|
|
||||||
*
|
|
||||||
* @param path A string representing the path that could not be written. This
|
|
||||||
* value will only be used for logging purposes.
|
|
||||||
*/
|
|
||||||
function handleCriticalWriteFailure(path) {
|
|
||||||
LOG(
|
|
||||||
"handleCriticalWriteFailure - Unable to write to critical update file: " +
|
|
||||||
path
|
|
||||||
);
|
|
||||||
if (!gUpdateFileWriteInfo.failure) {
|
|
||||||
gUpdateFileWriteInfo.failure = true;
|
|
||||||
let patchType = AUSTLMY.PATCH_UNKNOWN;
|
|
||||||
let update = UM.readyUpdate || UM.downloadingUpdate;
|
|
||||||
if (update) {
|
|
||||||
let patch = update.selectedPatch;
|
|
||||||
if (patch.type == "complete") {
|
|
||||||
patchType = AUSTLMY.PATCH_COMPLETE;
|
|
||||||
} else if (patch.type == "partial") {
|
|
||||||
patchType = AUSTLMY.PATCH_PARTIAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gUpdateFileWriteInfo.phase == "check") {
|
|
||||||
let updateServiceInstance = UpdateServiceFactory.createInstance();
|
|
||||||
let pingSuffix = updateServiceInstance._pingSuffix;
|
|
||||||
if (!pingSuffix) {
|
|
||||||
// If pingSuffix isn't defined then this this is a manual check which
|
|
||||||
// isn't recorded at this time.
|
|
||||||
AUSTLMY.pingCheckCode(pingSuffix, AUSTLMY.CHK_ERR_WRITE_FAILURE);
|
|
||||||
}
|
|
||||||
} else if (gUpdateFileWriteInfo.phase == "download") {
|
|
||||||
AUSTLMY.pingDownloadCode(patchType, AUSTLMY.DWNLD_ERR_WRITE_FAILURE);
|
|
||||||
} else if (gUpdateFileWriteInfo.phase == "stage") {
|
|
||||||
let suffix = patchType + "_" + AUSTLMY.STAGE;
|
|
||||||
AUSTLMY.pingStateCode(suffix, AUSTLMY.STATE_WRITE_FAILURE);
|
|
||||||
} else if (gUpdateFileWriteInfo.phase == "startup") {
|
|
||||||
let suffix = patchType + "_" + AUSTLMY.STARTUP;
|
|
||||||
AUSTLMY.pingStateCode(suffix, AUSTLMY.STATE_WRITE_FAILURE);
|
|
||||||
} else {
|
|
||||||
// Temporary failure code to see if there are failures without an update
|
|
||||||
// phase.
|
|
||||||
AUSTLMY.pingDownloadCode(
|
|
||||||
patchType,
|
|
||||||
AUSTLMY.DWNLD_UNKNOWN_PHASE_ERR_WRITE_FAILURE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixUpdateDirectoryPermissions();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a convenience function for calling the above function depending on a
|
|
||||||
* boolean success value.
|
|
||||||
*
|
|
||||||
* @param wroteSuccessfully A boolean representing whether or not the write was
|
|
||||||
* successful. When this is true, this function does
|
|
||||||
* nothing.
|
|
||||||
* @param path A string representing the path to the file that the operation
|
|
||||||
* attempted to write to. This value is only used for logging
|
|
||||||
* purposes.
|
|
||||||
*/
|
|
||||||
function handleCriticalWriteResult(wroteSuccessfully, path) {
|
|
||||||
if (!wroteSuccessfully) {
|
|
||||||
handleCriticalWriteFailure(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This returns true if the passed update is the same version or older than the
|
* This returns true if the passed update is the same version or older than the
|
||||||
* version and build ID values passed. Otherwise it returns false.
|
* version and build ID values passed. Otherwise it returns false.
|
||||||
|
@ -2741,7 +2608,6 @@ UpdateService.prototype = {
|
||||||
// update is broken and they should reinstall.
|
// update is broken and they should reinstall.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gUpdateFileWriteInfo = { phase: "startup", failure: false };
|
|
||||||
if (!this.canCheckForUpdates) {
|
if (!this.canCheckForUpdates) {
|
||||||
LOG(
|
LOG(
|
||||||
"UpdateService:_postUpdateProcessing - unable to check for " +
|
"UpdateService:_postUpdateProcessing - unable to check for " +
|
||||||
|
@ -4239,7 +4105,6 @@ UpdateManager.prototype = {
|
||||||
"stream. Exception: " +
|
"stream. Exception: " +
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
fixUpdateDirectoryPermissions();
|
|
||||||
return updates;
|
return updates;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -4475,12 +4340,7 @@ UpdateManager.prototype = {
|
||||||
// the lifetime of an active update and the file should always be updated
|
// the lifetime of an active update and the file should always be updated
|
||||||
// when saveUpdates is called.
|
// when saveUpdates is called.
|
||||||
let promises = [];
|
let promises = [];
|
||||||
promises[0] = this._writeUpdatesToXMLFile(
|
promises[0] = this._writeUpdatesToXMLFile(updates, FILE_ACTIVE_UPDATE_XML);
|
||||||
updates,
|
|
||||||
FILE_ACTIVE_UPDATE_XML
|
|
||||||
).then(wroteSuccessfully =>
|
|
||||||
handleCriticalWriteResult(wroteSuccessfully, FILE_ACTIVE_UPDATE_XML)
|
|
||||||
);
|
|
||||||
// The update history stored in the updates.xml file should only need to be
|
// The update history stored in the updates.xml file should only need to be
|
||||||
// updated when an active update has been added to it in which case
|
// updated when an active update has been added to it in which case
|
||||||
// |_updatesDirty| will be true.
|
// |_updatesDirty| will be true.
|
||||||
|
@ -4489,8 +4349,6 @@ UpdateManager.prototype = {
|
||||||
promises[1] = this._writeUpdatesToXMLFile(
|
promises[1] = this._writeUpdatesToXMLFile(
|
||||||
this._getUpdates(),
|
this._getUpdates(),
|
||||||
FILE_UPDATES_XML
|
FILE_UPDATES_XML
|
||||||
).then(wroteSuccessfully =>
|
|
||||||
handleCriticalWriteResult(wroteSuccessfully, FILE_UPDATES_XML)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
|
@ -4725,7 +4583,6 @@ Checker.prototype = {
|
||||||
*/
|
*/
|
||||||
checkForUpdates: function UC_checkForUpdates(listener, force) {
|
checkForUpdates: function UC_checkForUpdates(listener, force) {
|
||||||
LOG("Checker: checkForUpdates, force: " + force);
|
LOG("Checker: checkForUpdates, force: " + force);
|
||||||
gUpdateFileWriteInfo = { phase: "check", failure: false };
|
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||||
}
|
}
|
||||||
|
@ -5378,7 +5235,6 @@ Downloader.prototype = {
|
||||||
*/
|
*/
|
||||||
downloadUpdate: function Downloader_downloadUpdate(update) {
|
downloadUpdate: function Downloader_downloadUpdate(update) {
|
||||||
LOG("UpdateService:_downloadUpdate");
|
LOG("UpdateService:_downloadUpdate");
|
||||||
gUpdateFileWriteInfo = { phase: "download", failure: false };
|
|
||||||
if (!update) {
|
if (!update) {
|
||||||
AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
|
AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
|
||||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||||
|
@ -5880,7 +5736,6 @@ Downloader.prototype = {
|
||||||
);
|
);
|
||||||
// Prevent the preference from setting a value greater than 20.
|
// Prevent the preference from setting a value greater than 20.
|
||||||
maxFail = Math.min(maxFail, 20);
|
maxFail = Math.min(maxFail, 20);
|
||||||
let permissionFixingInProgress = false;
|
|
||||||
LOG(
|
LOG(
|
||||||
"Downloader:onStopRequest - status: " +
|
"Downloader:onStopRequest - status: " +
|
||||||
status +
|
status +
|
||||||
|
@ -5946,12 +5801,6 @@ Downloader.prototype = {
|
||||||
deleteActiveUpdate = true;
|
deleteActiveUpdate = true;
|
||||||
|
|
||||||
cleanUpDownloadingUpdateDir();
|
cleanUpDownloadingUpdateDir();
|
||||||
|
|
||||||
let failedWrite = readyDir.clone();
|
|
||||||
failedWrite.append(FILE_UPDATE_MAR);
|
|
||||||
permissionFixingInProgress = handleCriticalWriteFailure(
|
|
||||||
failedWrite.path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG("Downloader:onStopRequest - download verification failed");
|
LOG("Downloader:onStopRequest - download verification failed");
|
||||||
|
@ -6013,11 +5862,6 @@ Downloader.prototype = {
|
||||||
status == Cr.NS_ERROR_FILE_READ_ONLY
|
status == Cr.NS_ERROR_FILE_READ_ONLY
|
||||||
) {
|
) {
|
||||||
LOG("Downloader:onStopRequest - permission error");
|
LOG("Downloader:onStopRequest - permission error");
|
||||||
// This will either fix the permissions, or asynchronously show the
|
|
||||||
// reinstall prompt if it cannot fix them.
|
|
||||||
let patchFile = getDownloadingUpdateDir();
|
|
||||||
patchFile.append(FILE_UPDATE_MAR);
|
|
||||||
permissionFixingInProgress = handleCriticalWriteFailure(patchFile.path);
|
|
||||||
nonDownloadFailure = true;
|
nonDownloadFailure = true;
|
||||||
} else {
|
} else {
|
||||||
LOG("Downloader:onStopRequest - non-verification failure");
|
LOG("Downloader:onStopRequest - non-verification failure");
|
||||||
|
@ -6154,7 +5998,7 @@ Downloader.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allFailed && !permissionFixingInProgress) {
|
if (allFailed) {
|
||||||
let downloadAttempts = Services.prefs.getIntPref(
|
let downloadAttempts = Services.prefs.getIntPref(
|
||||||
PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS,
|
PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS,
|
||||||
0
|
0
|
||||||
|
@ -6231,7 +6075,6 @@ Downloader.prototype = {
|
||||||
"Downloader:onStopRequest - attempting to stage update: " +
|
"Downloader:onStopRequest - attempting to stage update: " +
|
||||||
this._update.name
|
this._update.name
|
||||||
);
|
);
|
||||||
gUpdateFileWriteInfo = { phase: "stage", failure: false };
|
|
||||||
// Stage the update
|
// Stage the update
|
||||||
try {
|
try {
|
||||||
Cc["@mozilla.org/updates/update-processor;1"]
|
Cc["@mozilla.org/updates/update-processor;1"]
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,23 +14,26 @@ typedef WCHAR NS_tchar;
|
||||||
typedef char NS_tchar;
|
typedef char NS_tchar;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool GetInstallHash(const char16_t* installPath, const char* vendor,
|
bool GetInstallHash(const char16_t* installPath,
|
||||||
mozilla::UniquePtr<NS_tchar[]>& result,
|
mozilla::UniquePtr<NS_tchar[]>& result);
|
||||||
bool useCompatibilityMode = false);
|
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
enum class SetPermissionsOf {
|
// In addition to getting the update directory, this function also creates it.
|
||||||
BaseDirIfNotExists,
|
// This is to ensure that, when it is created, it is created with the correct
|
||||||
AllFilesAndDirs,
|
// permissions. The default permissions on the containing directory can cause
|
||||||
FilesAndDirsWithBadPerms,
|
// problems, so it is very, very important that we make sure that the
|
||||||
};
|
// permissions are set properly. Thus, we won't even give out the path of the
|
||||||
// This function does two things. It retrieves the update directory and it sets
|
// update directory without ensuring that it was created with the correct
|
||||||
// the permissions of the directory and, optionally, its contents.
|
// permissions.
|
||||||
HRESULT GetCommonUpdateDirectory(const wchar_t* installPath,
|
HRESULT GetCommonUpdateDirectory(const wchar_t* installPath,
|
||||||
SetPermissionsOf dirPermsToSet,
|
|
||||||
mozilla::UniquePtr<wchar_t[]>& result);
|
mozilla::UniquePtr<wchar_t[]>& result);
|
||||||
HRESULT GetUserUpdateDirectory(const wchar_t* installPath, const char* vendor,
|
// Returns the old common update directory. Since this directory was used before
|
||||||
const char* appName,
|
// we made sure to always set the correct permissions, it is possible that the
|
||||||
|
// permissions on this directory are set such that files can only be modified
|
||||||
|
// or deleted by the user that created them. This function exists entirely to
|
||||||
|
// allow us to migrate files out of the old update directory and into the new
|
||||||
|
// one.
|
||||||
|
HRESULT GetOldUpdateDirectory(const wchar_t* installPath,
|
||||||
mozilla::UniquePtr<wchar_t[]>& result);
|
mozilla::UniquePtr<wchar_t[]>& result);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -462,30 +462,6 @@ interface nsIUpdateProcessor : nsISupports
|
||||||
*/
|
*/
|
||||||
void processUpdate();
|
void processUpdate();
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to fix the permissions of the update directory. This can be done
|
|
||||||
* in two ways. Firefox can attempt to fix the permissions itself, or it can
|
|
||||||
* call into the maintenance service to request that it attempt to fix the
|
|
||||||
* permissions.
|
|
||||||
*
|
|
||||||
* Fixing the permissions can take some time, so this work is all done off of
|
|
||||||
* the main thread.
|
|
||||||
*
|
|
||||||
* Currently, this function only has a Windows implementation. On other
|
|
||||||
* operating systems, it will throw NS_ERROR_NOT_IMPLEMENTED.
|
|
||||||
*
|
|
||||||
* Since this function does its work off of the main thread and does not
|
|
||||||
* block, it will only throw if it was unable to dispatch to another thread.
|
|
||||||
*
|
|
||||||
* @param useServiceOnFailure
|
|
||||||
* If set to false, Firefox will attempt to fix the permissions itself,
|
|
||||||
* but the maintenance service will not be used. If set to true and
|
|
||||||
* Firefox is unable to fix the permissions itself, it will attempt to
|
|
||||||
* call into the maintenance service to request that it attempt to fix
|
|
||||||
* the permissions.
|
|
||||||
*/
|
|
||||||
void fixUpdateDirectoryPerms(in boolean useServiceOnFailure);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The installer writes an installation-specific registry key if the
|
* The installer writes an installation-specific registry key if the
|
||||||
* Maintenance Service can be used for this installation. This function checks
|
* Maintenance Service can be used for this installation. This function checks
|
||||||
|
|
|
@ -344,8 +344,7 @@ int NS_main(int argc, NS_tchar** argv) {
|
||||||
if (!NS_tstrcmp(argv[1], NS_T("create-update-dir"))) {
|
if (!NS_tstrcmp(argv[1], NS_T("create-update-dir"))) {
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
mozilla::UniquePtr<wchar_t[]> updateDir;
|
mozilla::UniquePtr<wchar_t[]> updateDir;
|
||||||
HRESULT result = GetCommonUpdateDirectory(
|
HRESULT result = GetCommonUpdateDirectory(argv[2], updateDir);
|
||||||
argv[2], SetPermissionsOf::BaseDirIfNotExists, updateDir);
|
|
||||||
return SUCCEEDED(result) ? 0 : 1;
|
return SUCCEEDED(result) ? 0 : 1;
|
||||||
#else
|
#else
|
||||||
// Not implemented on non-Windows platforms
|
// Not implemented on non-Windows platforms
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace mozilla {
|
||||||
static bool GetLockFileName(const char* nameToken, const char16_t* installPath,
|
static bool GetLockFileName(const char* nameToken, const char16_t* installPath,
|
||||||
nsCString& filePath) {
|
nsCString& filePath) {
|
||||||
mozilla::UniquePtr<NS_tchar[]> pathHash;
|
mozilla::UniquePtr<NS_tchar[]> pathHash;
|
||||||
if (!GetInstallHash(installPath, MOZ_APP_VENDOR, pathHash)) {
|
if (!GetInstallHash(installPath, pathHash)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -797,254 +797,6 @@ nsUpdateProcessor::ProcessUpdate() {
|
||||||
r);
|
r);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsUpdateProcessor::FixUpdateDirectoryPerms(bool aUseServiceOnFailure) {
|
|
||||||
#ifndef XP_WIN
|
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
|
||||||
#else
|
|
||||||
enum class State {
|
|
||||||
Initializing,
|
|
||||||
WaitingToStart,
|
|
||||||
Starting,
|
|
||||||
WaitingForFinish,
|
|
||||||
};
|
|
||||||
|
|
||||||
class FixUpdateDirectoryPermsRunnable final : public mozilla::Runnable {
|
|
||||||
public:
|
|
||||||
FixUpdateDirectoryPermsRunnable(const char* aName,
|
|
||||||
bool aUseServiceOnFailure,
|
|
||||||
const nsAutoString& aInstallPath)
|
|
||||||
: Runnable(aName),
|
|
||||||
mState(State::Initializing)
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
,
|
|
||||||
mUseServiceOnFailure(aUseServiceOnFailure)
|
|
||||||
# endif
|
|
||||||
{
|
|
||||||
size_t installPathSize = aInstallPath.Length() + 1;
|
|
||||||
mInstallPath = mozilla::MakeUnique<wchar_t[]>(installPathSize);
|
|
||||||
if (mInstallPath) {
|
|
||||||
HRESULT hrv = StringCchCopyW(mInstallPath.get(), installPathSize,
|
|
||||||
PromiseFlatString(aInstallPath).get());
|
|
||||||
if (FAILED(hrv)) {
|
|
||||||
mInstallPath.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHOD Run() override {
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
// These constants control how often and how many times we poll the
|
|
||||||
// maintenance service to see if it has stopped. If there is no delay in
|
|
||||||
// the event queue, this works out to 8 minutes of polling.
|
|
||||||
const unsigned int kMaxQueries = 2400;
|
|
||||||
const unsigned int kQueryIntervalMS = 200;
|
|
||||||
// These constants control how often and how many times we attempt to
|
|
||||||
// start the service. If there is no delay in the event queue, this works
|
|
||||||
// out to 5 seconds of polling.
|
|
||||||
const unsigned int kMaxStartAttempts = 50;
|
|
||||||
const unsigned int kStartAttemptIntervalMS = 100;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
if (mState == State::Initializing) {
|
|
||||||
if (!mInstallPath) {
|
|
||||||
LOG(
|
|
||||||
("Warning: No install path available in "
|
|
||||||
"FixUpdateDirectoryPermsRunnable\n"));
|
|
||||||
}
|
|
||||||
// In the event that the directory is owned by this user, we may be able
|
|
||||||
// to fix things without the maintenance service
|
|
||||||
mozilla::UniquePtr<wchar_t[]> updateDir;
|
|
||||||
HRESULT permResult = GetCommonUpdateDirectory(
|
|
||||||
mInstallPath.get(), SetPermissionsOf::AllFilesAndDirs, updateDir);
|
|
||||||
if (SUCCEEDED(permResult)) {
|
|
||||||
LOG(("Successfully fixed permissions from within Firefox\n"));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
else if (!mUseServiceOnFailure) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to fix permissions within Firefox and "
|
|
||||||
"maintenance service is disabled\n"));
|
|
||||||
return ReportUpdateError();
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
LOG(("Error: Unable to fix permissions\n"));
|
|
||||||
return ReportUpdateError();
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
SC_HANDLE serviceManager =
|
|
||||||
OpenSCManager(nullptr, nullptr,
|
|
||||||
SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
|
|
||||||
mServiceManager.own(serviceManager);
|
|
||||||
if (!serviceManager) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to get the service manager. Cannot fix "
|
|
||||||
"permissions.\n"));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
SC_HANDLE service = OpenServiceW(serviceManager, MAINTENANCE_SVC_NAME,
|
|
||||||
SERVICE_QUERY_STATUS | SERVICE_START);
|
|
||||||
mService.own(service);
|
|
||||||
if (!service) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to get the maintenance service. Unable fix "
|
|
||||||
"permissions without it.\n"));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mStartServiceArgCount = mInstallPath ? 3 : 2;
|
|
||||||
mStartServiceArgs =
|
|
||||||
mozilla::MakeUnique<LPCWSTR[]>(mStartServiceArgCount);
|
|
||||||
mStartServiceArgs[0] = L"MozillaMaintenance";
|
|
||||||
mStartServiceArgs[1] = L"fix-update-directory-perms";
|
|
||||||
if (mInstallPath) {
|
|
||||||
mStartServiceArgs[2] = mInstallPath.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
mState = State::WaitingToStart;
|
|
||||||
mCurrentTry = 1;
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
if (mState == State::WaitingToStart ||
|
|
||||||
mState == State::WaitingForFinish) {
|
|
||||||
SERVICE_STATUS_PROCESS ssp;
|
|
||||||
DWORD bytesNeeded;
|
|
||||||
BOOL success =
|
|
||||||
QueryServiceStatusEx(mService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
|
|
||||||
sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded);
|
|
||||||
if (!success) {
|
|
||||||
DWORD lastError = GetLastError();
|
|
||||||
// These 3 errors can occur when the service is not yet stopped but it
|
|
||||||
// is stopping. If we got another error, waiting will probably not
|
|
||||||
// help.
|
|
||||||
if (lastError != ERROR_INVALID_SERVICE_CONTROL &&
|
|
||||||
lastError != ERROR_SERVICE_CANNOT_ACCEPT_CTRL &&
|
|
||||||
lastError != ERROR_SERVICE_NOT_ACTIVE) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to query service when fixing permissions. Got "
|
|
||||||
"an error that cannot be fixed by waiting: 0x%lx\n",
|
|
||||||
lastError));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
if (mCurrentTry >= kMaxQueries) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to query service when fixing permissions: "
|
|
||||||
"Timed out after %u attempts.\n",
|
|
||||||
mCurrentTry));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
return RetryInMS(kQueryIntervalMS);
|
|
||||||
} else { // We successfully queried the service
|
|
||||||
if (ssp.dwCurrentState != SERVICE_STOPPED) {
|
|
||||||
return RetryInMS(kQueryIntervalMS);
|
|
||||||
}
|
|
||||||
if (mState == State::WaitingForFinish) {
|
|
||||||
if (ssp.dwWin32ExitCode != NO_ERROR) {
|
|
||||||
LOG(
|
|
||||||
("Error: Maintenance Service was unable to fix update "
|
|
||||||
"directory permissions\n"));
|
|
||||||
return ReportUpdateError();
|
|
||||||
}
|
|
||||||
LOG(
|
|
||||||
("Maintenance service successully fixed update directory "
|
|
||||||
"permissions\n"));
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
mState = State::Starting;
|
|
||||||
mCurrentTry = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mState == State::Starting) {
|
|
||||||
BOOL success = StartServiceW(mService, mStartServiceArgCount,
|
|
||||||
mStartServiceArgs.get());
|
|
||||||
if (success) {
|
|
||||||
mState = State::WaitingForFinish;
|
|
||||||
mCurrentTry = 1;
|
|
||||||
return RetryInMS(kQueryIntervalMS);
|
|
||||||
} else if (mCurrentTry >= kMaxStartAttempts) {
|
|
||||||
LOG(
|
|
||||||
("Error: Unable to fix permissions: Timed out after %u attempts "
|
|
||||||
"to start the maintenance service\n",
|
|
||||||
mCurrentTry));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
return RetryInMS(kStartAttemptIntervalMS);
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
// We should not have fallen through all three state checks above
|
|
||||||
LOG(
|
|
||||||
("Error: Reached logically unreachable code when correcting update "
|
|
||||||
"directory permissions\n"));
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
State mState;
|
|
||||||
mozilla::UniquePtr<wchar_t[]> mInstallPath;
|
|
||||||
# ifdef MOZ_MAINTENANCE_SERVICE
|
|
||||||
bool mUseServiceOnFailure;
|
|
||||||
unsigned int mCurrentTry;
|
|
||||||
nsAutoServiceHandle mServiceManager;
|
|
||||||
nsAutoServiceHandle mService;
|
|
||||||
DWORD mStartServiceArgCount;
|
|
||||||
mozilla::UniquePtr<LPCWSTR[]> mStartServiceArgs;
|
|
||||||
|
|
||||||
nsresult RetryInMS(unsigned int aDelayMS) {
|
|
||||||
++mCurrentTry;
|
|
||||||
nsCOMPtr<nsIRunnable> runnable(this);
|
|
||||||
return NS_DelayedDispatchToCurrentThread(runnable.forget(), aDelayMS);
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
nsresult ReportUpdateError() {
|
|
||||||
return NS_DispatchToMainThread(NS_NewRunnableFunction(
|
|
||||||
"nsUpdateProcessor::FixUpdateDirectoryPerms::"
|
|
||||||
"FixUpdateDirectoryPermsRunnable::ReportUpdateError",
|
|
||||||
[]() -> void {
|
|
||||||
nsCOMPtr<nsIObserverService> observerService =
|
|
||||||
services::GetObserverService();
|
|
||||||
if (NS_WARN_IF(!observerService)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
observerService->NotifyObservers(nullptr, "update-error",
|
|
||||||
u"bad-perms");
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
nsCOMPtr<nsIProperties> dirSvc(
|
|
||||||
do_GetService("@mozilla.org/file/directory_service;1"));
|
|
||||||
NS_ENSURE_TRUE(dirSvc, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> appPath;
|
|
||||||
nsresult rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
|
|
||||||
getter_AddRefs(appPath));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> installDir;
|
|
||||||
rv = appPath->GetParent(getter_AddRefs(installDir));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsAutoString installPath;
|
|
||||||
rv = installDir->GetPath(installPath);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// Stream transport service has a thread pool we can use so that this happens
|
|
||||||
// off the main thread.
|
|
||||||
nsCOMPtr<nsIEventTarget> eventTarget =
|
|
||||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
||||||
NS_ENSURE_TRUE(eventTarget, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> runnable = new FixUpdateDirectoryPermsRunnable(
|
|
||||||
"FixUpdateDirectoryPermsRunnable", aUseServiceOnFailure, installPath);
|
|
||||||
rv = eventTarget->Dispatch(runnable.forget());
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
#endif
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nsUpdateProcessor::StartStagedUpdate() {
|
void nsUpdateProcessor::StartStagedUpdate() {
|
||||||
MOZ_ASSERT(!NS_IsMainThread(), "main thread");
|
MOZ_ASSERT(!NS_IsMainThread(), "main thread");
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ nsCOMPtr<nsIFile> gDataDirProfile = nullptr;
|
||||||
|
|
||||||
// These are required to allow nsXREDirProvider to be usable in xpcshell tests.
|
// These are required to allow nsXREDirProvider to be usable in xpcshell tests.
|
||||||
// where gAppData is null.
|
// where gAppData is null.
|
||||||
#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_UNIX)
|
#if defined(XP_MACOSX) || defined(XP_UNIX)
|
||||||
static const char* GetAppName() {
|
static const char* GetAppName() {
|
||||||
if (gAppData) {
|
if (gAppData) {
|
||||||
return gAppData->name;
|
return gAppData->name;
|
||||||
|
@ -122,12 +122,14 @@ static const char* GetAppName() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef XP_MACOSX
|
||||||
static const char* GetAppVendor() {
|
static const char* GetAppVendor() {
|
||||||
if (gAppData) {
|
if (gAppData) {
|
||||||
return gAppData->vendor;
|
return gAppData->vendor;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
nsXREDirProvider::nsXREDirProvider() : mProfileNotified(false) {
|
nsXREDirProvider::nsXREDirProvider() : mProfileNotified(false) {
|
||||||
gDirServiceProvider = this;
|
gDirServiceProvider = this;
|
||||||
|
@ -1110,14 +1112,8 @@ static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) {
|
static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) {
|
||||||
const char* vendor = GetAppVendor();
|
|
||||||
if (vendor && vendor[0] == '\0') {
|
|
||||||
vendor = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mozilla::UniquePtr<NS_tchar[]> hash;
|
mozilla::UniquePtr<NS_tchar[]> hash;
|
||||||
bool success =
|
bool success = ::GetInstallHash(PromiseFlatString(aInstallPath).get(), hash);
|
||||||
::GetInstallHash(PromiseFlatString(aInstallPath).get(), vendor, hash);
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -1296,19 +1292,10 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
|
||||||
mozilla::UniquePtr<wchar_t[]> updatePath;
|
mozilla::UniquePtr<wchar_t[]> updatePath;
|
||||||
HRESULT hrv;
|
HRESULT hrv;
|
||||||
if (aGetOldLocation) {
|
if (aGetOldLocation) {
|
||||||
const char* vendor = GetAppVendor();
|
hrv =
|
||||||
if (vendor && vendor[0] == '\0') {
|
GetOldUpdateDirectory(PromiseFlatString(installPath).get(), updatePath);
|
||||||
vendor = nullptr;
|
|
||||||
}
|
|
||||||
const char* appName = GetAppName();
|
|
||||||
if (appName && appName[0] == '\0') {
|
|
||||||
appName = nullptr;
|
|
||||||
}
|
|
||||||
hrv = GetUserUpdateDirectory(PromiseFlatString(installPath).get(), vendor,
|
|
||||||
appName, updatePath);
|
|
||||||
} else {
|
} else {
|
||||||
hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(),
|
hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(),
|
||||||
SetPermissionsOf::BaseDirIfNotExists,
|
|
||||||
updatePath);
|
updatePath);
|
||||||
}
|
}
|
||||||
if (FAILED(hrv)) {
|
if (FAILED(hrv)) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче