Bug 764269 - Handle symbolic links properly when copying the installation directory before staging an update; r=rstrong

This commit is contained in:
Ehsan Akhgari 2012-06-13 20:38:56 -04:00
parent 40be4b6271
commit 3bc64d4216
3 changed files with 161 additions and 5 deletions

View File

@ -48,6 +48,7 @@
# define NS_ttoi atoi
# define NS_tstat stat
# define NS_tgetcwd getcwd
# define NS_tfputs fputs
# define LOG_S "%s"
#endif
@ -171,6 +172,9 @@ int NS_main(int argc, NS_tchar **argv)
"Usage: WORKINGDIR INFILE OUTFILE -s SECONDS [FILETOLOCK]\n" \
" or: WORKINGDIR LOGFILE [ARG2 ARG3...]\n" \
" or: signature-check filepath\n" \
" or: setup-symlink dir1 dir2 file symlink\n" \
" or: remove-symlink dir1 dir2 file symlink\n" \
" or: check-symlink symlink\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" \
@ -205,6 +209,68 @@ int NS_main(int argc, NS_tchar **argv)
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("setup-symlink"))) {
#ifdef XP_UNIX
NS_tchar path[MAXPATHLEN];
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
mkdir(path, 0755);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
mkdir(path, 0755);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
FILE * file = NS_tfopen(path, NS_T("w"));
if (file) {
NS_tfputs(NS_T("test"), file);
fclose(file);
}
symlink(path, argv[5]);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
if (argc > 6 && !NS_tstrcmp(argv[6], NS_T("change-perm"))) {
chmod(path, 0644);
}
return 0;
#else
// Not implemented on non-Unix platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("remove-symlink"))) {
#ifdef XP_UNIX
NS_tchar path[MAXPATHLEN];
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
chmod(path, 0755);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3], argv[4]);
unlink(path);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s/%s"), NS_T("/tmp"), argv[2], argv[3]);
rmdir(path);
NS_tsnprintf(path, sizeof(path)/sizeof(path[0]),
NS_T("%s/%s"), NS_T("/tmp"), argv[2]);
rmdir(path);
return 0;
#else
// Not implemented on non-Unix platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("check-symlink"))) {
#ifdef XP_UNIX
struct stat ss;
lstat(argv[2], &ss);
return S_ISLNK(ss.st_mode) ? 0 : 1;
#else
// Not implemented on non-Unix platforms
return 1;
#endif
}
if (!NS_tstrcmp(argv[1], NS_T("wait-for-service-stop"))) {
#ifdef XP_WIN
const int maxWaitSeconds = NS_ttoi(argv[3]);

View File

@ -14,7 +14,7 @@ const MAX_TIME_DIFFERENCE = 60000;
// The files are listed in the same order as they are applied from the mar's
// update.manifest. Complete updates have remove file and rmdir directory
// operations located in the precomplete file performed first.
const TEST_FILES = [
var TEST_FILES = [
{
description : "Should never change",
fileName : "channel-prefs.js",
@ -234,6 +234,39 @@ ADDITIONAL_TEST_DIRS = [
dirRemoved : true
}];
function runHelperProcess(args) {
let helperBin = do_get_file(HELPER_BIN_FILE);
let process = AUS_Cc["@mozilla.org/process/util;1"].
createInstance(AUS_Ci.nsIProcess);
process.init(helperBin);
logTestInfo("Running " + helperBin.path + " " + args.join(" "));
process.run(true, args, args.length);
do_check_eq(process.exitValue, 0);
}
function createSymlink() {
let args = ["setup-symlink", "moz-foo", "moz-bar", "target",
getApplyDirFile().path + "/a/b/link"];
runHelperProcess(args);
args = ["setup-symlink", "moz-foo2", "moz-bar2", "target2",
getApplyDirFile().path + "/a/b/link2", "change-perm"];
runHelperProcess(args);
}
function removeSymlink() {
let args = ["remove-symlink", "moz-foo", "moz-bar", "target",
getApplyDirFile().path + "/a/b/link"];
runHelperProcess(args);
args = ["remove-symlink", "moz-foo2", "moz-bar2", "target2",
getApplyDirFile().path + "/a/b/link2"];
runHelperProcess(args);
}
function checkSymlink() {
let args = ["check-symlink", getApplyDirFile().path + "/a/b/link"];
runHelperProcess(args);
}
function run_test() {
do_test_pending();
do_register_cleanup(cleanupUpdaterTest);
@ -253,10 +286,31 @@ function run_test() {
applyToDir.lastModifiedTime = yesterday;
}
if (IS_UNIX) {
removeSymlink();
createSymlink();
do_register_cleanup(removeSymlink);
TEST_FILES.push({
description : "Readable symlink",
fileName : "link",
relPathDir : "a/b/",
originalContents : "test",
compareContents : "test",
originalFile : null,
compareFile : null,
originalPerms : 0664,
comparePerms : 0664
});
}
// apply the complete mar
let exitValue = runUpdate();
logTestInfo("testing updater binary process exitValue for success when " +
"applying a complete mar");
let updateLog = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX, true);
updateLog.append(FILE_UPDATE_LOG);
let updateLogContents = readFileBytes(updateLog);
logTestInfo(updateLogContents);
do_check_eq(exitValue, 0);
logTestInfo("testing update.status should be " + STATE_APPLIED);
@ -308,8 +362,10 @@ function run_test() {
}
checkFilesAfterUpdateSuccess();
if (IS_UNIX) {
checkSymlink();
} else {
// Sorting on Linux is different so skip this check for now.
if (!IS_UNIX) {
checkUpdateLogContents(LOG_COMPLETE_SWITCH_SUCCESS);
}

View File

@ -551,6 +551,27 @@ static int ensure_parent_dir(const NS_tchar *path)
return rv;
}
#ifdef XP_UNIX
static int ensure_copy_symlink(const NS_tchar *path, const NS_tchar *dest)
{
// Copy symlinks by creating a new symlink to the same target
NS_tchar target[MAXPATHLEN + 1] = {NS_T('\0')};
int rv = readlink(path, target, MAXPATHLEN);
if (rv == -1) {
LOG(("ensure_copy_symlink: failed to read the link: " LOG_S ", err: %d\n",
path, errno));
return READ_ERROR;
}
rv = symlink(target, dest);
if (rv == -1) {
LOG(("ensure_copy_symlink: failed to create the new link: " LOG_S ", target: " LOG_S " err: %d\n",
dest, target, errno));
return READ_ERROR;
}
return 0;
}
#endif
// Copy the file named path onto a new file named dest.
static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
{
@ -565,13 +586,19 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
return 0;
#else
struct stat ss;
int rv = NS_tstat(path, &ss);
int rv = NS_tlstat(path, &ss);
if (rv) {
LOG(("ensure_copy: failed to read file status info: " LOG_S ", err: %d\n",
path, errno));
return READ_ERROR;
}
#ifdef XP_UNIX
if (S_ISLNK(ss.st_mode)) {
return ensure_copy_symlink(path, dest);
}
#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\n",
@ -646,12 +673,19 @@ static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest,
copy_recursive_skiplist<N>& skiplist)
{
struct stat sInfo;
int rv = NS_tstat(path, &sInfo);
int rv = NS_tlstat(path, &sInfo);
if (rv) {
LOG(("ensure_copy_recursive: path doesn't exist: " LOG_S ", rv: %d, err: %d\n",
path, rv, errno));
return READ_ERROR;
}
#ifdef XP_UNIX
if (S_ISLNK(sInfo.st_mode)) {
return ensure_copy_symlink(path, dest);
}
#endif
if (!S_ISDIR(sInfo.st_mode)) {
return ensure_copy(path, dest);
}