162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * uncompress.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright 1999 Linus Torvalds
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * cramfs interfaces to the uncompression library. There's really just
862306a36Sopenharmony_ci * three entrypoints:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *  - cramfs_uncompress_init() - called to initialize the thing.
1162306a36Sopenharmony_ci *  - cramfs_uncompress_exit() - tell me when you're done
1262306a36Sopenharmony_ci *  - cramfs_uncompress_block() - uncompress a block.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
1562306a36Sopenharmony_ci * only have one stream, and we'll initialize it only once even if it
1662306a36Sopenharmony_ci * then is used by multiple filesystems.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/errno.h>
2362306a36Sopenharmony_ci#include <linux/vmalloc.h>
2462306a36Sopenharmony_ci#include <linux/zlib.h>
2562306a36Sopenharmony_ci#include "internal.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic z_stream stream;
2862306a36Sopenharmony_cistatic int initialized;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Returns length of decompressed data. */
3162306a36Sopenharmony_ciint cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	int err;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	stream.next_in = src;
3662306a36Sopenharmony_ci	stream.avail_in = srclen;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	stream.next_out = dst;
3962306a36Sopenharmony_ci	stream.avail_out = dstlen;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	err = zlib_inflateReset(&stream);
4262306a36Sopenharmony_ci	if (err != Z_OK) {
4362306a36Sopenharmony_ci		pr_err("zlib_inflateReset error %d\n", err);
4462306a36Sopenharmony_ci		zlib_inflateEnd(&stream);
4562306a36Sopenharmony_ci		zlib_inflateInit(&stream);
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	err = zlib_inflate(&stream, Z_FINISH);
4962306a36Sopenharmony_ci	if (err != Z_STREAM_END)
5062306a36Sopenharmony_ci		goto err;
5162306a36Sopenharmony_ci	return stream.total_out;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cierr:
5462306a36Sopenharmony_ci	pr_err("Error %d while decompressing!\n", err);
5562306a36Sopenharmony_ci	pr_err("%p(%d)->%p(%d)\n", src, srclen, dst, dstlen);
5662306a36Sopenharmony_ci	return -EIO;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciint cramfs_uncompress_init(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (!initialized++) {
6262306a36Sopenharmony_ci		stream.workspace = vmalloc(zlib_inflate_workspacesize());
6362306a36Sopenharmony_ci		if (!stream.workspace) {
6462306a36Sopenharmony_ci			initialized = 0;
6562306a36Sopenharmony_ci			return -ENOMEM;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci		stream.next_in = NULL;
6862306a36Sopenharmony_ci		stream.avail_in = 0;
6962306a36Sopenharmony_ci		zlib_inflateInit(&stream);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_civoid cramfs_uncompress_exit(void)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	if (!--initialized) {
7762306a36Sopenharmony_ci		zlib_inflateEnd(&stream);
7862306a36Sopenharmony_ci		vfree(stream.workspace);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
81