diff --git a/toolkit/mozapps/update/test/TestAUSHelper.cpp b/toolkit/mozapps/update/test/TestAUSHelper.cpp index e51466bc3029..854293bd4ae5 100644 --- a/toolkit/mozapps/update/test/TestAUSHelper.cpp +++ b/toolkit/mozapps/update/test/TestAUSHelper.cpp @@ -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]); diff --git a/toolkit/mozapps/update/test/unit/test_0113_general.js b/toolkit/mozapps/update/test/unit/test_0113_general.js index 09bdf70346e5..5f2e22f59be1 100644 --- a/toolkit/mozapps/update/test/unit/test_0113_general.js +++ b/toolkit/mozapps/update/test/unit/test_0113_general.js @@ -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(); - // Sorting on Linux is different so skip this check for now. - if (!IS_UNIX) { + if (IS_UNIX) { + checkSymlink(); + } else { + // Sorting on Linux is different so skip this check for now. checkUpdateLogContents(LOG_COMPLETE_SWITCH_SUCCESS); } diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 80ea7cc6717f..27206b7b491f 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -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& 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); }