| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (C) 2020 Bootlin | |
| * | |
| * Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com> | |
| */ | |
| #include <errno.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #if IS_ENABLED(CONFIG_LZO) | |
| #include <linux/lzo.h> | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZLIB) | |
| #include <u-boot/zlib.h> | |
| #endif | |
| #if IS_ENABLED(CONFIG_LZ4) | |
| #include <u-boot/lz4.h> | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZSTD) | |
| #include <linux/zstd.h> | |
| #endif | |
| #include "sqfs_decompressor.h" | |
| #include "sqfs_utils.h" | |
| int sqfs_decompressor_init(struct squashfs_ctxt *ctxt) | |
| { | |
| u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); | |
| switch (comp_type) { | |
| #if IS_ENABLED(CONFIG_LZO) | |
| case SQFS_COMP_LZO: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZLIB) | |
| case SQFS_COMP_ZLIB: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_LZ4) | |
| case SQFS_COMP_LZ4: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZSTD) | |
| case SQFS_COMP_ZSTD: | |
| ctxt->zstd_workspace = malloc(zstd_dctx_workspace_bound()); | |
| if (!ctxt->zstd_workspace) | |
| return -ENOMEM; | |
| break; | |
| #endif | |
| default: | |
| printf("Error: unknown compression type.\n"); | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| void sqfs_decompressor_cleanup(struct squashfs_ctxt *ctxt) | |
| { | |
| u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); | |
| switch (comp_type) { | |
| #if IS_ENABLED(CONFIG_LZO) | |
| case SQFS_COMP_LZO: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZLIB) | |
| case SQFS_COMP_ZLIB: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_LZ4) | |
| case SQFS_COMP_LZ4: | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZSTD) | |
| case SQFS_COMP_ZSTD: | |
| free(ctxt->zstd_workspace); | |
| break; | |
| #endif | |
| } | |
| } | |
| #if IS_ENABLED(CONFIG_ZLIB) | |
| static void zlib_decompression_status(int ret) | |
| { | |
| switch (ret) { | |
| case Z_BUF_ERROR: | |
| printf("Error: 'dest' buffer is not large enough.\n"); | |
| break; | |
| case Z_DATA_ERROR: | |
| printf("Error: corrupted compressed data.\n"); | |
| break; | |
| case Z_MEM_ERROR: | |
| printf("Error: insufficient memory.\n"); | |
| break; | |
| } | |
| } | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZSTD) | |
| static int sqfs_zstd_decompress(struct squashfs_ctxt *ctxt, void *dest, | |
| unsigned long dest_len, void *source, u32 src_len) | |
| { | |
| ZSTD_DCtx *ctx; | |
| size_t wsize; | |
| int ret; | |
| wsize = zstd_dctx_workspace_bound(); | |
| ctx = zstd_init_dctx(ctxt->zstd_workspace, wsize); | |
| if (!ctx) | |
| return -EINVAL; | |
| ret = zstd_decompress_dctx(ctx, dest, dest_len, source, src_len); | |
| return zstd_is_error(ret); | |
| } | |
| #endif /* CONFIG_ZSTD */ | |
| int sqfs_decompress(struct squashfs_ctxt *ctxt, void *dest, | |
| unsigned long *dest_len, void *source, u32 src_len) | |
| { | |
| u16 comp_type = get_unaligned_le16(&ctxt->sblk->compression); | |
| int ret = 0; | |
| switch (comp_type) { | |
| #if IS_ENABLED(CONFIG_LZO) | |
| case SQFS_COMP_LZO: { | |
| size_t lzo_dest_len = *dest_len; | |
| ret = lzo1x_decompress_safe(source, src_len, dest, &lzo_dest_len); | |
| if (ret) { | |
| printf("LZO decompression failed. Error code: %d\n", ret); | |
| return -EINVAL; | |
| } | |
| break; | |
| } | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZLIB) | |
| case SQFS_COMP_ZLIB: | |
| ret = uncompress(dest, dest_len, source, src_len); | |
| if (ret) { | |
| zlib_decompression_status(ret); | |
| return -EINVAL; | |
| } | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_LZ4) | |
| case SQFS_COMP_LZ4: | |
| ret = LZ4_decompress_safe(source, dest, src_len, *dest_len); | |
| if (ret < 0) { | |
| printf("LZ4 decompression failed.\n"); | |
| return -EINVAL; | |
| } | |
| ret = 0; | |
| break; | |
| #endif | |
| #if IS_ENABLED(CONFIG_ZSTD) | |
| case SQFS_COMP_ZSTD: | |
| ret = sqfs_zstd_decompress(ctxt, dest, *dest_len, source, src_len); | |
| if (ret) { | |
| printf("ZSTD Error code: %d\n", zstd_get_error_code(ret)); | |
| return -EINVAL; | |
| } | |
| break; | |
| #endif | |
| default: | |
| printf("Error: unknown compression type.\n"); | |
| return -EINVAL; | |
| } | |
| return ret; | |
| } |