Bug 1458314 - Move the update directory to an installation specific location r=rstrong

This change applies to Windows only.
Firefox will need to migrate the directory from the old location to the new location. This will be done only once by setting the pref `app.update.migrated.updateDir2.<install path hash>` to `true` once migration has completed.

Note: The pref name app.update.migrated.updateDir has already been used, thus the '2' suffix. It can be found in ESR24.

This also removes the old handling fallback for generating the update directory path. Since xulrunner is no longer supported, this should no longer be needed. If neither the vendor nor app name are defined, it falls back to the literal string "Mozilla".

The code to generate the update directory path and the installation hash have been moved to the updatecommon library. This will allow those functions to be used in Firefox, the Mozilla Maintenance Service, the Mozilla Maintenance Service Installer, and TestAUSHelper.

Additionally, the function that generates the update directory path now has extra functionality. It creates the update directory, sets the permissions on it and, optionally, recursively sets the permissions on everything within.

This patch adds functionality that allows Firefox to set permissions on the new update directory on write failure. It attempts to set the permissions itself and, if that fails and the maintenance service is enabled, it calls into the maintenance service to try from there. If a write fails and the permissions cannot be fixed, the user is prompted to reinstall.

Differential Revision: https://phabricator.services.mozilla.com/D4249

--HG--
rename : toolkit/mozapps/update/updater/win_dirent.cpp => toolkit/mozapps/update/common/win_dirent.cpp
rename : toolkit/mozapps/update/tests/unit_aus_update/cleanupSuccessLogMove.js => toolkit/mozapps/update/tests/unit_aus_update/updateDirectoryMigrate.js
extra : moz-landing-system : lando
This commit is contained in:
Kirk Steuber 2018-10-23 21:41:04 +00:00
Родитель fc154a3b91
Коммит 19c8368ea5
30 изменённых файлов: 2290 добавлений и 239 удалений

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

@ -352,6 +352,12 @@ Section "-Application" APP_IDX
StrCpy $AddDesktopSC "1"
${EndIf}
${CreateUpdateDir} "Mozilla"
${If} ${Errors}
Pop $0
${LogMsg} "** ERROR Failed to create update directory: $0"
${EndIf}
${LogHeader} "Adding Registry Entries"
SetShellVarContext current ; Set SHCTX to HKCU
${RegCleanMain} "Software\Mozilla"

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

@ -25,18 +25,6 @@
#include <objbase.h>
#include <objidl.h>
namespace {
struct LocalFreeDeleter
{
void operator()(void* aPtr)
{
::LocalFree(aPtr);
}
};
} // anonymous namespace
// This API from oleaut32.dll is not declared in Windows SDK headers
extern "C" void __cdecl SetOaNoCache(void);

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

@ -282,7 +282,7 @@ SvcMain(DWORD argc, LPWSTR *argv)
// The service command was executed, stop logging and set an event
// to indicate the work is done in case someone is waiting on a
// service stop operation.
ExecuteServiceCommand(argc, argv);
BOOL success = ExecuteServiceCommand(argc, argv);
LogFinish();
SetEvent(gWorkDoneEvent);
@ -291,7 +291,7 @@ SvcMain(DWORD argc, LPWSTR *argv)
// now. If we are already in a stopping state then the SERVICE_STOPPED state
// will be set by the SvcCtrlHandler.
if (!gServiceControlStopping) {
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
ReportSvcStatus(SERVICE_STOPPED, success ? NO_ERROR : 1, 0);
StartTerminationThread();
}
}

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

@ -19,6 +19,7 @@
#include "shellapi.h"
#include "readstrings.h"
#include "errors.h"
#include "commonupdatedir.h"
#pragma comment(lib, "version.lib")
@ -255,6 +256,15 @@ FixServicePath(SC_HANDLE service,
BOOL
SvcInstall(SvcInstallAction action)
{
mozilla::UniquePtr<wchar_t[]> updateDir;
HRESULT permResult = GetCommonUpdateDirectory(nullptr, nullptr, nullptr,
SetPermissionsOf::AllFilesAndDirs,
updateDir);
if (FAILED(permResult)) {
LOG_WARN(("Unable to set the permissions on the update directory ('%S'): %d",
updateDir.get(), permResult));
}
// Get a handle to the local computer SCM database with full access rights.
nsAutoServiceHandle schSCManager(OpenSCManager(nullptr, nullptr,
SC_MANAGER_ALL_ACCESS));

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

@ -27,6 +27,7 @@
#include "updatehelper.h"
#include "pathhash.h"
#include "errors.h"
#include "commonupdatedir.h"
#define PATCH_DIR_PATH L"\\updates\\0"
@ -690,6 +691,15 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
return FALSE;
}
mozilla::UniquePtr<wchar_t[]> updateDir;
HRESULT permResult = GetCommonUpdateDirectory(installDir, nullptr, nullptr,
SetPermissionsOf::AllFilesAndDirs,
updateDir);
if (FAILED(permResult)) {
LOG_WARN(("Unable to set the permissions on the update directory ('%S'): %d",
updateDir.get(), permResult));
}
if (!DoesFallbackKeyExist()) {
WCHAR maintenanceServiceKey[MAX_PATH + 1];
if (CalculateRegistryPathFromFilePath(installDir, maintenanceServiceKey)) {
@ -768,6 +778,24 @@ 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,
nullptr, 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
@ -775,5 +803,5 @@ ExecuteServiceCommand(int argc, LPWSTR *argv)
LOG(("service command %ls complete with result: %ls.",
argv[1], (result ? L"Success" : L"Failure")));
return TRUE;
return result;
}

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

@ -3285,41 +3285,68 @@
${If} $R2 != ""
; Backup the old update directory logs and delete the directory
${If} ${FileExists} "$R2\updates\last-update.log"
Rename "$R2\updates\last-update.log" "$TEMP\moz-update-old-last-update.log"
Rename "$R2\updates\last-update.log" "$TEMP\moz-update-oldest-last-update.log"
${EndIf}
${If} ${FileExists} "$R2\updates\backup-update.log"
Rename "$R2\updates\backup-update.log" "$TEMP\moz-update-old-backup-update.log"
Rename "$R2\updates\backup-update.log" "$TEMP\moz-update-oldest-backup-update.log"
${EndIf}
${If} ${FileExists} "$R2\updates"
RmDir /r "$R2"
${EndIf}
${EndIf}
${EndIf}
; Get the taskbar ID hash for this installation path
ReadRegStr $R1 HKLM "SOFTWARE\$R7\TaskBarIDs" $R6
${If} $R1 == ""
ReadRegStr $R1 HKCU "SOFTWARE\$R7\TaskBarIDs" $R6
; Get the taskbar ID hash for this installation path
ReadRegStr $R1 HKLM "SOFTWARE\$R7\TaskBarIDs" $R6
${If} $R1 == ""
ReadRegStr $R1 HKCU "SOFTWARE\$R7\TaskBarIDs" $R6
${EndIf}
; If the taskbar ID hash exists then delete the new update directory
; Backup its logs before deleting it.
${If} $R1 != ""
StrCpy $R0 "$LOCALAPPDATA\$R8\$R1"
${If} ${FileExists} "$R0\updates\last-update.log"
Rename "$R0\updates\last-update.log" "$TEMP\moz-update-older-last-update.log"
${EndIf}
; If the taskbar ID hash exists then delete the new update directory
; Backup its logs before deleting it.
${If} $R1 != ""
StrCpy $R0 "$LOCALAPPDATA\$R8\$R1"
${If} ${FileExists} "$R0\updates\backup-update.log"
Rename "$R0\updates\backup-update.log" "$TEMP\moz-update-older-backup-update.log"
${EndIf}
${If} ${FileExists} "$R0\updates\last-update.log"
Rename "$R0\updates\last-update.log" "$TEMP\moz-update-new-last-update.log"
${EndIf}
; Remove the old updates directory, located in the user's Windows profile directory
${If} ${FileExists} "$R0\updates"
RmDir /r "$R0"
${EndIf}
${If} ${FileExists} "$R0\updates\backup-update.log"
Rename "$R0\updates\backup-update.log" "$TEMP\moz-update-new-backup-update.log"
${EndIf}
; Get the new updates directory so we can remove that too
; The new update directory is in the Program Data directory
; (currently C:\ProgramData).
; This system call gets that directory path. The arguments are:
; A null ptr for hwnd
; $R0 for the output string
; CSIDL_COMMON_APPDATA == 0x0023 == 35 for the csidl indicating which dir to get
; false for fCreate (i.e. Do not create the folder if it doesn't exist)
; We could use %APPDATA% for this instead, but that requires state: the shell
; var context would need to be saved, set, and reset. It is easier just to use
; the system call.
System::Call "Shell32::SHGetSpecialFolderPathW(p 0, t.R0, i 35, i 0)"
StrCpy $R0 "$R0\$R8\$R1"
; Remove the old updates directory
${If} ${FileExists} "$R0\updates"
RmDir /r "$R0"
${EndIf}
${If} ${FileExists} "$R0\updates\last-update.log"
Rename "$R0\updates\last-update.log" "$TEMP\moz-update-newest-last-update.log"
${EndIf}
${If} ${FileExists} "$R0\updates\backup-update.log"
Rename "$R0\updates\backup-update.log" "$TEMP\moz-update-newest-backup-update.log"
${EndIf}
; Remove the new updates directory, which is shared by all users of the installation
${If} ${FileExists} "$R0\updates"
RmDir /r "$R0"
${EndIf}
${EndIf}
${EndIf}
@ -3375,6 +3402,92 @@
!endif
!macroend
/**
* Create the update directory and sets the permissions correctly
*
* @param ROOT_DIR_NAME
* The name of the update directory to be created in the common
* application directory. For example, if ROOT_DIR_NAME is "Mozilla",
* the created directory will be "C:\ProgramData\Mozilla".
*
* $R0 = Used for checking errors
* $R1 = The common application directory path
* $R9 = An error message to be returned on the stack
*/
!macro CreateUpdateDir ROOT_DIR_NAME
Push $R9
Push $R0
Push $R1
; The update directory is in the Program Data directory
; (currently C:\ProgramData).
; This system call gets that directory path. The arguments are:
; A null ptr for hwnd
; $R1 for the output string
; CSIDL_COMMON_APPDATA == 0x0023 == 35 for the csidl indicating which dir to get
; true for fCreate (i.e. Do create the folder if it doesn't exist)
; We could use %APPDATA% for this instead, but that requires state: the shell
; var context would need to be saved, set, and reset. It is easier just to use
; the system call.
System::Call "Shell32::SHGetSpecialFolderPathW(p 0, t.R1, i 35, i 1)"
StrCpy $R1 "$R1\${ROOT_DIR_NAME}"
ClearErrors
${IfNot} ${FileExists} "$R1"
CreateDirectory "$R1"
${If} ${Errors}
StrCpy $R9 "Unable to create directory: $R1"
GoTo end
${EndIf}
${EndIf}
; Grant Full Access to the Builtin User group
AccessControl::SetOnFile "$R1" "(BU)" "FullAccess"
Pop $R0
${If} $R0 == error
Pop $R9 ; Get AccessControl's Error Message
SetErrors
GoTo end
${EndIf}
; Grant Full Access to the Builtin Administrator group
AccessControl::SetOnFile "$R1" "(BA)" "FullAccess"
Pop $R0
${If} $R0 == error
Pop $R9 ; Get AccessControl's Error Message
SetErrors
GoTo end
${EndIf}
; Grant Full Access to the SYSTEM user
AccessControl::SetOnFile "$R1" "(SY)" "FullAccess"
Pop $R0
${If} $R0 == error
Pop $R9 ; Get AccessControl's Error Message
SetErrors
GoTo end
${EndIf}
; Remove inherited permissions
AccessControl::DisableFileInheritance "$R1"
Pop $R0
${If} $R0 == error
Pop $R9 ; Get AccessControl's Error Message
SetErrors
GoTo end
${EndIf}
end:
Pop $R1
Pop $R0
${If} ${Errors}
Exch $R9
${Else}
Pop $R9
${EndIf}
!macroend
!define CreateUpdateDir "!insertmacro CreateUpdateDir"
/**
* Deletes all relative profiles specified in an application's profiles.ini and
* performs various other cleanup.

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

@ -146,6 +146,7 @@ var UpdateListener = {
break;
case "check-attempts-exceeded":
case "unknown":
case "bad-perms":
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
this.clearCallbacks();

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

@ -86,6 +86,8 @@ var AUSTLMY = {
CHK_ELEVATION_OPTOUT_FOR_VERSION: 36,
// Update checks disabled by enterprise policy
CHK_DISABLED_BY_POLICY: 37,
// Update check failed due to write error
CHK_ERR_WRITE_FAILURE: 38,
/**
* Submit a telemetry ping for the update check result code or a telemetry
@ -173,6 +175,7 @@ var AUSTLMY = {
DWNLD_ERR_DOCUMENT_NOT_CACHED: 12,
DWNLD_ERR_VERIFY_NO_REQUEST: 13,
DWNLD_ERR_VERIFY_PATCH_SIZE_NOT_EQUAL: 14,
DWNLD_ERR_WRITE_FAILURE: 15,
/**
* Submit a telemetry ping for the update download result code.
@ -205,6 +208,10 @@ var AUSTLMY = {
}
},
// Previous state codes are defined in pingStateAndStatusCodes() in
// nsUpdateService.js
STATE_WRITE_FAILURE: 14,
/**
* Submit a telemetry ping for the update status state code.
*

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef COMMONUPDATEDIR_H
#define COMMONUPDATEDIR_H
#include "mozilla/UniquePtr.h"
#include "nsError.h"
#ifdef XP_WIN
#include <windows.h>
typedef WCHAR NS_tchar;
#else
typedef char NS_tchar;
#endif
nsresult GetInstallHash(const char16_t* installPath, const char* vendor,
mozilla::UniquePtr<NS_tchar[]>& result,
bool useCompatibilityMode = false);
#ifdef XP_WIN
enum class SetPermissionsOf {
BaseDir,
BaseDirIfNotExists,
AllFilesAndDirs,
};
// This function does two things. It retrieves the update directory and it sets
// the permissions of the directory and, optionally, its contents.
HRESULT GetCommonUpdateDirectory(const wchar_t* installPath,
const char* vendor,
const char* appName,
SetPermissionsOf dirPermsToSet,
mozilla::UniquePtr<wchar_t[]>& result);
HRESULT GetUserUpdateDirectory(const wchar_t* installPath,
const char* vendor,
const char* appName,
mozilla::UniquePtr<wchar_t[]>& result);
#endif
#endif

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

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'commonupdatedir.h',
'readstrings.h',
'updatecommon.h',
'updatedefines.h',
@ -16,6 +17,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'uachelper.h',
'updatehelper.cpp',
'updatehelper.h',
'win_dirent.h',
]
if CONFIG['MOZ_MAINTENANCE_SERVICE']:
EXPORTS += [
@ -35,9 +37,13 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'pathhash.cpp',
'uachelper.cpp',
'updatehelper.cpp',
'win_dirent.cpp',
]
OS_LIBS += [
'advapi32',
'ole32',
'rpcrt4',
'shell32',
]
if CONFIG['MOZ_MAINTENANCE_SERVICE']:
SOURCES += [
@ -50,9 +56,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
SOURCES += [
'/other-licenses/nsis/Contrib/CityHash/cityhash/city.cpp',
'commonupdatedir.cpp',
'readstrings.cpp',
'updatecommon.cpp',
]
if CONFIG['CC_TYPE'] == 'clang-cl':
AllowCompilerWarnings() # workaround for bug 1090497
LOCAL_INCLUDES += [
'/other-licenses/nsis/Contrib/CityHash/cityhash',
]
DEFINES['MOZ_APP_BASENAME'] = '"%s"' % CONFIG['MOZ_APP_BASENAME']

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

@ -51,7 +51,9 @@
# define CALLBACK_BACKUP_EXT L".moz-callback"
# define LOG_S "%S"
# define NS_T(str) L ## str
# define NS_CONCAT(x, y) x##y
// The extra layer of indirection here allows this macro to be passed macros
# define NS_T(str) NS_CONCAT(L, str)
# define NS_SLASH NS_T('\\')
static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...)

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

@ -47,13 +47,14 @@ closedir(DIR* dir)
return 0;
}
dirent* readdir(DIR* dir)
dirent*
readdir(DIR* dir)
{
WIN32_FIND_DATAW data;
if (dir->findHandle != INVALID_HANDLE_VALUE) {
BOOL result = FindNextFileW(dir->findHandle, &data);
if (!result) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
if (GetLastError() != ERROR_NO_MORE_FILES) {
errno = ENOENT;
}
return 0;
@ -70,9 +71,11 @@ dirent* readdir(DIR* dir)
return 0;
}
}
memset(gDirEnt.d_name, 0, sizeof(gDirEnt.d_name));
wcsncpy(gDirEnt.d_name, data.cFileName,
sizeof(gDirEnt.d_name)/sizeof(gDirEnt.d_name[0]));
size_t direntBufferLength = sizeof(gDirEnt.d_name)/sizeof(gDirEnt.d_name[0]);
wcsncpy(gDirEnt.d_name, data.cFileName, direntBufferLength);
// wcsncpy does not guarantee a null-terminated string if the source string is
// too long.
gDirEnt.d_name[direntBufferLength - 1] = '\0';
return &gDirEnt;
}

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

@ -17,12 +17,12 @@ struct DIR {
explicit DIR(const WCHAR* path);
~DIR();
HANDLE findHandle;
WCHAR name[MAX_PATH];
WCHAR name[MAX_PATH + 1];
};
struct dirent {
dirent();
WCHAR d_name[MAX_PATH];
WCHAR d_name[MAX_PATH + 1];
};
DIR* opendir(const WCHAR* path);

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

@ -407,6 +407,30 @@ interface nsIUpdateProcessor : nsISupports
* to updated application request.
*/
void processUpdate(in nsIUpdate update);
/**
* 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 shouldUseService 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 shouldUseService);
};
/**

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

@ -189,6 +189,9 @@ const APPID_TO_TOPIC = {
// A var is used for the delay so tests can set a smaller value.
var gSaveUpdateXMLDelay = 2000;
var gUpdateMutexHandle = null;
// The permissions of the update directory should be fixed no more than once per
// session
var gUpdateDirPermissionFixAttempted = false;
ChromeUtils.defineModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
@ -660,7 +663,10 @@ function readStatusFile(dir) {
function writeStatusFile(dir, state) {
let statusFile = dir.clone();
statusFile.append(FILE_UPDATE_STATUS);
writeStringToFile(statusFile, state);
let success = writeStringToFile(statusFile, state);
if (!success) {
handleCriticalWriteFailure(statusFile.path);
}
}
/**
@ -681,7 +687,10 @@ function writeStatusFile(dir, state) {
function writeVersionFile(dir, version) {
let versionFile = dir.clone();
versionFile.append(FILE_UPDATE_VERSION);
writeStringToFile(versionFile, version);
let success = writeStringToFile(versionFile, version);
if (!success) {
handleCriticalWriteFailure(versionFile.path);
}
}
/**
@ -813,12 +822,20 @@ function cleanupActiveUpdate() {
/**
* Writes a string of text to a file. A newline will be appended to the data
* written to the file. This function only works with ASCII text.
* @param file An nsIFile indicating what file to write to.
* @param text A string containing the text to write to the file.
* @return true on success, false on failure.
*/
function writeStringToFile(file, text) {
let fos = FileUtils.openSafeFileOutputStream(file);
text += "\n";
fos.write(text, text.length);
FileUtils.closeSafeFileOutputStream(fos);
try {
let fos = FileUtils.openSafeFileOutputStream(file);
text += "\n";
fos.write(text, text.length);
FileUtils.closeSafeFileOutputStream(fos);
} catch (e) {
return false;
}
return true;
}
function readStringFromInputStream(inputStream) {
@ -1026,6 +1043,8 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
case STATE_PENDING_ELEVATE:
stateCode = 13;
break;
// Note: Do not use stateCode 14 here. It is defined in
// UpdateTelemetry.jsm
default:
stateCode = 1;
}
@ -1041,6 +1060,101 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
AUSTLMY.pingStateCode(suffix, stateCode);
}
/**
* 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("Unable to write to critical update file: " + path);
let updateManager = Cc["@mozilla.org/updates/update-manager;1"].
getService(Ci.nsIUpdateManager);
let update = updateManager.activeUpdate;
if (update) {
let patch = update.selectedPatch;
let patchType = AUSTLMY.PATCH_UNKNOWN;
if (patch.type == "complete") {
patchType = AUSTLMY.PATCH_COMPLETE;
} else if (patch.type == "partial") {
patchType = AUSTLMY.PATCH_PARTIAL;
}
if (update.state == STATE_DOWNLOADING) {
if (!patch.downloadWriteFailureTelemetrySent) {
AUSTLMY.pingDownloadCode(patchType, AUSTLMY.DWNLD_ERR_WRITE_FAILURE);
patch.downloadWriteFailureTelemetrySent = true;
}
} else if (!patch.applyWriteFailureTelemetrySent) {
// It's not ideal to hardcode AUSTLMY.STARTUP below (it could be
// AUSTLMY.STAGE). But staging is not used anymore and neither value
// really makes sense for this code. For the other codes it indicates when
// that code was read from the update status file, but this code was never
// read from the update status file.
let suffix = patchType + "_" + AUSTLMY.STARTUP;
AUSTLMY.pingStateCode(suffix, AUSTLMY.STATE_WRITE_FAILURE);
patch.applyWriteFailureTelemetrySent = true;
}
} else {
let updateServiceInstance = UpdateServiceFactory.createInstance();
let request = updateServiceInstance.backgroundChecker._request;
if (!request.checkWriteFailureTelemetrySent) {
let pingSuffix = updateServiceInstance._pingSuffix;
AUSTLMY.pingCheckCode(pingSuffix, AUSTLMY.CHK_ERR_WRITE_FAILURE);
request.checkWriteFailureTelemetrySent = true;
}
}
if (!gUpdateDirPermissionFixAttempted) {
// Currently, we only have a mechanism for fixing update directory permissions
// on Windows.
if (AppConstants.platform != "win") {
LOG("There is currently no implementation for fixing update directory " +
"permissions on this platform");
return false;
}
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;
}
gUpdateDirPermissionFixAttempted = true;
return true;
}
return false;
}
/**
* 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);
}
}
/**
* Update Patch
* @param patch
@ -2551,9 +2665,15 @@ UpdateManager.prototype = {
return updates;
}
var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
try {
fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
} catch (e) {
LOG("UpdateManager:_loadXMLFileIntoArray - error initializing file " +
"stream. Exception: " + e);
return updates;
}
try {
var parser = new DOMParser();
var doc = parser.parseFromStream(fileStream, "UTF-8",
@ -2650,14 +2770,28 @@ UpdateManager.prototype = {
* An array of nsIUpdate objects
* @param fileName
* The file name in the updates directory to write to.
* @return true on success, false on error
*/
_writeUpdatesToXMLFile: async function UM__writeUpdatesToXMLFile(updates, fileName) {
let file = getUpdateFile([fileName]);
let file;
try {
file = getUpdateFile([fileName]);
} catch (e) {
LOG("UpdateManager:_writeUpdatesToXMLFile - Unable to get XML file - " +
"Exception: " + e);
return false;
}
if (updates.length == 0) {
LOG("UpdateManager:_writeUpdatesToXMLFile - no updates to write. " +
"removing file: " + file.path);
await OS.File.remove(file.path, {ignoreAbsent: true});
return;
try {
await OS.File.remove(file.path, {ignoreAbsent: true});
} catch (e) {
LOG("UpdateManager:_writeUpdatesToXMLFile - Delete file exception: " +
e);
return false;
}
return true;
}
const EMPTY_UPDATES_DOCUMENT_OPEN = "<?xml version=\"1.0\"?><updates xmlns=\"http://www.mozilla.org/2005/app-update\">";
@ -2677,7 +2811,9 @@ UpdateManager.prototype = {
await OS.File.setPermissions(file.path, {unixMode: FileUtils.PERMS_FILE});
} catch (e) {
LOG("UpdateManager:_writeUpdatesToXMLFile - Exception: " + e);
return false;
}
return true;
},
_updatesXMLSaver: null,
@ -2713,13 +2849,19 @@ UpdateManager.prototype = {
// the lifetime of an active update and the file should always be updated
// when saveUpdates is called.
this._writeUpdatesToXMLFile(this._activeUpdate ? [this._activeUpdate] : [],
FILE_ACTIVE_UPDATE_XML);
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
// updated when an active update has been added to it in which case
// |_updatesDirty| will be true.
if (this._updatesDirty) {
this._updatesDirty = false;
this._writeUpdatesToXMLFile(this._updates, FILE_UPDATES_XML);
this._writeUpdatesToXMLFile(this._updates, FILE_UPDATES_XML).then(
wroteSuccessfully => handleCriticalWriteResult(wroteSuccessfully,
FILE_UPDATES_XML)
);
}
},
@ -3525,6 +3667,7 @@ Downloader.prototype = {
* @param status
* Status code containing the reason for the cessation.
*/
/* eslint-disable-next-line complexity */
onStopRequest: function Downloader_onStopRequest(request, context, status) {
if (request instanceof Ci.nsIIncrementalDownload)
LOG("Downloader:onStopRequest - original URI spec: " + request.URI.spec +
@ -3545,6 +3688,7 @@ Downloader.prototype = {
DEFAULT_SOCKET_MAX_ERRORS);
// Prevent the preference from setting a value greater than 20.
maxFail = Math.min(maxFail, 20);
let permissionFixingInProgress = false;
LOG("Downloader:onStopRequest - status: " + status + ", " +
"current fail: " + this.updateService._consecutiveSocketErrors + ", " +
"max fail: " + maxFail + ", " +
@ -3619,7 +3763,18 @@ Downloader.prototype = {
deleteActiveUpdate = false;
} else if (status != Cr.NS_BINDING_ABORTED &&
status != Cr.NS_ERROR_ABORT) {
LOG("Downloader:onStopRequest - non-verification failure");
if (status == Cr.NS_ERROR_FILE_ACCESS_DENIED ||
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 = getUpdatesDir().clone();
patchFile.append(FILE_UPDATE_MAR);
permissionFixingInProgress = handleCriticalWriteFailure(patchFile.path);
} else {
LOG("Downloader:onStopRequest - non-verification failure");
}
let dwnldCode = AUSTLMY.DWNLD_ERR_BINDING_ABORTED;
if (status == Cr.NS_ERROR_ABORT) {
dwnldCode = AUSTLMY.DWNLD_ERR_ABORT;
@ -3682,7 +3837,7 @@ Downloader.prototype = {
}
}
if (allFailed) {
if (allFailed && !permissionFixingInProgress) {
if (Services.prefs.getBoolPref(PREF_APP_UPDATE_DOORHANGER, false)) {
let downloadAttempts = Services.prefs.getIntPref(PREF_APP_UPDATE_DOWNLOAD_ATTEMPTS, 0);
downloadAttempts++;
@ -3713,7 +3868,8 @@ Downloader.prototype = {
createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateError(this._update);
}
}
if (allFailed) {
// Prevent leaking the update object (bug 454964).
this._update = null;
}

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

@ -5,27 +5,48 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/FileUtils.jsm", this);
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
ChromeUtils.import("resource://gre/modules/AppConstants.jsm", this);
const DIR_UPDATES = "updates";
const FILE_UPDATE_STATUS = "update.status";
const FILE_ACTIVE_UPDATE_XML = "active-update.xml";
const FILE_LAST_UPDATE_LOG = "last-update.log";
const FILE_UPDATES_XML = "updates.xml";
const FILE_UPDATE_LOG = "update.log";
const KEY_UPDROOT = "UpdRootD";
const KEY_OLD_UPDROOT = "OldUpdRootD";
/**
* Gets the specified directory at the specified hierarchy under the update root
* directory without creating it if it doesn't exist.
* @param pathArray
* An array of path components to locate beneath the directory
* specified by |key|
* @return nsIFile object for the location specified.
*/
function getUpdateDirNoCreate(pathArray) {
return FileUtils.getDir(KEY_UPDROOT, pathArray, false);
}
// The pref prefix below should have the hash of the install path appended to
// ensure that this is a per-installation pref (i.e. to ensure that migration
// happens for every install rather than once per profile)
const PREF_PREFIX_UPDATE_DIR_MIGRATED = "app.update.migrated.updateDir2.";
const PREF_APP_UPDATE_LOG = "app.update.log";
XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
return Services.prefs.getBoolPref(PREF_APP_UPDATE_LOG, false);
});
function UpdateServiceStub() {
let statusFile = getUpdateDirNoCreate([DIR_UPDATES, "0"]);
let updateDir = FileUtils.getDir(KEY_UPDROOT, [], false);
let prefUpdateDirMigrated = PREF_PREFIX_UPDATE_DIR_MIGRATED
+ updateDir.leafName;
let statusFile = updateDir;
statusFile.append(DIR_UPDATES);
statusFile.append("0");
statusFile.append(FILE_UPDATE_STATUS);
updateDir = null; // We don't need updateDir anymore, plus now its nsIFile
// contains the status file's path
// We may need to migrate update data
if (AppConstants.platform == "win" &&
!Services.prefs.getBoolPref(prefUpdateDirMigrated, false)) {
migrateUpdateDirectory();
Services.prefs.setBoolPref(prefUpdateDirMigrated, true);
}
// If the update.status file exists then initiate post update processing.
if (statusFile.exists()) {
let aus = Cc["@mozilla.org/updates/update-service;1"].
@ -41,3 +62,111 @@ UpdateServiceStub.prototype = {
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdateServiceStub]);
/**
* This function should be called when there are files in the old update
* directory that may need to be migrated to the new update directory.
*/
function migrateUpdateDirectory() {
let sourceRootDir = FileUtils.getDir(KEY_OLD_UPDROOT, [], false);
let destRootDir = FileUtils.getDir(KEY_UPDROOT, [], false);
if (!sourceRootDir.exists()) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Abort: No migration " +
"necessary. Nothing to migrate.");
return;
}
if (destRootDir.exists()) {
// Migration must have already been done by another user
LOG("UpdateServiceStub:_migrateUpdateDirectory - migrated and unmigrated " +
"update directories found. Deleting the unmigrated directory: " +
sourceRootDir.path);
try {
sourceRootDir.remove(true);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Deletion of " +
"unmigrated directory failed. Exception: " + e);
}
return;
}
let sourceUpdateDir = sourceRootDir.clone();
sourceUpdateDir.append(DIR_UPDATES);
let destUpdateDir = destRootDir.clone();
destUpdateDir.append(DIR_UPDATES);
let sourcePatchDir = sourceUpdateDir.clone();
sourcePatchDir.append("0");
let destPatchDir = destUpdateDir.clone();
destPatchDir.append("0");
let sourceStatusFile = sourcePatchDir.clone();
sourceStatusFile.append(FILE_UPDATE_STATUS);
let destStatusFile = destPatchDir.clone();
destStatusFile.append(FILE_UPDATE_STATUS);
let sourceActiveXML = sourceRootDir.clone();
sourceActiveXML.append(FILE_ACTIVE_UPDATE_XML);
try {
sourceActiveXML.moveTo(destRootDir, sourceActiveXML.leafName);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Unable to move active " +
"update XML file. Exception: " + e);
}
let sourceUpdateXML = sourceRootDir.clone();
sourceUpdateXML.append(FILE_UPDATES_XML);
try {
sourceUpdateXML.moveTo(destRootDir, sourceUpdateXML.leafName);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Unable to move " +
"update XML file. Exception: " + e);
}
let sourceUpdateLog = sourcePatchDir.clone();
sourceUpdateLog.append(FILE_UPDATE_LOG);
try {
sourceUpdateLog.moveTo(destPatchDir, sourceUpdateLog.leafName);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Unable to move " +
"update log file. Exception: " + e);
}
let sourceLastUpdateLog = sourceUpdateDir.clone();
sourceLastUpdateLog.append(FILE_LAST_UPDATE_LOG);
try {
sourceLastUpdateLog.moveTo(destUpdateDir, sourceLastUpdateLog.leafName);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Unable to move " +
"last-update log file. Exception: " + e);
}
try {
sourceStatusFile.moveTo(destStatusFile.parent, destStatusFile.leafName);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Unable to move update " +
"status file. Exception: " + e);
}
// Remove all remaining files in the old update directory. We don't need
// them anymore
try {
sourceRootDir.remove(true);
} catch (e) {
LOG("UpdateServiceStub:_migrateUpdateDirectory - Deletion of old update " +
"directory failed. Exception: " + e);
}
}
/**
* Logs a string to the error console.
* @param string
* The string to write to the error console.
*/
function LOG(string) {
if (gLogEnabled) {
dump("*** AUS:SVC " + string + "\n");
Services.console.logStringMessage("AUS:SVC " + string);
}
}

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

@ -9,6 +9,7 @@
# include <softpub.h>
# include <direct.h>
# include <io.h>
# include "commonupdatedir.h"
typedef WCHAR NS_tchar;
# define NS_main wmain
# ifndef F_OK
@ -194,6 +195,7 @@ int NS_main(int argc, NS_tchar **argv)
" or: remove-symlink dir1 dir2 file symlink\n" \
" or: check-symlink symlink\n" \
" or: post-update\n" \
" or: create-update-dir\n" \
"\n" \
" WORKINGDIR \tThe relative path to the working directory to use.\n" \
" INFILE \tThe relative path from the working directory for the file to\n" \
@ -356,6 +358,19 @@ int NS_main(int argc, NS_tchar **argv)
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("create-update-dir"))) {
#ifdef XP_WIN
mozilla::UniquePtr<wchar_t[]> updateDir;
HRESULT result = GetCommonUpdateDirectory(argv[2], nullptr, nullptr,
SetPermissionsOf::AllFilesAndDirs,
updateDir);
return SUCCEEDED(result) ? 0 : 1;
#else
// Not implemented on non-Windows platforms
return 1;
#endif
}
if (NS_tchdir(argv[1]) != 0) {
return 1;
}

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

@ -10,16 +10,12 @@
#ifdef XP_WIN
#include <windows.h>
#define NS_main wmain
#define NS_tstrrchr wcsrchr
#define NS_T(str) L ## str
#define PATH_SEPARATOR_CHAR L'\\'
// On Windows, argv[0] can also have forward slashes instead
#define ALT_PATH_SEPARATOR_CHAR L'/'
#else
#include <unistd.h>
#define NS_main main
#define NS_tstrrchr strrchr
#define NS_T(str) str
#define PATH_SEPARATOR_CHAR '/'
#endif
@ -31,6 +27,7 @@
#include "updater/progressui.h"
#include "common/readstrings.h"
#include "common/errors.h"
#include "common/updatedefines.h"
#include "mozilla/ArrayUtils.h"
#ifndef MAXPATHLEN

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

@ -38,10 +38,11 @@ const PREF_DISABLE_SECURITY = "security.turn_off_all_security_so_that_v
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
const NS_GRE_DIR = "GreD";
const NS_GRE_BIN_DIR = "GreBinD";
const NS_GRE_DIR = "GreD";
const NS_XPCOM_CURRENT_PROCESS_DIR = "XCurProcD";
const XRE_EXECUTABLE_FILE = "XREExeF";
const XRE_OLD_UPDATE_ROOT_DIR = "OldUpdRootD";
const XRE_UPDATE_ROOT_DIR = "UpdRootD";
const DIR_PATCH = "0";

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

@ -840,6 +840,8 @@ function setupTestCommon() {
// adjustGeneralPaths registers a cleanup function that calls end_test when
// it is defined as a function.
adjustGeneralPaths();
createWorldWritableAppUpdateDir();
// Logged once here instead of in the mock directory provider to lessen test
// log spam.
debugDump("Updates Directory (UpdRootD) Path: " + getMockUpdRootD().path);
@ -1596,6 +1598,15 @@ XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir", function test_gLADD() {
return getSpecialFolderDir(CSIDL_LOCAL_APPDATA);
});
XPCOMUtils.defineLazyGetter(this, "gCommonAppDataDir", function test_gCDD() {
if (!IS_WIN) {
do_throw("Windows only function called by a different platform!");
}
const CSIDL_COMMON_APPDATA = 0x0023;
return getSpecialFolderDir(CSIDL_COMMON_APPDATA);
});
XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", function test_gPFD() {
if (!IS_WIN) {
do_throw("Windows only function called by a different platform!");
@ -1610,10 +1621,14 @@ XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", function test_gPFD() {
* returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir
* in nsXREDirProvider.cpp so an application will be able to find the update
* when running a test that launches the application.
*
* The aGetOldLocation argument performs the same function that the argument
* with the same name in nsXREDirProvider::GetUpdateRootDir performs. If true,
* the old (pre-migration) update directory is returned.
*/
function getMockUpdRootD() {
function getMockUpdRootD(aGetOldLocation = false) {
if (IS_WIN) {
return getMockUpdRootDWin();
return getMockUpdRootDWin(aGetOldLocation);
}
if (IS_MACOSX) {
@ -1629,12 +1644,17 @@ function getMockUpdRootD() {
* in nsXREDirProvider.cpp so an application will be able to find the update
* when running a test that launches the application.
*/
function getMockUpdRootDWin() {
function getMockUpdRootDWin(aGetOldLocation) {
if (!IS_WIN) {
do_throw("Windows only function called by a different platform!");
}
let localAppDataDir = gLocalAppDataDir.clone();
let dataDirectory;
if (aGetOldLocation) {
dataDirectory = gLocalAppDataDir.clone();
} else {
dataDirectory = gCommonAppDataDir.clone();
}
let progFilesDir = gProgFilesDir.clone();
let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).parent;
@ -1669,10 +1689,19 @@ function getMockUpdRootDWin() {
let updatesDir = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsIFile);
updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates);
updatesDir.initWithPath(dataDirectory.path + "\\" + relPathUpdates);
return updatesDir;
}
function createWorldWritableAppUpdateDir() {
// This function is only necessary in Windows
if (IS_WIN) {
let installDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, Ci.nsIFile).parent;
let exitValue = runTestHelperSync(["create-update-dir", installDir.path]);
Assert.equal(exitValue, 0, "The helper process exit value should be 0");
}
}
XPCOMUtils.defineLazyGetter(this, "gUpdatesRootDir", function test_gURD() {
if (!IS_MACOSX) {
do_throw("Mac OS X only function called by a different platform!");
@ -3926,8 +3955,8 @@ function getProcessArgs(aExtraArgs) {
scriptContents);
args = [launchScript.path];
} else {
args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-test-process-updates"].
concat(aExtraArgs).concat([PIPE_TO_NULL]);
args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-test-process-updates",
"-wait-for-browser"].concat(aExtraArgs).concat([PIPE_TO_NULL]);
}
return args;
}
@ -3981,6 +4010,8 @@ function adjustGeneralPaths() {
return getApplyDirFile(DIR_MACOS + FILE_APP_BIN, true);
case XRE_UPDATE_ROOT_DIR:
return getMockUpdRootD();
case XRE_OLD_UPDATE_ROOT_DIR:
return getMockUpdRootD(true);
}
return null;
},

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

@ -0,0 +1,152 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Gets the root directory for the old (unmigrated) updates directory.
*
* @return nsIFile for the updates root directory.
*/
function getOldUpdatesRootDir() {
return Services.dirsvc.get(XRE_OLD_UPDATE_ROOT_DIR, Ci.nsIFile);
}
/**
* Gets the old (unmigrated) updates directory.
*
* @return nsIFile for the updates directory.
*/
function getOldUpdatesDir() {
let dir = getOldUpdatesRootDir();
dir.append(DIR_UPDATES);
return dir;
}
/**
* Gets the directory for update patches in the old (unmigrated) updates
* directory.
*
* @return nsIFile for the updates directory.
*/
function getOldUpdatesPatchDir() {
let dir = getOldUpdatesDir();
dir.append(DIR_PATCH);
return dir;
}
/**
* Returns either the active or regular update database XML file in the old
* (unmigrated) updates directory
*
* @param isActiveUpdate
* If true this will return the active-update.xml otherwise it will
* return the updates.xml file.
*/
function getOldUpdatesXMLFile(aIsActiveUpdate) {
let file = getOldUpdatesRootDir();
file.append(aIsActiveUpdate ? FILE_ACTIVE_UPDATE_XML : FILE_UPDATES_XML);
return file;
}
/**
* Writes the updates specified to either the active-update.xml or the
* updates.xml in the old (unmigrated) update directory
*
* @param aContent
* The updates represented as a string to write to the XML file.
* @param isActiveUpdate
* If true this will write to the active-update.xml otherwise it will
* write to the updates.xml file.
*/
function writeUpdatesToOldXMLFile(aContent, aIsActiveUpdate) {
writeFile(getOldUpdatesXMLFile(aIsActiveUpdate), aContent);
}
/**
* Writes the given update operation/state to a file in the old (unmigrated)
* patch directory, indicating to the patching system what operations need
* to be performed.
*
* @param aStatus
* The status value to write.
*/
function writeOldStatusFile(aStatus) {
let file = getOldUpdatesPatchDir();
file.append(FILE_UPDATE_STATUS);
writeFile(file, aStatus + "\n");
}
/**
* Gets the specified update log from the old (unmigrated) update directory
*
* @param aLogLeafName
* The leaf name of the log to get.
* @return nsIFile for the update log.
*/
function getOldUpdateLog(aLogLeafName) {
let updateLog = getOldUpdatesDir();
if (aLogLeafName == FILE_UPDATE_LOG) {
updateLog.append(DIR_PATCH);
}
updateLog.append(aLogLeafName);
return updateLog;
}
function run_test() {
setupTestCommon();
debugDump("testing that the update directory is migrated after a successful update");
Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, 5);
let patchProps = {state: STATE_PENDING};
let patches = getLocalPatchString(patchProps);
let updates = getLocalUpdateString({}, patches);
writeUpdatesToOldXMLFile(getLocalUpdatesXMLString(updates), true);
writeOldStatusFile(STATE_SUCCEEDED);
let log = getOldUpdateLog(FILE_UPDATE_LOG);
writeFile(log, "Last Update Log");
standardInit();
Assert.ok(!gUpdateManager.activeUpdate,
"there should not be an active update");
Assert.equal(gUpdateManager.updateCount, 1,
"the update manager update count" + MSG_SHOULD_EQUAL);
executeSoon(waitForUpdateXMLFiles);
}
/**
* Called after the call to waitForUpdateXMLFiles finishes.
*/
function waitForUpdateXMLFilesFinished() {
let cancelations = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS, 0);
Assert.equal(cancelations, 0,
"the " + PREF_APP_UPDATE_CANCELATIONS + " preference " +
MSG_SHOULD_EQUAL);
let oldDir = getOldUpdatesRootDir();
let newDir = getUpdatesRootDir();
if (oldDir.path != newDir.path) {
Assert.ok(!oldDir.exists(),
"Old update directory should have been deleted after migration");
}
let log = getUpdateLog(FILE_UPDATE_LOG);
Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST);
log = getUpdateLog(FILE_LAST_UPDATE_LOG);
Assert.ok(log.exists(), MSG_SHOULD_EXIST);
Assert.equal(readFile(log), "Last Update Log",
"the last update log contents" + MSG_SHOULD_EQUAL);
log = getUpdateLog(FILE_BACKUP_UPDATE_LOG);
Assert.ok(!log.exists(), MSG_SHOULD_NOT_EXIST);
let dir = getUpdatesPatchDir();
Assert.ok(dir.exists(), MSG_SHOULD_EXIST);
doTestFinish();
}

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

@ -28,3 +28,6 @@ head = head_update.js
[uiSilentPref.js]
[uiUnsupportedAlreadyNotified.js]
[uiAutoPref.js]
[updateDirectoryMigrate.js]
skip-if = os != 'win'
reason = Update directory migration is currently Windows only

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

@ -22,7 +22,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
srcs += [
'loaddlls.cpp',
'progressui_win.cpp',
'win_dirent.cpp',
]
RCINCLUDE = '%supdater.rc' % updater_rel_path
DEFINES['UNICODE'] = True

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

@ -47,6 +47,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'WinDllServices.h',
]
UNIFIED_SOURCES += [
'/toolkit/mozapps/update/common/win_dirent.cpp',
'nsNativeAppSupportWin.cpp',
'WinDllServices.cpp',
]
@ -98,6 +99,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
]
UNIFIED_SOURCES += [
'/toolkit/mozapps/update/common/commonupdatedir.cpp',
'AutoSQLiteLifetime.cpp',
'Bootstrap.cpp',
'CreateAppData.cpp',

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

@ -25,6 +25,7 @@
#include "nsPrintfCString.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Printf.h"
#include "mozilla/UniquePtr.h"
#ifdef XP_MACOSX
#include "nsILocalFileMac.h"
@ -39,6 +40,8 @@
# include <process.h>
# include <windows.h>
# include <shlwapi.h>
# include "commonupdatedir.h"
# include "nsWindowsHelpers.h"
# define getcwd(path, size) _getcwd(path, size)
# define getpid() GetCurrentProcessId()
#elif defined(XP_UNIX)
@ -53,6 +56,7 @@ static LazyLogModule sUpdateLog("updatedriver");
#ifdef XP_WIN
#define UPDATER_BIN "updater.exe"
#define MAINTENANCE_SVC_NAME L"MozillaMaintenance"
#elif XP_MACOSX
#define UPDATER_BIN "org.mozilla.updater"
#else
@ -899,7 +903,234 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
r);
}
NS_IMETHODIMP
nsUpdateProcessor::FixUpdateDirectoryPerms(bool aShouldUseService)
{
#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 aShouldUseService,
const nsAutoString& aInstallPath)
: Runnable(aName)
, mShouldUseService(aShouldUseService)
, mState(State::Initializing)
{
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
{
// 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;
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(), nullptr,
nullptr, SetPermissionsOf::AllFilesAndDirs,
updateDir);
if (SUCCEEDED(permResult)) {
LOG(("Successfully fixed permissions from within Firefox\n"));
return NS_OK;
} else if (!mShouldUseService) {
LOG(("Error: Unable to fix permissions within Firefox and "
"maintenance service is disabled\n"));
return ReportUpdateError();
}
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);
if (!mStartServiceArgs) {
LOG(("Error: Unable to allocate memory for argument pointers. Cannot "
"fix permissions.\n"));
return NS_ERROR_FAILURE;
}
mStartServiceArgs[0] = L"MozillaMaintenance";
mStartServiceArgs[1] = L"fix-update-directory-perms";
if (mInstallPath) {
mStartServiceArgs[2] = mInstallPath.get();
}
mState = State::WaitingToStart;
mCurrentTry = 1;
}
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);
}
// 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:
bool mShouldUseService;
unsigned int mCurrentTry;
State mState;
mozilla::UniquePtr<wchar_t[]> mInstallPath;
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);
}
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",
aShouldUseService, installPath);
rv = eventTarget->Dispatch(runnable.forget());
NS_ENSURE_SUCCESS(rv, rv);
#endif
return NS_OK;
}
void
nsUpdateProcessor::StartStagedUpdate()

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

@ -43,11 +43,11 @@
#include "mozilla/Telemetry.h"
#include <stdlib.h>
#include "city.h"
#ifdef XP_WIN
#include <windows.h>
#include <shlobj.h>
#include "commonupdatedir.h"
#endif
#ifdef XP_MACOSX
#include "nsILocalFileMac.h"
@ -435,6 +435,9 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
else if (!strcmp(aProperty, XRE_UPDATE_ROOT_DIR)) {
rv = GetUpdateRootDir(getter_AddRefs(file));
}
else if (!strcmp(aProperty, XRE_OLD_UPDATE_ROOT_DIR)) {
rv = GetUpdateRootDir(getter_AddRefs(file), true);
}
else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) {
rv = GetUserAppDataDirectory(getter_AddRefs(file));
if (NS_SUCCEEDED(rv))
@ -1256,100 +1259,52 @@ GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval)
return NS_OK;
}
static bool
GetCachedHash(HKEY rootKey, const nsAString &regPath, const nsAString &path,
nsAString &cachedHash)
{
HKEY baseKey;
if (RegOpenKeyExW(rootKey, reinterpret_cast<const wchar_t*>(regPath.BeginReading()), 0, KEY_READ, &baseKey) !=
ERROR_SUCCESS) {
return false;
}
wchar_t cachedHashRaw[512];
DWORD bufferSize = sizeof(cachedHashRaw);
LONG result = RegQueryValueExW(baseKey, reinterpret_cast<const wchar_t*>(path.BeginReading()), 0, nullptr,
(LPBYTE)cachedHashRaw, &bufferSize);
RegCloseKey(baseKey);
if (result == ERROR_SUCCESS) {
cachedHash.Assign(cachedHashRaw);
}
return ERROR_SUCCESS == result;
}
#endif
// Temporary for nsIXREDirProvider until compatibility mode goes away.
nsresult
nsXREDirProvider::GetInstallHash(nsAString & aPathHash)
{
return GetInstallHash(aPathHash, false);
}
// Compatibility Mode (aUseCompatibilityMode) outputs hashes that are what this
// function has historically returned. The new default is to output hashes that
// are consistent with those generated by the installer.
nsresult
nsXREDirProvider::GetInstallHash(nsAString & aPathHash, bool aUseCompatibilityMode)
{
nsCOMPtr<nsIFile> updRoot;
nsCOMPtr<nsIFile> installDir;
nsCOMPtr<nsIFile> appFile;
bool per = false;
nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = appFile->GetParent(getter_AddRefs(updRoot));
rv = appFile->GetParent(getter_AddRefs(installDir));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString appDirPath;
rv = updRoot->GetPath(appDirPath);
nsAutoString installPath;
rv = installDir->GetPath(installPath);
NS_ENSURE_SUCCESS(rv, rv);
aPathHash.Truncate();
const char* vendor = GetAppVendor();
if (vendor && vendor[0] == '\0') {
vendor = nullptr;
}
mozilla::UniquePtr<NS_tchar[]> hash;
rv = ::GetInstallHash(PromiseFlatString(installPath).get(), vendor, hash);
NS_ENSURE_SUCCESS(rv, rv);
// The hash string is a NS_tchar*, which is wchar* in Windows and char*
// elsewhere.
#ifdef XP_WIN
// Figure out where we should check for a cached hash value. If the
// application doesn't have the nsXREAppData vendor value defined check
// under SOFTWARE\Mozilla.
bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0;
wchar_t regPath[1024] = { L'\0' };
swprintf_s(regPath, mozilla::ArrayLength(regPath), L"SOFTWARE\\%S\\%S\\TaskBarIDs",
(hasVendor ? GetAppVendor() : "Mozilla"), MOZ_APP_BASENAME);
// If we pre-computed the hash, grab it from the registry.
if (GetCachedHash(HKEY_LOCAL_MACHINE, nsDependentString(regPath), appDirPath,
aPathHash)) {
return NS_OK;
}
if (GetCachedHash(HKEY_CURRENT_USER, nsDependentString(regPath), appDirPath,
aPathHash)) {
return NS_OK;
}
aPathHash.Assign(hash.get());
#else
aPathHash.AssignASCII(hash.get());
#endif
// This should only happen when the installer isn't used (e.g. zip builds).
void* buffer = appDirPath.BeginWriting();
uint32_t length = appDirPath.Length() * sizeof(nsAutoString::char_type);
uint64_t hash = CityHash64(static_cast<const char*>(buffer), length);
if (aUseCompatibilityMode) {
aPathHash.AppendInt((int)(hash >> 32), 16);
aPathHash.AppendInt((int)hash, 16);
// The installer implementation writes the registry values that were checked
// in the previous block for this value in uppercase and since it is an
// option to have a case sensitive file system on Windows this value must
// also be in uppercase.
ToUpperCase(aPathHash);
} else {
aPathHash.AppendPrintf("%" PRIX64, hash);
}
return NS_OK;
}
nsresult
nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult, bool aGetOldLocation)
{
#ifndef XP_WIN
// There is no old update location on platforms other than Windows. Windows is
// the only platform for which we migrated the update directory.
if (aGetOldLocation) {
return NS_ERROR_NOT_IMPLEMENTED;
}
#endif
nsCOMPtr<nsIFile> updRoot;
nsCOMPtr<nsIFile> appFile;
bool per = false;
@ -1394,74 +1349,36 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
return NS_OK;
#elif XP_WIN
nsAutoString pathHash;
rv = GetInstallHash(pathHash, true);
nsAutoString installPath;
rv = updRoot->GetPath(installPath);
NS_ENSURE_SUCCESS(rv, rv);
// As a last ditch effort, get the local app data directory and if a vendor
// name exists append it. If only a product name exists, append it. If neither
// exist fallback to old handling. We don't use the product name on purpose
// because we want a shared update directory for different apps run from the
// same path.
nsCOMPtr<nsIFile> localDir;
bool hasVendor = GetAppVendor() && strlen(GetAppVendor()) != 0;
if ((hasVendor || GetAppName()) &&
NS_SUCCEEDED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true)) &&
NS_SUCCEEDED(localDir->AppendNative(nsDependentCString(hasVendor ?
GetAppVendor() : GetAppName()))) &&
NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("updates"))) &&
NS_SUCCEEDED(localDir->Append(pathHash))) {
localDir.forget(aResult);
return NS_OK;
const char* vendor = GetAppVendor();
if (vendor && vendor[0] == '\0') {
vendor = nullptr;
}
const char* appName = GetAppName();
if (appName && appName[0] == '\0') {
appName = nullptr;
}
nsAutoString appPath;
rv = updRoot->GetPath(appPath);
NS_ENSURE_SUCCESS(rv, rv);
// AppDir may be a short path. Convert to long path to make sure
// the consistency of the update folder location
nsString longPath;
wchar_t* buf;
uint32_t bufLength = longPath.GetMutableData(&buf, MAXPATHLEN);
NS_ENSURE_TRUE(bufLength >= MAXPATHLEN, NS_ERROR_OUT_OF_MEMORY);
DWORD len = GetLongPathNameW(appPath.get(), buf, bufLength);
// Failing GetLongPathName() is not fatal.
if (len <= 0 || len >= bufLength)
longPath.Assign(appPath);
else
longPath.SetLength(len);
// Use <UserLocalDataDir>\updates\<relative path to app dir from
// Program Files> if app dir is under Program Files to avoid the
// folder virtualization mess on Windows Vista
nsAutoString programFiles;
rv = GetShellFolderPath(FOLDERID_ProgramFiles, programFiles);
NS_ENSURE_SUCCESS(rv, rv);
programFiles.Append('\\');
uint32_t programFilesLen = programFiles.Length();
nsAutoString programName;
if (_wcsnicmp(programFiles.get(), longPath.get(), programFilesLen) == 0) {
programName = Substring(longPath, programFilesLen);
mozilla::UniquePtr<wchar_t[]> updatePath;
HRESULT hrv;
if (aGetOldLocation) {
hrv = GetUserUpdateDirectory(PromiseFlatString(installPath).get(), vendor,
appName, updatePath);
} else {
// We need the update root directory to live outside of the installation
// directory, because otherwise the updater writing the log file can cause
// the directory to be locked, which prevents it from being replaced after
// background updates.
programName.AssignASCII(MOZ_APP_NAME);
hrv = GetCommonUpdateDirectory(PromiseFlatString(installPath).get(), vendor,
appName,
SetPermissionsOf::BaseDirIfNotExists,
updatePath);
}
rv = GetUserLocalDataDirectory(getter_AddRefs(updRoot));
NS_ENSURE_SUCCESS(rv, rv);
rv = updRoot->AppendRelativePath(programName);
NS_ENSURE_SUCCESS(rv, rv);
if (FAILED(hrv)) {
return NS_ERROR_FAILURE;
}
nsAutoString updatePathStr;
updatePathStr.Assign(updatePath.get());
updRoot->InitWithPath(updatePathStr);
#endif // XP_WIN
updRoot.forget(aResult);
return NS_OK;

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

@ -78,17 +78,19 @@ public:
return mGREDir;
}
/**
* Get a hash for the install directory.
*/
nsresult GetInstallHash(nsAString & aPathHash, bool aUseCompatibilityMode);
/**
* Get the directory under which update directory is created.
* This method may be called before XPCOM is started. aResult
* is a clone, it may be modified.
*
* If aGetOldLocation is true, this function will return the location of
* the update directory before it was moved from the user profile directory
* to a per-installation directory. This functionality is only meant to be
* used for migration of the update directory to the new location. It is only
* valid to request the old update location on Windows, since that is the only
* platform on which the update directory was migrated.
*/
nsresult GetUpdateRootDir(nsIFile* *aResult);
nsresult GetUpdateRootDir(nsIFile** aResult, bool aGetOldLocation = false);
/**
* Get the profile startup directory as determined by this class or by

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

@ -297,6 +297,25 @@ public:
};
template<>
class nsAutoRefTraits<PSID>
{
public:
typedef PSID RawRef;
static RawRef Void()
{
return nullptr;
}
static void Release(RawRef aFD)
{
if (aFD != Void()) {
FreeSid(aFD);
}
}
};
typedef nsAutoRef<HKEY> nsAutoRegKey;
typedef nsAutoRef<HDC> nsAutoHDC;
typedef nsAutoRef<HBRUSH> nsAutoBrush;
@ -308,6 +327,7 @@ typedef nsAutoRef<HMODULE> nsModuleHandle;
typedef nsAutoRef<DEVMODEW*> nsAutoDevMode;
typedef nsAutoRef<nsHGLOBAL> nsAutoGlobalMem;
typedef nsAutoRef<nsHPRINTER> nsAutoPrinter;
typedef nsAutoRef<PSID> nsAutoSid;
namespace {
@ -368,4 +388,12 @@ LoadLibrarySystem32(LPCWSTR aModule)
}
struct LocalFreeDeleter
{
void operator()(void* aPtr)
{
::LocalFree(aPtr);
}
};
#endif

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

@ -155,23 +155,15 @@
* A directory service key which provides the update directory. Callers should
* fall back to appDir.
* Windows: If vendor name exists:
* Documents and Settings\<User>\Local Settings\Application Data\
* <vendor name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILEs parent directory>
* ProgramData\<vendor name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
*
* If vendor name doesn't exist, but product name exists:
* Documents and Settings\<User>\Local Settings\Application Data\
* <product name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILEs parent directory>
* ProgramData\<product name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
*
* If neither vendor nor product name exists:
* If app dir is under Program Files:
* Documents and Settings\<User>\Local Settings\Application Data\
* <relative path to app dir from Program Files>
*
* If app dir isnt under Program Files:
* Documents and Settings\<User>\Local Settings\Application Data\
* <MOZ_APP_NAME>
* ProgramData\Mozilla\updates
*
* Mac: ~/Library/Caches/Mozilla/updates/<absolute path to app dir>
*
@ -179,6 +171,28 @@
*/
#define XRE_UPDATE_ROOT_DIR "UpdRootD"
/**
* A directory service key which provides the *old* update directory. This
* path should only be used when data needs to be migrated from the old update
* directory.
* Windows: If vendor name exists:
* Documents and Settings\<User>\Local Settings\Application Data\
* <vendor name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
*
* If vendor name doesn't exist, but product name exists:
* Documents and Settings\<User>\Local Settings\Application Data\
* <product name>\updates\
* <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
*
* If neither vendor nor product name exists:
* Documents and Settings\<User>\Local Settings\Application Data\
* Mozilla\updates
*
* This path does not exist on other operating systems
*/
#define XRE_OLD_UPDATE_ROOT_DIR "OldUpdRootD"
/**
* Begin an XUL application. Does not return until the user exits the
* application.