Bug 1732435 - r=nalexander,application-update-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D127894
This commit is contained in:
Kirk Steuber 2021-12-08 19:13:00 +00:00
Родитель 3e2edea9fd
Коммит b3546869e2
9 изменённых файлов: 122 добавлений и 1754 удалений

Просмотреть файл

@ -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)) {