diff mbox series

[v5] Add Zstd compression support

Message ID 20191022211136.151818-1-tolga.hosgor@siemens.com
State Changes Requested
Headers show
Series [v5] Add Zstd compression support | expand

Commit Message

Hosgor, Tolga (IOT DS EU TR MTS) Oct. 22, 2019, 9:11 p.m. UTC
Zstd compression support can now be enabled with the CONFIG_ZSTD build
configuration.

This commit extends the 'compressed' field in the sw-description,
allowing to specify 'compressed = "zstd"' or 'compressed = "zlib". The
old method of specfying the boolean 'compressed = true' will default to
using the 'zlib' format.

Signed-off-by: Hosgor, Tolga (IOT DS EU TR MTS) <tolga.hosgor@siemens.com>
---
The difference of this from v4 is that it does not introduce the value
'compression = "none"' and a documentation correction about unknown
strings causing an error.

 Kconfig                       |   8 ++
 Makefile.deps                 |   4 +
 Makefile.flags                |   4 +
 core/cpio_utils.c             | 158 ++++++++++++++++++++++++++--------
 corelib/lua_interface.c       |  30 ++++++-
 doc/source/roadmap.rst        |   9 +-
 doc/source/sw-description.rst |  11 ++-
 doc/source/swupdate.rst       |   2 +-
 include/swupdate.h            |   7 ++
 parser/parse_external.c       |  19 +++-
 parser/parser.c               |  16 +++-
 11 files changed, 218 insertions(+), 50 deletions(-)

Comments

Stefan Herbrechtsmeier Oct. 24, 2019, 8:18 a.m. UTC | #1
Hi,

Am 22.10.19 um 23:11 schrieb Hosgor, Tolga (IOT DS EU TR MTS):
> Zstd compression support can now be enabled with the CONFIG_ZSTD build
> configuration.
> 
> This commit extends the 'compressed' field in the sw-description,
> allowing to specify 'compressed = "zstd"' or 'compressed = "zlib". The
> old method of specfying the boolean 'compressed = true' will default to
> using the 'zlib' format.
> 
> Signed-off-by: Hosgor, Tolga (IOT DS EU TR MTS) <tolga.hosgor@siemens.com>
> ---
> The difference of this from v4 is that it does not introduce the value
> 'compression = "none"' and a documentation correction about unknown
> strings causing an error.
> 
>   Kconfig                       |   8 ++
>   Makefile.deps                 |   4 +
>   Makefile.flags                |   4 +
>   core/cpio_utils.c             | 158 ++++++++++++++++++++++++++--------
>   corelib/lua_interface.c       |  30 ++++++-
>   doc/source/roadmap.rst        |   9 +-
>   doc/source/sw-description.rst |  11 ++-
>   doc/source/swupdate.rst       |   2 +-
>   include/swupdate.h            |   7 ++
>   parser/parse_external.c       |  19 +++-
>   parser/parser.c               |  16 +++-
>   11 files changed, 218 insertions(+), 50 deletions(-)
> 

[snip]

> diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
> index 5a66db9..0c865f2 100644
> --- a/corelib/lua_interface.c
> +++ b/corelib/lua_interface.c
> @@ -34,6 +34,12 @@ extern const char EMBEDDED_LUA_SRC_END[];
>   	lua_settable(L, -3);			\
>   } while (0)
>   
> +#define LUA_PUSH_IMG_STRING_VALUE(img, attr, value)  do { \
> +	lua_pushstring(L, attr);		\
> +	lua_pushstring(L, value);		\
> +	lua_settable(L, -3);			\
> +} while (0)
> +
>   #define LUA_PUSH_IMG_BOOL(img, attr, field)  do { \
>   	lua_pushstring(L, attr);		\
>   	lua_pushboolean(L, img->field);		\
> @@ -241,6 +247,16 @@ static void lua_string_to_img(struct img_type *img, const char *key,
>   	const char offset[] = "offset";
>   	char seek_str[MAX_SEEK_STRING_SIZE];
>   
> +	if (!strcmp(key, "compressed")) {
> +		if (!strcmp(value, "zlib")) {
> +			img->compressed = COMPRESSED_ZLIB;
> +		} else if (!strcmp(value, "zstd")) {
> +			img->compressed = COMPRESSED_ZSTD;
> +		} else {
> +			ERROR("compressed argument: '%s' invalid, defaulting to 'false'", value);
> +			img->compressed = false;
> +		}
> +	}

What happen if the value is true?

>   	if (!strcmp(key, "name")) {
>   		strncpy(img->id.name, value,
>   			sizeof(img->id.name));
> @@ -317,7 +333,6 @@ static void lua_number_to_img(struct img_type *img, const char *key,
>   		img->size = (long long)val;
>   	if (!strcmp(key, "checksum"))
>   		img->checksum = (unsigned int)val;
> -
>   }
>   
>   #ifdef CONFIG_HANDLER_IN_LUA
> @@ -461,7 +476,6 @@ static void update_table(lua_State* L, struct img_type *img)
>   		LUA_PUSH_IMG_STRING(img, "data", type_data);
>   		LUA_PUSH_IMG_STRING(img, "filesystem", filesystem);
>   
> -		LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
>   		LUA_PUSH_IMG_BOOL(img, "installed_directly", install_directly);
>   		LUA_PUSH_IMG_BOOL(img, "install_if_different", id.install_if_different);
>   		LUA_PUSH_IMG_BOOL(img, "install_if_higher", id.install_if_higher);
> @@ -473,6 +487,18 @@ 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);
>   
> +		switch (img->compressed) {
> +			case COMPRESSED_ZLIB:
> +				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zlib");
> +				break;
> +			case COMPRESSED_ZSTD:
> +				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zstd");
> +				break;
> +			default:
> +				LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
> +				break;
> +		}
> +

Does we really need a backward compatibility for the lua scripts?

Maybe we could use "nil" instead of a Boolean value to be backward 
compatible:
https://www.lua.org/pil/2.2.html

This means "compressed" could have on of the following values:
"zlib"
"zstd"
nil

A condition of "compressed" will return true for "zlib" and "zstd" and 
false for nil.

This means we could make the Boolean value of "compressed" obsolete and 
keep a backward compatibility by interpret the true value as "zlib".


>   		lua_pushstring(L, "properties");
>   		lua_newtable (L);
>   		LIST_FOREACH(property, &img->properties, next) {
> diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst
> index bf0a25e..bb9d546 100644
> --- a/doc/source/roadmap.rst
> +++ b/doc/source/roadmap.rst
> @@ -33,10 +33,11 @@ and not only UBI volumes, and add further features as restoring configuration da
>   Support for further compressors
>   ===============================
>   
> -SWUpdate supports image compressed with zlib. This is a compromise between compression rate
> -and speed to decompress the single artifact. To reduce bandwidth or for big images, a stronger
> -compressor could help. Adding a new compressor must be careful done because it changes the
> -core of handling an image.
> +SWUpdate supports image compressed with following formats: zlib, zstd. This is
> +a compromise between compression rate and speed to decompress the single artifact.
> +To reduce bandwidth or for big images, a stronger compressor could help.
> +Adding a new compressor must be careful done because it changes the core of
> +handling an image.
>   
>   System Update
>   =============
> diff --git a/doc/source/sw-description.rst b/doc/source/sw-description.rst
> index 70efa30..e1d1709 100644
> --- a/doc/source/sw-description.rst
> +++ b/doc/source/sw-description.rst
> @@ -1242,9 +1242,14 @@ There are 4 main sections inside sw-description:
>      |             |          | scripts    | regitsters itself.                    |
>      |             |          |            | Example: "ubivol", "raw", "rawfile",  |
>      +-------------+----------+------------+---------------------------------------+
> -   | compressed  | bool     | images     | flag to indicate that "filename" is   |
> -   |             |          | files      | zlib-compressed and must be           |
> -   |             |          |            | decompressed before being installed   |
> +   | compressed  | string   | images     | string to indicate the "filename" is  |
> +   |             | or bool  | files      | compressed and must be decompressed   |
> +   |             |          |            | before being installed. the value     |
> +   |             |          |            | denotes the compression type.         |
> +   |             |          |            | currently supported values are "zlib" |
> +   |             |          |            | and "zstd" . if the bool form is used |
> +   |             |          |            | , then the compression type is        |
> +   |             |          |            | assumed to be "zlib".                 |
>      +-------------+----------+------------+---------------------------------------+
>      | installed-\ | bool     | images     | flag to indicate that image is        |
>      | directly    |          |            | streamed into the target without any  |
> diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
> index 8cf911b..6c7a98e 100644
> --- a/doc/source/swupdate.rst
> +++ b/doc/source/swupdate.rst
> @@ -56,7 +56,7 @@ General Overview
>     is saved and restored after repartitioning with all data
>     it contains,  to maintain user's data.
>   
> -- support for compressed images, using the zlib library.
> +- support for compressed images, using the zlib and zstd library.
>     tarball (tgz file) are supported.
>   
>   - support for partitioned USB-pen or unpartitioned (mainly
> diff --git a/include/swupdate.h b/include/swupdate.h
> index b28504c..8cd1574 100644
> --- a/include/swupdate.h
> +++ b/include/swupdate.h
> @@ -41,6 +41,13 @@ enum {
>   	INSTALL_FROM_STREAM
>   };
>   
> +enum {
> +  COMPRESSED_FALSE,
> +  COMPRESSED_TRUE,
> +  COMPRESSED_ZLIB,
> +  COMPRESSED_ZSTD,
> +};
> +

Do we really need the COMPRESSED_TRUE value?

>   struct sw_version {
>   	char name[SWUPDATE_GENERAL_STRING_SIZE];
>   	char version[SWUPDATE_GENERAL_STRING_SIZE];

[snip]

Regards
   Stefan
Tolga HOŞGÖR Oct. 24, 2019, 1:51 p.m. UTC | #2
Hello,

On Thu, 2019-10-24 at 10:18 +0200, Stefan Herbrechtsmeier wrote:
> Hi,
> 
> Am 22.10.19 um 23:11 schrieb Hosgor, Tolga (IOT DS EU TR MTS):
> > Zstd compression support can now be enabled with the CONFIG_ZSTD
> > build
> > configuration.
> > 
> > This commit extends the 'compressed' field in the sw-description,
> > allowing to specify 'compressed = "zstd"' or 'compressed = "zlib".
> > The
> > old method of specfying the boolean 'compressed = true' will
> > default to
> > using the 'zlib' format.
> > 
> > Signed-off-by: Hosgor, Tolga (IOT DS EU TR MTS) <
> > tolga.hosgor@siemens.com>
> > ---
> > The difference of this from v4 is that it does not introduce the
> > value
> > 'compression = "none"' and a documentation correction about unknown
> > strings causing an error.
> > 
> >   Kconfig                       |   8 ++
> >   Makefile.deps                 |   4 +
> >   Makefile.flags                |   4 +
> >   core/cpio_utils.c             | 158 ++++++++++++++++++++++++++---
> > -----
> >   corelib/lua_interface.c       |  30 ++++++-
> >   doc/source/roadmap.rst        |   9 +-
> >   doc/source/sw-description.rst |  11 ++-
> >   doc/source/swupdate.rst       |   2 +-
> >   include/swupdate.h            |   7 ++
> >   parser/parse_external.c       |  19 +++-
> >   parser/parser.c               |  16 +++-
> >   11 files changed, 218 insertions(+), 50 deletions(-)
> > 
> 
> [snip]
> 
> > diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
> > index 5a66db9..0c865f2 100644
> > --- a/corelib/lua_interface.c
> > +++ b/corelib/lua_interface.c
> > @@ -34,6 +34,12 @@ extern const char EMBEDDED_LUA_SRC_END[];
> >   	lua_settable(L, -3);			\
> >   } while (0)
> >   
> > +#define LUA_PUSH_IMG_STRING_VALUE(img, attr, value)  do { \
> > +	lua_pushstring(L, attr);		\
> > +	lua_pushstring(L, value);		\
> > +	lua_settable(L, -3);			\
> > +} while (0)
> > +
> >   #define LUA_PUSH_IMG_BOOL(img, attr, field)  do { \
> >   	lua_pushstring(L, attr);		\
> >   	lua_pushboolean(L, img->field);		\
> > @@ -241,6 +247,16 @@ static void lua_string_to_img(struct img_type
> > *img, const char *key,
> >   	const char offset[] = "offset";
> >   	char seek_str[MAX_SEEK_STRING_SIZE];
> >   
> > +	if (!strcmp(key, "compressed")) {
> > +		if (!strcmp(value, "zlib")) {
> > +			img->compressed = COMPRESSED_ZLIB;
> > +		} else if (!strcmp(value, "zstd")) {
> > +			img->compressed = COMPRESSED_ZSTD;
> > +		} else {
> > +			ERROR("compressed argument: '%s' invalid,
> > defaulting to 'false'", value);
> > +			img->compressed = false;
> > +		}
> > +	}
> 
> What happen if the value is true?
Boolean cases for the lua is still handled in `lua_bool_to_img`. I kept
it as it is.
> 
> >   	if (!strcmp(key, "name")) {
> >   		strncpy(img->id.name, value,
> >   			sizeof(img->id.name));
> > @@ -317,7 +333,6 @@ static void lua_number_to_img(struct img_type
> > *img, const char *key,
> >   		img->size = (long long)val;
> >   	if (!strcmp(key, "checksum"))
> >   		img->checksum = (unsigned int)val;
> > -
> >   }
> >   
> >   #ifdef CONFIG_HANDLER_IN_LUA
> > @@ -461,7 +476,6 @@ static void update_table(lua_State* L, struct
> > img_type *img)
> >   		LUA_PUSH_IMG_STRING(img, "data", type_data);
> >   		LUA_PUSH_IMG_STRING(img, "filesystem", filesystem);
> >   
> > -		LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
> >   		LUA_PUSH_IMG_BOOL(img, "installed_directly",
> > install_directly);
> >   		LUA_PUSH_IMG_BOOL(img, "install_if_different",
> > id.install_if_different);
> >   		LUA_PUSH_IMG_BOOL(img, "install_if_higher",
> > id.install_if_higher);
> > @@ -473,6 +487,18 @@ 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);
> >   
> > +		switch (img->compressed) {
> > +			case COMPRESSED_ZLIB:
> > +				LUA_PUSH_IMG_STRING_VALUE(img,
> > "compressed", "zlib");
> > +				break;
> > +			case COMPRESSED_ZSTD:
> > +				LUA_PUSH_IMG_STRING_VALUE(img,
> > "compressed", "zstd");
> > +				break;
> > +			default:
> > +				LUA_PUSH_IMG_BOOL(img, "compressed",
> > compressed);
> > +				break;
> > +		}
> > +
> 
> Does we really need a backward compatibility for the lua scripts?
I don't know. Could lose the boolean form altogether if that is
acceptable. It would have a maintenance cost to users to update.

> 
> Maybe we could use "nil" instead of a Boolean value to be backward 
> compatible:
> https://www.lua.org/pil/2.2.html
> 
> This means "compressed" could have on of the following values:
> "zlib"
> "zstd"
> nil
> 
> A condition of "compressed" will return true for "zlib" and "zstd"
> and 
> false for nil.
I was going for that at first. That would work for `if compressed` but
one could have written `if compressed == true`. Same goes for `false`.
If we drop the boolean form, then `if compressed == true` will be
invalid anyway.

> 
> This means we could make the Boolean value of "compressed" obsolete
> and 
> keep a backward compatibility by interpret the true value as "zlib".
It may create confusion. The variable would read as a string in Lua
scripts although it was a boolean. I would rather get rid of the
boolean form altogether.
> 
> 
> >   		lua_pushstring(L, "properties");
> >   		lua_newtable (L);
> >   		LIST_FOREACH(property, &img->properties, next) {
> > diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst
> > index bf0a25e..bb9d546 100644
> > --- a/doc/source/roadmap.rst
> > +++ b/doc/source/roadmap.rst
> > @@ -33,10 +33,11 @@ and not only UBI volumes, and add further
> > features as restoring configuration da
> >   Support for further compressors
> >   ===============================
> >   
> > -SWUpdate supports image compressed with zlib. This is a compromise
> > between compression rate
> > -and speed to decompress the single artifact. To reduce bandwidth
> > or for big images, a stronger
> > -compressor could help. Adding a new compressor must be careful
> > done because it changes the
> > -core of handling an image.
> > +SWUpdate supports image compressed with following formats: zlib,
> > zstd. This is
> > +a compromise between compression rate and speed to decompress the
> > single artifact.
> > +To reduce bandwidth or for big images, a stronger compressor could
> > help.
> > +Adding a new compressor must be careful done because it changes
> > the core of
> > +handling an image.
> >   
> >   System Update
> >   =============
> > diff --git a/doc/source/sw-description.rst b/doc/source/sw-
> > description.rst
> > index 70efa30..e1d1709 100644
> > --- a/doc/source/sw-description.rst
> > +++ b/doc/source/sw-description.rst
> > @@ -1242,9 +1242,14 @@ There are 4 main sections inside sw-
> > description:
> >      |             |          | scripts    | regitsters
> > itself.                    |
> >      |             |          |            | Example: "ubivol",
> > "raw", "rawfile",  |
> >      +-------------+----------+------------+-----------------------
> > ----------------+
> > -   | compressed  | bool     | images     | flag to indicate that
> > "filename" is   |
> > -   |             |          | files      | zlib-compressed and
> > must be           |
> > -   |             |          |            | decompressed before
> > being installed   |
> > +   | compressed  | string   | images     | string to indicate the
> > "filename" is  |
> > +   |             | or bool  | files      | compressed and must be
> > decompressed   |
> > +   |             |          |            | before being installed.
> > the value     |
> > +   |             |          |            | denotes the compression
> > type.         |
> > +   |             |          |            | currently supported
> > values are "zlib" |
> > +   |             |          |            | and "zstd" . if the
> > bool form is used |
> > +   |             |          |            | , then the compression
> > type is        |
> > +   |             |          |            | assumed to be
> > "zlib".                 |
> >      +-------------+----------+------------+-----------------------
> > ----------------+
> >      | installed-\ | bool     | images     | flag to indicate that
> > image is        |
> >      | directly    |          |            | streamed into the
> > target without any  |
> > diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
> > index 8cf911b..6c7a98e 100644
> > --- a/doc/source/swupdate.rst
> > +++ b/doc/source/swupdate.rst
> > @@ -56,7 +56,7 @@ General Overview
> >     is saved and restored after repartitioning with all data
> >     it contains,  to maintain user's data.
> >   
> > -- support for compressed images, using the zlib library.
> > +- support for compressed images, using the zlib and zstd library.
> >     tarball (tgz file) are supported.
> >   
> >   - support for partitioned USB-pen or unpartitioned (mainly
> > diff --git a/include/swupdate.h b/include/swupdate.h
> > index b28504c..8cd1574 100644
> > --- a/include/swupdate.h
> > +++ b/include/swupdate.h
> > @@ -41,6 +41,13 @@ enum {
> >   	INSTALL_FROM_STREAM
> >   };
> >   
> > +enum {
> > +  COMPRESSED_FALSE,
> > +  COMPRESSED_TRUE,
> > +  COMPRESSED_ZLIB,
> > +  COMPRESSED_ZSTD,
> > +};
> > +
> 
> Do we really need the COMPRESSED_TRUE value?
It is used to distinguish between 'compressed = true' and 'compressed =
"zlib"' in `update_table` function so that `if compressed == true` does
not break due to the 'compressed = true' variable getting converted to
"zstd".

> 
> >   struct sw_version {
> >   	char name[SWUPDATE_GENERAL_STRING_SIZE];
> >   	char version[SWUPDATE_GENERAL_STRING_SIZE];
> 
> [snip]
> 
> Regards
>    Stefan
> 

Best Regards,
Tolga.
Stefan Herbrechtsmeier Oct. 24, 2019, 2:12 p.m. UTC | #3
Hi,

Am 24.10.19 um 15:51 schrieb Tolga HOŞGÖR:
> Hello,
> 
> On Thu, 2019-10-24 at 10:18 +0200, Stefan Herbrechtsmeier wrote:
>> Hi,
>>
>> Am 22.10.19 um 23:11 schrieb Hosgor, Tolga (IOT DS EU TR MTS):
>>> Zstd compression support can now be enabled with the CONFIG_ZSTD
>>> build
>>> configuration.
>>>
>>> This commit extends the 'compressed' field in the sw-description,
>>> allowing to specify 'compressed = "zstd"' or 'compressed = "zlib".
>>> The
>>> old method of specfying the boolean 'compressed = true' will
>>> default to
>>> using the 'zlib' format.
>>>
>>> Signed-off-by: Hosgor, Tolga (IOT DS EU TR MTS) <
>>> tolga.hosgor@siemens.com>
>>> ---
>>> The difference of this from v4 is that it does not introduce the
>>> value
>>> 'compression = "none"' and a documentation correction about unknown
>>> strings causing an error.
>>>
>>>    Kconfig                       |   8 ++
>>>    Makefile.deps                 |   4 +
>>>    Makefile.flags                |   4 +
>>>    core/cpio_utils.c             | 158 ++++++++++++++++++++++++++---
>>> -----
>>>    corelib/lua_interface.c       |  30 ++++++-
>>>    doc/source/roadmap.rst        |   9 +-
>>>    doc/source/sw-description.rst |  11 ++-
>>>    doc/source/swupdate.rst       |   2 +-
>>>    include/swupdate.h            |   7 ++
>>>    parser/parse_external.c       |  19 +++-
>>>    parser/parser.c               |  16 +++-
>>>    11 files changed, 218 insertions(+), 50 deletions(-)
>>>
>>
>> [snip]
>>
>>> diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
>>> index 5a66db9..0c865f2 100644
>>> --- a/corelib/lua_interface.c
>>> +++ b/corelib/lua_interface.c
>>> @@ -34,6 +34,12 @@ extern const char EMBEDDED_LUA_SRC_END[];
>>>    	lua_settable(L, -3);			\
>>>    } while (0)
>>>    
>>> +#define LUA_PUSH_IMG_STRING_VALUE(img, attr, value)  do { \
>>> +	lua_pushstring(L, attr);		\
>>> +	lua_pushstring(L, value);		\
>>> +	lua_settable(L, -3);			\
>>> +} while (0)
>>> +
>>>    #define LUA_PUSH_IMG_BOOL(img, attr, field)  do { \
>>>    	lua_pushstring(L, attr);		\
>>>    	lua_pushboolean(L, img->field);		\
>>> @@ -241,6 +247,16 @@ static void lua_string_to_img(struct img_type
>>> *img, const char *key,
>>>    	const char offset[] = "offset";
>>>    	char seek_str[MAX_SEEK_STRING_SIZE];
>>>    
>>> +	if (!strcmp(key, "compressed")) {
>>> +		if (!strcmp(value, "zlib")) {
>>> +			img->compressed = COMPRESSED_ZLIB;
>>> +		} else if (!strcmp(value, "zstd")) {
>>> +			img->compressed = COMPRESSED_ZSTD;
>>> +		} else {
>>> +			ERROR("compressed argument: '%s' invalid,
>>> defaulting to 'false'", value);
>>> +			img->compressed = false;
>>> +		}
>>> +	}
>>
>> What happen if the value is true?
> Boolean cases for the lua is still handled in `lua_bool_to_img`. I kept
> it as it is.
>>
>>>    	if (!strcmp(key, "name")) {
>>>    		strncpy(img->id.name, value,
>>>    			sizeof(img->id.name));
>>> @@ -317,7 +333,6 @@ static void lua_number_to_img(struct img_type
>>> *img, const char *key,
>>>    		img->size = (long long)val;
>>>    	if (!strcmp(key, "checksum"))
>>>    		img->checksum = (unsigned int)val;
>>> -
>>>    }
>>>    
>>>    #ifdef CONFIG_HANDLER_IN_LUA
>>> @@ -461,7 +476,6 @@ static void update_table(lua_State* L, struct
>>> img_type *img)
>>>    		LUA_PUSH_IMG_STRING(img, "data", type_data);
>>>    		LUA_PUSH_IMG_STRING(img, "filesystem", filesystem);
>>>    
>>> -		LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
>>>    		LUA_PUSH_IMG_BOOL(img, "installed_directly",
>>> install_directly);
>>>    		LUA_PUSH_IMG_BOOL(img, "install_if_different",
>>> id.install_if_different);
>>>    		LUA_PUSH_IMG_BOOL(img, "install_if_higher",
>>> id.install_if_higher);
>>> @@ -473,6 +487,18 @@ 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);
>>>    
>>> +		switch (img->compressed) {
>>> +			case COMPRESSED_ZLIB:
>>> +				LUA_PUSH_IMG_STRING_VALUE(img,
>>> "compressed", "zlib");
>>> +				break;
>>> +			case COMPRESSED_ZSTD:
>>> +				LUA_PUSH_IMG_STRING_VALUE(img,
>>> "compressed", "zstd");
>>> +				break;
>>> +			default:
>>> +				LUA_PUSH_IMG_BOOL(img, "compressed",
>>> compressed);
>>> +				break;
>>> +		}
>>> +
>>
>> Does we really need a backward compatibility for the lua scripts?
> I don't know. Could lose the boolean form altogether if that is
> acceptable. It would have a maintenance cost to users to update.
> 
>>
>> Maybe we could use "nil" instead of a Boolean value to be backward
>> compatible:
>> https://www.lua.org/pil/2.2.html
>>
>> This means "compressed" could have on of the following values:
>> "zlib"
>> "zstd"
>> nil
>>
>> A condition of "compressed" will return true for "zlib" and "zstd"
>> and
>> false for nil.
> I was going for that at first. That would work for `if compressed` but
> one could have written `if compressed == true`. Same goes for `false`.
> If we drop the boolean form, then `if compressed == true` will be
> invalid anyway.

This is bad.

> 
>>
>> This means we could make the Boolean value of "compressed" obsolete
>> and
>> keep a backward compatibility by interpret the true value as "zlib".
> It may create confusion. The variable would read as a string in Lua
> scripts although it was a boolean. I would rather get rid of the
> boolean form altogether.
>>
>>
>>>    		lua_pushstring(L, "properties");
>>>    		lua_newtable (L);
>>>    		LIST_FOREACH(property, &img->properties, next) {
>>> diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst
>>> index bf0a25e..bb9d546 100644
>>> --- a/doc/source/roadmap.rst
>>> +++ b/doc/source/roadmap.rst
>>> @@ -33,10 +33,11 @@ and not only UBI volumes, and add further
>>> features as restoring configuration da
>>>    Support for further compressors
>>>    ===============================
>>>    
>>> -SWUpdate supports image compressed with zlib. This is a compromise
>>> between compression rate
>>> -and speed to decompress the single artifact. To reduce bandwidth
>>> or for big images, a stronger
>>> -compressor could help. Adding a new compressor must be careful
>>> done because it changes the
>>> -core of handling an image.
>>> +SWUpdate supports image compressed with following formats: zlib,
>>> zstd. This is
>>> +a compromise between compression rate and speed to decompress the
>>> single artifact.
>>> +To reduce bandwidth or for big images, a stronger compressor could
>>> help.
>>> +Adding a new compressor must be careful done because it changes
>>> the core of
>>> +handling an image.
>>>    
>>>    System Update
>>>    =============
>>> diff --git a/doc/source/sw-description.rst b/doc/source/sw-
>>> description.rst
>>> index 70efa30..e1d1709 100644
>>> --- a/doc/source/sw-description.rst
>>> +++ b/doc/source/sw-description.rst
>>> @@ -1242,9 +1242,14 @@ There are 4 main sections inside sw-
>>> description:
>>>       |             |          | scripts    | regitsters
>>> itself.                    |
>>>       |             |          |            | Example: "ubivol",
>>> "raw", "rawfile",  |
>>>       +-------------+----------+------------+-----------------------
>>> ----------------+
>>> -   | compressed  | bool     | images     | flag to indicate that
>>> "filename" is   |
>>> -   |             |          | files      | zlib-compressed and
>>> must be           |
>>> -   |             |          |            | decompressed before
>>> being installed   |
>>> +   | compressed  | string   | images     | string to indicate the
>>> "filename" is  |
>>> +   |             | or bool  | files      | compressed and must be
>>> decompressed   |
>>> +   |             |          |            | before being installed.
>>> the value     |
>>> +   |             |          |            | denotes the compression
>>> type.         |
>>> +   |             |          |            | currently supported
>>> values are "zlib" |
>>> +   |             |          |            | and "zstd" . if the
>>> bool form is used |
>>> +   |             |          |            | , then the compression
>>> type is        |
>>> +   |             |          |            | assumed to be
>>> "zlib".                 |
>>>       +-------------+----------+------------+-----------------------
>>> ----------------+
>>>       | installed-\ | bool     | images     | flag to indicate that
>>> image is        |
>>>       | directly    |          |            | streamed into the
>>> target without any  |
>>> diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
>>> index 8cf911b..6c7a98e 100644
>>> --- a/doc/source/swupdate.rst
>>> +++ b/doc/source/swupdate.rst
>>> @@ -56,7 +56,7 @@ General Overview
>>>      is saved and restored after repartitioning with all data
>>>      it contains,  to maintain user's data.
>>>    
>>> -- support for compressed images, using the zlib library.
>>> +- support for compressed images, using the zlib and zstd library.
>>>      tarball (tgz file) are supported.
>>>    
>>>    - support for partitioned USB-pen or unpartitioned (mainly
>>> diff --git a/include/swupdate.h b/include/swupdate.h
>>> index b28504c..8cd1574 100644
>>> --- a/include/swupdate.h
>>> +++ b/include/swupdate.h
>>> @@ -41,6 +41,13 @@ enum {
>>>    	INSTALL_FROM_STREAM
>>>    };
>>>    
>>> +enum {
>>> +  COMPRESSED_FALSE,
>>> +  COMPRESSED_TRUE,
>>> +  COMPRESSED_ZLIB,
>>> +  COMPRESSED_ZSTD,
>>> +};
>>> +
>>
>> Do we really need the COMPRESSED_TRUE value?
> It is used to distinguish between 'compressed = true' and 'compressed =
> "zlib"' in `update_table` function so that `if compressed == true` does
> not break due to the 'compressed = true' variable getting converted to
> "zstd".

You are right. I thought we could remove the Boolean type and keep 
backward compatibility but it isn't possible. I don't like a property 
with two possible types. Maybe we should mark the Boolean deprecated and 
print a warning. Alternative we could add compression and deprecated 
compressed.

Regards
   Stefan
Tolga HOŞGÖR Oct. 24, 2019, 3:19 p.m. UTC | #4
On Thu, 2019-10-24 at 16:12 +0200, Stefan Herbrechtsmeier wrote:
> Hi,
> 
> Am 24.10.19 um 15:51 schrieb Tolga HOŞGÖR:
> > Hello,
> > 
> > On Thu, 2019-10-24 at 10:18 +0200, Stefan Herbrechtsmeier wrote:
> > > Hi,
> > > 
> > > Am 22.10.19 um 23:11 schrieb Hosgor, Tolga (IOT DS EU TR MTS):
> > > > Zstd compression support can now be enabled with the
> > > > CONFIG_ZSTD
> > > > build
> > > > configuration.
> > > > 
> > > > This commit extends the 'compressed' field in the sw-
> > > > description,
> > > > allowing to specify 'compressed = "zstd"' or 'compressed =
> > > > "zlib".
> > > > The
> > > > old method of specfying the boolean 'compressed = true' will
> > > > default to
> > > > using the 'zlib' format.
> > > > 
> > > > Signed-off-by: Hosgor, Tolga (IOT DS EU TR MTS) <
> > > > tolga.hosgor@siemens.com>
> > > > ---
> > > > The difference of this from v4 is that it does not introduce
> > > > the
> > > > value
> > > > 'compression = "none"' and a documentation correction about
> > > > unknown
> > > > strings causing an error.
> > > > 
> > > >    Kconfig                       |   8 ++
> > > >    Makefile.deps                 |   4 +
> > > >    Makefile.flags                |   4 +
> > > >    core/cpio_utils.c             | 158
> > > > ++++++++++++++++++++++++++---
> > > > -----
> > > >    corelib/lua_interface.c       |  30 ++++++-
> > > >    doc/source/roadmap.rst        |   9 +-
> > > >    doc/source/sw-description.rst |  11 ++-
> > > >    doc/source/swupdate.rst       |   2 +-
> > > >    include/swupdate.h            |   7 ++
> > > >    parser/parse_external.c       |  19 +++-
> > > >    parser/parser.c               |  16 +++-
> > > >    11 files changed, 218 insertions(+), 50 deletions(-)
> > > > 
> > > 
> > > [snip]
> > > 
> > > > diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
> > > > index 5a66db9..0c865f2 100644
> > > > --- a/corelib/lua_interface.c
> > > > +++ b/corelib/lua_interface.c
> > > > @@ -34,6 +34,12 @@ extern const char EMBEDDED_LUA_SRC_END[];
> > > >    	lua_settable(L, -3);			\
> > > >    } while (0)
> > > >    
> > > > +#define LUA_PUSH_IMG_STRING_VALUE(img, attr, value)  do { \
> > > > +	lua_pushstring(L, attr);		\
> > > > +	lua_pushstring(L, value);		\
> > > > +	lua_settable(L, -3);			\
> > > > +} while (0)
> > > > +
> > > >    #define LUA_PUSH_IMG_BOOL(img, attr, field)  do { \
> > > >    	lua_pushstring(L, attr);		\
> > > >    	lua_pushboolean(L, img->field);		\
> > > > @@ -241,6 +247,16 @@ static void lua_string_to_img(struct
> > > > img_type
> > > > *img, const char *key,
> > > >    	const char offset[] = "offset";
> > > >    	char seek_str[MAX_SEEK_STRING_SIZE];
> > > >    
> > > > +	if (!strcmp(key, "compressed")) {
> > > > +		if (!strcmp(value, "zlib")) {
> > > > +			img->compressed = COMPRESSED_ZLIB;
> > > > +		} else if (!strcmp(value, "zstd")) {
> > > > +			img->compressed = COMPRESSED_ZSTD;
> > > > +		} else {
> > > > +			ERROR("compressed argument: '%s'
> > > > invalid,
> > > > defaulting to 'false'", value);
> > > > +			img->compressed = false;
> > > > +		}
> > > > +	}
> > > 
> > > What happen if the value is true?
> > Boolean cases for the lua is still handled in `lua_bool_to_img`. I
> > kept
> > it as it is.
> > > >    	if (!strcmp(key, "name")) {
> > > >    		strncpy(img->id.name, value,
> > > >    			sizeof(img->id.name));
> > > > @@ -317,7 +333,6 @@ static void lua_number_to_img(struct
> > > > img_type
> > > > *img, const char *key,
> > > >    		img->size = (long long)val;
> > > >    	if (!strcmp(key, "checksum"))
> > > >    		img->checksum = (unsigned int)val;
> > > > -
> > > >    }
> > > >    
> > > >    #ifdef CONFIG_HANDLER_IN_LUA
> > > > @@ -461,7 +476,6 @@ static void update_table(lua_State* L,
> > > > struct
> > > > img_type *img)
> > > >    		LUA_PUSH_IMG_STRING(img, "data", type_data);
> > > >    		LUA_PUSH_IMG_STRING(img, "filesystem",
> > > > filesystem);
> > > >    
> > > > -		LUA_PUSH_IMG_BOOL(img, "compressed",
> > > > compressed);
> > > >    		LUA_PUSH_IMG_BOOL(img, "installed_directly",
> > > > install_directly);
> > > >    		LUA_PUSH_IMG_BOOL(img, "install_if_different",
> > > > id.install_if_different);
> > > >    		LUA_PUSH_IMG_BOOL(img, "install_if_higher",
> > > > id.install_if_higher);
> > > > @@ -473,6 +487,18 @@ 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);
> > > >    
> > > > +		switch (img->compressed) {
> > > > +			case COMPRESSED_ZLIB:
> > > > +				LUA_PUSH_IMG_STRING_VALUE(img,
> > > > "compressed", "zlib");
> > > > +				break;
> > > > +			case COMPRESSED_ZSTD:
> > > > +				LUA_PUSH_IMG_STRING_VALUE(img,
> > > > "compressed", "zstd");
> > > > +				break;
> > > > +			default:
> > > > +				LUA_PUSH_IMG_BOOL(img,
> > > > "compressed",
> > > > compressed);
> > > > +				break;
> > > > +		}
> > > > +
> > > 
> > > Does we really need a backward compatibility for the lua scripts?
> > I don't know. Could lose the boolean form altogether if that is
> > acceptable. It would have a maintenance cost to users to update.
> > 
> > > Maybe we could use "nil" instead of a Boolean value to be
> > > backward
> > > compatible:
> > > https://www.lua.org/pil/2.2.html
> > > 
> > > This means "compressed" could have on of the following values:
> > > "zlib"
> > > "zstd"
> > > nil
> > > 
> > > A condition of "compressed" will return true for "zlib" and
> > > "zstd"
> > > and
> > > false for nil.
> > I was going for that at first. That would work for `if compressed`
> > but
> > one could have written `if compressed == true`. Same goes for
> > `false`.
> > If we drop the boolean form, then `if compressed == true` will be
> > invalid anyway.
> 
> This is bad.
> 
> > > This means we could make the Boolean value of "compressed"
> > > obsolete
> > > and
> > > keep a backward compatibility by interpret the true value as
> > > "zlib".
> > It may create confusion. The variable would read as a string in Lua
> > scripts although it was a boolean. I would rather get rid of the
> > boolean form altogether.
> > > 
> > > >    		lua_pushstring(L, "properties");
> > > >    		lua_newtable (L);
> > > >    		LIST_FOREACH(property, &img->properties, next)
> > > > {
> > > > diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst
> > > > index bf0a25e..bb9d546 100644
> > > > --- a/doc/source/roadmap.rst
> > > > +++ b/doc/source/roadmap.rst
> > > > @@ -33,10 +33,11 @@ and not only UBI volumes, and add further
> > > > features as restoring configuration da
> > > >    Support for further compressors
> > > >    ===============================
> > > >    
> > > > -SWUpdate supports image compressed with zlib. This is a
> > > > compromise
> > > > between compression rate
> > > > -and speed to decompress the single artifact. To reduce
> > > > bandwidth
> > > > or for big images, a stronger
> > > > -compressor could help. Adding a new compressor must be careful
> > > > done because it changes the
> > > > -core of handling an image.
> > > > +SWUpdate supports image compressed with following formats:
> > > > zlib,
> > > > zstd. This is
> > > > +a compromise between compression rate and speed to decompress
> > > > the
> > > > single artifact.
> > > > +To reduce bandwidth or for big images, a stronger compressor
> > > > could
> > > > help.
> > > > +Adding a new compressor must be careful done because it
> > > > changes
> > > > the core of
> > > > +handling an image.
> > > >    
> > > >    System Update
> > > >    =============
> > > > diff --git a/doc/source/sw-description.rst b/doc/source/sw-
> > > > description.rst
> > > > index 70efa30..e1d1709 100644
> > > > --- a/doc/source/sw-description.rst
> > > > +++ b/doc/source/sw-description.rst
> > > > @@ -1242,9 +1242,14 @@ There are 4 main sections inside sw-
> > > > description:
> > > >       |             |          | scripts    | regitsters
> > > > itself.                    |
> > > >       |             |          |            | Example:
> > > > "ubivol",
> > > > "raw", "rawfile",  |
> > > >       +-------------+----------+------------+----------------
> > > > -------
> > > > ----------------+
> > > > -   | compressed  | bool     | images     | flag to indicate
> > > > that
> > > > "filename" is   |
> > > > -   |             |          | files      | zlib-compressed and
> > > > must be           |
> > > > -   |             |          |            | decompressed before
> > > > being installed   |
> > > > +   | compressed  | string   | images     | string to indicate
> > > > the
> > > > "filename" is  |
> > > > +   |             | or bool  | files      | compressed and must
> > > > be
> > > > decompressed   |
> > > > +   |             |          |            | before being
> > > > installed.
> > > > the value     |
> > > > +   |             |          |            | denotes the
> > > > compression
> > > > type.         |
> > > > +   |             |          |            | currently supported
> > > > values are "zlib" |
> > > > +   |             |          |            | and "zstd" . if the
> > > > bool form is used |
> > > > +   |             |          |            | , then the
> > > > compression
> > > > type is        |
> > > > +   |             |          |            | assumed to be
> > > > "zlib".                 |
> > > >       +-------------+----------+------------+----------------
> > > > -------
> > > > ----------------+
> > > >       | installed-\ | bool     | images     | flag to indicate
> > > > that
> > > > image is        |
> > > >       | directly    |          |            | streamed into the
> > > > target without any  |
> > > > diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
> > > > index 8cf911b..6c7a98e 100644
> > > > --- a/doc/source/swupdate.rst
> > > > +++ b/doc/source/swupdate.rst
> > > > @@ -56,7 +56,7 @@ General Overview
> > > >      is saved and restored after repartitioning with all data
> > > >      it contains,  to maintain user's data.
> > > >    
> > > > -- support for compressed images, using the zlib library.
> > > > +- support for compressed images, using the zlib and zstd
> > > > library.
> > > >      tarball (tgz file) are supported.
> > > >    
> > > >    - support for partitioned USB-pen or unpartitioned (mainly
> > > > diff --git a/include/swupdate.h b/include/swupdate.h
> > > > index b28504c..8cd1574 100644
> > > > --- a/include/swupdate.h
> > > > +++ b/include/swupdate.h
> > > > @@ -41,6 +41,13 @@ enum {
> > > >    	INSTALL_FROM_STREAM
> > > >    };
> > > >    
> > > > +enum {
> > > > +  COMPRESSED_FALSE,
> > > > +  COMPRESSED_TRUE,
> > > > +  COMPRESSED_ZLIB,
> > > > +  COMPRESSED_ZSTD,
> > > > +};
> > > > +
> > > 
> > > Do we really need the COMPRESSED_TRUE value?
> > It is used to distinguish between 'compressed = true' and
> > 'compressed =
> > "zlib"' in `update_table` function so that `if compressed == true`
> > does
> > not break due to the 'compressed = true' variable getting converted
> > to
> > "zstd".
> 
> You are right. I thought we could remove the Boolean type and keep 
> backward compatibility but it isn't possible. I don't like a
> property 
> with two possible types. Maybe we should mark the Boolean deprecated
> and 
> print a warning. Alternative we could add compression and deprecated 
> compressed.

I have added "deprecated" statements as v6 and v7. I don't know about
introducing "compression". It's a valid idea.

Positive impacts:
* cleaner code maybe
* cleaner docu

Negative impacts:
* not really I guess

> 
> Regards
>    Stefan
diff mbox series

Patch

diff --git a/Kconfig b/Kconfig
index 70907c9..6f1e7ad 100644
--- a/Kconfig
+++ b/Kconfig
@@ -61,6 +61,10 @@  config HAVE_ZLIB
 	bool
 	option env="HAVE_ZLIB"
 
+config HAVE_ZSTD
+	bool
+	option env="HAVE_ZSTD"
+
 config HAVE_LIBSSL
 	bool
 	option env="HAVE_LIBSSL"
@@ -434,5 +438,9 @@  config GUNZIP
 	depends on HAVE_ZLIB
 	default y
 
+config ZSTD
+	bool "Zstd compression support"
+	depends on HAVE_ZSTD
+
 source parser/Config.in
 source handlers/Config.in
diff --git a/Makefile.deps b/Makefile.deps
index 512d4b8..5e2bd2f 100644
--- a/Makefile.deps
+++ b/Makefile.deps
@@ -38,6 +38,10 @@  ifeq ($(HAVE_ZLIB),)
 export HAVE_ZLIB = y
 endif
 
+ifeq ($(HAVE_ZSTD),)
+export HAVE_ZSTD = y
+endif
+
 ifeq ($(HAVE_LIBUBOOTENV),)
 export HAVE_LIBUBOOTENV = y
 endif
diff --git a/Makefile.flags b/Makefile.flags
index b880d32..614d772 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -162,6 +162,10 @@  ifeq ($(CONFIG_GUNZIP),y)
 LDLIBS += z
 endif
 
+ifeq ($(CONFIG_ZSTD),y)
+LDLIBS += zstd
+endif
+
 ifeq ($(CONFIG_RDIFFHANDLER),y)
 LDLIBS += rsync
 endif
diff --git a/core/cpio_utils.c b/core/cpio_utils.c
index 1b6cb14..5910c69 100644
--- a/core/cpio_utils.c
+++ b/core/cpio_utils.c
@@ -13,6 +13,9 @@ 
 #ifdef CONFIG_GUNZIP
 #include <zlib.h>
 #endif
+#ifdef CONFIG_ZSTD
+#include <zstd.h>
+#endif
 
 #include "generated/autoconf.h"
 #include "cpiohdr.h"
@@ -233,22 +236,29 @@  static int decrypt_step(void *state, void *buffer, size_t size)
 	return 0;
 }
 
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
+typedef int (*DecompressStep)(void *state, void *buffer, size_t size);
 
-struct GunzipState
-{
+struct DecompressState {
 	PipelineStep upstream_step;
 	void *upstream_state;
+	void *impl_state;
+	uint8_t input[BUFF_SIZE];
+	bool eof;
+};
+#endif
 
+#ifdef CONFIG_GUNZIP
+
+struct GunzipState {
 	z_stream strm;
 	bool initialized;
-	uint8_t input[BUFF_SIZE];
-	bool eof;
 };
 
 static int gunzip_step(void *state, void *buffer, size_t size)
 {
-	struct GunzipState *s = (struct GunzipState *)state;
+	struct DecompressState *ds = (struct DecompressState *)state;
+	struct GunzipState *s = (struct GunzipState *)ds->impl_state;
 	int ret;
 	int outlen = 0;
 
@@ -256,23 +266,23 @@  static int gunzip_step(void *state, void *buffer, size_t size)
 	s->strm.avail_out = size;
 	while (outlen == 0) {
 		if (s->strm.avail_in == 0) {
-			ret = s->upstream_step(s->upstream_state, s->input, sizeof s->input);
+			ret = ds->upstream_step(ds->upstream_state, ds->input, sizeof ds->input);
 			if (ret < 0) {
 				return ret;
 			} else if (ret == 0) {
-				s->eof = true;
+				ds->eof = true;
 			}
 			s->strm.avail_in = ret;
-			s->strm.next_in = s->input;
+			s->strm.next_in = ds->input;
 		}
-		if (s->eof) {
+		if (ds->eof) {
 			break;
 		}
 
 		ret = inflate(&s->strm, Z_NO_FLUSH);
 		outlen = size - s->strm.avail_out;
 		if (ret == Z_STREAM_END) {
-			s->eof = true;
+			ds->eof = true;
 			break;
 		}
 		if (ret != Z_OK && ret != Z_BUF_ERROR) {
@@ -285,6 +295,52 @@  static int gunzip_step(void *state, void *buffer, size_t size)
 
 #endif
 
+#ifdef CONFIG_ZSTD
+
+struct ZstdState {
+	ZSTD_DStream* dctx;
+	ZSTD_inBuffer input_view;
+};
+
+static int zstd_step(void* state, void* buffer, size_t size)
+{
+	struct DecompressState *ds = (struct DecompressState *)state;
+	struct ZstdState *s = (struct ZstdState *)ds->impl_state;
+	size_t decompress_ret;
+	int ret;
+	ZSTD_outBuffer output = { buffer, size, 0 };
+
+	do {
+		if (s->input_view.pos == s->input_view.size) {
+			ret = ds->upstream_step(ds->upstream_state, ds->input, sizeof ds->input);
+			if (ret < 0) {
+				return ret;
+			} else if (ret == 0) {
+				ds->eof = true;
+			}
+			s->input_view.size = ret;
+			s->input_view.pos = 0;
+		}
+
+		do {
+			decompress_ret = ZSTD_decompressStream(s->dctx, &output, &s->input_view);
+
+			if (ZSTD_isError(decompress_ret)) {
+				ERROR("ZSTD_decompressStream failed (returned %zu)", decompress_ret);
+				return -1;
+			}
+
+			if (output.pos == output.size) {
+				break;
+			}
+		} while (s->input_view.pos < s->input_view.size);
+	} while (output.pos == 0 && !ds->eof);
+
+	return output.pos;
+}
+
+#endif
+
 int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsigned long long seek,
 	int skip_file, int __attribute__ ((__unused__)) compressed,
 	uint32_t *checksum, unsigned char *hash, int encrypted, writeimage callback)
@@ -314,17 +370,29 @@  int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		.outlen = 0, .eof = false
 	};
 
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
+	struct DecompressState decompress_state = {
+		.upstream_step = NULL, .upstream_state = NULL,
+		.impl_state = NULL
+	};
+
+	DecompressStep decompress_step = NULL;
 #ifdef CONFIG_GUNZIP
 	struct GunzipState gunzip_state = {
-		.upstream_step = NULL, .upstream_state = NULL,
 		.strm = {
 			.zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL,
 			.avail_in = 0, .next_in = Z_NULL,
 			.avail_out = 0, .next_out = Z_NULL
 		},
 		.initialized = false,
-		.eof = false
 	};
+#endif
+#ifdef CONFIG_ZSTD
+	struct ZstdState zstd_state = {
+		.dctx = NULL,
+		.input_view = { NULL, 0, 0 },
+	};
+#endif
 #endif
 
 	PipelineStep step = NULL;
@@ -357,21 +425,38 @@  int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 
 	if (compressed) {
 #ifdef CONFIG_GUNZIP
-		/*
-		 * 16 + MAX_WBITS means that Zlib should expect and decode a
-		 * gzip header.
-		 */
-		if (inflateInit2(&gunzip_state.strm, 16 + MAX_WBITS) != Z_OK) {
-			ERROR("inflateInit2 failed");
-			ret = -EFAULT;
+		if (compressed == COMPRESSED_ZLIB || compressed == COMPRESSED_TRUE) {
+			/*
+			 * 16 + MAX_WBITS means that Zlib should expect and decode a
+			 * gzip header.
+			 */
+			if (inflateInit2(&gunzip_state.strm, 16 + MAX_WBITS) != Z_OK) {
+				ERROR("inflateInit2 failed");
+				ret = -EFAULT;
+				goto copyfile_exit;
+			}
+			gunzip_state.initialized = true;
+			decompress_step = &gunzip_step;
+			decompress_state.impl_state = &gunzip_state;
+		} else
+#endif
+#ifdef CONFIG_ZSTD
+		if (compressed == COMPRESSED_ZSTD) {
+			if ((zstd_state.dctx = ZSTD_createDStream()) == NULL) {
+				ERROR("ZSTD_createDStream failed");
+				ret = -EFAULT;
+				goto copyfile_exit;
+			}
+			zstd_state.input_view.src = decompress_state.input;
+			decompress_step = &zstd_step;
+			decompress_state.impl_state = &zstd_state;
+		} else
+#endif
+		{
+			TRACE("Requested decompression method (%d) is not configured!", compressed);
+			ret = -EINVAL;
 			goto copyfile_exit;
 		}
-		gunzip_state.initialized = true;
-#else
-		TRACE("Request decompressing, but CONFIG_GUNZIP not set !");
-		ret = -EINVAL;
-		goto copyfile_exit;
-#endif
 	}
 
 	if (seek) {
@@ -384,19 +469,19 @@  int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 		}
 	}
 
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
 	if (compressed) {
 		if (encrypted) {
 			decrypt_state.upstream_step = &input_step;
 			decrypt_state.upstream_state = &input_state;
-			gunzip_state.upstream_step = &decrypt_step;
-			gunzip_state.upstream_state = &decrypt_state;
+			decompress_state.upstream_step = &decrypt_step;
+			decompress_state.upstream_state = &decrypt_state;
 		} else {
-			gunzip_state.upstream_step = &input_step;
-			gunzip_state.upstream_state = &input_state;
+			decompress_state.upstream_step = &input_step;
+			decompress_state.upstream_state = &input_state;
 		}
-		step = &gunzip_step;
-		state = &gunzip_state;
+		step = decompress_step;
+		state = &decompress_state;
 	} else {
 #endif
 		if (encrypted) {
@@ -408,7 +493,7 @@  int copyfile(int fdin, void *out, unsigned int nbytes, unsigned long *offs, unsi
 			step = &input_step;
 			state = &input_state;
 		}
-#ifdef CONFIG_GUNZIP
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
 	}
 #endif
 
@@ -487,6 +572,11 @@  copyfile_exit:
 		inflateEnd(&gunzip_state.strm);
 	}
 #endif
+#ifdef CONFIG_ZSTD
+	if (zstd_state.dctx != NULL) {
+		ZSTD_freeDStream(zstd_state.dctx);
+	}
+#endif
 
 	return ret;
 }
diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 5a66db9..0c865f2 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -34,6 +34,12 @@  extern const char EMBEDDED_LUA_SRC_END[];
 	lua_settable(L, -3);			\
 } while (0)
 
+#define LUA_PUSH_IMG_STRING_VALUE(img, attr, value)  do { \
+	lua_pushstring(L, attr);		\
+	lua_pushstring(L, value);		\
+	lua_settable(L, -3);			\
+} while (0)
+
 #define LUA_PUSH_IMG_BOOL(img, attr, field)  do { \
 	lua_pushstring(L, attr);		\
 	lua_pushboolean(L, img->field);		\
@@ -241,6 +247,16 @@  static void lua_string_to_img(struct img_type *img, const char *key,
 	const char offset[] = "offset";
 	char seek_str[MAX_SEEK_STRING_SIZE];
 
+	if (!strcmp(key, "compressed")) {
+		if (!strcmp(value, "zlib")) {
+			img->compressed = COMPRESSED_ZLIB;
+		} else if (!strcmp(value, "zstd")) {
+			img->compressed = COMPRESSED_ZSTD;
+		} else {
+			ERROR("compressed argument: '%s' invalid, defaulting to 'false'", value);
+			img->compressed = false;
+		}
+	}
 	if (!strcmp(key, "name")) {
 		strncpy(img->id.name, value,
 			sizeof(img->id.name));
@@ -317,7 +333,6 @@  static void lua_number_to_img(struct img_type *img, const char *key,
 		img->size = (long long)val;
 	if (!strcmp(key, "checksum"))
 		img->checksum = (unsigned int)val;
-
 }
 
 #ifdef CONFIG_HANDLER_IN_LUA
@@ -461,7 +476,6 @@  static void update_table(lua_State* L, struct img_type *img)
 		LUA_PUSH_IMG_STRING(img, "data", type_data);
 		LUA_PUSH_IMG_STRING(img, "filesystem", filesystem);
 
-		LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
 		LUA_PUSH_IMG_BOOL(img, "installed_directly", install_directly);
 		LUA_PUSH_IMG_BOOL(img, "install_if_different", id.install_if_different);
 		LUA_PUSH_IMG_BOOL(img, "install_if_higher", id.install_if_higher);
@@ -473,6 +487,18 @@  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);
 
+		switch (img->compressed) {
+			case COMPRESSED_ZLIB:
+				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zlib");
+				break;
+			case COMPRESSED_ZSTD:
+				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zstd");
+				break;
+			default:
+				LUA_PUSH_IMG_BOOL(img, "compressed", compressed);
+				break;
+		}
+
 		lua_pushstring(L, "properties");
 		lua_newtable (L);
 		LIST_FOREACH(property, &img->properties, next) {
diff --git a/doc/source/roadmap.rst b/doc/source/roadmap.rst
index bf0a25e..bb9d546 100644
--- a/doc/source/roadmap.rst
+++ b/doc/source/roadmap.rst
@@ -33,10 +33,11 @@  and not only UBI volumes, and add further features as restoring configuration da
 Support for further compressors
 ===============================
 
-SWUpdate supports image compressed with zlib. This is a compromise between compression rate
-and speed to decompress the single artifact. To reduce bandwidth or for big images, a stronger
-compressor could help. Adding a new compressor must be careful done because it changes the
-core of handling an image.
+SWUpdate supports image compressed with following formats: zlib, zstd. This is
+a compromise between compression rate and speed to decompress the single artifact.
+To reduce bandwidth or for big images, a stronger compressor could help.
+Adding a new compressor must be careful done because it changes the core of
+handling an image.
 
 System Update
 =============
diff --git a/doc/source/sw-description.rst b/doc/source/sw-description.rst
index 70efa30..e1d1709 100644
--- a/doc/source/sw-description.rst
+++ b/doc/source/sw-description.rst
@@ -1242,9 +1242,14 @@  There are 4 main sections inside sw-description:
    |             |          | scripts    | regitsters itself.                    |
    |             |          |            | Example: "ubivol", "raw", "rawfile",  |
    +-------------+----------+------------+---------------------------------------+
-   | compressed  | bool     | images     | flag to indicate that "filename" is   |
-   |             |          | files      | zlib-compressed and must be           |
-   |             |          |            | decompressed before being installed   |
+   | compressed  | string   | images     | string to indicate the "filename" is  |
+   |             | or bool  | files      | compressed and must be decompressed   |
+   |             |          |            | before being installed. the value     |
+   |             |          |            | denotes the compression type.         |
+   |             |          |            | currently supported values are "zlib" |
+   |             |          |            | and "zstd" . if the bool form is used |
+   |             |          |            | , then the compression type is        |
+   |             |          |            | assumed to be "zlib".                 |
    +-------------+----------+------------+---------------------------------------+
    | installed-\ | bool     | images     | flag to indicate that image is        |
    | directly    |          |            | streamed into the target without any  |
diff --git a/doc/source/swupdate.rst b/doc/source/swupdate.rst
index 8cf911b..6c7a98e 100644
--- a/doc/source/swupdate.rst
+++ b/doc/source/swupdate.rst
@@ -56,7 +56,7 @@  General Overview
   is saved and restored after repartitioning with all data
   it contains,  to maintain user's data.
 
-- support for compressed images, using the zlib library.
+- support for compressed images, using the zlib and zstd library.
   tarball (tgz file) are supported.
 
 - support for partitioned USB-pen or unpartitioned (mainly
diff --git a/include/swupdate.h b/include/swupdate.h
index b28504c..8cd1574 100644
--- a/include/swupdate.h
+++ b/include/swupdate.h
@@ -41,6 +41,13 @@  enum {
 	INSTALL_FROM_STREAM
 };
 
+enum {
+  COMPRESSED_FALSE,
+  COMPRESSED_TRUE,
+  COMPRESSED_ZLIB,
+  COMPRESSED_ZSTD,
+};
+
 struct sw_version {
 	char name[SWUPDATE_GENERAL_STRING_SIZE];
 	char version[SWUPDATE_GENERAL_STRING_SIZE];
diff --git a/parser/parse_external.c b/parser/parse_external.c
index 39b4300..ef6c74c 100644
--- a/parser/parse_external.c
+++ b/parser/parse_external.c
@@ -83,8 +83,19 @@  static void sw_append_stream(struct img_type *img, const char *key,
 		ascii_to_hash(img->sha256, value);
 	if (!strcmp(key, "encrypted"))
 		img->is_encrypted = 1;
-	if (!strcmp(key, "compressed"))
-		img->compressed = 1;
+	if (!strcmp(key, "compressed")) {
+		if (value != NULL) {
+			if (!strcmp(value, "zlib")) {
+				img->compressed = COMPRESSED_ZLIB;
+			} else if (!strcmp(value, "zstd")) {
+				img->compressed = COMPRESSED_ZSTD;
+			} else {
+				img->compressed = COMPRESSED_TRUE;
+			}
+		} else {
+			img->compressed = COMPRESSED_TRUE;
+		}
+	}
 	if (!strcmp(key, "installed-directly"))
 		img->install_directly = 1;
 	if (!strcmp(key, "install-if-different"))
@@ -113,7 +124,7 @@  int parse_external(struct swupdate_cfg *software, const char *filename)
 	if (ret) {
 		LUAstackDump(L);
 		ERROR("ERROR preparing Parser in Lua %d", ret);
-		
+
 		return 1;
 	}
 
@@ -153,7 +164,7 @@  int parse_external(struct swupdate_cfg *software, const char *filename)
 
 		if (lua_type(L, -1) == LUA_TTABLE) {
 			lua_pushnil(L);
-			image = (struct img_type *)calloc(1, sizeof(struct img_type));	
+			image = (struct img_type *)calloc(1, sizeof(struct img_type));
 			if (!image) {
 				ERROR( "No memory: malloc failed");
 				return -ENOMEM;
diff --git a/parser/parser.c b/parser/parser.c
index 3cb3c97..67a56f7 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -139,7 +139,7 @@  static bool get_common_fields(parsertype p, void *cfg, struct swupdate_cfg *swcf
 		ERROR("Missing version in configuration file");
 		return false;
 	}
-	
+
 	GET_FIELD_STRING(p, setting, NULL, swcfg->version);
 	TRACE("Version %s", swcfg->version);
 
@@ -263,6 +263,7 @@  static int run_embscript(parsertype p, void *elem, struct img_type *img,
 static int parse_common_attributes(parsertype p, void *elem, struct img_type *image)
 {
 	char seek_str[MAX_SEEK_STRING_SIZE];
+	const char* compressed;
 
 	/*
 	 * GET_FIELD_STRING does not touch the passed string if it is not
@@ -290,7 +291,18 @@  static int parse_common_attributes(parsertype p, void *elem, struct img_type *im
 		return -1;
 	}
 
-	get_field(p, elem, "compressed", &image->compressed);
+	if ((compressed = get_field_string(p, elem, "compressed")) != NULL) {
+		if (!strcmp(compressed, "zlib")) {
+			image->compressed = COMPRESSED_ZLIB;
+		} else if (!strcmp(compressed, "zstd")) {
+			image->compressed = COMPRESSED_ZSTD;
+		} else {
+			ERROR("compressed argument: '%s' unknown", compressed);
+			return -1;
+		}
+	} else {
+		get_field(p, elem, "compressed", &image->compressed);
+	}
 	get_field(p, elem, "installed-directly", &image->install_directly);
 	get_field(p, elem, "preserve-attributes", &image->preserve_attributes);
 	get_field(p, elem, "install-if-different", &image->id.install_if_different);