Skip to content
This repository has been archived by the owner on Apr 19, 2019. It is now read-only.

Commit

Permalink
swupd-client: Update the client and server to use bsdtar
Browse files Browse the repository at this point in the history
This patch adds bsdtar support to swupd-client and swupd-server
and enables it.

The reason why it's done this way is that:
    - bsdtar works better with IMA (opens files only once and then
      updates content and xattrs together);
    - swupd remains fully functional, including xattrs support,
      even when a distro disables GPLv3 licensed code.

Signed-off-by: Dmitry Rozhkov <[email protected]>
  • Loading branch information
Dmitry Rozhkov committed Mar 30, 2016
1 parent 215ff54 commit d0326c5
Show file tree
Hide file tree
Showing 8 changed files with 692 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
From 1f37511e52754f7231c52489ba4f7d8f7de1e2af Mon Sep 17 00:00:00 2001
From: "Brad T. Peters" <[email protected]>
Date: Thu, 7 Jan 2016 14:37:17 -0800
Subject: [PATCH] swupd-client: Add existence check to staging target

Patch adds an stat() check to ensure that:
1. target path for a staged file exists, and
2. target path is indeed a directory

Follow-on patch will add correct corrective behavior once
verify_fix_path() is implemented

Upstream-Status: Accepted

Signed-off-by: Brad T. Peters <[email protected]>
---
src/staging.c | 39 ++++++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/src/staging.c b/src/staging.c
index 3a847e2..b8545c1 100644
--- a/src/staging.c
+++ b/src/staging.c
@@ -277,9 +277,11 @@ int do_staging(struct file *file)
#if SWUPD_LINUX_ROOTFS
char *original = NULL;
char *target = NULL;
+ char *targetpath = NULL;
+ char *symbase = NULL;
#endif
int ret;
- struct stat stat;
+ struct stat s;

tmp = strdup(file->filename);
tmp2 = strdup(file->filename);
@@ -294,6 +296,29 @@ int do_staging(struct file *file)
string_or_die(&original, "%s/staged/%s", STATE_DIR, file->hash);

#if SWUPD_LINUX_ROOTFS
+ string_or_die(&targetpath, "%s%s", path_prefix, rel_dir);
+ ret = stat(targetpath, &s);
+
+ if (S_ISLNK(s.st_mode)) {
+ /* Follow symlink to ultimate target and redo stat */
+ symbase = realpath(targetpath, NULL);
+ if (symbase != NULL) {
+ free(targetpath);
+ targetpath = strdup(symbase);
+ ret = stat(targetpath, &s);
+ free(symbase);
+ }
+ }
+
+ /* For now, just report on error conditions. Once we implement
+ * verify_fix_path(char *path, int targetversion), we'll want to call it here */
+ if ((ret == -1) && (errno == ENOENT)) {
+ printf("Error: Update target directory does not exist: %s\n", targetpath);
+ } else if (!S_ISDIR(s.st_mode)) {
+ printf("Error: Update target exists but is NOT a directory: %s\n", targetpath);
+ }
+
+ free(targetpath);
string_or_die(&target, "%s%s/.update.%s", path_prefix, rel_dir, base);
ret = swupd_rm(target);
if (ret == 0)
@@ -306,12 +331,12 @@ int do_staging(struct file *file)
string_or_die(&statfile, "%s/%s/%s", STAGING_SUBVOL, rel_dir, base);
#endif

- memset(&stat, 0, sizeof(struct stat));
- ret = lstat(statfile, &stat);
+ memset(&s, 0, sizeof(struct stat));
+ ret = lstat(statfile, &s);
if (ret == 0) {
- if ((file->is_dir && !S_ISDIR(stat.st_mode)) ||
- (file->is_link && !S_ISLNK(stat.st_mode)) ||
- (file->is_file && !S_ISREG(stat.st_mode))) {
+ if ((file->is_dir && !S_ISDIR(s.st_mode)) ||
+ (file->is_link && !S_ISLNK(s.st_mode)) ||
+ (file->is_file && !S_ISREG(s.st_mode))) {
LOG_INFO(file, "Type changed!", class_osvol_staging, "%s", statfile);
//file type changed, move old out of the way for new
ret = swupd_rm(statfile);
@@ -325,7 +350,7 @@ int do_staging(struct file *file)
free(statfile);

#if SWUPD_LINUX_ROOTFS
- if (file->is_dir || S_ISDIR(stat.st_mode)) {
+ if (file->is_dir || S_ISDIR(s.st_mode)) {
/* In the btrfs only scenario there is an implicit
* "create_or_update_dir()" via un-tar-ing a directory.tar after
* download and the untar happens in the staging subvolume which
--
2.5.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
From e9ad32a273efe2d177c1bbd394ae944ae598fd50 Mon Sep 17 00:00:00 2001
From: Dmitry Rozhkov <[email protected]>
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 <[email protected]>
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 <[email protected]>
---
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 <swupd.h>

+/* 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

Loading

0 comments on commit d0326c5

Please sign in to comment.