diff mbox series

[1/1] Archive handler: Add attribute 'strip-components'

Message ID 20210523023215.449160-1-david.degrave@mind.be
State Changes Requested
Headers show
Series [1/1] Archive handler: Add attribute 'strip-components' | expand

Commit Message

David De Grave (Essensium/Mind) May 23, 2021, 2:32 a.m. UTC
Some tar archives, by default, contains the name of the archive itself as
the first directory where everything is extracted.  This option let us strip
that first directory out of the extracted paths such that it can be used to
override some parts of the file system to be updated.  This is exactly the
same option as for the tar archiver itself (see the man page of tar for the
argument '--strip-components' for more informations).

Example of an 'images' record in sw-description:
    images: ({
        filename = "rootfs-override.tar.gz";
        type = "archive";
        compressed = true;
        strip-components = 1;
        device = "/dev/mmcblk0p1";
        filesystem = "ext4";
        path = "/";
    });

When extracting, the paths inside the tarball will be stripped like:
    rootfs-override/etc/foo.conf -> etc/foo.conf
    rootfs-override/bin/foo -> bin/foo
    ...

Signed-off-by: David De Grave (Essensium/Mind) <david.degrave@mind.be>
---
 corelib/lua_interface.c    |  3 ++
 handlers/archive_handler.c | 83 +++++++++++++++++++++++++++++++++++++-
 include/swupdate.h         |  1 +
 parser/parser.c            |  1 +
 4 files changed, 87 insertions(+), 1 deletion(-)

Comments

Stefano Babic May 26, 2021, 8:19 a.m. UTC | #1
Hi David,

On 23.05.21 04:32, David De Grave (Essensium/Mind) wrote:
> Some tar archives, by default, contains the name of the archive itself as
> the first directory where everything is extracted.  This option let us strip
> that first directory out of the extracted paths such that it can be used to
> override some parts of the file system to be updated.  This is exactly the
> same option as for the tar archiver itself (see the man page of tar for the
> argument '--strip-components' for more informations).
> 
> Example of an 'images' record in sw-description:
>      images: ({
>          filename = "rootfs-override.tar.gz";
>          type = "archive";
>          compressed = true;
>          strip-components = 1;

Each entry in sw-description has general attributes and specific 
attributes. The generic atributzes are common to all entries (at least 
of the same type, images / files / scripts), while the specific ones are 
just interpreted by the specific handler. That means the parser does not 
care of it and the handler checks for "properties".

This is the case here because "strip-components" is just for archive. 
The syntax should be:

     images: ({
         filename = "rootfs-override.tar.gz";
         type = "archive";
         compressed = true;
         device = "/dev/mmcblk0p1";
         filesystem = "ext4";
         path = "/";
	properties (
		strip-component = 1;
	)
     });


Please consider that you have not added any documentation: this is for 
the archive handler, you should then extend the documentation for this 
handler to add the new attribute.

That means that patch must be reworked ==> I skip most of the 
implementation, just some common topics.

>          device = "/dev/mmcblk0p1";
>          filesystem = "ext4";
>          path = "/";
>      });
> 
> When extracting, the paths inside the tarball will be stripped like:
>      rootfs-override/etc/foo.conf -> etc/foo.conf
>      rootfs-override/bin/foo -> bin/foo
>      ...
> 
> Signed-off-by: David De Grave (Essensium/Mind) <david.degrave@mind.be>
> ---
>   corelib/lua_interface.c    |  3 ++
>   handlers/archive_handler.c | 83 +++++++++++++++++++++++++++++++++++++-
>   include/swupdate.h         |  1 +
>   parser/parser.c            |  1 +
>   4 files changed, 87 insertions(+), 1 deletion(-)
> 
> diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
> index 86e95bd..6b7bb58 100644
> --- a/corelib/lua_interface.c
> +++ b/corelib/lua_interface.c
> @@ -343,6 +343,8 @@ static void lua_number_to_img(struct img_type *img, const char *key,
>   		img->checksum = (unsigned int)val;
>   	if (!strcmp(key, "skip"))
>   		img->skip = (unsigned int)val;
> +	if (!strcmp(key, "strip_components"))
> +		img->strip_components = (unsigned int)val;
>   }
>   
>   #ifdef CONFIG_HANDLER_IN_LUA
> @@ -501,6 +503,7 @@ static void update_table(lua_State* L, struct img_type *img)
>   		LUA_PUSH_IMG_NUMBER(img, "size", size);
>   		LUA_PUSH_IMG_NUMBER(img, "checksum", checksum);
>   		LUA_PUSH_IMG_NUMBER(img, "skip", skip);
> +		LUA_PUSH_IMG_NUMBER(img, "strip_components", strip_components);
>   
>   		switch (img->compressed) {
>   			case COMPRESSED_ZLIB:
> diff --git a/handlers/archive_handler.c b/handlers/archive_handler.c
> index 9812d42..0c807bc 100644
> --- a/handlers/archive_handler.c
> +++ b/handlers/archive_handler.c
> @@ -36,6 +36,7 @@ pthread_t extract_thread;
>   
>   struct extract_data {
>   	int flags;
> +	int strip_components;
>   	int exitval;
>   };
>   
> @@ -66,6 +67,44 @@ copy_data(struct archive *ar, struct archive *aw)
>   	}
>   }
>   
> +/* strip_components()
> + * Copyright (c) 2003-2007 Tim Kientzle

This is not allowed, SWUpdate uses SPDX for licencing, see also the WIP 
regarding REUSE.



> + * All rights reserved.
> + * https://github.com/libarchive/libarchive/blob/307e76a48e8c9c4d53034a4603a5acccaf3318b3/tar/util.c
> + */
> +static const char *
> +strip_components(const char *p, int elements)
> +{
> +       /* Skip as many elements as necessary. */
> +       while (elements > 0) {
> +               switch (*(p++)) {
> +               case '/':
> +                       elements--;
> +                       break;
> +               case '\0':
> +                       /* Path is too short, skip it. */
> +                       return (NULL);
> +               }
> +       }
> +
> +       /* Skip any / characters.  This handles short paths that have
> +        * additional / termination.  This also handles the case where
> +        * the logic above stops in the middle of a duplicate //
> +        * sequence (which would otherwise get converted to an
> +        * absolute path). */
> +       for (;;) {
> +               switch (*p) {
> +               case '/':
> +                       p++;
> +                       break;
> +               case '\0':
> +                       return (NULL);
> +               default:
> +                       return (p);
> +               }
> +       }
> +}

I am also asking if it make sense to copy&paste this code from 
libarchive that it seems to have silght differences (in result) with 
some common functions. You could use splitargs() that returns an array 
of paths (using '/' as delimiter), and mstrcat() to build a new path.


> +
>   static void *
>   extract(void *p)
>   {
> @@ -77,9 +116,10 @@ extract(void *p)
>   	struct archive *ext = NULL;
>   	struct archive_entry *entry = NULL;
>   	int r;
> -	int flags;
> +	int flags, strip_cnr;
>   	struct extract_data *data = (struct extract_data *)p;
>   	flags = data->flags;
> +	strip_cnr = data->strip_components;
>   	int exitval = -EFAULT;
>   	char *FIFO = NULL;
>   
> @@ -146,6 +186,46 @@ extract(void *p)
>   			goto out;
>   		}
>   
> +		/* Handle the stripping of n component(s) from the paths */
> +		if ( strip_cnr > 0 ) {
> +			const char *ptr = archive_entry_pathname(entry);
> +			if (ptr) {
> +				if (debug)
> +					TRACE("strip_cnr[%d] pathname[%s]", strip_cnr, ptr);
> +
> +				ptr = strip_components(ptr, strip_cnr);
> +				if (ptr) {
> +					char buf[strlen(ptr)+1];
> +					strcpy(buf, ptr);
> +					if (debug)
> +						TRACE("|==> [%s]", buf);
> +					archive_entry_copy_pathname(entry, buf);
> +				} else {
> +					if (debug)
> +						TRACE("|==> Is now empty ... Skipping ...");
> +					continue; // for (;;)
> +				}
> +			}
> +			ptr = archive_entry_hardlink(entry);
> +			if (ptr) {
> +				if (debug)
> +					TRACE("strip_cnr[%d] hardlink[%s]", strip_cnr, ptr);
> +
> +				ptr = strip_components(ptr, strip_cnr);
> +				if (ptr) {
> +					char buf[strlen(ptr)+1];
> +					strcpy(buf, ptr);
> +					if (debug)
> +						TRACE("|==> [%s]", buf);
> +					archive_entry_copy_hardlink(entry, buf);
> +				} else {
> +					if (debug)
> +						TRACE("|==> Is now empty ... Skipping ...");
> +					continue; // for (;;)
> +				}
> +			}
> +		}
> +
>   		if (debug)
>   			TRACE("Extracting %s", archive_entry_pathname(entry));
>   
> @@ -288,6 +368,7 @@ static int install_archive_image(struct img_type *img,
>   		img->preserve_attributes ? "preserving" : "ignoring");
>   
>   	tf.flags = 0;
> +	tf.strip_components = img->strip_components;
>   	tf.exitval = -EFAULT;
>   
>   	if (img->preserve_attributes) {
> diff --git a/include/swupdate.h b/include/swupdate.h
> index 4cce892..9266d41 100644
> --- a/include/swupdate.h
> +++ b/include/swupdate.h
> @@ -81,6 +81,7 @@ struct img_type {
>   	int provided;
>   	int compressed;
>   	int preserve_attributes; /* whether to preserve attributes in archives */
> +	int strip_components; /* archive: Remove n leading components when extracting */
>   	bool is_encrypted;
>   	char ivt_ascii[33];
>   	int install_directly;
> diff --git a/parser/parser.c b/parser/parser.c
> index 85241f4..5b1693f 100644
> --- a/parser/parser.c
> +++ b/parser/parser.c
> @@ -429,6 +429,7 @@ static int parse_common_attributes(parsertype p, void *elem, struct img_type *im
>   	get_field(p, elem, "install-if-different", &image->id.install_if_different);
>   	get_field(p, elem, "install-if-higher", &image->id.install_if_higher);
>   	get_field(p, elem, "encrypted", &image->is_encrypted);
> +	get_field(p, elem, "strip-components", &image->strip_components);
>   	GET_FIELD_STRING(p, elem, "ivt", image->ivt_ascii);
>   
>   	if (is_image_installed(&cfg->installed_sw_list, image)) {
> 

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 86e95bd..6b7bb58 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -343,6 +343,8 @@  static void lua_number_to_img(struct img_type *img, const char *key,
 		img->checksum = (unsigned int)val;
 	if (!strcmp(key, "skip"))
 		img->skip = (unsigned int)val;
+	if (!strcmp(key, "strip_components"))
+		img->strip_components = (unsigned int)val;
 }
 
 #ifdef CONFIG_HANDLER_IN_LUA
@@ -501,6 +503,7 @@  static void update_table(lua_State* L, struct img_type *img)
 		LUA_PUSH_IMG_NUMBER(img, "size", size);
 		LUA_PUSH_IMG_NUMBER(img, "checksum", checksum);
 		LUA_PUSH_IMG_NUMBER(img, "skip", skip);
+		LUA_PUSH_IMG_NUMBER(img, "strip_components", strip_components);
 
 		switch (img->compressed) {
 			case COMPRESSED_ZLIB:
diff --git a/handlers/archive_handler.c b/handlers/archive_handler.c
index 9812d42..0c807bc 100644
--- a/handlers/archive_handler.c
+++ b/handlers/archive_handler.c
@@ -36,6 +36,7 @@  pthread_t extract_thread;
 
 struct extract_data {
 	int flags;
+	int strip_components;
 	int exitval;
 };
 
@@ -66,6 +67,44 @@  copy_data(struct archive *ar, struct archive *aw)
 	}
 }
 
+/* strip_components()
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ * https://github.com/libarchive/libarchive/blob/307e76a48e8c9c4d53034a4603a5acccaf3318b3/tar/util.c
+ */
+static const char *
+strip_components(const char *p, int elements)
+{
+       /* Skip as many elements as necessary. */
+       while (elements > 0) {
+               switch (*(p++)) {
+               case '/':
+                       elements--;
+                       break;
+               case '\0':
+                       /* Path is too short, skip it. */
+                       return (NULL);
+               }
+       }
+
+       /* Skip any / characters.  This handles short paths that have
+        * additional / termination.  This also handles the case where
+        * the logic above stops in the middle of a duplicate //
+        * sequence (which would otherwise get converted to an
+        * absolute path). */
+       for (;;) {
+               switch (*p) {
+               case '/':
+                       p++;
+                       break;
+               case '\0':
+                       return (NULL);
+               default:
+                       return (p);
+               }
+       }
+}
+
 static void *
 extract(void *p)
 {
@@ -77,9 +116,10 @@  extract(void *p)
 	struct archive *ext = NULL;
 	struct archive_entry *entry = NULL;
 	int r;
-	int flags;
+	int flags, strip_cnr;
 	struct extract_data *data = (struct extract_data *)p;
 	flags = data->flags;
+	strip_cnr = data->strip_components;
 	int exitval = -EFAULT;
 	char *FIFO = NULL;
 
@@ -146,6 +186,46 @@  extract(void *p)
 			goto out;
 		}
 
+		/* Handle the stripping of n component(s) from the paths */
+		if ( strip_cnr > 0 ) {
+			const char *ptr = archive_entry_pathname(entry);
+			if (ptr) {
+				if (debug)
+					TRACE("strip_cnr[%d] pathname[%s]", strip_cnr, ptr);
+
+				ptr = strip_components(ptr, strip_cnr);
+				if (ptr) {
+					char buf[strlen(ptr)+1];
+					strcpy(buf, ptr);
+					if (debug)
+						TRACE("|==> [%s]", buf);
+					archive_entry_copy_pathname(entry, buf);
+				} else {
+					if (debug)
+						TRACE("|==> Is now empty ... Skipping ...");
+					continue; // for (;;)
+				}
+			}
+			ptr = archive_entry_hardlink(entry);
+			if (ptr) {
+				if (debug)
+					TRACE("strip_cnr[%d] hardlink[%s]", strip_cnr, ptr);
+
+				ptr = strip_components(ptr, strip_cnr);
+				if (ptr) {
+					char buf[strlen(ptr)+1];
+					strcpy(buf, ptr);
+					if (debug)
+						TRACE("|==> [%s]", buf);
+					archive_entry_copy_hardlink(entry, buf);
+				} else {
+					if (debug)
+						TRACE("|==> Is now empty ... Skipping ...");
+					continue; // for (;;)
+				}
+			}
+		}
+
 		if (debug)
 			TRACE("Extracting %s", archive_entry_pathname(entry));
 
@@ -288,6 +368,7 @@  static int install_archive_image(struct img_type *img,
 		img->preserve_attributes ? "preserving" : "ignoring");
 
 	tf.flags = 0;
+	tf.strip_components = img->strip_components;
 	tf.exitval = -EFAULT;
 
 	if (img->preserve_attributes) {
diff --git a/include/swupdate.h b/include/swupdate.h
index 4cce892..9266d41 100644
--- a/include/swupdate.h
+++ b/include/swupdate.h
@@ -81,6 +81,7 @@  struct img_type {
 	int provided;
 	int compressed;
 	int preserve_attributes; /* whether to preserve attributes in archives */
+	int strip_components; /* archive: Remove n leading components when extracting */
 	bool is_encrypted;
 	char ivt_ascii[33];
 	int install_directly;
diff --git a/parser/parser.c b/parser/parser.c
index 85241f4..5b1693f 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -429,6 +429,7 @@  static int parse_common_attributes(parsertype p, void *elem, struct img_type *im
 	get_field(p, elem, "install-if-different", &image->id.install_if_different);
 	get_field(p, elem, "install-if-higher", &image->id.install_if_higher);
 	get_field(p, elem, "encrypted", &image->is_encrypted);
+	get_field(p, elem, "strip-components", &image->strip_components);
 	GET_FIELD_STRING(p, elem, "ivt", image->ivt_ascii);
 
 	if (is_image_installed(&cfg->installed_sw_list, image)) {