зеркало из 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
|
||||
// installer will stop the service.
|
||||
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 {
|
||||
LOG_WARN(("Service command not recognized: %ls.", argv[2]));
|
||||
// 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.
|
||||
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;
|
||||
// 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.
|
||||
var gLogfileOutputStream;
|
||||
// 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: " +
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1171,10 +1160,7 @@ function readBinaryTransparencyResult(dir) {
|
|||
function writeStatusFile(dir, state) {
|
||||
let statusFile = dir.clone();
|
||||
statusFile.append(FILE_UPDATE_STATUS);
|
||||
let success = writeStringToFile(statusFile, state);
|
||||
if (!success) {
|
||||
handleCriticalWriteFailure(statusFile.path);
|
||||
}
|
||||
writeStringToFile(statusFile, state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1195,10 +1181,7 @@ function writeStatusFile(dir, state) {
|
|||
function writeVersionFile(dir, version) {
|
||||
let versionFile = dir.clone();
|
||||
versionFile.append(FILE_UPDATE_VERSION);
|
||||
let success = writeStringToFile(versionFile, version);
|
||||
if (!success) {
|
||||
handleCriticalWriteFailure(versionFile.path);
|
||||
}
|
||||
writeStringToFile(versionFile, version);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1811,122 +1794,6 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
|
|||
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
|
||||
* version and build ID values passed. Otherwise it returns false.
|
||||
|
@ -2741,7 +2608,6 @@ UpdateService.prototype = {
|
|||
// update is broken and they should reinstall.
|
||||
return;
|
||||
}
|
||||
gUpdateFileWriteInfo = { phase: "startup", failure: false };
|
||||
if (!this.canCheckForUpdates) {
|
||||
LOG(
|
||||
"UpdateService:_postUpdateProcessing - unable to check for " +
|
||||
|
@ -4239,7 +4105,6 @@ UpdateManager.prototype = {
|
|||
"stream. Exception: " +
|
||||
e
|
||||
);
|
||||
fixUpdateDirectoryPermissions();
|
||||
return updates;
|
||||
}
|
||||
try {
|
||||
|
@ -4475,12 +4340,7 @@ UpdateManager.prototype = {
|
|||
// the lifetime of an active update and the file should always be updated
|
||||
// when saveUpdates is called.
|
||||
let promises = [];
|
||||
promises[0] = this._writeUpdatesToXMLFile(
|
||||
updates,
|
||||
FILE_ACTIVE_UPDATE_XML
|
||||
).then(wroteSuccessfully =>
|
||||
handleCriticalWriteResult(wroteSuccessfully, FILE_ACTIVE_UPDATE_XML)
|
||||
);
|
||||
promises[0] = this._writeUpdatesToXMLFile(updates, FILE_ACTIVE_UPDATE_XML);
|
||||
// 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
|
||||
// |_updatesDirty| will be true.
|
||||
|
@ -4489,8 +4349,6 @@ UpdateManager.prototype = {
|
|||
promises[1] = this._writeUpdatesToXMLFile(
|
||||
this._getUpdates(),
|
||||
FILE_UPDATES_XML
|
||||
).then(wroteSuccessfully =>
|
||||
handleCriticalWriteResult(wroteSuccessfully, FILE_UPDATES_XML)
|
||||
);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
|
@ -4725,7 +4583,6 @@ Checker.prototype = {
|
|||
*/
|
||||
checkForUpdates: function UC_checkForUpdates(listener, force) {
|
||||
LOG("Checker: checkForUpdates, force: " + force);
|
||||
gUpdateFileWriteInfo = { phase: "check", failure: false };
|
||||
if (!listener) {
|
||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||
}
|
||||
|
@ -5378,7 +5235,6 @@ Downloader.prototype = {
|
|||
*/
|
||||
downloadUpdate: function Downloader_downloadUpdate(update) {
|
||||
LOG("UpdateService:_downloadUpdate");
|
||||
gUpdateFileWriteInfo = { phase: "download", failure: false };
|
||||
if (!update) {
|
||||
AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE);
|
||||
throw Components.Exception("", Cr.NS_ERROR_NULL_POINTER);
|
||||
|
@ -5880,7 +5736,6 @@ Downloader.prototype = {
|
|||
);
|
||||
// Prevent the preference from setting a value greater than 20.
|
||||
maxFail = Math.min(maxFail, 20);
|
||||
let permissionFixingInProgress = false;
|
||||
LOG(
|
||||
"Downloader:onStopRequest - status: " +
|
||||
status +
|
||||
|
@ -5946,12 +5801,6 @@ Downloader.prototype = {
|
|||
deleteActiveUpdate = true;
|
||||
|
||||
cleanUpDownloadingUpdateDir();
|
||||
|
||||
let failedWrite = readyDir.clone();
|
||||
failedWrite.append(FILE_UPDATE_MAR);
|
||||
permissionFixingInProgress = handleCriticalWriteFailure(
|
||||
failedWrite.path
|
||||
);
|
||||
}
|
||||
} else {
|
||||
LOG("Downloader:onStopRequest - download verification failed");
|
||||
|
@ -6013,11 +5862,6 @@ Downloader.prototype = {
|
|||
status == Cr.NS_ERROR_FILE_READ_ONLY
|
||||
) {
|
||||
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;
|
||||
} else {
|
||||
LOG("Downloader:onStopRequest - non-verification failure");
|
||||
|
@ -6154,7 +5998,7 @@ Downloader.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (allFailed && !permissionFixingInProgress) {
|
||||
if (allFailed) {
|
||||
let downloadAttempts = Services.prefs.getIntPref(
|
||||
PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS,
|
||||
0
|
||||
|
@ -6231,7 +6075,6 @@ Downloader.prototype = {
|
|||
"Downloader:onStopRequest - attempting to stage update: " +
|
||||
this._update.name
|
||||
);
|
||||
gUpdateFileWriteInfo = { phase: "stage", failure: false };
|
||||
// Stage the update
|
||||
try {
|
||||
Cc["@mozilla.org/updates/update-processor;1"]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -14,24 +14,27 @@ typedef WCHAR NS_tchar;
|
|||
typedef char NS_tchar;
|
||||
#endif
|
||||
|
||||
bool GetInstallHash(const char16_t* installPath, const char* vendor,
|
||||
mozilla::UniquePtr<NS_tchar[]>& result,
|
||||
bool useCompatibilityMode = false);
|
||||
bool GetInstallHash(const char16_t* installPath,
|
||||
mozilla::UniquePtr<NS_tchar[]>& result);
|
||||
|
||||
#ifdef XP_WIN
|
||||
enum class SetPermissionsOf {
|
||||
BaseDirIfNotExists,
|
||||
AllFilesAndDirs,
|
||||
FilesAndDirsWithBadPerms,
|
||||
};
|
||||
// This function does two things. It retrieves the update directory and it sets
|
||||
// the permissions of the directory and, optionally, its contents.
|
||||
// In addition to getting the update directory, this function also creates it.
|
||||
// This is to ensure that, when it is created, it is created with the correct
|
||||
// permissions. The default permissions on the containing directory can cause
|
||||
// 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
|
||||
// update directory without ensuring that it was created with the correct
|
||||
// permissions.
|
||||
HRESULT GetCommonUpdateDirectory(const wchar_t* installPath,
|
||||
SetPermissionsOf dirPermsToSet,
|
||||
mozilla::UniquePtr<wchar_t[]>& result);
|
||||
HRESULT GetUserUpdateDirectory(const wchar_t* installPath, const char* vendor,
|
||||
const char* appName,
|
||||
mozilla::UniquePtr<wchar_t[]>& result);
|
||||
// Returns the old common update directory. Since this directory was used before
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -462,30 +462,6 @@ interface nsIUpdateProcessor : nsISupports
|
|||
*/
|
||||
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
|
||||
* 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"))) {
|
||||
#ifdef XP_WIN
|
||||
mozilla::UniquePtr<wchar_t[]> updateDir;
|
||||
HRESULT result = GetCommonUpdateDirectory(
|
||||
argv[2], SetPermissionsOf::BaseDirIfNotExists, updateDir);
|
||||
HRESULT result = GetCommonUpdateDirectory(argv[2], updateDir);
|
||||
return SUCCEEDED(result) ? 0 : 1;
|
||||
#else
|
||||
// Not implemented on non-Windows platforms
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace mozilla {
|
|||
static bool GetLockFileName(const char* nameToken, const char16_t* installPath,
|
||||
nsCString& filePath) {
|
||||
mozilla::UniquePtr<NS_tchar[]> pathHash;
|
||||
if (!GetInstallHash(installPath, MOZ_APP_VENDOR, pathHash)) {
|
||||
if (!GetInstallHash(installPath, pathHash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -797,254 +797,6 @@ nsUpdateProcessor::ProcessUpdate() {
|
|||
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() {
|
||||
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.
|
||||
// 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() {
|
||||
if (gAppData) {
|
||||
return gAppData->name;
|
||||
|
@ -122,12 +122,14 @@ static const char* GetAppName() {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
static const char* GetAppVendor() {
|
||||
if (gAppData) {
|
||||
return gAppData->vendor;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
nsXREDirProvider::nsXREDirProvider() : mProfileNotified(false) {
|
||||
gDirServiceProvider = this;
|
||||
|
@ -1110,14 +1112,8 @@ static nsresult GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval) {
|
|||
#endif
|
||||
|
||||
static nsresult HashInstallPath(nsAString& aInstallPath, nsAString& aPathHash) {
|
||||
const char* vendor = GetAppVendor();
|
||||
if (vendor && vendor[0] == '\0') {
|
||||
vendor = nullptr;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<NS_tchar[]> hash;
|
||||
bool success =
|
||||
::GetInstallHash(PromiseFlatString(aInstallPath).get(), vendor, hash);
|
||||
bool success = ::GetInstallHash(PromiseFlatString(aInstallPath).get(), hash);
|
||||
if (!success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -1296,19 +1292,10 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
|
|||
mozilla::UniquePtr<wchar_t[]> updatePath;
|
||||
HRESULT hrv;
|
||||
if (aGetOldLocation) {
|
||||
const char* vendor = GetAppVendor();
|
||||
if (vendor && vendor[0] == '\0') {
|
||||
vendor = nullptr;
|
||||
}
|
||||
const char* appName = GetAppName();
|
||||
if (appName && appName[0] == '\0') {
|
||||
appName = nullptr;
|
||||
}
|
||||
hrv = GetUserUpdateDirectory(PromiseFlatString(installPath).get(), vendor,
|
||||
appName, updatePath);
|
||||
hrv =
|
||||
GetOldUpdateDirectory(PromiseFlatString(installPath).get(), updatePath);
|
||||
} else {
|
||||
hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(),
|
||||
SetPermissionsOf::BaseDirIfNotExists,
|
||||
updatePath);
|
||||
}
|
||||
if (FAILED(hrv)) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче