summaryrefslogtreecommitdiff
path: root/onlineupdate
diff options
context:
space:
mode:
authorMarkus Mohrhard <markus.mohrhard@googlemail.com>2016-09-08 07:55:48 +0200
committerMarkus Mohrhard <markus.mohrhard@googlemail.com>2017-05-19 03:43:23 +0200
commitb01dff505f2b3cf678c426a7de8a2a1ea21134b5 (patch)
tree6cecf3c927fe8a6f1b6adb36bb1db305ac336f02 /onlineupdate
parent8d50f9825cb19ae184a631bd85837635eb7fab20 (diff)
update the updater code from mozilla
Change-Id: I400a7d103cf5bbd1a8b86c54a5d1b84c7dbf34e8
Diffstat (limited to 'onlineupdate')
-rw-r--r--onlineupdate/source/update/updater/updater.cxx1106
1 files changed, 769 insertions, 337 deletions
diff --git a/onlineupdate/source/update/updater/updater.cxx b/onlineupdate/source/update/updater/updater.cxx
index 033447160d4f..e1d791ef4d4a 100644
--- a/onlineupdate/source/update/updater/updater.cxx
+++ b/onlineupdate/source/update/updater/updater.cxx
@@ -80,8 +80,19 @@
#if defined(MACOSX)
// These functions are defined in launchchild_osx.mm
-void LaunchChild(int argc, char **argv);
+void CleanupElevatedMacUpdate(bool aFailureOccurred);
+bool IsOwnedByGroupAdmin(const char* aAppBundle);
+bool IsRecursivelyWritable(const char* aPath);
+void LaunchChild(int argc, const char** argv);
void LaunchMacPostProcess(const char* aAppBundle);
+bool ObtainUpdaterArguments(int* argc, char*** argv);
+bool ServeElevatedUpdate(int argc, const char** argv);
+void SetGroupOwnershipAndPermissions(const char* aAppBundle);
+struct UpdateServerThreadArgs
+{
+ int argc;
+ const NS_tchar** argv;
+};
#endif
#ifndef SSIZE_MAX
@@ -102,6 +113,13 @@ void LaunchMacPostProcess(const char* aAppBundle);
#endif
#ifdef _WIN32
+#ifdef MOZ_MAINTENANCE_SERVICE
+#include "registrycertificates.h"
+#endif
+BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
+BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,
+ LPCWSTR siblingFilePath,
+ LPCWSTR newFileName);
#include "updatehelper.h"
// Closes the handle if valid and if the updater is elevated returns with the
@@ -113,6 +131,7 @@ void LaunchMacPostProcess(const char* aAppBundle);
CloseHandle(handle); \
} \
if (_waccess(path, F_OK) == 0 && NS_tremove(path) != 0) { \
+ LogFinish(); \
return retCode; \
} \
}
@@ -204,6 +223,7 @@ static bool sIsOSUpdate = false;
static NS_tchar* gDestPath;
static NS_tchar gCallbackRelPath[MAXPATHLEN];
static NS_tchar gCallbackBackupPath[MAXPATHLEN];
+static NS_tchar gDeleteDirPath[MAXPATHLEN];
#endif
static const NS_tchar kWhitespace[] = NS_T(" \t");
@@ -264,9 +284,8 @@ EnvHasValue(const char *name)
return (val && *val);
}
-#ifdef _WIN32
/**
- * Coverts a relative update path to a full path for Windows.
+ * Coverts a relative update path to a full path.
*
* @param relpath
* The relative path to convert to a full path.
@@ -275,23 +294,56 @@ EnvHasValue(const char *name)
static NS_tchar*
get_full_path(const NS_tchar *relpath)
{
- size_t lendestpath = NS_tstrlen(gDestPath);
+ NS_tchar *destpath = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
+ size_t lendestpath = NS_tstrlen(destpath);
size_t lenrelpath = NS_tstrlen(relpath);
- NS_tchar *s = (NS_tchar *) malloc((lendestpath + lenrelpath + 1) * sizeof(NS_tchar));
- if (!s)
+ NS_tchar *s = new NS_tchar[lendestpath + lenrelpath + 2];
+ if (!s) {
return nullptr;
+ }
NS_tchar *c = s;
- NS_tstrcpy(c, gDestPath);
+ NS_tstrcpy(c, destpath);
c += lendestpath;
+ NS_tstrcat(c, NS_T("/"));
+ c++;
+
NS_tstrcat(c, relpath);
c += lenrelpath;
*c = NS_T('\0');
- c++;
return s;
}
+
+/**
+ * Converts a full update path into a relative path; reverses get_full_path.
+ *
+ * @param fullpath
+ * The absolute path to convert into a relative path.
+ * return pointer to the location within fullpath where the relative path starts
+ * or fullpath itself if it already looks relative.
+ */
+static const NS_tchar*
+get_relative_path(const NS_tchar *fullpath)
+{
+ // If the path isn't absolute, just return it as-is.
+#ifdef _WIN32
+ if (fullpath[1] != ':' && fullpath[2] != '\\') {
+#else
+ if (fullpath[0] != '/') {
#endif
+ return fullpath;
+ }
+
+ NS_tchar *prefix = sStagedUpdate ? gWorkingDirPath : gInstallDirPath;
+
+ // If the path isn't long enough to be absolute, return it as-is.
+ if (NS_tstrlen(fullpath) <= NS_tstrlen(prefix)) {
+ return fullpath;
+ }
+
+ return fullpath + NS_tstrlen(prefix) + 1;
+}
/**
* Gets the platform specific path and performs simple checks to the path. If
@@ -852,14 +904,18 @@ static int backup_create(const NS_tchar *path)
// Rename the backup of the specified file that was created by renaming it back
// to the original file.
-static int backup_restore(const NS_tchar *path)
+static int backup_restore(const NS_tchar *path, const NS_tchar *relPath)
{
NS_tchar backup[MAXPATHLEN];
NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
NS_T("%s") BACKUP_EXT, path);
+ NS_tchar relBackup[MAXPATHLEN];
+ NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
+ NS_T("%s") BACKUP_EXT, relPath);
+
if (NS_taccess(backup, F_OK)) {
- LOG(("backup_restore: backup file doesn't exist: " LOG_S, backup));
+ LOG(("backup_restore: backup file doesn't exist: " LOG_S, relBackup));
return OK;
}
@@ -867,12 +923,16 @@ static int backup_restore(const NS_tchar *path)
}
// Discard the backup of the specified file that was created by renaming it.
-static int backup_discard(const NS_tchar *path)
+static int backup_discard(const NS_tchar *path, const NS_tchar *relPath)
{
NS_tchar backup[MAXPATHLEN];
NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]),
NS_T("%s") BACKUP_EXT, path);
+ NS_tchar relBackup[MAXPATHLEN];
+ NS_tsnprintf(relBackup, sizeof(relBackup) / sizeof(relBackup[0]),
+ NS_T("%s") BACKUP_EXT, relPath);
+
// Nothing to discard
if (NS_taccess(backup, F_OK)) {
return OK;
@@ -881,12 +941,12 @@ static int backup_discard(const NS_tchar *path)
int rv = ensure_remove(backup);
#if defined(_WIN32)
if (rv && !sStagedUpdate && !sReplaceRequest) {
- LOG(("backup_discard: unable to remove: " LOG_S, backup));
+ LOG(("backup_discard: unable to remove: " LOG_S, relBackup));
NS_tchar path[MAXPATHLEN];
- GetTempFileNameW(DELETE_DIR, L"moz", 0, path);
+ GetTempFileNameW(gDeleteDirPath, L"moz", 0, path);
if (rename_file(backup, path)) {
LOG(("backup_discard: failed to rename file:" LOG_S ", dst:" LOG_S,
- backup, path));
+ relBackup, relPath));
return WRITE_ERROR_DELETE_BACKUP;
}
// The MoveFileEx call to remove the file on OS reboot will fail if the
@@ -896,10 +956,10 @@ static int backup_discard(const NS_tchar *path)
// applied, on reinstall, and on uninstall.
if (MoveFileEx(path, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
LOG(("backup_discard: file renamed and will be removed on OS " \
- "reboot: " LOG_S, path));
+ "reboot: " LOG_S, relPath));
} else {
LOG(("backup_discard: failed to schedule OS reboot removal of " \
- "file: " LOG_S, path));
+ "file: " LOG_S, relPath));
}
}
#else
@@ -911,12 +971,13 @@ static int backup_discard(const NS_tchar *path)
}
// Helper function for post-processing a temporary backup.
-static void backup_finish(const NS_tchar *path, int status)
+static void backup_finish(const NS_tchar *path, const NS_tchar *relPath,
+ int status)
{
if (status == OK)
- backup_discard(path);
+ backup_discard(path, relPath);
else
- backup_restore(path);
+ backup_restore(path, relPath);
}
//-----------------------------------------------------------------------------
@@ -954,7 +1015,7 @@ private:
class RemoveFile : public Action
{
public:
- RemoveFile() : mFile(nullptr), mSkip(0) { }
+ RemoveFile() : mSkip(0) { }
int Parse(NS_tchar *line);
int Prepare();
@@ -962,7 +1023,8 @@ public:
void Finish(int status);
private:
- const NS_tchar *mFile;
+ std::unique_ptr<const NS_tchar> mFile;
+ std::unique_ptr<NS_tchar> mRelPath;
int mSkip;
};
@@ -971,10 +1033,18 @@ RemoveFile::Parse(NS_tchar *line)
{
// format "<deadfile>"
- mFile = get_valid_path(&line);
- if (!mFile)
+ NS_tchar* validPath = get_valid_path(&line);
+ if (!validPath)
return PARSE_ERROR;
+ mRelPath = std::unique_ptr<NS_tchar>(new NS_tchar[MAXPATHLEN]);
+ NS_tstrcpy(mRelPath.get(), validPath);
+
+ mFile.reset(get_full_path(validPath));
+ if (!mFile) {
+ return PARSE_ERROR;
+ }
+
return OK;
}
@@ -982,33 +1052,33 @@ int
RemoveFile::Prepare()
{
// Skip the file if it already doesn't exist.
- int rv = NS_taccess(mFile, F_OK);
+ int rv = NS_taccess(mFile.get(), F_OK);
if (rv) {
mSkip = 1;
mProgressCost = 0;
return OK;
}
- LOG(("PREPARE REMOVEFILE " LOG_S, mFile));
+ LOG(("PREPARE REMOVEFILE " LOG_S, mRelPath.get()));
// Make sure that we're actually a file...
struct NS_tstat_t fileInfo;
- rv = NS_tstat(mFile, &fileInfo);
+ rv = NS_tstat(mFile.get(), &fileInfo);
if (rv) {
- LOG(("failed to read file status info: " LOG_S ", err: %d", mFile,
+ LOG(("failed to read file status info: " LOG_S ", err: %d", mFile.get(),
errno));
return READ_ERROR;
}
if (!S_ISREG(fileInfo.st_mode)) {
- LOG(("path present, but not a file: " LOG_S, mFile));
+ LOG(("path present, but not a file: " LOG_S, mFile.get()));
return DELETE_ERROR_EXPECTED_FILE;
}
- NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile, NS_T('/'));
+ NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile.get(), NS_T('/'));
if (slash) {
*slash = NS_T('\0');
- rv = NS_taccess(mFile, W_OK);
+ rv = NS_taccess(mFile.get(), W_OK);
*slash = NS_T('/');
} else {
rv = NS_taccess(NS_T("."), W_OK);
@@ -1028,11 +1098,11 @@ RemoveFile::Execute()
if (mSkip)
return OK;
- LOG(("EXECUTE REMOVEFILE " LOG_S, mFile));
+ LOG(("EXECUTE REMOVEFILE " LOG_S, mRelPath.get()));
// The file is checked for existence here and in Prepare since it might have
// been removed by a separate instruction: bug 311099.
- int rv = NS_taccess(mFile, F_OK);
+ int rv = NS_taccess(mFile.get(), F_OK);
if (rv) {
LOG(("file cannot be removed because it does not exist; skipping"));
mSkip = 1;
@@ -1040,7 +1110,7 @@ RemoveFile::Execute()
}
// Rename the old file. It will be removed in Finish.
- rv = backup_create(mFile);
+ rv = backup_create(mFile.get());
if (rv) {
LOG(("backup_create failed: %d", rv));
return rv;
@@ -1055,15 +1125,15 @@ RemoveFile::Finish(int status)
if (mSkip)
return;
- LOG(("FINISH REMOVEFILE " LOG_S, mFile));
+ LOG(("FINISH REMOVEFILE " LOG_S, mRelPath.get()));
- backup_finish(mFile, status);
+ backup_finish(mFile.get(), mRelPath.get(), status);
}
class RemoveDir : public Action
{
public:
- RemoveDir() : mDir(nullptr), mSkip(0) { }
+ RemoveDir() : mSkip(0) { }
virtual int Parse(NS_tchar *line);
virtual int Prepare(); // check that the source dir exists
@@ -1071,7 +1141,8 @@ public:
virtual void Finish(int status);
private:
- const NS_tchar *mDir;
+ std::unique_ptr<NS_tchar> mDir;
+ std::unique_ptr<NS_tchar> mRelPath;
int mSkip;
};
@@ -1080,9 +1151,16 @@ RemoveDir::Parse(NS_tchar *line)
{
// format "<deaddir>/"
- mDir = get_valid_path(&line, true);
- if (!mDir)
+ NS_tchar* validPath = get_valid_path(&line, true);
+ if (!validPath)
+ return PARSE_ERROR;
+ mRelPath = std::unique_ptr<NS_tchar>(new NS_tchar[MAXPATHLEN]);
+ NS_tstrcpy(mRelPath.get(), validPath);
+
+ mDir.reset(get_full_path(validPath));
+ if (!mDir) {
return PARSE_ERROR;
+ }
return OK;
}
@@ -1091,30 +1169,30 @@ int
RemoveDir::Prepare()
{
// We expect the directory to exist if we are to remove it.
- int rv = NS_taccess(mDir, F_OK);
+ int rv = NS_taccess(mDir.get(), F_OK);
if (rv) {
mSkip = 1;
mProgressCost = 0;
return OK;
}
- LOG(("PREPARE REMOVEDIR " LOG_S "/", mDir));
+ LOG(("PREPARE REMOVEDIR " LOG_S "/", mRelPath.get()));
// Make sure that we're actually a dir.
struct NS_tstat_t dirInfo;
- rv = NS_tstat(mDir, &dirInfo);
+ rv = NS_tstat(mDir.get(), &dirInfo);
if (rv) {
- LOG(("failed to read directory status info: " LOG_S ", err: %d", mDir,
+ LOG(("failed to read directory status info: " LOG_S ", err: %d", mRelPath.get(),
errno));
return READ_ERROR;
}
if (!S_ISDIR(dirInfo.st_mode)) {
- LOG(("path present, but not a directory: " LOG_S, mDir));
+ LOG(("path present, but not a directory: " LOG_S, mRelPath.get()));
return DELETE_ERROR_EXPECTED_DIR;
}
- rv = NS_taccess(mDir, W_OK);
+ rv = NS_taccess(mDir.get(), W_OK);
if (rv) {
LOG(("access failed: %d, %d", rv, errno));
return WRITE_ERROR_DIR_ACCESS_DENIED;
@@ -1129,11 +1207,11 @@ RemoveDir::Execute()
if (mSkip)
return OK;
- LOG(("EXECUTE REMOVEDIR " LOG_S "/", mDir));
+ LOG(("EXECUTE REMOVEDIR " LOG_S "/", mRelPath.get()));
// The directory is checked for existence at every step since it might have
// been removed by a separate instruction: bug 311099.
- int rv = NS_taccess(mDir, F_OK);
+ int rv = NS_taccess(mDir.get(), F_OK);
if (rv) {
LOG(("directory no longer exists; skipping"));
mSkip = 1;
@@ -1148,11 +1226,11 @@ RemoveDir::Finish(int status)
if (mSkip || status != OK)
return;
- LOG(("FINISH REMOVEDIR " LOG_S "/", mDir));
+ LOG(("FINISH REMOVEDIR " LOG_S "/", mRelPath.get()));
// The directory is checked for existence at every step since it might have
// been removed by a separate instruction: bug 311099.
- int rv = NS_taccess(mDir, F_OK);
+ int rv = NS_taccess(mDir.get(), F_OK);
if (rv) {
LOG(("directory no longer exists; skipping"));
return;
@@ -1160,9 +1238,9 @@ RemoveDir::Finish(int status)
if (status == OK) {
- if (NS_trmdir(mDir)) {
+ if (NS_trmdir(mDir.get())) {
LOG(("non-fatal error removing directory: " LOG_S "/, rv: %d, err: %d",
- mDir, rv, errno));
+ mRelPath.get(), rv, errno));
}
}
}
@@ -1170,9 +1248,7 @@ RemoveDir::Finish(int status)
class AddFile : public Action
{
public:
- AddFile() : mFile(nullptr)
- , mAdded(false)
- { }
+ AddFile() : mAdded(false) { }
virtual int Parse(NS_tchar *line);
virtual int Prepare();
@@ -1180,7 +1256,8 @@ public:
virtual void Finish(int status);
private:
- const NS_tchar *mFile;
+ std::unique_ptr<NS_tchar> mFile;
+ std::unique_ptr<NS_tchar> mRelPath;
bool mAdded;
};
@@ -1189,9 +1266,18 @@ AddFile::Parse(NS_tchar *line)
{
// format "<newfile>"
- mFile = get_valid_path(&line);
- if (!mFile)
+ NS_tchar* validPath = get_valid_path(&line);
+ if (!validPath)
+ return PARSE_ERROR;
+
+ mRelPath = std::unique_ptr<NS_tchar>(new NS_tchar[MAXPATHLEN]);
+
+ NS_tstrcpy(mRelPath.get(), validPath);
+
+ mFile.reset(get_full_path(validPath));
+ if (!mFile) {
return PARSE_ERROR;
+ }
return OK;
}
@@ -1199,7 +1285,7 @@ AddFile::Parse(NS_tchar *line)
int
AddFile::Prepare()
{
- LOG(("PREPARE ADD " LOG_S, mFile));
+ LOG(("PREPARE ADD " LOG_S, mRelPath.get()));
return OK;
}
@@ -1207,33 +1293,33 @@ AddFile::Prepare()
int
AddFile::Execute()
{
- LOG(("EXECUTE ADD " LOG_S, mFile));
+ LOG(("EXECUTE ADD " LOG_S, mRelPath.get()));
int rv;
// First make sure that we can actually get rid of any existing file.
- rv = NS_taccess(mFile, F_OK);
+ rv = NS_taccess(mFile.get(), F_OK);
if (rv == 0) {
- rv = backup_create(mFile);
+ rv = backup_create(mFile.get());
if (rv)
return rv;
} else {
- rv = ensure_parent_dir(mFile);
+ rv = ensure_parent_dir(mFile.get());
if (rv)
return rv;
}
#ifdef _WIN32
char sourcefile[MAXPATHLEN];
- if (!WideCharToMultiByte(CP_UTF8, 0, mFile, -1, sourcefile, MAXPATHLEN,
- nullptr, nullptr)) {
+ if (!WideCharToMultiByte(CP_UTF8, 0, mRelPath.get(), -1, sourcefile,
+ MAXPATHLEN, nullptr, nullptr)) {
LOG(("error converting wchar to utf8: %d", GetLastError()));
return STRING_CONVERSION_ERROR;
}
- rv = gArchiveReader.ExtractFile(sourcefile, mFile);
+ rv = gArchiveReader.ExtractFile(sourcefile, mFile.get());
#else
- rv = gArchiveReader.ExtractFile(mFile, mFile);
+ rv = gArchiveReader.ExtractFile(mRelPath.get(), mFile.get());
#endif
if (!rv) {
mAdded = true;
@@ -1244,18 +1330,18 @@ AddFile::Execute()
void
AddFile::Finish(int status)
{
- LOG(("FINISH ADD " LOG_S, mFile));
+ LOG(("FINISH ADD " LOG_S, mRelPath.get()));
// When there is an update failure and a file has been added it is removed
// here since there might not be a backup to replace it.
if (status && mAdded)
- NS_tremove(mFile);
- backup_finish(mFile, status);
+ NS_tremove(mFile.get());
+ backup_finish(mFile.get(), mRelPath.get(), status);
}
class PatchFile : public Action
{
public:
- PatchFile() : mPatchIndex(-1), buf(nullptr) { }
+ PatchFile() : mPatchFile(nullptr), mPatchIndex(-1), buf(nullptr) { }
virtual ~PatchFile();
@@ -1270,17 +1356,29 @@ private:
static int sPatchIndex;
const NS_tchar *mPatchFile;
- const NS_tchar *mFile;
+ std::unique_ptr<NS_tchar> mFile;
+ std::unique_ptr<NS_tchar> mFileRelPath;
int mPatchIndex;
MBSPatchHeader header;
unsigned char *buf;
NS_tchar spath[MAXPATHLEN];
+ AutoFile mPatchStream;
};
int PatchFile::sPatchIndex = 0;
PatchFile::~PatchFile()
{
+ // Make sure mPatchStream gets unlocked on Windows; the system will do that,
+ // but not until some indeterminate future time, and we want determinism.
+ // Normally this happens at the end of Execute, when we close the stream;
+ // this call is here in case Execute errors out.
+#ifdef _WIN32
+ if (mPatchStream) {
+ UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1);
+ }
+#endif
+
// delete the temporary patch file
if (spath[0])
NS_tremove(spath);
@@ -1296,7 +1394,7 @@ PatchFile::LoadSourceFile(FILE* ofile)
int rv = fstat(fileno((FILE *)ofile), &os);
if (rv) {
LOG(("LoadSourceFile: unable to stat destination file: " LOG_S ", " \
- "err: %d", mFile, errno));
+ "err: %d", mFileRelPath.get(), errno));
return READ_ERROR;
}
@@ -1317,7 +1415,7 @@ PatchFile::LoadSourceFile(FILE* ofile)
size_t c = fread(rb, 1, count, ofile);
if (c != count) {
LOG(("LoadSourceFile: error reading destination file: " LOG_S,
- mFile));
+ mFileRelPath.get()));
return READ_ERROR;
}
@@ -1353,9 +1451,16 @@ PatchFile::Parse(NS_tchar *line)
if (!q)
return PARSE_ERROR;
- mFile = get_valid_path(&line);
- if (!mFile)
+ NS_tchar* validPath = get_valid_path(&line);
+ if (!validPath)
return PARSE_ERROR;
+ mFileRelPath = std::unique_ptr<NS_tchar>(new NS_tchar[MAXPATHLEN]);
+ NS_tstrcpy(mFileRelPath.get(), validPath);
+
+ mFile.reset(get_full_path(validPath));
+ if (!mFile) {
+ return PARSE_ERROR;
+ }
return OK;
}
@@ -1363,7 +1468,7 @@ PatchFile::Parse(NS_tchar *line)
int
PatchFile::Prepare()
{
- LOG(("PREPARE PATCH " LOG_S, mFile));
+ LOG(("PREPARE PATCH " LOG_S, mFileRelPath.get()));
// extract the patch to a temporary file
mPatchIndex = sPatchIndex++;
@@ -1373,11 +1478,17 @@ PatchFile::Prepare()
NS_tremove(spath);
- FILE *fp = NS_tfopen(spath, NS_T("wb"));
- if (!fp)
+ mPatchStream = NS_tfopen(spath, NS_T("wb"));
+ if (!mPatchStream)
return WRITE_ERROR;
#ifdef _WIN32
+ // Lock the patch file, so it can't be messed with between
+ // when we're done creating it and when we go to apply it.
+ if (!LockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1)) {
+ LOG(("Couldn't lock patch file: %d", GetLastError()));
+ return LOCK_ERROR_PATCH_FILE;
+ }
char sourcefile[MAXPATHLEN];
if (!WideCharToMultiByte(CP_UTF8, 0, mPatchFile, -1, sourcefile, MAXPATHLEN,
nullptr, nullptr)) {
@@ -1385,43 +1496,41 @@ PatchFile::Prepare()
return STRING_CONVERSION_ERROR;
}
- int rv = gArchiveReader.ExtractFileToStream(sourcefile, fp);
+ int rv = gArchiveReader.ExtractFileToStream(sourcefile, mPatchStream);
#else
- int rv = gArchiveReader.ExtractFileToStream(mPatchFile, fp);
+ int rv = gArchiveReader.ExtractFileToStream(mPatchFile, mPatchStream);
#endif
- fclose(fp);
+
return rv;
}
int
PatchFile::Execute()
{
- LOG(("EXECUTE PATCH " LOG_S, mFile));
+ LOG(("EXECUTE PATCH " LOG_S, mFileRelPath.get()));
- AutoFile pfile(NS_tfopen(spath, NS_T("rb")));
- if (pfile == nullptr)
- return READ_ERROR;
+ fseek(mPatchStream, 0, SEEK_SET);
- int rv = MBS_ReadHeader(pfile, &header);
+ int rv = MBS_ReadHeader(mPatchStream, &header);
if (rv)
return rv;
FILE *origfile = nullptr;
#ifdef _WIN32
- if (NS_tstrcmp(mFile, gCallbackRelPath) == 0) {
+ if (NS_tstrcmp(mFileRelPath.get(), gCallbackRelPath) == 0) {
// Read from the copy of the callback when patching since the callback can't
// be opened for reading to prevent the application from being launched.
origfile = NS_tfopen(gCallbackBackupPath, NS_T("rb"));
} else {
- origfile = NS_tfopen(mFile, NS_T("rb"));
+ origfile = NS_tfopen(mFile.get(), NS_T("rb"));
}
#else
- origfile = NS_tfopen(mFile, NS_T("rb"));
+ origfile = NS_tfopen(mFile.get(), NS_T("rb"));
#endif
if (!origfile) {
- LOG(("unable to open destination file: " LOG_S ", err: %d", mFile,
- errno));
+ LOG(("unable to open destination file: " LOG_S ", err: %d",
+ mFileRelPath.get(), errno));
return READ_ERROR;
}
@@ -1435,21 +1544,21 @@ PatchFile::Execute()
// Rename the destination file if it exists before proceeding so it can be
// used to restore the file to its original state if there is an error.
struct NS_tstat_t ss;
- rv = NS_tstat(mFile, &ss);
+ rv = NS_tstat(mFile.get(), &ss);
if (rv) {
- LOG(("failed to read file status info: " LOG_S ", err: %d", mFile,
- errno));
+ LOG(("failed to read file status info: " LOG_S ", err: %d",
+ mFileRelPath.get(), errno));
return READ_ERROR;
}
- rv = backup_create(mFile);
+ rv = backup_create(mFile.get());
if (rv)
return rv;
#if defined(HAVE_POSIX_FALLOCATE)
- AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+ AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
posix_fallocate(fileno((FILE *)ofile), 0, header.dlen);
-#elif defined(WNT)
+#elif defined(_WIN32)
bool shouldTruncate = true;
// Creating the file, setting the size, and then closing the file handle
// lessens fragmentation more than any other method tested. Other methods that
@@ -1458,7 +1567,7 @@ PatchFile::Execute()
// 2. _get_osfhandle and then setting the size reduced fragmentation though
// not completely. There are also reports of _get_osfhandle failing on
// mingw.
- HANDLE hfile = CreateFileW(mFile,
+ HANDLE hfile = CreateFileW(mFile.get(),
GENERIC_WRITE,
0,
nullptr,
@@ -1475,10 +1584,10 @@ PatchFile::Execute()
CloseHandle(hfile);
}
- AutoFile ofile(ensure_open(mFile, shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
+ AutoFile ofile(ensure_open(mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"),
ss.st_mode));
#elif defined(MACOSX)
- AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+ AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
// Modified code from FileUtils.cpp
fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen};
// Try to get a continuous chunk of disk space
@@ -1493,11 +1602,12 @@ PatchFile::Execute()
ftruncate(fileno((FILE *)ofile), header.dlen);
}
#else
- AutoFile ofile(ensure_open(mFile, NS_T("wb+"), ss.st_mode));
+ AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode));
#endif
if (ofile == nullptr) {
- LOG(("unable to create new file: " LOG_S ", err: %d", mFile, errno));
+ LOG(("unable to create new file: " LOG_S ", err: %d", mFileRelPath.get(),
+ errno));
return WRITE_ERROR_OPEN_PATCH_FILE;
}
@@ -1507,40 +1617,42 @@ PatchFile::Execute()
}
#endif
- rv = MBS_ApplyPatch(&header, pfile, buf, ofile);
+ rv = MBS_ApplyPatch(&header, mPatchStream, buf, ofile);
// Go ahead and do a bit of cleanup now to minimize runtime overhead.
- // Set pfile to nullptr to make AutoFile close the file so it can be deleted
- // on Windows.
- pfile = nullptr;
+ // Make sure mPatchStream gets unlocked on Windows; the system will do that,
+ // but not until some indeterminate future time, and we want determinism.
+#ifdef XP_WIN
+ UnlockFile((HANDLE)_get_osfhandle(fileno(mPatchStream)), 0, 0, -1, -1);
+#endif
+ // Set mPatchStream to nullptr to make AutoFile close the file,
+ // so it can be deleted on Windows.
+ mPatchStream = nullptr;
NS_tremove(spath);
spath[0] = NS_T('\0');
free(buf);
buf = nullptr;
-
return rv;
}
void
PatchFile::Finish(int status)
{
- LOG(("FINISH PATCH " LOG_S, mFile));
+ LOG(("FINISH PATCH " LOG_S, mFileRelPath.get()));
- backup_finish(mFile, status);
+ backup_finish(mFile.get(), mFileRelPath.get(), status);
}
class AddIfFile : public AddFile
{
public:
- AddIfFile() : mTestFile(nullptr) { }
-
virtual int Parse(NS_tchar *line);
virtual int Prepare();
virtual int Execute();
virtual void Finish(int status);
protected:
- const NS_tchar *mTestFile;
+ std::unique_ptr<NS_tchar> mTestFile;
};
int
@@ -1548,7 +1660,7 @@ AddIfFile::Parse(NS_tchar *line)
{
// format "<testfile>" "<newfile>"
- mTestFile = get_valid_path(&line);
+ mTestFile.reset(get_full_path(get_valid_path(&line)));
if (!mTestFile)
return PARSE_ERROR;
@@ -1564,7 +1676,7 @@ int
AddIfFile::Prepare()
{
// If the test file does not exist, then skip this action.
- if (NS_taccess(mTestFile, F_OK)) {
+ if (NS_taccess(mTestFile.get(), F_OK)) {
mTestFile = nullptr;
return OK;
}
@@ -1593,15 +1705,13 @@ AddIfFile::Finish(int status)
class AddIfNotFile : public AddFile
{
public:
- AddIfNotFile() : mTestFile(NULL) { }
-
virtual int Parse(NS_tchar *line);
virtual int Prepare();
virtual int Execute();
virtual void Finish(int status);
protected:
- const NS_tchar *mTestFile;
+ std::unique_ptr<NS_tchar> mTestFile;
};
int
@@ -1609,7 +1719,7 @@ AddIfNotFile::Parse(NS_tchar *line)
{
// format "<testfile>" "<newfile>"
- mTestFile = get_valid_path(&line);
+ mTestFile.reset(get_full_path(get_valid_path(&line)));
if (!mTestFile)
return PARSE_ERROR;
@@ -1625,7 +1735,7 @@ int
AddIfNotFile::Prepare()
{
// If the test file exists, then skip this action.
- if (!NS_taccess(mTestFile, F_OK)) {
+ if (!NS_taccess(mTestFile.get(), F_OK)) {
mTestFile = NULL;
return OK;
}
@@ -1654,15 +1764,13 @@ AddIfNotFile::Finish(int status)
class PatchIfFile : public PatchFile
{
public:
- PatchIfFile() : mTestFile(nullptr) { }
-
virtual int Parse(NS_tchar *line);
virtual int Prepare(); // should check for patch file and for checksum here
virtual int Execute();
virtual void Finish(int status);
private:
- const NS_tchar *mTestFile;
+ std::unique_ptr<NS_tchar> mTestFile;
};
int
@@ -1670,7 +1778,7 @@ PatchIfFile::Parse(NS_tchar *line)
{
// format "<testfile>" "<patchfile>" "<filetopatch>"
- mTestFile = get_valid_path(&line);
+ mTestFile.reset(get_full_path(get_valid_path(&line)));
if (!mTestFile)
return PARSE_ERROR;
@@ -1686,7 +1794,7 @@ int
PatchIfFile::Prepare()
{
// If the test file does not exist, then skip this action.
- if (NS_taccess(mTestFile, F_OK)) {
+ if (NS_taccess(mTestFile.get(), F_OK)) {
mTestFile = nullptr;
return OK;
}
@@ -1714,6 +1822,136 @@ PatchIfFile::Finish(int status)
//-----------------------------------------------------------------------------
+#ifdef _WIN32
+#include "nsWindowsRestart.cpp"
+#include "nsWindowsHelpers.h"
+#include "uachelper.h"
+#include "pathhash.h"
+
+/**
+ * Launch the post update application (helper.exe). It takes in the path of the
+ * callback application to calculate the path of helper.exe. For service updates
+ * this is called from both the system account and the current user account.
+ *
+ * @param installationDir The path to the callback application binary.
+ * @param updateInfoDir The directory where update info is stored.
+ * @return true if there was no error starting the process.
+ */
+bool
+LaunchWinPostProcess(const WCHAR *installationDir,
+ const WCHAR *updateInfoDir)
+{
+ WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(workingDirectory, installationDir, MAX_PATH);
+
+ // Launch helper.exe to perform post processing (e.g. registry and log file
+ // modifications) for the update.
+ WCHAR inifile[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(inifile, installationDir, MAX_PATH);
+ if (!PathAppendSafe(inifile, L"updater.ini")) {
+ return false;
+ }
+
+ WCHAR exefile[MAX_PATH + 1];
+ WCHAR exearg[MAX_PATH + 1];
+ WCHAR exeasync[10];
+ bool async = true;
+ if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
+ exefile, MAX_PATH + 1, inifile)) {
+ return false;
+ }
+
+ if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
+ MAX_PATH + 1, inifile)) {
+ return false;
+ }
+
+ if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
+ exeasync,
+ sizeof(exeasync)/sizeof(exeasync[0]),
+ inifile)) {
+ return false;
+ }
+
+ // Verify that exeFile doesn't contain relative paths
+ if (wcsstr(exefile, L"..") != nullptr) {
+ return false;
+ }
+
+ WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(exefullpath, installationDir, MAX_PATH);
+ if (!PathAppendSafe(exefullpath, exefile)) {
+ return false;
+ }
+
+#if !defined(TEST_UPDATER) && defined(MOZ_MAINTENANCE_SERVICE)
+ if (sUsingService &&
+ !DoesBinaryMatchAllowedCertificates(installationDir, exefullpath)) {
+ return false;
+ }
+#endif
+
+ WCHAR dlogFile[MAX_PATH + 1];
+ if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) {
+ return false;
+ }
+
+ WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
+ wcsncpy(slogFile, updateInfoDir, MAX_PATH);
+ if (!PathAppendSafe(slogFile, L"update.log")) {
+ return false;
+ }
+
+ WCHAR dummyArg[14] = { L'\0' };
+ wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
+
+ size_t len = wcslen(exearg) + wcslen(dummyArg);
+ WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
+ if (!cmdline) {
+ return false;
+ }
+
+ wcsncpy(cmdline, dummyArg, len);
+ wcscat(cmdline, exearg);
+
+ if (sUsingService ||
+ !_wcsnicmp(exeasync, L"false", 6) ||
+ !_wcsnicmp(exeasync, L"0", 2)) {
+ async = false;
+ }
+
+ // We want to launch the post update helper app to update the Windows
+ // registry even if there is a failure with removing the uninstall.update
+ // file or copying the update.log file.
+ CopyFileW(slogFile, dlogFile, false);
+
+ STARTUPINFOW si = {sizeof(si), 0};
+ si.lpDesktop = L"";
+ PROCESS_INFORMATION pi = {0};
+
+ bool ok = CreateProcessW(exefullpath,
+ cmdline,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ false, // don't inherit filehandles
+ 0, // No special process creation flags
+ nullptr, // inherit my environment
+ workingDirectory,
+ &si,
+ &pi);
+ free(cmdline);
+ if (ok) {
+ if (!async) {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ return ok;
+}
+
+#endif
+
static void
LaunchCallbackApp(const NS_tchar *workingDir,
int argc,
@@ -1734,8 +1972,7 @@ LaunchCallbackApp(const NS_tchar *workingDir,
(void) argc; (void) usingService; // avoid warnings
execv(argv[0], argv);
#elif defined(MACOSX)
- (void) usingService; // avoid warnings
- LaunchChild(argc, argv);
+ LaunchChild(argc, (const char**)argv);
#elif defined(WNT)
// Do not allow the callback to run when running an update through the
// service as session 0. The unelevated updater.exe will do the launching.
@@ -1750,20 +1987,41 @@ LaunchCallbackApp(const NS_tchar *workingDir,
static bool
WriteStatusFile(const char* aStatus)
{
- NS_tchar filename[MAXPATHLEN];
+ NS_tchar filename[MAXPATHLEN] = {NS_T('\0')};
+#if defined(_WIN32)
+ // The temp file is not removed on failure since there is client code that
+ // will remove it.
+ GetTempFileNameW(gPatchDirPath, L"sta", 0, filename);
+#else
NS_tsnprintf(filename, sizeof(filename)/sizeof(filename[0]),
NS_T("%s/update.status"), gPatchDirPath);
+#endif
// Make sure that the directory for the update status file exists
if (ensure_parent_dir(filename))
return false;
- AutoFile file(NS_tfopen(filename, NS_T("wb+")));
- if (file == nullptr)
- return false;
+ // This is scoped to make the AutoFile close the file so it is possible to
+ // move the temp file to the update.status file on Windows.
+ {
+ AutoFile file(NS_tfopen(filename, NS_T("wb+")));
+ if (file == nullptr) {
+ return false;
+ }
- if (fwrite(aStatus, strlen(aStatus), 1, file) != 1)
+ if (fwrite(aStatus, strlen(aStatus), 1, file) != 1) {
+ return false;
+ }
+ }
+
+#if defined(_WIN32)
+ NS_tchar dstfilename[MAXPATHLEN] = {NS_T('\0')};
+ NS_tsnprintf(dstfilename, sizeof(dstfilename)/sizeof(dstfilename[0]),
+ NS_T("%s\\update.status"), gPatchDirPath);
+ if (MoveFileExW(filename, dstfilename, MOVEFILE_REPLACE_EXISTING) == 0) {
return false;
+ }
+#endif
return true;
}
@@ -1985,6 +2243,22 @@ ProcessReplaceRequest()
return rv;
}
+#if !defined(_WIN32) && !defined(MACOSX)
+ // Platforms that have their updates directory in the installation directory
+ // need to have the last-update.log and backup-update.log files moved from the
+ // old installation directory to the new installation directory.
+ NS_tchar tmpLog[MAXPATHLEN];
+ NS_tsnprintf(tmpLog, sizeof(tmpLog)/sizeof(tmpLog[0]),
+ NS_T("%s/updates/last-update.log"), tmpDir);
+ if (!NS_taccess(tmpLog, F_OK)) {
+ NS_tchar destLog[MAXPATHLEN];
+ NS_tsnprintf(destLog, sizeof(destLog)/sizeof(destLog[0]),
+ NS_T("%s/updates/last-update.log"), destDir);
+ NS_tremove(destLog);
+ NS_trename(tmpLog, destLog);
+ }
+#endif
+
LOG(("Now, remove the tmpDir"));
rv = ensure_remove_recursive(tmpDir, true);
if (rv) {
@@ -2164,7 +2438,18 @@ UpdateThreadFunc(void * /*param*/)
}
}
- if (sReplaceRequest && rv) {
+ if (rv && (sReplaceRequest || sStagedUpdate)) {
+#ifdef _WIN32
+ // On Windows, the current working directory of the process should be changed
+ // so that it's not locked.
+ if (sStagedUpdate) {
+ NS_tchar sysDir[MAX_PATH + 1] = { L'\0' };
+ if (GetSystemDirectoryW(sysDir, MAX_PATH + 1)) {
+ NS_tchdir(sysDir);
+ }
+ }
+#endif
+ ensure_remove_recursive(gWorkingDirPath);
// When attempting to replace the application, we should fall back
// to non-staged updates in case of a failure. We do this by
// setting the status to pending, exiting the updater, and
@@ -2172,8 +2457,11 @@ UpdateThreadFunc(void * /*param*/)
// startup path will see the pending status, and will start the
// updater application again in order to apply the update without
// staging.
- ensure_remove_recursive(gWorkingDirPath);
- WriteStatusFile(sUsingService ? "pending-service" : "pending");
+ if (sReplaceRequest) {
+ WriteStatusFile(sUsingService ? "pending-service" : "pending");
+ } else {
+ WriteStatusFile(rv);
+ }
#ifdef TEST_UPDATER
// Some tests need to use --test-process-updates again.
putenv(const_cast<char*>("MOZ_TEST_PROCESS_UPDATES="));
@@ -2200,8 +2488,93 @@ UpdateThreadFunc(void * /*param*/)
QuitProgressUI();
}
+#ifdef MACOSX
+static void
+ServeElevatedUpdateThreadFunc(void* param)
+{
+ UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param;
+ gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv);
+ if (!gSucceeded) {
+ WriteStatusFile(ELEVATION_CANCELED);
+ }
+ QuitProgressUI();
+}
+
+void freeArguments(int argc, char** argv)
+{
+ for (int i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+#endif
+
+int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv,
+ int callbackIndex
+#ifdef _WIN32
+ , const WCHAR* elevatedLockFilePath
+ , HANDLE updateLockFileHandle
+#elif MACOSX
+ , bool isElevated
+#endif
+ )
+{
+ if (argc > callbackIndex) {
+#if defined(_WIN32)
+ if (gSucceeded) {
+ if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
+ fprintf(stderr, "The post update process was not launched");
+ }
+
+ // The service update will only be executed if it is already installed.
+ // For first time installs of the service, the install will happen from
+ // the PostUpdate process. We do the service update process here
+ // because it's possible we are updating with updater.exe without the
+ // service if the service failed to apply the update. We want to update
+ // the service to a newer version in that case. If we are not running
+ // through the service, then MOZ_USING_SERVICE will not exist.
+ if (!sUsingService) {
+ StartServiceUpdate(gInstallDirPath);
+ }
+ }
+ EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
+#elif MACOSX
+ if (!isElevated) {
+ if (gSucceeded) {
+ LaunchMacPostProcess(gInstallDirPath);
+ }
+#endif
+
+ LaunchCallbackApp(argv[5],
+ argc - callbackIndex,
+ argv + callbackIndex,
+ sUsingService);
+#ifdef XP_MACOSX
+ } // if (!isElevated)
+#endif /* XP_MACOSX */
+ }
+ return 0;
+}
+
int NS_main(int argc, NS_tchar **argv)
{
+ // The callback is the remaining arguments starting at callbackIndex.
+ // The argument specified by callbackIndex is the callback executable and the
+ // argument prior to callbackIndex is the working directory.
+ const int callbackIndex = 6;
+
+#ifdef MACOSX
+ bool isElevated =
+ strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0;
+ if (isElevated) {
+ if (!ObtainUpdaterArguments(&argc, &argv)) {
+ // Won't actually get here because ObtainUpdaterArguments will terminate
+ // the current process on failure.
+ return 1;
+ }
+ }
+#endif
+
#if defined(VERIFY_MAR_SIGNATURE) && !defined(_WIN32) && !defined(MACOSX)
// On Windows and Mac we rely on native APIs to do verifications so we don't
// need to initialize NSS at all there.
@@ -2215,7 +2588,13 @@ int NS_main(int argc, NS_tchar **argv)
}
#endif
- InitProgressUI(&argc, &argv);
+#ifdef MACOSX
+ if (!isElevated) {
+#endif
+ InitProgressUI(&argc, &argv);
+#ifdef MACOSX
+ }
+#endif
// To process an update the updater command line must at a minimum have the
// directory path containing the updater.mar file to process as the first
@@ -2233,11 +2612,18 @@ int NS_main(int argc, NS_tchar **argv)
// launched.
if (argc < 4) {
fprintf(stderr, "Usage: updater patch-dir install-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
+#ifdef MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
// The directory containing the update information.
gPatchDirPath = argv[1];
+
// The directory we're going to update to.
// We copy this string because we need to remove trailing slashes. The C++
// standard says that it's always safe to write to strings pointed to by argv
@@ -2252,8 +2638,7 @@ int NS_main(int argc, NS_tchar **argv)
#ifdef _WIN32
bool useService = false;
bool testOnlyFallbackKeyExists = false;
- bool noServiceFallback = EnvHasValue("MOZ_NO_SERVICE_FALLBACK");
- putenv(const_cast<char*>("MOZ_NO_SERVICE_FALLBACK="));
+ bool noServiceFallback = false;
// We never want the service to be used unless we build with
// the maintenance service.
@@ -2318,34 +2703,42 @@ int NS_main(int argc, NS_tchar **argv)
*slash = NS_T('\0');
}
+#ifdef MACOSX
+ if (!isElevated && !IsRecursivelyWritable(argv[2])) {
+ // If the app directory isn't recursively writeable, an elevated update is
+ // required.
+ UpdateServerThreadArgs threadArgs;
+ threadArgs.argc = argc;
+ threadArgs.argv = const_cast<const NS_tchar**>(argv);
+
+ Thread t1;
+ if (t1.Run(ServeElevatedUpdateThreadFunc, &threadArgs) == 0) {
+ // Show an indeterminate progress bar while an elevated update is in
+ // progress.
+ ShowProgressUI(true);
+ }
+ t1.Join();
+
+ LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex, false);
+ return gSucceeded ? 0 : 1;
+ }
+#endif
+
if (EnvHasValue("MOZ_OS_UPDATE")) {
sIsOSUpdate = true;
putenv(const_cast<char*>("MOZ_OS_UPDATE="));
}
- if (sReplaceRequest) {
- // If we're attempting to replace the application, try to append to the
- // log generated when staging the staged update.
-#ifdef _WIN32
- NS_tchar* logDir = gPatchDirPath;
-#else
-#ifdef MACOSX
- NS_tchar* logDir = gPatchDirPath;
-#else
- NS_tchar logDir[MAXPATHLEN];
- NS_tsnprintf(logDir, sizeof(logDir)/sizeof(logDir[0]),
- NS_T("%s/updated/updates"),
- gInstallDirPath);
-#endif
-#endif
-
- LogInitAppend(logDir, NS_T("last-update.log"), NS_T("update.log"));
- } else {
- LogInit(gPatchDirPath, NS_T("update.log"));
- }
+ LogInit(gPatchDirPath, NS_T("update.log"));
if (!WriteStatusFile("applying")) {
LOG(("failed setting status to 'applying'"));
+#ifdef MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
+ }
+#endif
return 1;
}
@@ -2360,6 +2753,38 @@ int NS_main(int argc, NS_tchar **argv)
LOG(("WORKING DIRECTORY " LOG_S, gWorkingDirPath));
#ifdef _WIN32
+ if (_wcsnicmp(gWorkingDirPath, gInstallDirPath, MAX_PATH) != 0) {
+ if (!sStagedUpdate && !sReplaceRequest) {
+ WriteStatusFile(INVALID_APPLYTO_DIR_ERROR);
+ LOG(("Installation directory and working directory must be the same "
+ "for non-staged updates. Exiting."));
+ LogFinish();
+ return 1;
+ }
+
+ NS_tchar workingDirParent[MAX_PATH];
+ NS_tsnprintf(workingDirParent,
+ sizeof(workingDirParent) / sizeof(workingDirParent[0]),
+ NS_T("%s"), gWorkingDirPath);
+ if (!PathRemoveFileSpecW(workingDirParent)) {
+ WriteStatusFile(REMOVE_FILE_SPEC_ERROR);
+ LOG(("Error calling PathRemoveFileSpecW: %d", GetLastError()));
+ LogFinish();
+ return 1;
+ }
+
+ if (_wcsnicmp(workingDirParent, gInstallDirPath, MAX_PATH) != 0) {
+ WriteStatusFile(INVALID_APPLYTO_DIR_STAGED_ERROR);
+ LOG(("The apply-to directory must be the same as or "
+ "a child of the installation directory! Exiting."));
+ LogFinish();
+ return 1;
+ }
+ }
+#endif
+
+
+#ifdef _WIN32
if (pid > 0) {
HANDLE parent = OpenProcess(SYNCHRONIZE, false, (DWORD) pid);
// May return nullptr if the parent process has already gone away.
@@ -2378,25 +2803,11 @@ int NS_main(int argc, NS_tchar **argv)
waitpid(pid, nullptr, 0);
#endif
- if (sReplaceRequest) {
-#ifdef _WIN32
- // On Windows, the current working directory of the process should be changed
- // so that it's not locked.
- NS_tchar sysDir[MAX_PATH + 1] = { L'\0' };
- if (GetSystemDirectoryW(sysDir, MAX_PATH + 1)) {
- NS_tchdir(sysDir);
- }
-#endif
- }
-
- // The callback is the remaining arguments starting at callbackIndex.
- // The argument specified by callbackIndex is the callback executable and the
- // argument prior to callbackIndex is the working directory.
- const int callbackIndex = 6;
-
#if defined(_WIN32)
+#ifdef MOZ_MAINTENANCE_SERVICE
sUsingService = EnvHasValue("MOZ_USING_SERVICE");
putenv(const_cast<char*>("MOZ_USING_SERVICE="));
+#endif
// lastFallbackError keeps track of the last error for the service not being
// used, in case of an error when fallback is not enabled we write the
// error to the update.status file.
@@ -2541,7 +2952,9 @@ int NS_main(int argc, NS_tchar **argv)
&baseKey) == ERROR_SUCCESS) {
RegCloseKey(baseKey);
} else {
+#ifdef TEST_UPDATER
useService = testOnlyFallbackKeyExists;
+#endif
if (!useService) {
lastFallbackError = FALLBACKKEY_NOKEY_ERROR;
}
@@ -2606,7 +3019,7 @@ int NS_main(int argc, NS_tchar **argv)
if (updateLockFileHandle != INVALID_HANDLE_VALUE) {
CloseHandle(updateLockFileHandle);
}
- WriteStatusPending(gPatchDirPath);
+ WriteStatusFile("pending");
return 0;
}
@@ -2621,8 +3034,7 @@ int NS_main(int argc, NS_tchar **argv)
bool updateStatusSucceeded = false;
if (IsUpdateStatusSucceeded(updateStatusSucceeded) &&
updateStatusSucceeded) {
- if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath, false,
- nullptr)) {
+ if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
fprintf(stderr, "The post update process which runs as the user"
" for service update could not be launched.");
}
@@ -2673,7 +3085,7 @@ int NS_main(int argc, NS_tchar **argv)
// The elevated updater.exe is responsible for writing out the
// update.status file.
return 0;
- } else if(useService) {
+ } else if (useService) {
// The service command was launched. The service is responsible for
// writing out the update.status file.
if (updateLockFileHandle != INVALID_HANDLE_VALUE) {
@@ -2700,20 +3112,16 @@ int NS_main(int argc, NS_tchar **argv)
ensure_remove_recursive(gWorkingDirPath);
}
if (!sReplaceRequest) {
- // Change current directory to the directory where we need to apply the update.
- if (NS_tchdir(gWorkingDirPath) != 0) {
- // Try to create the destination directory if it doesn't exist
- int rv = NS_tmkdir(gWorkingDirPath, 0755);
- if (rv == OK && errno != EEXIST) {
- // Try changing the current directory again
- if (NS_tchdir(gWorkingDirPath) != 0) {
- // OK, time to give up!
- return 1;
- }
- } else {
- // Failed to create the directory, bail out
- return 1;
+ // Try to create the destination directory if it doesn't exist
+ int rv = NS_tmkdir(gWorkingDirPath, 0755);
+ if (rv != OK && errno != EEXIST) {
+#ifdef MACOSX
+ if (isElevated) {
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(true);
}
+#endif
+ return 1;
}
}
@@ -2839,115 +3247,147 @@ int NS_main(int argc, NS_tchar **argv)
sizeof(gCallbackBackupPath)/sizeof(gCallbackBackupPath[0]),
NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
NS_tremove(gCallbackBackupPath);
- CopyFileW(argv[callbackIndex], gCallbackBackupPath, false);
-
- // Since the process may be signaled as exited by WaitForSingleObject before
- // the release of the executable image try to lock the main executable file
- // multiple times before giving up. If we end up giving up, we won't
- // fail the update.
- const int max_retries = 10;
- int retries = 1;
- DWORD lastWriteError = 0;
- do {
- // By opening a file handle without FILE_SHARE_READ to the callback
- // executable, the OS will prevent launching the process while it is
- // being updated.
- callbackFile = CreateFileW(targetPath,
- DELETE | GENERIC_WRITE,
- // allow delete, rename, and write
- FILE_SHARE_DELETE | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, 0, nullptr);
- if (callbackFile != INVALID_HANDLE_VALUE)
- break;
-
- lastWriteError = GetLastError();
- LOG(("NS_main: callback app file open attempt %d failed. " \
- "File: " LOG_S ". Last error: %d", retries,
- targetPath, lastWriteError));
-
- Sleep(100);
- } while (++retries <= max_retries);
-
- // CreateFileW will fail if the callback executable is already in use.
- if (callbackFile == INVALID_HANDLE_VALUE) {
- // Only fail the update if the last error was not a sharing violation.
- if (lastWriteError != ERROR_SHARING_VIOLATION) {
- LOG(("NS_main: callback app file in use, failed to exclusively open " \
- "executable file: " LOG_S, argv[callbackIndex]));
- LogFinish();
- if (lastWriteError == ERROR_ACCESS_DENIED) {
- WriteStatusFile(WRITE_ERROR_ACCESS_DENIED);
+ if(!CopyFileW(argv[callbackIndex], gCallbackBackupPath, true)) {
+ DWORD copyFileError = GetLastError();
+ LOG(("NS_main: failed to copy callback file " LOG_S
+ " into place at " LOG_S, argv[callbackIndex], gCallbackBackupPath));
+ LogFinish();
+ if (copyFileError == ERROR_ACCESS_DENIED) {
+ WriteStatusFile(WRITE_ERROR_ACCESS_DENIED);
} else {
WriteStatusFile(WRITE_ERROR_CALLBACK_APP);
}
- NS_tremove(gCallbackBackupPath);
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
- LaunchCallbackApp(argv[5],
+ LaunchCallbackApp(argv[callbackIndex],
argc - callbackIndex,
argv + callbackIndex,
sUsingService);
return 1;
}
- LOG(("NS_main: callback app file in use, continuing without " \
- "exclusive access for executable file: " LOG_S,
- argv[callbackIndex]));
+
+ // Since the process may be signaled as exited by WaitForSingleObject before
+ // the release of the executable image try to lock the main executable file
+ // multiple times before giving up. If we end up giving up, we won't
+ // fail the update.
+ const int max_retries = 10;
+ int retries = 1;
+ DWORD lastWriteError = 0;
+ do {
+ // By opening a file handle wihout FILE_SHARE_READ to the callback
+ // executable, the OS will prevent launching the process while it is
+ // being updated.
+ callbackFile = CreateFileW(targetPath,
+ DELETE | GENERIC_WRITE,
+ // allow delete, rename, and write
+ FILE_SHARE_DELETE | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, 0, nullptr);
+ if (callbackFile != INVALID_HANDLE_VALUE)
+ break;
+
+ lastWriteError = GetLastError();
+ LOG(("NS_main: callback app file open attempt %d failed. " \
+ "File: " LOG_S ". Last error: %d", retries,
+ targetPath, lastWriteError));
+
+ Sleep(100);
+ } while (++retries <= max_retries);
+
+ // CreateFileW will fail if the callback executable is already in use.
+ if (callbackFile == INVALID_HANDLE_VALUE) {
+ // Only fail the update if the last error was not a sharing violation.
+ if (lastWriteError != ERROR_SHARING_VIOLATION) {
+ LOG(("NS_main: callback app file in use, failed to exclusively open " \
+ "executable file: " LOG_S, argv[callbackIndex]));
+ LogFinish();
+ if (lastWriteError == ERROR_ACCESS_DENIED) {
+ WriteStatusFile(WRITE_ERROR_ACCESS_DENIED);
+ } else {
+ WriteStatusFile(WRITE_ERROR_CALLBACK_APP);
+ }
+
+ NS_tremove(gCallbackBackupPath);
+ EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
+ LaunchCallbackApp(argv[5],
+ argc - callbackIndex,
+ argv + callbackIndex,
+ sUsingService);
+ return 1;
+ }
+ LOG(("NS_main: callback app file in use, continuing without " \
+ "exclusive access for executable file: " LOG_S,
+ argv[callbackIndex]));
+ }
}
}
- }
- // DELETE_DIR is not required when staging an update.
- if (!sStagedUpdate && !sReplaceRequest) {
- // The directory to move files that are in use to on Windows. This directory
- // will be deleted after the update is finished or on OS reboot using
- // MoveFileEx if it contains files that are in use.
- if (NS_taccess(DELETE_DIR, F_OK)) {
- NS_tmkdir(DELETE_DIR, 0755);
+ // DELETE_DIR is not required when performing a staged update or replace
+ // request; it can be used during a replace request but then it doesn't
+ // use gDeleteDirPath.
+ if (!sStagedUpdate && !sReplaceRequest) {
+ // The directory to move files that are in use to on Windows. This directory
+ // will be deleted after the update is finished, on OS reboot using
+ // MoveFileEx if it contains files that are in use, or by the post update
+ // process after the update finishes. On Windows when performing a normal
+ // update (e.g. the update is not a staged update and is not a replace
+ // request) gWorkingDirPath is the same as gInstallDirPath and
+ // gWorkingDirPath is used because it is the destination directory.
+ NS_tsnprintf(gDeleteDirPath,
+ sizeof(gDeleteDirPath) / sizeof(gDeleteDirPath[0]),
+ NS_T("%s/%s"), gWorkingDirPath, DELETE_DIR);
+
+ if (NS_taccess(gDeleteDirPath, F_OK)) {
+ NS_tmkdir(gDeleteDirPath, 0755);
+ }
}
- }
-#endif /* WNT */
+#endif /* XP_WIN */
- // Run update process on a background thread. ShowProgressUI may return
- // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
- // terminate. Avoid showing the progress UI when staging an update.
- std::thread updateThread(UpdateThreadFunc, nullptr);
- if (!sStagedUpdate && !sReplaceRequest)
- {
- ShowProgressUI();
- }
- updateThread.join();
+ // Run update process on a background thread. ShowProgressUI may return
+ // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
+ // terminate. Avoid showing the progress UI when staging an update, or if this
+ // is an elevated process on OSX.
+ std::thread t(UpdateThreadFunc, nullptr);
+ if (!sStagedUpdate && !sReplaceRequest
+#ifdef XP_MACOSX
+ && !isElevated
+#endif
+ ) {
+ ShowProgressUI();
+ }
+ t.join();
-#ifdef _WIN32
- if (argc > callbackIndex && !sReplaceRequest) {
- if (callbackFile != INVALID_HANDLE_VALUE) {
- CloseHandle(callbackFile);
- }
- // Remove the copy of the callback executable.
- NS_tremove(gCallbackBackupPath);
- }
-
- if (!sStagedUpdate && !sReplaceRequest && _wrmdir(DELETE_DIR)) {
- LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d",
- DELETE_DIR, errno));
- // The directory probably couldn't be removed due to it containing files
- // that are in use and will be removed on OS reboot. The call to remove the
- // directory on OS reboot is done after the calls to remove the files so the
- // files are removed first on OS reboot since the directory must be empty
- // for the directory removal to be successful. The MoveFileEx call to remove
- // the directory on OS reboot will fail if the process doesn't have write
- // access to the HKEY_LOCAL_MACHINE registry key but this is ok since the
- // installer / uninstaller will delete the directory along with its contents
- // after an update is applied, on reinstall, and on uninstall.
- if (MoveFileEx(DELETE_DIR, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
- LOG(("NS_main: directory will be removed on OS reboot: " LOG_S,
- DELETE_DIR));
- } else {
- LOG(("NS_main: failed to schedule OS reboot removal of " \
- "directory: " LOG_S, DELETE_DIR));
+#ifdef XP_WIN
+ if (argc > callbackIndex && !sReplaceRequest) {
+ if (callbackFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(callbackFile);
+ }
+ // Remove the copy of the callback executable.
+ NS_tremove(gCallbackBackupPath);
+ }
+
+ if (!sStagedUpdate && !sReplaceRequest && _wrmdir(gDeleteDirPath)) {
+ LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d",
+ DELETE_DIR, errno));
+ // The directory probably couldn't be removed due to it containing files
+ // that are in use and will be removed on OS reboot. The call to remove the
+ // directory on OS reboot is done after the calls to remove the files so the
+ // files are removed first on OS reboot since the directory must be empty
+ // for the directory removal to be successful. The MoveFileEx call to remove
+ // the directory on OS reboot will fail if the process doesn't have write
+ // access to the HKEY_LOCAL_MACHINE registry key but this is ok since the
+ // installer / uninstaller will delete the directory along with its contents
+ // after an update is applied, on reinstall, and on uninstall.
+ if (MoveFileEx(gDeleteDirPath, nullptr, MOVEFILE_DELAY_UNTIL_REBOOT)) {
+ LOG(("NS_main: directory will be removed on OS reboot: " LOG_S,
+ DELETE_DIR));
+ } else {
+ LOG(("NS_main: failed to schedule OS reboot removal of " \
+ "directory: " LOG_S, DELETE_DIR));
+ }
}
- }
#endif /* WNT */
+
#ifdef MACOSX
// When the update is successful remove the precomplete file in the root of
// the application bundle and move the distribution directory from
@@ -2986,42 +3426,32 @@ int NS_main(int argc, NS_tchar **argv)
}
}
}
+
+ if (isElevated) {
+ SetGroupOwnershipAndPermissions(gInstallDirPath);
+ freeArguments(argc, argv);
+ CleanupElevatedMacUpdate(false);
+ } else if (IsOwnedByGroupAdmin(gInstallDirPath)) {
+ // If the group ownership of the Firefox .app bundle was set to the "admin"
+ // group during a previous elevated update, we need to ensure that all files
+ // in the bundle have group ownership of "admin" as well as write permission
+ // for the group to not break updates in the future.
+ SetGroupOwnershipAndPermissions(gInstallDirPath);
+ }
#endif /* MACOSX */
LogFinish();
- if (argc > callbackIndex) {
-#if defined(_WIN32)
- if (gSucceeded) {
- // The service update will only be executed if it is already installed.
- // For first time installs of the service, the install will happen from
- // the PostUpdate process. We do the service update process here
- // because it's possible we are updating with updater.exe without the
- // service if the service failed to apply the update. We want to update
- // the service to a newer version in that case. If we are not running
- // through the service, then MOZ_USING_SERVICE will not exist.
- if (!sUsingService) {
- if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath, false, nullptr)) {
- LOG(("NS_main: The post update process could not be launched."));
- }
-
- StartServiceUpdate(gInstallDirPath);
- }
- }
- EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
-#endif /* WNT */
-#ifdef MACOSX
- if (gSucceeded) {
- LaunchMacPostProcess(gInstallDirPath);
- }
-#endif /* MACOSX */
- LaunchCallbackApp(argv[5],
- argc - callbackIndex,
- argv + callbackIndex,
- sUsingService);
- }
+ int retVal = LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex
+#ifdef XP_WIN
+ , elevatedLockFilePath
+ , updateLockFileHandle
+#elif XP_MACOSX
+ , isElevated
+#endif
+ );
- return gSucceeded ? 0 : 1;
+ return retVal ? retVal : (gSucceeded ? 0 : 1);
}
class ActionList
@@ -3151,9 +3581,9 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
NS_tsnprintf(searchspec, sizeof(searchspec)/sizeof(searchspec[0]),
NS_T("%s*"), dirpath);
- const NS_tchar *pszSpec = get_full_path(searchspec);
+ std::unique_ptr<const NS_tchar> pszSpec(get_full_path(searchspec));
- hFindFile = FindFirstFileW(pszSpec, &finddata);
+ hFindFile = FindFirstFileW(pszSpec.get(), &finddata);
if (hFindFile != INVALID_HANDLE_VALUE) {
do {
// Don't process the current or parent directory.
@@ -3185,6 +3615,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
quotedpath, rv));
return rv;
}
+ free(quotedpath);
list->Append(action);
}
@@ -3204,6 +3635,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
quotedpath, rv));
else
list->Append(action);
+ free(quotedpath);
}
}
@@ -3214,24 +3646,17 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
{
int rv = OK;
- NS_tchar searchpath[MAXPATHLEN];
NS_tchar foundpath[MAXPATHLEN];
struct {
dirent dent_buffer;
char chars[MAXNAMLEN];
} ent_buf;
struct dirent* ent;
+ std::unique_ptr<NS_tchar> searchpath(get_full_path(dirpath));
-
- NS_tsnprintf(searchpath, sizeof(searchpath)/sizeof(searchpath[0]), NS_T("%s"),
- dirpath);
- // Remove the trailing slash so the paths don't contain double slashes. The
- // existence of the slash has already been checked in DoUpdate.
- searchpath[NS_tstrlen(searchpath) - 1] = NS_T('\0');
-
- DIR* dir = opendir(searchpath);
+ DIR* dir = opendir(searchpath.get());
if (!dir) {
- LOG(("add_dir_entries error on opendir: " LOG_S ", err: %d", searchpath,
+ LOG(("add_dir_entries error on opendir: " LOG_S ", err: %d", searchpath.get(),
errno));
return UNEXPECTED_FILE_OPERATION_ERROR;
}
@@ -3242,7 +3667,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
continue;
NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
- NS_T("%s%s"), dirpath, ent->d_name);
+ NS_T("%s%s"), searchpath.get(), ent->d_name);
struct stat64 st_buf;
int test = stat64(foundpath, &st_buf);
if (test) {
@@ -3261,7 +3686,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
}
} else {
// Add the file to be removed to the ActionList.
- NS_tchar *quotedpath = get_quoted_path(foundpath);
+ NS_tchar *quotedpath = get_quoted_path(get_relative_path(foundpath));
if (!quotedpath) {
closedir(dir);
return PARSE_ERROR;
@@ -3282,7 +3707,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
closedir(dir);
// Add the directory to be removed to the ActionList.
- NS_tchar *quotedpath = get_quoted_path(dirpath);
+ NS_tchar *quotedpath = get_quoted_path(get_relative_path(dirpath));
if (!quotedpath)
return PARSE_ERROR;
@@ -3306,14 +3731,12 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
int rv = OK;
FTS *ftsdir;
FTSENT *ftsdirEntry;
- NS_tchar searchpath[MAXPATHLEN];
+ std::unique_ptr<NS_tchar> searchpath(get_full_path(dirpath));
- NS_tsnprintf(searchpath, sizeof(searchpath)/sizeof(searchpath[0]), NS_T("%s"),
- dirpath);
// Remove the trailing slash so the paths don't contain double slashes. The
// existence of the slash has already been checked in DoUpdate.
- searchpath[NS_tstrlen(searchpath) - 1] = NS_T('\0');
- char* const pathargv[] = {searchpath, nullptr};
+ searchpath.get()[NS_tstrlen(searchpath.get()) - 1] = NS_T('\0');
+ char* const pathargv[] = {searchpath.get(), nullptr};
// FTS_NOCHDIR is used so relative paths from the destination directory are
// returned.
@@ -3324,7 +3747,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
while ((ftsdirEntry = fts_read(ftsdir)) != nullptr) {
NS_tchar foundpath[MAXPATHLEN];
- NS_tchar *quotedpath;
+ NS_tchar *quotedpath = nullptr;
Action *action = nullptr;
switch (ftsdirEntry->fts_info) {
@@ -3342,13 +3765,14 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
// Add the file to be removed to the ActionList.
NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
NS_T("%s"), ftsdirEntry->fts_accpath);
- quotedpath = get_quoted_path(foundpath);
+ quotedpath = get_quoted_path(get_relative_path(foundpath));
if (!quotedpath) {
rv = UPDATER_QUOTED_PATH_MEM_ERROR;
break;
}
action = new RemoveFile();
rv = action->Parse(quotedpath);
+ free(quotedpath);
if (!rv)
list->Append(action);
break;
@@ -3359,7 +3783,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
// Add the directory to be removed to the ActionList.
NS_tsnprintf(foundpath, sizeof(foundpath)/sizeof(foundpath[0]),
NS_T("%s/"), ftsdirEntry->fts_accpath);
- quotedpath = get_quoted_path(foundpath);
+ quotedpath = get_quoted_path(get_relative_path(foundpath));
if (!quotedpath) {
rv = UPDATER_QUOTED_PATH_MEM_ERROR;
break;
@@ -3367,6 +3791,7 @@ int add_dir_entries(const NS_tchar *dirpath, ActionList *list)
action = new RemoveDir();
rv = action->Parse(quotedpath);
+ free(quotedpath);
if (!rv)
list->Append(action);
break;
@@ -3438,6 +3863,7 @@ GetManifestContents(const NS_tchar *manifest)
size_t c = fread(rb, 1, count, mfile);
if (c != count) {
LOG(("GetManifestContents: error reading manifest file: " LOG_S, manifest));
+ free(mbuf);
return nullptr;
}
@@ -3451,8 +3877,10 @@ GetManifestContents(const NS_tchar *manifest)
return rb;
#else
NS_tchar *wrb = (NS_tchar *) malloc((ms.st_size + 1) * sizeof(NS_tchar));
- if (!wrb)
+ if (!wrb) {
+ free(mbuf);
return nullptr;
+ }
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, rb, -1, wrb,
ms.st_size + 1)) {
@@ -3474,10 +3902,14 @@ int AddPreCompleteActions(ActionList *list)
}
#ifdef MACOSX
- NS_tchar *rb = GetManifestContents(NS_T("Contents/Resources/precomplete"));
+ std::unique_ptr<NS_tchar> manifestPath(get_full_path(
+ NS_T("Contents/Resources/precomplete")));
#else
- NS_tchar *rb = GetManifestContents(NS_T("precomplete"));
+ std::unique_ptr<NS_tchar> manifestPath(get_full_path(
+ NS_T("precomplete")));
#endif
+
+ NS_tchar *rb = GetManifestContents(manifestPath.get());
if (rb == nullptr) {
LOG(("AddPreCompleteActions: error getting contents of precomplete " \
"manifest"));