diff mbox series

Add support for XZ decompression

Message ID 20250507182059.1894915-1-joakim.tjernlund@infinera.com
State Changes Requested
Headers show
Series Add support for XZ decompression | expand

Commit Message

Joakim Tjernlund May 7, 2025, 6:19 p.m. UTC
XZ compresses better than GZIP

Signed-off-by: Joakim Tjernlund <joakim.tjernlund@infinera.com>
---
 Kconfig                 |  8 +++++
 Makefile.deps           |  4 +++
 Makefile.flags          |  4 +++
 core/cpio_utils.c       | 80 +++++++++++++++++++++++++++++++++++++++--
 corelib/lua_interface.c |  5 +++
 include/util.h          |  1 +
 parser/parse_external.c |  2 ++
 parser/parser.c         |  2 ++
 8 files changed, 103 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/Kconfig b/Kconfig
index 79237bf5..fd74d013 100644
--- a/Kconfig
+++ b/Kconfig
@@ -81,6 +81,10 @@  config HAVE_ZLIB
 	bool
 	option env="HAVE_ZLIB"
 
+config HAVE_XZ
+	bool
+	option env="HAVE_XZ"
+
 config HAVE_ZSTD
 	bool
 	option env="HAVE_ZSTD"
@@ -529,6 +533,10 @@  config GUNZIP
 	depends on HAVE_ZLIB
 	default y
 
+config XZ
+	bool "XZ compression support"
+	depends on HAVE_XZ
+
 config ZSTD
 	bool "Zstd compression support"
 	depends on HAVE_ZSTD
diff --git a/Makefile.deps b/Makefile.deps
index c62f1af0..02e420f0 100644
--- a/Makefile.deps
+++ b/Makefile.deps
@@ -62,6 +62,10 @@  ifeq ($(HAVE_ZLIB),)
 export HAVE_ZLIB = y
 endif
 
+ifeq ($(HAVE_XZ),)
+export HAVE_XZ = y
+endif
+
 ifeq ($(HAVE_ZSTD),)
 export HAVE_ZSTD = y
 endif
diff --git a/Makefile.flags b/Makefile.flags
index 4ce1bacb..0b0ebd6d 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -175,6 +175,10 @@  ifeq ($(CONFIG_GUNZIP),y)
 LDLIBS += z
 endif
 
+ifeq ($(CONFIG_XZ),y)
+LDLIBS += lzma
+endif
+
 ifeq ($(CONFIG_ZSTD),y)
 LDLIBS += zstd
 endif
diff --git a/core/cpio_utils.c b/core/cpio_utils.c
index 1ed5c707..ca8336fd 100644
--- a/core/cpio_utils.c
+++ b/core/cpio_utils.c
@@ -16,6 +16,9 @@ 
 #ifdef CONFIG_GUNZIP
 #include <zlib.h>
 #endif
+#ifdef CONFIG_XZ
+#include <lzma.h>
+#endif
 #ifdef CONFIG_ZSTD
 #include <zstd.h>
 #endif
@@ -328,7 +331,7 @@  static int decrypt_step(void *state, void *buffer, size_t size)
 	return 0;
 }
 
-#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD) || defined(CONFIG_XZ)
 typedef int (*DecompressStep)(void *state, void *buffer, size_t size);
 
 struct DecompressState {
@@ -387,6 +390,53 @@  static int gunzip_step(void *state, void *buffer, size_t size)
 
 #endif
 
+#ifdef CONFIG_XZ
+struct XzState {
+	lzma_stream strm;
+	bool initialized;
+};
+static int xz_step(void* state, void* buffer, size_t size)
+{
+	struct DecompressState *ds = (struct DecompressState *)state;
+	struct XzState *s = (struct XzState *)ds->impl_state;
+	lzma_ret ret;
+	int outlen = 0;
+	lzma_action action = LZMA_RUN;
+
+	s->strm.next_out = buffer;
+	s->strm.avail_out = size;
+
+	while (outlen == 0) {
+		if (s->strm.avail_in == 0) {
+			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->strm.avail_in = ret;
+			s->strm.next_in = ds->input;
+		}
+		if (ds->eof) {
+			action = LZMA_FINISH;
+		}
+
+		ret = lzma_code(&s->strm, action);
+		outlen = size - s->strm.avail_out;
+		if (ret == LZMA_STREAM_END) {
+			ds->eof = true;
+			break;
+		}
+		if (ret != LZMA_OK && ret != LZMA_BUF_ERROR) {
+			ERROR("xz failed (returned %d)", ret);
+			return -1;
+		}
+	}
+	return outlen;
+}
+
+#endif
+
 #ifdef CONFIG_ZSTD
 
 struct ZstdState {
@@ -492,7 +542,7 @@  int copyfile(struct swupdate_copy *args)
 		.outlen = 0, .eof = false
 	};
 
-#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD) || defined(CONFIG_XZ)
 	struct DecompressState decompress_state = {
 		.upstream_step = NULL, .upstream_state = NULL,
 		.impl_state = NULL
@@ -509,6 +559,12 @@  int copyfile(struct swupdate_copy *args)
 		.initialized = false,
 	};
 #endif
+#ifdef CONFIG_XZ
+	struct XzState xz_state = {
+		.strm = LZMA_STREAM_INIT,
+		.initialized = false,
+	};
+#endif
 #ifdef CONFIG_ZSTD
 	struct ZstdState zstd_state = {
 		.dctx = NULL,
@@ -581,6 +637,19 @@  int copyfile(struct swupdate_copy *args)
 			decompress_state.impl_state = &gunzip_state;
 		} else
 #endif
+#ifdef CONFIG_XZ
+		if (args->compressed == COMPRESSED_XZ) {
+			if (lzma_stream_decoder(&xz_state.strm, UINT32_MAX,
+						LZMA_CONCATENATED) != LZMA_OK) {
+				ERROR("(lzma_stream_decoder failed");
+				ret = -EFAULT;
+				goto copyfile_exit;
+			}
+			xz_state.initialized = true;
+			decompress_step = &xz_step;
+			decompress_state.impl_state = &xz_state;
+		} else
+#endif
 #ifdef CONFIG_ZSTD
 		if (args->compressed == COMPRESSED_ZSTD) {
 			if ((zstd_state.dctx = ZSTD_createDStream()) == NULL) {
@@ -626,7 +695,7 @@  int copyfile(struct swupdate_copy *args)
 		state = &decrypt_state;
 	}
 
-#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD)
+#if defined(CONFIG_GUNZIP) || defined(CONFIG_ZSTD) || defined(CONFIG_XZ)
 	if (args->compressed) {
 		decompress_state.upstream_step = step;
 		decompress_state.upstream_state = state;
@@ -695,6 +764,11 @@  copyfile_exit:
 		inflateEnd(&gunzip_state.strm);
 	}
 #endif
+#ifdef CONFIG_XZ
+	if (xz_state.initialized) {
+		lzma_end(&xz_state.strm);
+	}
+#endif
 #ifdef CONFIG_ZSTD
 	if (zstd_state.dctx != NULL) {
 		ZSTD_freeDStream(zstd_state.dctx);
diff --git a/corelib/lua_interface.c b/corelib/lua_interface.c
index 77e4aef5..2aaacf98 100644
--- a/corelib/lua_interface.c
+++ b/corelib/lua_interface.c
@@ -282,6 +282,8 @@  static void lua_string_to_img(struct img_type *img, const char *key,
 	if (!strcmp(key, "compressed")) {
 		if (!strcmp(value, "zlib")) {
 			img->compressed = COMPRESSED_ZLIB;
+		} else if (!strcmp(value, "xz")) {
+			img->compressed = COMPRESSED_XZ;
 		} else if (!strcmp(value, "zstd")) {
 			img->compressed = COMPRESSED_ZSTD;
 		} else {
@@ -529,6 +531,9 @@  static void update_table(lua_State* L, struct img_type *img)
 			case COMPRESSED_ZLIB:
 				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zlib");
 				break;
+			case COMPRESSED_XZ:
+				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "xz");
+				break;
 			case COMPRESSED_ZSTD:
 				LUA_PUSH_IMG_STRING_VALUE(img, "compressed", "zstd");
 				break;
diff --git a/include/util.h b/include/util.h
index 068f0195..a916ea57 100644
--- a/include/util.h
+++ b/include/util.h
@@ -60,6 +60,7 @@  enum compression_type {
   COMPRESSED_FALSE,
   COMPRESSED_TRUE,
   COMPRESSED_ZLIB,
+  COMPRESSED_XZ,
   COMPRESSED_ZSTD,
 };
 
diff --git a/parser/parse_external.c b/parser/parse_external.c
index 20ac0771..d67bcca0 100644
--- a/parser/parse_external.c
+++ b/parser/parse_external.c
@@ -87,6 +87,8 @@  static void sw_append_stream(struct img_type *img, const char *key,
 		if (value != NULL) {
 			if (!strcmp(value, "zlib")) {
 				img->compressed = COMPRESSED_ZLIB;
+			} else if (!strcmp(value, "xz")) {
+				img->compressed = COMPRESSED_XZ;
 			} else if (!strcmp(value, "zstd")) {
 				img->compressed = COMPRESSED_ZSTD;
 			} else {
diff --git a/parser/parser.c b/parser/parser.c
index 99c3396e..6ff8695f 100644
--- a/parser/parser.c
+++ b/parser/parser.c
@@ -443,6 +443,8 @@  static int parse_common_attributes(parsertype p, void *elem, struct img_type *im
 	if ((compressed = get_field_string(p, elem, "compressed")) != NULL) {
 		if (!strcmp(compressed, "zlib")) {
 			image->compressed = COMPRESSED_ZLIB;
+		} else if (!strcmp(compressed, "xz")) {
+			image->compressed = COMPRESSED_XZ;
 		} else if (!strcmp(compressed, "zstd")) {
 			image->compressed = COMPRESSED_ZSTD;
 		} else {