From e9ad32a273efe2d177c1bbd394ae944ae598fd50 Mon Sep 17 00:00:00 2001 From: Dmitry Rozhkov Date: Mon, 8 Feb 2016 18:12:48 +0200 Subject: [PATCH] Backport: Use rename instead of tar transform This patch is a backport from swupd-client v2.88 Author: William Douglas Subject: Use rename instead of tar transform In order to prevent issues with transform name escaping, update logic for moving an object from staging. First rename the object in the staging path to its final name (in case of a directory the rename places it in a seperate directory first to avoid hash colisions), then use tar to update or create the object in the filesystem. Once finished rename the object back to the hash name so it can be reused as needed. This also fixes up some issues with the SWUPD_LINUX_ROOTFS checks not always encapsulating variable use within the do_staging function. Note: the SWUPD_LINUX_ROOTFS checks have been removed entirely, since they are not used anywhere in the code at present. Upstream-Status: Backported [v2.88+] Signed-off-by: Dmitry Rozhkov --- src/staging.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/src/staging.c b/src/staging.c index b8545c1..16dafbb 100644 --- a/src/staging.c +++ b/src/staging.c @@ -36,6 +36,31 @@ #include "swupd-build-variant.h" #include +/* clean then recreate temporary folder for tar renames */ +static int create_staging_renamedir(char *rename_tmpdir) +{ + int ret; + char *rmcommand = NULL; + + string_or_die(&rmcommand, "rm -fr %s", rename_tmpdir); + if (!system(rmcommand)) { + /* Not fatal but pretty scary, likely to really fail at the + * next command too. Pass for now as printing may just cause + * confusion */ + ; + } + free(rmcommand); + + ret = mkdir(rename_tmpdir, S_IRWXU); + if (ret == -1 && errno != EEXIST) { + ret = -errno; + } else { + ret = 0; + } + + return ret; +} + #ifdef SWUPD_WITH_BTRFS static int create_staging_subvol_from(const char *version) { @@ -269,6 +294,9 @@ int prepare(bool UNUSED_PARAM *is_corrupted, int UNUSED_PARAM current_version, i #endif /* Do the staging of new files into the filesystem */ +#warning do_staging is currently not able to be run in parallel +/* Consider adding a remove_leftovers() that runs in verify/fix in order to + * allow this function to mkdtemp create folders for parallel build */ int do_staging(struct file *file) { char *statfile = NULL, *tmp = NULL, *tmp2 = NULL; @@ -280,6 +308,8 @@ int do_staging(struct file *file) char *targetpath = NULL; char *symbase = NULL; #endif + char *rename_target = NULL; + char *rename_tmpdir = NULL; int ret; struct stat s; @@ -360,12 +390,28 @@ int do_staging(struct file *file) * attributes and it includes internal logic that does the * right thing to overlay a directory onto something * pre-existing: */ - string_or_die(&tarcommand, "tar -C %s/staged " TAR_PERM_ATTR_ARGS " -cf - %s 2> /dev/null | " - "tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - --transform=\"s/%s/%s/\" 2> /dev/null", - STATE_DIR, file->hash, path_prefix, rel_dir, file->hash, base); + /* In order to avoid tar transforms with directories, rename + * the directory before and after the tar command */ + string_or_die(&rename_tmpdir, "%s/tmprenamedir", STATE_DIR); + ret = create_staging_renamedir(rename_tmpdir); + if (ret) { + goto out; + } + string_or_die(&rename_target, "%s/%s", rename_tmpdir, base); + if (rename(original, rename_target)) { + ret = -errno; + goto out; + } + string_or_die(&tarcommand, "tar -C %s " TAR_PERM_ATTR_ARGS " -cf - %s 2> /dev/null | " + "tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null", + rename_tmpdir, base, path_prefix, rel_dir); LOG_DEBUG(file, "directory overwrite", class_osvol_staging, "%s", tarcommand); ret = system(tarcommand); free(tarcommand); + if (rename(rename_target, original)) { + ret = -errno; + goto out; + } if (ret < 0) { LOG_ERROR(file, "Failed directory overwrite", class_osvol_staging, "%s", strerror(errno)); ret = -EDIR_OVERWRITE; @@ -386,12 +432,25 @@ int do_staging(struct file *file) } if (ret < 0) { /* either the hardlink failed, or it was undesirable (config), do a tar-tar dance */ - string_or_die(&tarcommand, "tar -C %s/staged " TAR_PERM_ATTR_ARGS " -cf - %s 2> /dev/null | " - "tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - --transform=\"s/%s/.update.%s/\" 2> /dev/null", - STATE_DIR, file->hash, path_prefix, rel_dir, file->hash, base); + /* In order to avoid tar transforms, rename the file + * before and after the tar command */ + string_or_die(&rename_target, "%s/staged/.update.%s", STATE_DIR, base); + ret = rename(original, rename_target); + if (ret) { + ret = -errno; + goto out; + } + string_or_die(&tarcommand, "tar -C %s/staged " TAR_PERM_ATTR_ARGS " -cf - .update.%s 2> /dev/null | " + "tar -C %s%s " TAR_PERM_ATTR_ARGS " -xf - 2> /dev/null", + STATE_DIR, base, path_prefix, rel_dir); LOG_DEBUG(file, "dotfile install", class_osvol_staging, "%s", tarcommand); ret = system(tarcommand); free(tarcommand); + ret = rename(rename_target, original); + if (ret) { + ret = -errno; + goto out; + } } if (ret < 0) { LOG_ERROR(file, "Failed tar dotfile install", class_osvol_staging, @@ -436,6 +495,8 @@ int do_staging(struct file *file) out: free(target); free(original); + free(rename_target); + free(rename_tmpdir); free(tmp); free(tmp2); -- 2.5.0