Bug 1355361 - nsUpdateProcessor::ProcessUpdate should use the directory service instead of nsXREDirProvider (also removes app update gonk support). r=mhowell

Since the ApplyUpdate and SwitchToUpdatedApp functions are almost entirely the same this moves everything into ApplyUpdate.
Removes most of the gonk code from application update.
Makes client code and xpcshell tests use the same code for directory providers in nsUpdateDriver.cpp.
This commit is contained in:
Robert Strong 2017-04-12 18:07:07 -07:00
Родитель 1565fca250
Коммит f9a82146b7
9 изменённых файлов: 255 добавлений и 1153 удалений

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

@ -772,6 +772,8 @@ function setupTestCommon() {
let caller = Components.stack.caller;
gTestID = caller.filename.toString().split("/").pop().split(".")[0];
createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0");
// Tests that don't work with XULRunner.
const XUL_RUNNER_INCOMPATIBLE = ["marAppApplyUpdateAppBinInUseStageSuccess_win",
"marAppApplyUpdateStageSuccess",
@ -1105,7 +1107,6 @@ function checkPostUpdateRunningFile(aShouldExist) {
* update service stub.
*/
function standardInit() {
createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0");
// Initialize the update service stub component
initUpdateServiceStub();
}
@ -1892,10 +1893,15 @@ function stageUpdate(aCheckSvcLog) {
setAppBundleModTime();
setEnvironment();
// Stage the update.
Cc["@mozilla.org/updates/update-processor;1"].
createInstance(Ci.nsIUpdateProcessor).
processUpdate(gUpdateManager.activeUpdate);
try {
// Stage the update.
Cc["@mozilla.org/updates/update-processor;1"].
createInstance(Ci.nsIUpdateProcessor).
processUpdate(gUpdateManager.activeUpdate);
} catch (e) {
Assert.ok(false,
"error thrown while calling processUpdate, exception: " + e);
}
// The environment is not reset here because processUpdate in
// nsIUpdateProcessor uses a new thread and clearing the environment

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

@ -1,251 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include <android/log.h>
#include <cutils/android_reboot.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/reboot.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "automounter_gonk.h"
#include "updatedefines.h"
#include "updatelogging.h"
#define LOG_TAG "GonkAutoMounter"
#define GONK_LOG(level, format, ...) \
LOG((LOG_TAG ": " format "\n", ##__VA_ARGS__)); \
__android_log_print(level, LOG_TAG, format, ##__VA_ARGS__)
#define LOGI(format, ...) GONK_LOG(ANDROID_LOG_INFO, format, ##__VA_ARGS__)
#define LOGE(format, ...) GONK_LOG(ANDROID_LOG_ERROR, format, ##__VA_ARGS__)
const char *kGonkMountsPath = "/proc/mounts";
const char *kGonkSystemPath = "/system";
GonkAutoMounter::GonkAutoMounter() : mDevice(nullptr), mAccess(Unknown)
{
if (!RemountSystem(ReadWrite)) {
LOGE("Could not remount %s as read-write.", kGonkSystemPath);
}
}
GonkAutoMounter::~GonkAutoMounter()
{
bool result = RemountSystem(ReadOnly);
free(mDevice);
if (!result) {
// Don't take any chances when remounting as read-only fails, just reboot.
Reboot();
}
}
void
GonkAutoMounter::Reboot()
{
// The android_reboot wrapper provides more safety, doing fancier read-only
// remounting and attempting to sync() the filesystem first. If this fails
// our only hope is to force a reboot directly without these protections.
// For more, see system/core/libcutils/android_reboot.c
LOGE("Could not remount %s as read-only, forcing a system reboot.",
kGonkSystemPath);
LogFlush();
if (android_reboot(ANDROID_RB_RESTART, 0, nullptr) != 0) {
LOGE("Safe system reboot failed, attempting to force");
LogFlush();
if (reboot(RB_AUTOBOOT) != 0) {
LOGE("CRITICAL: Failed to force restart");
}
}
}
static const char *
MountAccessToString(MountAccess access)
{
switch (access) {
case ReadOnly: return "read-only";
case ReadWrite: return "read-write";
default: return "unknown";
}
}
bool
GonkAutoMounter::RemountSystem(MountAccess access)
{
if (!UpdateMountStatus()) {
return false;
}
if (mAccess == access) {
return true;
}
unsigned long flags = MS_REMOUNT;
if (access == ReadOnly) {
flags |= MS_RDONLY;
// Give the system a chance to flush file buffers
sync();
}
if (!MountSystem(flags)) {
return false;
}
// Check status again to verify /system has been properly remounted
if (!UpdateMountStatus()) {
return false;
}
if (mAccess != access) {
LOGE("Updated mount status %s should be %s",
MountAccessToString(mAccess),
MountAccessToString(access));
return false;
}
return true;
}
bool
GonkAutoMounter::UpdateMountStatus()
{
FILE *mountsFile = NS_tfopen(kGonkMountsPath, "r");
if (mountsFile == nullptr) {
LOGE("Error opening %s: %s", kGonkMountsPath, strerror(errno));
return false;
}
// /proc/mounts returns a 0 size from fstat, so we use the same
// pre-allocated buffer size that ADB does here
const int mountsMaxSize = 4096;
char mountData[mountsMaxSize];
size_t read = fread(mountData, 1, mountsMaxSize - 1, mountsFile);
mountData[read + 1] = '\0';
if (ferror(mountsFile)) {
LOGE("Error reading %s, %s", kGonkMountsPath, strerror(errno));
fclose(mountsFile);
return false;
}
char *token, *tokenContext;
bool foundSystem = false;
for (token = strtok_r(mountData, "\n", &tokenContext);
token;
token = strtok_r(nullptr, "\n", &tokenContext))
{
if (ProcessMount(token)) {
foundSystem = true;
break;
}
}
fclose(mountsFile);
if (!foundSystem) {
LOGE("Couldn't find %s mount in %s", kGonkSystemPath, kGonkMountsPath);
}
return foundSystem;
}
bool
GonkAutoMounter::ProcessMount(const char *mount)
{
const int strSize = 256;
char mountDev[strSize];
char mountDir[strSize];
char mountAccess[strSize];
int rv = sscanf(mount, "%255s %255s %*s %255s %*d %*d\n",
mountDev, mountDir, mountAccess);
mountDev[strSize - 1] = '\0';
mountDir[strSize - 1] = '\0';
mountAccess[strSize - 1] = '\0';
if (rv != 3) {
return false;
}
if (strcmp(kGonkSystemPath, mountDir) != 0) {
return false;
}
free(mDevice);
mDevice = strdup(mountDev);
mAccess = Unknown;
char *option, *optionContext;
for (option = strtok_r(mountAccess, ",", &optionContext);
option;
option = strtok_r(nullptr, ",", &optionContext))
{
if (strcmp("ro", option) == 0) {
mAccess = ReadOnly;
break;
} else if (strcmp("rw", option) == 0) {
mAccess = ReadWrite;
break;
}
}
return true;
}
/*
* Mark the given block device as read-write or read-only, using the BLKROSET
* ioctl.
*/
static void SetBlockReadWriteStatus(const char *blockdev, bool setReadOnly) {
int fd;
int roMode = setReadOnly ? 1 : 0;
fd = open(blockdev, O_RDONLY);
if (fd < 0) {
return;
}
if (ioctl(fd, BLKROSET, &roMode) == -1) {
LOGE("Error setting read-only mode on %s to %s: %s", blockdev,
setReadOnly ? "true": "false", strerror(errno));
}
close(fd);
}
bool
GonkAutoMounter::MountSystem(unsigned long flags)
{
if (!mDevice) {
LOGE("No device was found for %s", kGonkSystemPath);
return false;
}
// Without setting the block device ro mode to false, we get a permission
// denied error while trying to remount it in read-write.
SetBlockReadWriteStatus(mDevice, (flags & MS_RDONLY));
const char *readOnly = flags & MS_RDONLY ? "read-only" : "read-write";
int result = mount(mDevice, kGonkSystemPath, "none", flags, nullptr);
if (result != 0) {
LOGE("Error mounting %s as %s: %s", kGonkSystemPath, readOnly,
strerror(errno));
return false;
}
LOGI("Mounted %s partition as %s", kGonkSystemPath, readOnly);
return true;
}

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

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 AUTOMOUNTER_GONK_H__
#define AUTOMOUNTER_GONK_H__
typedef enum {
ReadOnly,
ReadWrite,
Unknown
} MountAccess;
/**
* This class will remount the /system partition as read-write in Gonk to allow
* the updater write access. Upon destruction, /system will be remounted back to
* read-only. If something causes /system to remain read-write, this class will
* reboot the device and allow the system to mount as read-only.
*
* Code inspired from AOSP system/core/adb/remount_service.c
*/
class GonkAutoMounter
{
public:
GonkAutoMounter();
~GonkAutoMounter();
MountAccess GetAccess() const
{
return mAccess;
}
private:
bool RemountSystem(MountAccess access);
bool ForceRemountReadOnly();
bool UpdateMountStatus();
bool ProcessMount(const char *mount);
bool MountSystem(unsigned long flags);
void Reboot();
private:
char *mDevice;
MountAccess mAccess;
};
#endif // AUTOMOUNTER_GONK_H__

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

@ -1,53 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
#include <assert.h>
#include <stdio.h>
#include <string>
#include "android/log.h"
#include "progressui.h"
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoUpdater" , ## args)
using namespace std;
int InitProgressUI(int *argc, char ***argv)
{
return 0;
}
int ShowProgressUI()
{
LOG("Starting to apply update ...\n");
return 0;
}
void QuitProgressUI()
{
LOG("Finished applying update\n");
}
void UpdateProgressUI(float progress)
{
assert(0.0f <= progress && progress <= 100.0f);
static const size_t kProgressBarLength = 50;
static size_t sLastNumBars;
size_t numBars = size_t(float(kProgressBarLength) * progress / 100.0f);
if (numBars == sLastNumBars) {
return;
}
sLastNumBars = numBars;
size_t numSpaces = kProgressBarLength - numBars;
string bars(numBars, '=');
string spaces(numSpaces, ' ');
LOG("Progress [ %s%s ]\n", bars.c_str(), spaces.c_str());
}

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

@ -90,17 +90,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
LOCAL_INCLUDES += [
'/toolkit/xre',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
have_progressui = 1
srcs += [
'automounter_gonk.cpp',
'progressui_gonk.cpp',
]
DISABLE_STL_WRAPPING = True
OS_LIBS += [
'cutils',
'sysutils',
]
if have_progressui == 0:
srcs += [

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

@ -110,33 +110,8 @@ struct UpdateServerThreadArgs
#define USE_EXECV
#endif
#if defined(MOZ_WIDGET_GONK)
# include "automounter_gonk.h"
# include <unistd.h>
# include <android/log.h>
# include <linux/ioprio.h>
# include <sys/resource.h>
#if ANDROID_VERSION < 21
// The only header file in bionic which has a function prototype for ioprio_set
// is libc/include/sys/linux-unistd.h. However, linux-unistd.h conflicts
// badly with unistd.h, so we declare the prototype for ioprio_set directly.
extern "C" MOZ_EXPORT int ioprio_set(int which, int who, int ioprio);
#else
# include <sys/syscall.h>
static int ioprio_set(int which, int who, int ioprio) {
return syscall(__NR_ioprio_set, which, who, ioprio);
}
#endif
# define MAYBE_USE_HARD_LINKS 1
static bool sUseHardLinks = true;
#else
# define MAYBE_USE_HARD_LINKS 0
#endif
#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
!defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
!defined(XP_MACOSX)
#include "nss.h"
#include "prerror.h"
#endif
@ -314,7 +289,6 @@ static bool gSucceeded = false;
static bool sStagedUpdate = false;
static bool sReplaceRequest = false;
static bool sUsingService = false;
static bool sIsOSUpdate = false;
#ifdef XP_WIN
// The current working directory specified in the command line.
@ -375,12 +349,20 @@ mstrtok(const NS_tchar *delims, NS_tchar **str)
return ret;
}
#if defined(TEST_UPDATER)
#define HAS_ENV_CHECK 1
#elif defined(MOZ_MAINTENANCE_SERVICE)
#define HAS_ENV_CHECK 1
#endif
#if defined(HAS_ENV_CHECK)
static bool
EnvHasValue(const char *name)
{
const char *val = getenv(name);
return (val && *val);
}
#endif
/**
* Coverts a relative update path to a full path.
@ -688,25 +670,6 @@ static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest)
}
#endif
#if MAYBE_USE_HARD_LINKS
/*
* Creates a hardlink (destFilename) which points to the existing file
* (srcFilename).
*
* @return 0 if successful, an error otherwise
*/
static int
create_hard_link(const NS_tchar *srcFilename, const NS_tchar *destFilename)
{
if (link(srcFilename, destFilename) < 0) {
LOG(("link(%s, %s) failed errno = %d", srcFilename, destFilename, errno));
return WRITE_ERROR;
}
return OK;
}
#endif
// Copy the file named path onto a new file named dest.
static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
{
@ -734,16 +697,6 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
}
#endif
#if MAYBE_USE_HARD_LINKS
if (sUseHardLinks) {
if (!create_hard_link(path, dest)) {
return OK;
}
// Since we failed to create the hard link, fall through and copy the file.
sUseHardLinks = false;
}
#endif
AutoFile infile(ensure_open(path, NS_T("rb"), ss.st_mode));
if (!infile) {
LOG(("ensure_copy: failed to open the file for reading: " LOG_S ", err: %d",
@ -2476,44 +2429,8 @@ ReadMARChannelIDs(const NS_tchar *path, MARChannelStringTable *results)
static int
GetUpdateFileName(NS_tchar *fileName, int maxChars)
{
#if defined(MOZ_WIDGET_GONK)
// If an update.link file exists, then it will contain the name
// of the update file (terminated by a newline).
NS_tchar linkFileName[MAXPATHLEN];
NS_tsnprintf(linkFileName, sizeof(linkFileName)/sizeof(linkFileName[0]),
NS_T("%s/update.link"), gPatchDirPath);
AutoFile linkFile(NS_tfopen(linkFileName, NS_T("rb")));
if (linkFile == nullptr) {
NS_tsnprintf(fileName, maxChars,
NS_T("%s/update.mar"), gPatchDirPath);
return OK;
}
char dataFileName[MAXPATHLEN];
size_t bytesRead;
if ((bytesRead = fread(dataFileName, 1, sizeof(dataFileName)-1, linkFile)) <= 0) {
*fileName = NS_T('\0');
return READ_ERROR;
}
if (dataFileName[bytesRead-1] == '\n') {
// Strip trailing newline (for \n and \r\n)
bytesRead--;
}
if (dataFileName[bytesRead-1] == '\r') {
// Strip trailing CR (for \r, \r\n)
bytesRead--;
}
dataFileName[bytesRead] = '\0';
strncpy(fileName, dataFileName, maxChars-1);
fileName[maxChars-1] = '\0';
#else
// We currently only support update.link files under GONK
NS_tsnprintf(fileName, maxChars,
NS_T("%s/update.mar"), gPatchDirPath);
#endif
return OK;
}
@ -2595,7 +2512,7 @@ UpdateThreadFunc(void *param)
}
#endif
if (rv == OK && sStagedUpdate && !sIsOSUpdate) {
if (rv == OK && sStagedUpdate) {
#ifdef TEST_UPDATER
// The MOZ_TEST_SKIP_UPDATE_STAGE environment variable prevents copying
// the files in dist/bin in the test updater when staging an update since
@ -2757,26 +2674,7 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
#if defined(MOZ_WIDGET_GONK)
if (EnvHasValue("LD_PRELOAD")) {
// If the updater is launched with LD_PRELOAD set, then we wind up
// preloading libmozglue.so. Under some circumstances, this can cause
// the remount of /system to fail when going from rw to ro, so if we
// detect LD_PRELOAD we unsetenv it and relaunch ourselves without it.
// This will cause the offending preloaded library to be closed.
//
// For a variety of reasons, this is really hard to do in a safe manner
// in the parent process, so we do it here.
unsetenv("LD_PRELOAD");
execv(argv[0], argv);
__android_log_print(ANDROID_LOG_INFO, "updater",
"execve failed: errno: %d. Exiting...", errno);
_exit(1);
}
#endif
#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
!defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
// On Windows and Mac we rely on native APIs to do verifications so we don't
// need to initialize NSS at all there.
// Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS
@ -2929,11 +2827,6 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
if (EnvHasValue("MOZ_OS_UPDATE")) {
sIsOSUpdate = true;
putenv(const_cast<char*>("MOZ_OS_UPDATE="));
}
LogInit(gPatchDirPath, NS_T("update.log"));
if (!WriteStatusFile("applying")) {
@ -2988,36 +2881,6 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
#ifdef MOZ_WIDGET_GONK
const char *prioEnv = getenv("MOZ_UPDATER_PRIO");
if (prioEnv) {
int32_t prioVal;
int32_t oomScoreAdj;
int32_t ioprioClass;
int32_t ioprioLevel;
if (sscanf(prioEnv, "%d/%d/%d/%d",
&prioVal, &oomScoreAdj, &ioprioClass, &ioprioLevel) == 4) {
LOG(("MOZ_UPDATER_PRIO=%s", prioEnv));
if (setpriority(PRIO_PROCESS, 0, prioVal)) {
LOG(("setpriority(%d) failed, errno = %d", prioVal, errno));
}
if (ioprio_set(IOPRIO_WHO_PROCESS, 0,
IOPRIO_PRIO_VALUE(ioprioClass, ioprioLevel))) {
LOG(("ioprio_set(%d,%d) failed: errno = %d",
ioprioClass, ioprioLevel, errno));
}
FILE *fs = fopen("/proc/self/oom_score_adj", "w");
if (fs) {
fprintf(fs, "%d", oomScoreAdj);
fclose(fs);
} else {
LOG(("Unable to open /proc/self/oom_score_adj for writing, errno = %d",
errno));
}
}
}
#endif
#ifdef XP_WIN
if (pid > 0) {
HANDLE parent = OpenProcess(SYNCHRONIZE, false, (DWORD) pid);
@ -3342,27 +3205,6 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
#if defined(MOZ_WIDGET_GONK)
// In gonk, the master b2g process sets its umask to 0027 because
// there's no reason for it to ever create world-readable files.
// The updater binary, however, needs to do this, and it inherits
// the master process's cautious umask. So we drop down a bit here.
umask(0022);
// Remount the /system partition as read-write for gonk. The destructor will
// remount /system as read-only. We add an extra level of scope here to avoid
// calling LogFinish() before the GonkAutoMounter destructor has a chance
// to be called
{
#if !defined(TEST_UPDATER)
GonkAutoMounter mounter;
if (mounter.GetAccess() != MountAccess::ReadWrite) {
WriteStatusFile(FILESYSTEM_MOUNT_READWRITE_ERROR);
return 1;
}
#endif
#endif
if (sStagedUpdate) {
// When staging updates, blow away the old installation directory and create
// it from scratch.
@ -3662,10 +3504,6 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif /* XP_WIN */
#if defined(MOZ_WIDGET_GONK)
} // end the extra level of scope for the GonkAutoMounter
#endif
#ifdef XP_MACOSX
// When the update is successful remove the precomplete file in the root of
// the application bundle and move the distribution directory from
@ -4177,10 +4015,6 @@ GetManifestContents(const NS_tchar *manifest)
int AddPreCompleteActions(ActionList *list)
{
if (sIsOSUpdate) {
return OK;
}
#ifdef XP_MACOSX
mozilla::UniquePtr<NS_tchar[]> manifestPath(get_full_path(
NS_T("Contents/Resources/precomplete")));

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

@ -3965,7 +3965,7 @@ XREMain::XRE_mainStartup(bool* aExitFlag)
}
#endif
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
// Check for and process any available updates
nsCOMPtr<nsIFile> updRoot;
bool persistent;

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

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

@ -46,14 +46,9 @@ class nsIFile;
*
* This function does not modify appDir.
*/
nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir,
nsIFile *updRootDir,
int argc, char **argv,
const char *appVersion,
bool restart = true,
bool isOSUpdate = false,
nsIFile *osApplyToDir = nullptr,
ProcessType *pid = nullptr);
nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
int argc, char **argv, const char *appVersion,
bool restart = true, ProcessType *pid = nullptr);
// The implementation of the update processor handles the task of loading the
// updater application for staging an update.